Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Gimp/libgimpwidgets/   (Gimp Bildeditor Version 3.2.0©)  Datei vom 14.2.2026 mit Größe 18 kB image not shown  

Quelle  gimpzoommodel.c   Sprache: C

 

/* LIBGIMP - The GIMP Library
 * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
 *
 * gimpzoommodel.c
 * Copyright (C) 2005  David Odin <dindinx@gimp.org>
 *
 * This library is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <https://www.gnu.org/licenses/>.
 */


#include "config.h"

#include <gtk/gtk.h>

#include "gimpwidgetstypes.h"

#include "libgimpbase/gimpbase.h"
#include "libgimpmath/gimpmath.h"

#include "gimphelpui.h"
#include "gimpwidgetsmarshal.h"
#include "gimpzoommodel.h"


/**
 * SECTION: gimpzoommodel
 * @title: GimpZoomModel
 * @short_description: A model for zoom values.
 *
 * A model for zoom values.
 **/



#define ZOOM_MIN  (1.0 / 256.0)
#define ZOOM_MAX  (256.0)

enum
{
  ZOOMED,
  LAST_SIGNAL
};

enum
{
  PROP_0,
  PROP_VALUE,
  PROP_MINIMUM,
  PROP_MAXIMUM,
  PROP_FRACTION,
  PROP_PERCENTAGE,
  N_PROPS
};


struct _GimpZoomModel
{
  GObject  parent_instance;

  gdouble  value;
  gdouble  minimum;
  gdouble  maximum;
};


static void  gimp_zoom_model_set_property (GObject      *object,
                                           guint         property_id,
                                           const GValue *value,
                                           GParamSpec   *pspec);
static void  gimp_zoom_model_get_property (GObject      *object,
                                           guint         property_id,
                                           GValue       *value,
                                           GParamSpec   *pspec);


static guint zoom_model_signals[LAST_SIGNAL] = { 0, };
static GParamSpec *object_props[N_PROPS] = { NULL, };

G_DEFINE_TYPE (GimpZoomModel, gimp_zoom_model, G_TYPE_OBJECT)

#define parent_class gimp_zoom_model_parent_class


static void
gimp_zoom_model_class_init (GimpZoomModelClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  /**
   * GimpZoomModel::zoomed:
   * @model: the object that received the signal
   * @old_factor: the zoom factor before it changes
   * @new_factor: the zoom factor after it has changed.
   *
   * Emitted when the zoom factor of the zoom model changes.
   */

  zoom_model_signals[ZOOMED] =
      g_signal_new ("zoomed",
                    G_TYPE_FROM_CLASS (klass),
                    G_SIGNAL_RUN_LAST,
                    0,
                    NULL, NULL,
                    _gimp_widgets_marshal_VOID__DOUBLE_DOUBLE,
                    G_TYPE_NONE, 2,
                    G_TYPE_DOUBLE, G_TYPE_DOUBLE);

  object_class->set_property = gimp_zoom_model_set_property;
  object_class->get_property = gimp_zoom_model_get_property;

  /**
   * GimpZoomModel:value:
   *
   * The zoom factor.
   */

  object_props[PROP_VALUE] = g_param_spec_double ("value",
                                                  "Value",
                                                  "Zoom factor",
                                                  ZOOM_MIN, ZOOM_MAX,
                                                  1.0,
                                                  GIMP_PARAM_READWRITE);
  /**
   * GimpZoomModel:minimum:
   *
   * The minimum zoom factor.
   */

  object_props[PROP_MINIMUM] = g_param_spec_double ("minimum",
                                                    "Minimum",
                                                    "Lower limit for the zoom factor",
                                                    ZOOM_MIN, ZOOM_MAX,
                                                    ZOOM_MIN,
                                                    GIMP_PARAM_READWRITE);
  /**
   * GimpZoomModel:maximum:
   *
   * The maximum zoom factor.
   */

  object_props[PROP_MAXIMUM] = g_param_spec_double ("maximum",
                                                    "Maximum",
                                                    "Upper limit for the zoom factor",
                                                    ZOOM_MIN, ZOOM_MAX,
                                                    ZOOM_MAX,
                                                    GIMP_PARAM_READWRITE);

  /**
   * GimpZoomModel:fraction:
   *
   * The zoom factor expressed as a fraction.
   */

  object_props[PROP_FRACTION] = g_param_spec_string ("fraction",
                                                     "Fraction",
                                                     "The zoom factor expressed as a fraction",
                                                     "1:1",
                                                     GIMP_PARAM_READABLE);
  /**
   * GimpZoomModel:percentage:
   *
   * The zoom factor expressed as percentage.
   */

  object_props[PROP_PERCENTAGE] = g_param_spec_string ("percentage",
                                                       "Percentage",
                                                       "The zoom factor expressed as a percentage",
                                                       "100%",
                                                       GIMP_PARAM_READABLE);

  g_object_class_install_properties (object_class, N_PROPS, object_props);
}

static void
gimp_zoom_model_init (GimpZoomModel *model)
{
  model->value   = 1.0;
  model->minimum = ZOOM_MIN;
  model->maximum = ZOOM_MAX;
}

static void
gimp_zoom_model_set_property (GObject      *object,
                              guint         property_id,
                              const GValue *value,
                              GParamSpec   *pspec)
{
  GimpZoomModel *model = GIMP_ZOOM_MODEL (object);
  gdouble        previous_value;

  previous_value = model->value;
  g_object_freeze_notify (object);

  switch (property_id)
    {
    case PROP_VALUE:
      model->value = g_value_get_double (value);

      g_object_notify_by_pspec (object, object_props[PROP_VALUE]);
      g_object_notify_by_pspec (object, object_props[PROP_FRACTION]);
      g_object_notify_by_pspec (object, object_props[PROP_PERCENTAGE]);
      break;

    case PROP_MINIMUM:
      model->minimum = MIN (g_value_get_double (value), model->maximum);
      break;

    case PROP_MAXIMUM:
      model->maximum = MAX (g_value_get_double (value), model->minimum);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }

  if (model->value > model->maximum || model->value < model->minimum)
    {
      model->value = CLAMP (model->value, model->minimum, model->maximum);

      g_object_notify_by_pspec (object, object_props[PROP_VALUE]);
      g_object_notify_by_pspec (object, object_props[PROP_FRACTION]);
      g_object_notify_by_pspec (object, object_props[PROP_PERCENTAGE]);
    }

  g_object_thaw_notify (object);

  if (model->value != previous_value)
    {
      g_signal_emit (object, zoom_model_signals[ZOOMED],
                     0, previous_value, model->value);
    }
}

static void
gimp_zoom_model_get_property (GObject    *object,
                              guint       property_id,
                              GValue     *value,
                              GParamSpec *pspec)
{
  GimpZoomModel *model = GIMP_ZOOM_MODEL (object);
  gchar         *tmp;

  switch (property_id)
    {
    case PROP_VALUE:
      g_value_set_double (value, model->value);
      break;

    case PROP_MINIMUM:
      g_value_set_double (value, model->minimum);
      break;

    case PROP_MAXIMUM:
      g_value_set_double (value, model->maximum);
      break;

    case PROP_FRACTION:
      {
        gint  numerator;
        gint  denominator;

        gimp_zoom_model_get_fraction (GIMP_ZOOM_MODEL (object),
                                      &numerator, &denominator);

        tmp = g_strdup_printf ("%d:%d", numerator, denominator);
        g_value_set_string (value, tmp);
        g_free (tmp);
      }
      break;

    case PROP_PERCENTAGE:
      tmp = g_strdup_printf (model->value >= 0.15 ? "%.0f%%" : "%.2f%%",
                             model->value * 100.0);
      g_value_set_string (value, tmp);
      g_free (tmp);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
gimp_zoom_model_zoom_in (GimpZoomModel *model)
{
  if (model->value < model->maximum)
    gimp_zoom_model_zoom (model, GIMP_ZOOM_IN, 0.0);
}

static void
gimp_zoom_model_zoom_out (GimpZoomModel *model)
{
  if (model->value > model->minimum)
    gimp_zoom_model_zoom (model, GIMP_ZOOM_OUT, 0.0);
}

/**
 * gimp_zoom_model_new:
 *
 * Creates a new #GimpZoomModel.
 *
 * Returns: a new #GimpZoomModel.
 *
 * Since GIMP 2.4
 **/

GimpZoomModel *
gimp_zoom_model_new (void)
{
  return g_object_new (GIMP_TYPE_ZOOM_MODEL, NULL);
}


/**
 * gimp_zoom_model_set_range:
 * @model: a #GimpZoomModel
 * @min: new lower limit for zoom factor
 * @max: new upper limit for zoom factor
 *
 * Sets the allowed range of the @model.
 *
 * Since GIMP 2.4
 **/

void
gimp_zoom_model_set_range (GimpZoomModel *model,
                           gdouble        min,
                           gdouble        max)
{
  g_return_if_fail (GIMP_IS_ZOOM_MODEL (model));
  g_return_if_fail (min < max);
  g_return_if_fail (min >= ZOOM_MIN);
  g_return_if_fail (max <= ZOOM_MAX);

  g_object_set (model,
                "minimum", min,
                "maximum", max,
                NULL);
}

/**
 * gimp_zoom_model_zoom:
 * @model:     a #GimpZoomModel
 * @zoom_type: the #GimpZoomType
 * @scale:     ignored unless @zoom_type == %GIMP_ZOOM_TO
 *
 * Since GIMP 2.4
 **/

void
gimp_zoom_model_zoom (GimpZoomModel *model,
                      GimpZoomType   zoom_type,
                      gdouble        scale)
{
  gdouble delta = 0.0;

  g_return_if_fail (GIMP_IS_ZOOM_MODEL (model));

  if (zoom_type == GIMP_ZOOM_SMOOTH)
    delta = scale;

  if (zoom_type != GIMP_ZOOM_TO)
    scale = gimp_zoom_model_get_factor (model);

  g_object_set (model,
                "value", gimp_zoom_model_zoom_step (zoom_type, scale, delta),
                NULL);
}

/**
 * gimp_zoom_model_get_factor:
 * @model: a #GimpZoomModel
 *
 * Retrieves the current zoom factor of @model.
 *
 * Returns: the current scale factor
 *
 * Since GIMP 2.4
 **/

gdouble
gimp_zoom_model_get_factor (GimpZoomModel *model)
{
  g_return_val_if_fail (GIMP_IS_ZOOM_MODEL (model), 1.0);

  return model->value;
}


/**
 * gimp_zoom_model_get_fraction
 * @model:       a #GimpZoomModel
 * @numerator: (out): return location for numerator
 * @denominator: (out): return location for denominator
 *
 * Retrieves the current zoom factor of @model as a fraction.
 *
 * Since GIMP 2.4
 **/

void
gimp_zoom_model_get_fraction (GimpZoomModel *model,
                              gint          *numerator,
                              gint          *denominator)
{
  gint     p0, p1, p2;
  gint     q0, q1, q2;
  gdouble  zoom_factor;
  gdouble  remainder, next_cf;
  gboolean swapped = FALSE;

  g_return_if_fail (GIMP_IS_ZOOM_MODEL (model));
  g_return_if_fail (numerator != NULL && denominator != NULL);

  zoom_factor = gimp_zoom_model_get_factor (model);

  /* make sure that zooming behaves symmetrically */
  if (zoom_factor < 1.0)
    {
      zoom_factor = 1.0 / zoom_factor;
      swapped = TRUE;
    }

  /* calculate the continued fraction for the desired zoom factor */

  p0 = 1;
  q0 = 0;
  p1 = floor (zoom_factor);
  q1 = 1;

  remainder = zoom_factor - p1;

  while (fabs (remainder) >= 0.0001 &&
         fabs (((gdouble) p1 / q1) - zoom_factor) > 0.0001)
    {
      remainder = 1.0 / remainder;

      next_cf = floor (remainder);

      p2 = next_cf * p1 + p0;
      q2 = next_cf * q1 + q0;

      /* Numerator and Denominator are limited by 256 */
      /* also absurd ratios like 170:171 are excluded */
      if (p2 > 256 || q2 > 256 || (p2 > 1 && q2 > 1 && p2 * q2 > 200))
        break;

      /* remember the last two fractions */
      p0 = p1;
      p1 = p2;
      q0 = q1;
      q1 = q2;

      remainder = remainder - next_cf;
    }

  zoom_factor = (gdouble) p1 / q1;

  /* hard upper and lower bounds for zoom ratio */

  if (zoom_factor > 256.0)
    {
      p1 = 256;
      q1 = 1;
    }
  else if (zoom_factor < 1.0 / 256.0)
    {
      p1 = 1;
      q1 = 256;
    }

  if (swapped)
    {
      *numerator = q1;
      *denominator = p1;
    }
  else
    {
      *numerator = p1;
      *denominator = q1;
    }
}

static GtkWidget *
zoom_button_new (const gchar *icon_name,
                 GtkIconSize  icon_size)
{
  GtkWidget *button;
  GtkWidget *image;

  image = gtk_image_new_from_icon_name (icon_name,
                                        icon_size > 0 ?
                                        icon_size : GTK_ICON_SIZE_BUTTON);

  button = gtk_button_new ();
  gtk_container_add (GTK_CONTAINER (button), image);
  gtk_widget_show (image);

  return button;
}

static void
zoom_in_button_callback (GimpZoomModel *model,
                         gdouble        old,
                         gdouble        new,
                         GtkWidget     *button)
{
  gtk_widget_set_sensitive (button, model->value != model->maximum);
}

static void
zoom_out_button_callback (GimpZoomModel *model,
                          gdouble        old,
                          gdouble        new,
                          GtkWidget     *button)
{
  gtk_widget_set_sensitive (button, model->value != model->minimum);
}

/**
 * gimp_zoom_button_new:
 * @model:     a #GimpZoomModel
 * @zoom_type:
 * @icon_size: use 0 for a button with text labels
 *
 * Returns: (transfer full): a newly created GtkButton
 *
 * Since GIMP 2.4
 **/

GtkWidget *
gimp_zoom_button_new (GimpZoomModel *model,
                      GimpZoomType   zoom_type,
                      GtkIconSize    icon_size)
{
  GtkWidget *button = NULL;

  g_return_val_if_fail (GIMP_IS_ZOOM_MODEL (model), NULL);

  switch (zoom_type)
    {
    case GIMP_ZOOM_IN:
      button = zoom_button_new ("zoom-in", icon_size);
      g_signal_connect_swapped (button, "clicked",
                                G_CALLBACK (gimp_zoom_model_zoom_in),
                                model);
      g_signal_connect_object (model, "zoomed",
                               G_CALLBACK (zoom_in_button_callback),
                               button, 0);
      break;

    case GIMP_ZOOM_OUT:
      button = zoom_button_new ("zoom-out", icon_size);
      g_signal_connect_swapped (button, "clicked",
                                G_CALLBACK (gimp_zoom_model_zoom_out),
                                model);
      g_signal_connect_object (model, "zoomed",
                               G_CALLBACK (zoom_out_button_callback),
                               button, 0);
      break;

    default:
      g_warning ("sorry, no button for this zoom type (%d)", zoom_type);
      break;
    }

  if (button)
    {
      gdouble zoom = gimp_zoom_model_get_factor (model);

      /*  set initial button sensitivity  */
      g_signal_emit (model, zoom_model_signals[ZOOMED], 0, zoom, zoom);

      if (icon_size > 0)
        {
          const gchar *desc;

          if (gimp_enum_get_value (GIMP_TYPE_ZOOM_TYPE, zoom_type,
                                   NULL, NULL, &desc, NULL))
            {
              gimp_help_set_help_data (button, desc, NULL);
            }
        }
    }

  return button;
}

/**
 * gimp_zoom_model_zoom_step:
 * @zoom_type: the zoom type
 * @scale:     ignored unless @zoom_type == %GIMP_ZOOM_TO
 * @delta:     the delta from a smooth zoom event
 *
 * Utility function to calculate a new scale factor.
 *
 * Returns: the new scale factor
 *
 * Since GIMP 2.4
 **/

gdouble
gimp_zoom_model_zoom_step (GimpZoomType zoom_type,
                           gdouble      scale,
                           gdouble      delta)
{
  gint    i, n_presets;
  gdouble new_scale = 1.0;

  /* This table is constructed to have fractions, that approximate
   * sqrt(2)^k. This gives a smooth feeling regardless of the starting
   * zoom level.
   *
   * Zooming in/out always jumps to a zoom step from the list below.
   * However, we try to guarantee a certain size of the step, to
   * avoid silly jumps from 101% to 100%.
   *
   * The factor 1.1 is chosen a bit arbitrary, but feels better
   * than the geometric median of the zoom steps (2^(1/4)).
   */


#define ZOOM_MIN_STEP 1.1

  const gdouble presets[] = {
    1.0 / 256, 1.0 / 180, 1.0 / 128, 1.0 / 90,
    1.0 / 64,  1.0 / 45,  1.0 / 32,  1.0 / 23,
    1.0 / 16,  1.0 / 11,  1.0 / 8,   2.0 / 11,
    1.0 / 4,   1.0 / 3,   1.0 / 2,   2.0 / 3,
      1.0,
               3.0 / 2,      2.0,      3.0,
      4.0,    11.0 / 2,      8.0,     11.0,
      16.0,     23.0,       32.0,     45.0,
      64.0,     90.0,      128.0,    180.0,
      256.0,
  };

  n_presets = G_N_ELEMENTS (presets);

  switch (zoom_type)
    {
    case GIMP_ZOOM_IN:
      scale *= ZOOM_MIN_STEP;

      new_scale = presets[n_presets - 1];
      for (i = n_presets - 1; i >= 0 && presets[i] > scale; i--)
        new_scale = presets[i];

      break;

    case GIMP_ZOOM_OUT:
      scale /= ZOOM_MIN_STEP;

      new_scale = presets[0];
      for (i = 0; i < n_presets && presets[i] < scale; i++)
        new_scale = presets[i];

      break;

    case GIMP_ZOOM_IN_MORE:
      scale = gimp_zoom_model_zoom_step (GIMP_ZOOM_IN, scale, 0.0);
      scale = gimp_zoom_model_zoom_step (GIMP_ZOOM_IN, scale, 0.0);
      scale = gimp_zoom_model_zoom_step (GIMP_ZOOM_IN, scale, 0.0);
      new_scale = scale;
      break;

    case GIMP_ZOOM_OUT_MORE:
      scale = gimp_zoom_model_zoom_step (GIMP_ZOOM_OUT, scale, 0.0);
      scale = gimp_zoom_model_zoom_step (GIMP_ZOOM_OUT, scale, 0.0);
      scale = gimp_zoom_model_zoom_step (GIMP_ZOOM_OUT, scale, 0.0);
      new_scale = scale;
      break;

    case GIMP_ZOOM_IN_MAX:
      new_scale = ZOOM_MAX;
      break;

    case GIMP_ZOOM_OUT_MAX:
      new_scale = ZOOM_MIN;
      break;

    case GIMP_ZOOM_TO:
      new_scale = scale;
      break;

    case GIMP_ZOOM_SMOOTH:
      if (delta > 0.0)
        new_scale = scale * (1.0 + 0.1 * delta);
      else if (delta < 0.0)
        new_scale = scale / (1.0 + 0.1 * -delta);
      else
        new_scale = scale;
      break;

    case GIMP_ZOOM_PINCH:
      if (delta > 0.0)
        new_scale = scale * (1.0 + delta);
      else if (delta < 0.0)
        new_scale = scale / (1.0 + -delta);
      else
        new_scale = scale;
      break;
    }

  return CLAMP (new_scale, ZOOM_MIN, ZOOM_MAX);

#undef ZOOM_MIN_STEP
}

Messung
CHZG
849710093

¤ Dauer der Verarbeitung: 0.4 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.