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; }
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"); }
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 (); } }