Issue #3512 - feather selection doesn't work at edges of images
Add a "gboolean edge_lock" parameter to GimpChannel::feather() and a
"Selected areas continue outside the image" toggle to the "Feather
Selection" dialog, just like they exist for shrink selection and
border selection. At the end, convert the boolean to the right abyss
policy for gegl:gaussian-blur.
(cherry picked from commit aace6b179b
)
This commit is contained in:
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -96,6 +96,7 @@ struct _GimpDialogConfig
|
||||
gboolean vectors_import_scale;
|
||||
|
||||
gdouble selection_feather_radius;
|
||||
gboolean selection_feather_edge_lock;
|
||||
|
||||
gdouble selection_grow_radius;
|
||||
|
||||
|
@ -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.")
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
);
|
||||
|
Reference in New Issue
Block a user