Exemplo n.º 1
0
GimpCurvesConfig *
gimp_levels_config_to_curves_config (GimpLevelsConfig *config)
{
  GimpCurvesConfig     *curves;
  GimpHistogramChannel  channel;

  g_return_val_if_fail (GIMP_IS_LEVELS_CONFIG (config), NULL);

  curves = g_object_new (GIMP_TYPE_CURVES_CONFIG, NULL);

  for (channel = GIMP_HISTOGRAM_VALUE;
       channel <= GIMP_HISTOGRAM_ALPHA;
       channel++)
    {
      GimpCurve  *curve    = curves->curve[channel];
      const gint  n_points = gimp_curve_get_n_points (curve);
      static const gint n  = 4;
      gint        point    = -1;
      gdouble     gamma    = config->gamma[channel];
      gdouble     delta_in;
      gdouble     delta_out;
      gdouble     x, y;

      /* clear the points set by default */
      gimp_curve_set_point (curve, 0, -1, -1);
      gimp_curve_set_point (curve, n_points - 1, -1, -1);

      delta_in  = config->high_input[channel] - config->low_input[channel];
      delta_out = config->high_output[channel] - config->low_output[channel];

      x = config->low_input[channel];
      y = config->low_output[channel];

      point = CLAMP (n_points * x, point + 1, n_points - 1 - n);
      gimp_curve_set_point (curve, point, x, y);

      if (delta_out != 0 && gamma != 1.0)
        {
          /* The Levels tool performs gamma correction, which is a
           * power law, while the Curves tool uses cubic Bézier
           * curves. Here we try to approximate this gamma correction
           * with a Bézier curve with 5 control points. Two of them
           * must be (low_input, low_output) and (high_input,
           * high_output), so we need to add 3 more control points in
           * the middle.
           */
          gint i;

          if (gamma > 1)
            {
              /* Case no. 1: γ > 1
               *
               * The curve should look like a horizontal
               * parabola. Since its curvature is greatest when x is
               * small, we add more control points there, so the
               * approximation is more accurate. I decided to set the
               * length of the consecutive segments to x₀, γ⋅x₀, γ²⋅x₀
               * and γ³⋅x₀ and I saw that the curves looked
               * good. Still, this is completely arbitrary.
               */
              gdouble dx = 0;
              gdouble x0;

              for (i = 0; i < n; ++i)
                dx = dx * gamma + 1;
              x0 = delta_in / dx;

              dx = 0;
              for (i = 1; i < n; ++i)
                {
                  dx = dx * gamma + x0;
                  x = config->low_input[channel] + dx;
                  y = config->low_output[channel] + delta_out *
                      gimp_operation_levels_map_input (config, channel, x);
                  point = CLAMP (n_points * x, point + 1, n_points - 1 - n + i);
                  gimp_curve_set_point (curve, point, x, y);
                }
            }
          else
            {
              /* Case no. 2: γ < 1
               *
               * The curve is the same as the one in case no. 1,
               * observed through a reflexion along the y = x axis. So
               * if we invert γ and swap the x and y axes we can use
               * the same method as in case no. 1.
               */
              GimpLevelsConfig *config_inv;
              gdouble           dy = 0;
              gdouble           y0;
              const gdouble     gamma_inv = 1 / gamma;

              config_inv = gimp_config_duplicate (GIMP_CONFIG (config));

              config_inv->gamma[channel]       = gamma_inv;
              config_inv->low_input[channel]   = config->low_output[channel];
              config_inv->low_output[channel]  = config->low_input[channel];
              config_inv->high_input[channel]  = config->high_output[channel];
              config_inv->high_output[channel] = config->high_input[channel];

              for (i = 0; i < n; ++i)
                dy = dy * gamma_inv + 1;
              y0 = delta_out / dy;

              dy = 0;
              for (i = 1; i < n; ++i)
                {
                  dy = dy * gamma_inv + y0;
                  y = config->low_output[channel] + dy;
                  x = config->low_input[channel] + delta_in *
                      gimp_operation_levels_map_input (config_inv, channel, y);
                  point = CLAMP (n_points * x, point + 1, n_points - 1 - n + i);
                  gimp_curve_set_point (curve, point, x, y);
                }

              g_object_unref (config_inv);
            }
        }

      x = config->high_input[channel];
      y = config->high_output[channel];

      point = CLAMP (n_points * x, point + 1, n_points - 1);
      gimp_curve_set_point (curve, point, x, y);
    }

  return curves;
}
Exemplo n.º 2
0
static void
gimp_tool_preset_set_options (GimpToolPreset  *preset,
                              GimpToolOptions *options)
{
  if (preset->tool_options)
    {
      g_signal_handlers_disconnect_by_func (preset->tool_options,
                                            gimp_tool_preset_options_notify,
                                            preset);

       g_signal_handlers_disconnect_by_func (preset->tool_options,
                                             gimp_tool_preset_options_prop_name_changed,
                                             preset);

      g_object_unref (preset->tool_options);
      preset->tool_options = NULL;
    }

  if (options)
    {
      GimpContextPropMask serialize_props;

      preset->tool_options =
        GIMP_TOOL_OPTIONS (gimp_config_duplicate (GIMP_CONFIG (options)));

      serialize_props =
        gimp_context_get_serialize_properties (GIMP_CONTEXT (preset->tool_options));

      gimp_context_set_serialize_properties (GIMP_CONTEXT (preset->tool_options),
                                             serialize_props |
                                             GIMP_CONTEXT_TOOL_MASK);

      if (! (serialize_props & GIMP_CONTEXT_FOREGROUND_MASK))
        g_object_set (preset, "use-fg-bg", FALSE, NULL);

      if (! (serialize_props & GIMP_CONTEXT_BRUSH_MASK))
        g_object_set (preset, "use-brush", FALSE, NULL);

      if (! (serialize_props & GIMP_CONTEXT_DYNAMICS_MASK))
        g_object_set (preset, "use-dynamics", FALSE, NULL);

      if (! (serialize_props & GIMP_CONTEXT_GRADIENT_MASK))
        g_object_set (preset, "use-gradient", FALSE, NULL);

      if (! (serialize_props & GIMP_CONTEXT_PATTERN_MASK))
        g_object_set (preset, "use-pattern", FALSE, NULL);

      if (! (serialize_props & GIMP_CONTEXT_PALETTE_MASK))
        g_object_set (preset, "use-palette", FALSE, NULL);

      if (! (serialize_props & GIMP_CONTEXT_FONT_MASK))
        g_object_set (preset, "use-font", FALSE, NULL);

      g_signal_connect (preset->tool_options, "notify",
                        G_CALLBACK (gimp_tool_preset_options_notify),
                        preset);

      g_signal_connect (preset->tool_options, "prop-name-changed",
                        G_CALLBACK (gimp_tool_preset_options_prop_name_changed),
                        preset);
    }

  g_object_notify (G_OBJECT (preset), "tool-options");
}
Exemplo n.º 3
0
static void
gimp_text_undo_pop (GimpUndo            *undo,
                    GimpUndoMode         undo_mode,
                    GimpUndoAccumulator *accum)
{
  GimpTextUndo  *text_undo = GIMP_TEXT_UNDO (undo);
  GimpTextLayer *layer     = GIMP_TEXT_LAYER (GIMP_ITEM_UNDO (undo)->item);

  GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum);

  switch (undo->undo_type)
    {
    case GIMP_UNDO_TEXT_LAYER:
      if (text_undo->pspec)
        {
          GValue *value;

          g_return_if_fail (layer->text != NULL);

          value = g_slice_new0 (GValue);
          g_value_init (value, text_undo->pspec->value_type);

          g_object_get_property (G_OBJECT (layer->text),
                                 text_undo->pspec->name, value);

          g_object_set_property (G_OBJECT (layer->text),
                                 text_undo->pspec->name, text_undo->value);

          g_value_unset (text_undo->value);
          g_slice_free (GValue, text_undo->value);

          text_undo->value = value;
        }
      else
        {
          GimpText *text;

          text = (layer->text ?
                  gimp_config_duplicate (GIMP_CONFIG (layer->text)) : NULL);

          if (layer->text && text_undo->text)
            gimp_config_sync (G_OBJECT (text_undo->text),
                              G_OBJECT (layer->text), 0);
          else
            gimp_text_layer_set_text (layer, text_undo->text);

          if (text_undo->text)
            g_object_unref (text_undo->text);

          text_undo->text = text;
        }
      break;

    case GIMP_UNDO_TEXT_LAYER_MODIFIED:
      {
        gboolean modified;

#if 0
        g_print ("setting layer->modified from %s to %s\n",
                 layer->modified ? "TRUE" : "FALSE",
                 text_undo->modified ? "TRUE" : "FALSE");
#endif

        modified = layer->modified;
        g_object_set (layer, "modified", text_undo->modified, NULL);
        text_undo->modified = modified;

        gimp_viewable_invalidate_preview (GIMP_VIEWABLE (layer));
      }
      break;

    case GIMP_UNDO_TEXT_LAYER_CONVERT:
      {
        const Babl *format;

        format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
        gimp_drawable_convert_type (GIMP_DRAWABLE (layer),
                                    gimp_item_get_image (GIMP_ITEM (layer)),
                                    gimp_babl_format_get_base_type (text_undo->format),
                                    gimp_babl_format_get_precision (text_undo->format),
                                    babl_format_has_alpha (text_undo->format),
                                    NULL, NULL,
                                    GEGL_DITHER_NONE, GEGL_DITHER_NONE,
                                    FALSE, NULL);
        text_undo->format = format;
      }
      break;

    default:
      gimp_assert_not_reached ();
    }
}