diff --git a/app/actions/select-commands.c b/app/actions/select-commands.c index 35bd106f80..5fdc7817f9 100644 --- a/app/actions/select-commands.c +++ b/app/actions/select-commands.c @@ -148,6 +148,7 @@ select_feather_cmd_callback (GtkAction *action, if (! dialog) { GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GtkWidget *button; gdouble xres; gdouble yres; @@ -165,6 +166,19 @@ select_feather_cmd_callback (GtkAction *action, G_OBJECT (image), "disconnect", select_feather_callback, image); + /* Edge lock button */ + button = gtk_check_button_new_with_mnemonic (_("_Selected areas continue outside the image")); + g_object_set_data (G_OBJECT (dialog), "edge-lock-toggle", button); + gimp_help_set_help_data (button, + _("When feathering, act as if selected areas" + "continued outside the image."), + NULL); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), + config->selection_feather_edge_lock); + gtk_box_pack_start (GTK_BOX (GIMP_QUERY_BOX_VBOX (dialog)), button, + FALSE, FALSE, 0); + gtk_widget_show (button); + dialogs_attach_dialog (G_OBJECT (image), FEATHER_DIALOG_KEY, dialog); } @@ -479,11 +493,16 @@ select_feather_callback (GtkWidget *widget, { GimpImage *image = GIMP_IMAGE (data); GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GtkWidget *button; gdouble radius_x; gdouble radius_y; + button = g_object_get_data (G_OBJECT (widget), "edge-lock-toggle"); + g_object_set (config, "selection-feather-radius", size, + "selection-feather-edge-lock", + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)), NULL); radius_x = config->selection_feather_radius; @@ -506,7 +525,9 @@ select_feather_callback (GtkWidget *widget, radius_x *= factor; } - gimp_channel_feather (gimp_image_get_mask (image), radius_x, radius_y, TRUE); + gimp_channel_feather (gimp_image_get_mask (image), radius_x, radius_y, + config->selection_feather_edge_lock, + TRUE); gimp_image_flush (image); } @@ -617,7 +638,7 @@ select_shrink_callback (GtkWidget *widget, button = g_object_get_data (G_OBJECT (widget), "edge-lock-toggle"); g_object_set (config, - "selection-shrink-radius", size, + "selection-shrink-radius", size, "selection-shrink-edge-lock", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)), NULL); diff --git a/app/config/gimpdialogconfig.c b/app/config/gimpdialogconfig.c index 9a226cf9d5..f81d778d86 100644 --- a/app/config/gimpdialogconfig.c +++ b/app/config/gimpdialogconfig.c @@ -98,6 +98,7 @@ enum PROP_VECTORS_IMPORT_SCALE, PROP_SELECTION_FEATHER_RADIUS, + PROP_SELECTION_FEATHER_EDGE_LOCK, PROP_SELECTION_GROW_RADIUS, @@ -465,6 +466,13 @@ gimp_dialog_config_class_init (GimpDialogConfigClass *klass) 0.0, 32767.0, 5.0, GIMP_PARAM_STATIC_STRINGS); + GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SELECTION_FEATHER_EDGE_LOCK, + "selection-feather-edge-lock", + "Selection feather edge lock", + SELECTION_FEATHER_EDGE_LOCK_BLURB, + TRUE, + GIMP_PARAM_STATIC_STRINGS); + GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_SELECTION_GROW_RADIUS, "selection-grow-radius", "Selection grow radius", @@ -736,6 +744,9 @@ gimp_dialog_config_set_property (GObject *object, case PROP_SELECTION_FEATHER_RADIUS: config->selection_feather_radius = g_value_get_double (value); break; + case PROP_SELECTION_FEATHER_EDGE_LOCK: + config->selection_feather_edge_lock = g_value_get_boolean (value); + break; case PROP_SELECTION_GROW_RADIUS: config->selection_grow_radius = g_value_get_double (value); @@ -921,6 +932,9 @@ gimp_dialog_config_get_property (GObject *object, case PROP_SELECTION_FEATHER_RADIUS: g_value_set_double (value, config->selection_feather_radius); break; + case PROP_SELECTION_FEATHER_EDGE_LOCK: + g_value_set_boolean (value, config->selection_feather_edge_lock); + break; case PROP_SELECTION_GROW_RADIUS: g_value_set_double (value, config->selection_grow_radius); diff --git a/app/config/gimpdialogconfig.h b/app/config/gimpdialogconfig.h index c59f12b556..1053f6d339 100644 --- a/app/config/gimpdialogconfig.h +++ b/app/config/gimpdialogconfig.h @@ -96,6 +96,7 @@ struct _GimpDialogConfig gboolean vectors_import_scale; gdouble selection_feather_radius; + gboolean selection_feather_edge_lock; gdouble selection_grow_radius; diff --git a/app/config/gimprc-blurbs.h b/app/config/gimprc-blurbs.h index ec62116686..7566d4a5b1 100644 --- a/app/config/gimprc-blurbs.h +++ b/app/config/gimprc-blurbs.h @@ -594,6 +594,10 @@ _("Sets the default 'Scale imported paths to fit size' state for the 'Import Pat #define SELECTION_FEATHER_RADIUS_BLURB \ _("Sets the default feather radius for the 'Feather Selection' dialog.") +#define SELECTION_FEATHER_EDGE_LOCK_BLURB \ +_("Sets the default 'Selected areas continue outside the image' setting " \ + "for the 'Feather Selection' dialog.") + #define SELECTION_GROW_RADIUS_BLURB \ _("Sets the default grow radius for the 'Grow Selection' dialog.") diff --git a/app/core/gimpchannel.c b/app/core/gimpchannel.c index ba392754d2..5abee03436 100644 --- a/app/core/gimpchannel.c +++ b/app/core/gimpchannel.c @@ -184,6 +184,7 @@ static gboolean gimp_channel_real_is_empty (GimpChannel *channel); static void gimp_channel_real_feather (GimpChannel *channel, gdouble radius_x, gdouble radius_y, + gboolean edge_lock, gboolean push_undo); static void gimp_channel_real_sharpen (GimpChannel *channel, gboolean push_undo); @@ -1174,6 +1175,7 @@ static void gimp_channel_real_feather (GimpChannel *channel, gdouble radius_x, gdouble radius_y, + gboolean edge_lock, gboolean push_undo) { gint x1, y1, x2, y2; @@ -1205,7 +1207,8 @@ gimp_channel_real_feather (GimpChannel *channel, gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)), GEGL_RECTANGLE (x1, y1, x2 - x1, y2 - y1), radius_x, - radius_y); + radius_y, + edge_lock); gimp_drawable_update (GIMP_DRAWABLE (channel), 0, 0, -1, -1); } @@ -1887,6 +1890,7 @@ void gimp_channel_feather (GimpChannel *channel, gdouble radius_x, gdouble radius_y, + gboolean edge_lock, gboolean push_undo) { g_return_if_fail (GIMP_IS_CHANNEL (channel)); @@ -1895,7 +1899,7 @@ gimp_channel_feather (GimpChannel *channel, push_undo = FALSE; GIMP_CHANNEL_GET_CLASS (channel)->feather (channel, radius_x, radius_y, - push_undo); + edge_lock, push_undo); } void diff --git a/app/core/gimpchannel.h b/app/core/gimpchannel.h index 468645050a..348f4f64f6 100644 --- a/app/core/gimpchannel.h +++ b/app/core/gimpchannel.h @@ -77,6 +77,7 @@ struct _GimpChannelClass void (* feather) (GimpChannel *channel, gdouble radius_x, gdouble radius_y, + gboolean edge_lock, gboolean push_undo); void (* sharpen) (GimpChannel *channel, gboolean push_undo); @@ -180,6 +181,7 @@ gboolean gimp_channel_is_empty (GimpChannel *mask); void gimp_channel_feather (GimpChannel *mask, gdouble radius_x, gdouble radius_y, + gboolean edge_lock, gboolean push_undo); void gimp_channel_sharpen (GimpChannel *mask, gboolean push_undo); diff --git a/app/dialogs/preferences-dialog.c b/app/dialogs/preferences-dialog.c index 6172e8edda..1a9e010e98 100644 --- a/app/dialogs/preferences-dialog.c +++ b/app/dialogs/preferences-dialog.c @@ -2387,6 +2387,10 @@ prefs_dialog_new (Gimp *gimp, _("Feather radius:"), GTK_TABLE (table), 0, size_group); + prefs_check_button_add (object, "selection-feather-edge-lock", + _("Selected areas continue outside the image"), + GTK_BOX (vbox2)); + /* Grow Selection Dialog */ vbox2 = prefs_frame_new (_("Grow Selection Dialog"), GTK_CONTAINER (vbox), FALSE); diff --git a/app/gegl/gimp-gegl-apply-operation.c b/app/gegl/gimp-gegl-apply-operation.c index ad838cd41e..737d083035 100644 --- a/app/gegl/gimp-gegl-apply-operation.c +++ b/app/gegl/gimp-gegl-apply-operation.c @@ -360,12 +360,20 @@ gimp_gegl_apply_feather (GeglBuffer *src_buffer, GeglBuffer *dest_buffer, const GeglRectangle *dest_rect, gdouble radius_x, - gdouble radius_y) + gdouble radius_y, + gboolean edge_lock) { + GaussianBlurAbyssPolicy abyss_policy; + g_return_if_fail (GEGL_IS_BUFFER (src_buffer)); g_return_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress)); g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); + if (edge_lock) + abyss_policy = GAUSSIAN_BLUR_ABYSS_CLAMP; + else + abyss_policy = GAUSSIAN_BLUR_ABYSS_NONE; + /* 3.5 is completely magic and picked to visually match the old * gaussian_blur_region() on a crappy laptop display */ @@ -373,7 +381,8 @@ gimp_gegl_apply_feather (GeglBuffer *src_buffer, progress, undo_desc, dest_buffer, dest_rect, radius_x / 3.5, - radius_y / 3.5); + radius_y / 3.5, + abyss_policy); } void @@ -544,13 +553,14 @@ gimp_gegl_apply_flood (GeglBuffer *src_buffer, } void -gimp_gegl_apply_gaussian_blur (GeglBuffer *src_buffer, - GimpProgress *progress, - const gchar *undo_desc, - GeglBuffer *dest_buffer, - const GeglRectangle *dest_rect, - gdouble std_dev_x, - gdouble std_dev_y) +gimp_gegl_apply_gaussian_blur (GeglBuffer *src_buffer, + GimpProgress *progress, + const gchar *undo_desc, + GeglBuffer *dest_buffer, + const GeglRectangle *dest_rect, + gdouble std_dev_x, + gdouble std_dev_y, + GaussianBlurAbyssPolicy abyss_policy) { GeglNode *node; @@ -559,9 +569,10 @@ gimp_gegl_apply_gaussian_blur (GeglBuffer *src_buffer, g_return_if_fail (GEGL_IS_BUFFER (dest_buffer)); node = gegl_node_new_child (NULL, - "operation", "gegl:gaussian-blur", - "std-dev-x", std_dev_x, - "std-dev-y", std_dev_y, + "operation", "gegl:gaussian-blur", + "std-dev-x", std_dev_x, + "std-dev-y", std_dev_y, + "abyss-policy", abyss_policy, NULL); gimp_gegl_apply_operation (src_buffer, progress, undo_desc, diff --git a/app/gegl/gimp-gegl-apply-operation.h b/app/gegl/gimp-gegl-apply-operation.h index e001266e85..985ebe3d29 100644 --- a/app/gegl/gimp-gegl-apply-operation.h +++ b/app/gegl/gimp-gegl-apply-operation.h @@ -69,7 +69,8 @@ void gimp_gegl_apply_feather (GeglBuffer *src_buffer, GeglBuffer *dest_buffer, const GeglRectangle *dest_rect, gdouble radius_x, - gdouble radius_y); + gdouble radius_y, + gboolean edge_lock); void gimp_gegl_apply_border (GeglBuffer *src_buffer, GimpProgress *progress, @@ -104,13 +105,21 @@ void gimp_gegl_apply_flood (GeglBuffer *src_buffer, GeglBuffer *dest_buffer, const GeglRectangle *dest_rect); +/* UGLY: private enum of gegl:gaussian-blur */ +typedef enum +{ + GAUSSIAN_BLUR_ABYSS_NONE, + GAUSSIAN_BLUR_ABYSS_CLAMP +} GaussianBlurAbyssPolicy; + void gimp_gegl_apply_gaussian_blur (GeglBuffer *src_buffer, GimpProgress *progress, const gchar *undo_desc, GeglBuffer *dest_buffer, const GeglRectangle *dest_rect, gdouble std_dev_x, - gdouble std_dev_y); + gdouble std_dev_y, + GaussianBlurAbyssPolicy abyss_policy); void gimp_gegl_apply_invert_gamma (GeglBuffer *src_buffer, GimpProgress *progress, diff --git a/app/pdb/selection-cmds.c b/app/pdb/selection-cmds.c index 12f54a0cbb..fd240033ac 100644 --- a/app/pdb/selection-cmds.c +++ b/app/pdb/selection-cmds.c @@ -337,8 +337,9 @@ selection_feather_invoker (GimpProcedure *procedure, if (success) { + /* FIXME: "edge-lock" hardcoded to TRUE */ gimp_channel_feather (gimp_image_get_mask (image), - radius, radius, TRUE); + radius, radius, TRUE, TRUE); } return gimp_procedure_get_return_values (procedure, success, diff --git a/pdb/groups/selection.pdb b/pdb/groups/selection.pdb index 480a48f966..1bd60f5b19 100644 --- a/pdb/groups/selection.pdb +++ b/pdb/groups/selection.pdb @@ -332,8 +332,9 @@ HELP %invoke = ( code => <<'CODE' { + /* FIXME: "edge-lock" hardcoded to TRUE */ gimp_channel_feather (gimp_image_get_mask (image), - radius, radius, TRUE); + radius, radius, TRUE, TRUE); } CODE );