[gimp] Issue #3512 - feather selection doesn't work at edges of images



commit aace6b179b29c5728529d5c1c725ed33892d7056
Author: Michael Natterer <mitch gimp org>
Date:   Sun Jun 16 16:51:30 2019 +0200

    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.

 app/actions/select-commands.c        | 25 +++++++++++++++++++++++--
 app/config/gimpdialogconfig.c        | 14 ++++++++++++++
 app/config/gimpdialogconfig.h        |  1 +
 app/config/gimprc-blurbs.h           |  4 ++++
 app/core/gimpchannel.c               |  8 ++++++--
 app/core/gimpchannel.h               |  2 ++
 app/dialogs/preferences-dialog.c     |  4 ++++
 app/gegl/gimp-gegl-apply-operation.c | 35 +++++++++++++++++++++++------------
 app/gegl/gimp-gegl-apply-operation.h | 13 +++++++++++--
 app/pdb/selection-cmds.c             |  3 ++-
 pdb/groups/selection.pdb             |  3 ++-
 11 files changed, 92 insertions(+), 20 deletions(-)
---
diff --git a/app/actions/select-commands.c b/app/actions/select-commands.c
index 1258a71466..c6bbd3e71b 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);
     }
 
@@ -478,11 +492,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;
@@ -505,7 +524,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);
 }
 
@@ -616,7 +637,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 5d6bf63b83..f63b27f5db 100644
--- a/app/config/gimprc-blurbs.h
+++ b/app/config/gimprc-blurbs.h
@@ -591,6 +591,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 efc330f4e6..3c9c2be7f2 100644
--- a/app/core/gimpchannel.c
+++ b/app/core/gimpchannel.c
@@ -186,6 +186,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);
@@ -1177,6 +1178,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;
@@ -1208,7 +1210,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);
 }
@@ -1890,6 +1893,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));
@@ -1898,7 +1902,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 fb37b506bb..98514394d6 100644
--- a/app/dialogs/preferences-dialog.c
+++ b/app/dialogs/preferences-dialog.c
@@ -2388,6 +2388,10 @@ prefs_dialog_new (Gimp       *gimp,
                          _("Feather radius:"),
                          GTK_GRID (grid), 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 e4f58e526b..931d147d86 100644
--- a/app/gegl/gimp-gegl-apply-operation.c
+++ b/app/gegl/gimp-gegl-apply-operation.c
@@ -361,12 +361,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
    */
@@ -374,7 +382,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
@@ -545,13 +554,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;
 
@@ -560,9 +570,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 ad863db2be..7864026b99 100644
--- a/app/gegl/gimp-gegl-apply-operation.h
+++ b/app/gegl/gimp-gegl-apply-operation.h
@@ -70,7 +70,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,
@@ -105,13 +106,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 a54e2b9f61..5e0641ebb0 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 920c23261c..dbde6fc029 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
     );


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]