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



commit a65042aebf40b4b1144d154eaeda38a8717e1036
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.
    
    (cherry picked from commit aace6b179b29c5728529d5c1c725ed33892d7056)

 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 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
     );


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