1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <gegl.h>
21 #include <gtk/gtk.h>
22 
23 #include "libgimpmath/gimpmath.h"
24 #include "libgimpwidgets/gimpwidgets.h"
25 
26 #include "actions-types.h"
27 
28 #include "config/gimpdialogconfig.h"
29 
30 #include "core/gimp.h"
31 #include "core/gimpchannel.h"
32 #include "core/gimpimage.h"
33 #include "core/gimpselection.h"
34 
35 #include "widgets/gimphelp-ids.h"
36 #include "widgets/gimpdialogfactory.h"
37 #include "widgets/gimpwidgets-utils.h"
38 #include "widgets/gimpwindowstrategy.h"
39 
40 #include "display/gimpdisplay.h"
41 #include "display/gimpdisplayshell.h"
42 
43 #include "dialogs/dialogs.h"
44 
45 #include "actions.h"
46 #include "items-commands.h"
47 #include "select-commands.h"
48 
49 #include "gimp-intl.h"
50 
51 
52 /*  local function prototypes  */
53 
54 static void   select_feather_callback (GtkWidget *widget,
55                                        gdouble    size,
56                                        GimpUnit   unit,
57                                        gpointer   data);
58 static void   select_border_callback  (GtkWidget *widget,
59                                        gdouble    size,
60                                        GimpUnit   unit,
61                                        gpointer   data);
62 static void   select_grow_callback    (GtkWidget *widget,
63                                        gdouble    size,
64                                        GimpUnit   unit,
65                                        gpointer   data);
66 static void   select_shrink_callback  (GtkWidget *widget,
67                                        gdouble    size,
68                                        GimpUnit   unit,
69                                        gpointer   data);
70 
71 
72 /*  public functions  */
73 
74 void
select_all_cmd_callback(GimpAction * action,GVariant * value,gpointer data)75 select_all_cmd_callback (GimpAction *action,
76                          GVariant   *value,
77                          gpointer    data)
78 {
79   GimpImage *image;
80   return_if_no_image (image, data);
81 
82   gimp_channel_all (gimp_image_get_mask (image), TRUE);
83   gimp_image_flush (image);
84 }
85 
86 void
select_none_cmd_callback(GimpAction * action,GVariant * value,gpointer data)87 select_none_cmd_callback (GimpAction *action,
88                           GVariant   *value,
89                           gpointer    data)
90 {
91   GimpImage *image;
92   return_if_no_image (image, data);
93 
94   gimp_channel_clear (gimp_image_get_mask (image), NULL, TRUE);
95   gimp_image_flush (image);
96 }
97 
98 void
select_invert_cmd_callback(GimpAction * action,GVariant * value,gpointer data)99 select_invert_cmd_callback (GimpAction *action,
100                             GVariant   *value,
101                             gpointer    data)
102 {
103   GimpImage *image;
104   return_if_no_image (image, data);
105 
106   gimp_channel_invert (gimp_image_get_mask (image), TRUE);
107   gimp_image_flush (image);
108 }
109 
110 void
select_float_cmd_callback(GimpAction * action,GVariant * value,gpointer data)111 select_float_cmd_callback (GimpAction *action,
112                            GVariant   *value,
113                            gpointer    data)
114 {
115   GimpImage *image;
116   GtkWidget *widget;
117   GError    *error = NULL;
118   return_if_no_image (image, data);
119   return_if_no_widget (widget, data);
120 
121   if (gimp_selection_float (GIMP_SELECTION (gimp_image_get_mask (image)),
122                             gimp_image_get_active_drawable (image),
123                             action_data_get_context (data),
124                             TRUE, 0, 0, &error))
125     {
126       gimp_image_flush (image);
127     }
128   else
129     {
130       gimp_message_literal (image->gimp,
131                             G_OBJECT (widget), GIMP_MESSAGE_WARNING,
132                             error->message);
133       g_clear_error (&error);
134     }
135 }
136 
137 void
select_feather_cmd_callback(GimpAction * action,GVariant * value,gpointer data)138 select_feather_cmd_callback (GimpAction *action,
139                              GVariant   *value,
140                              gpointer    data)
141 {
142   GimpDisplay *display;
143   GimpImage   *image;
144   GtkWidget   *dialog;
145   return_if_no_display (display, data);
146 
147   image = gimp_display_get_image (display);
148 
149 #define FEATHER_DIALOG_KEY "gimp-selection-feather-dialog"
150 
151   dialog = dialogs_get_dialog (G_OBJECT (image), FEATHER_DIALOG_KEY);
152 
153   if (! dialog)
154     {
155       GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
156       GtkWidget        *button;
157       gdouble           xres;
158       gdouble           yres;
159 
160       gimp_image_get_resolution (image, &xres, &yres);
161 
162       dialog = gimp_query_size_box (_("Feather Selection"),
163                                     GTK_WIDGET (gimp_display_get_shell (display)),
164                                     gimp_standard_help_func,
165                                     GIMP_HELP_SELECTION_FEATHER,
166                                     _("Feather selection by"),
167                                     config->selection_feather_radius, 0, 32767, 3,
168                                     gimp_display_get_shell (display)->unit,
169                                     MIN (xres, yres),
170                                     FALSE,
171                                     G_OBJECT (image), "disconnect",
172                                     select_feather_callback, image);
173 
174       /* Edge lock button */
175       button = gtk_check_button_new_with_mnemonic (_("_Selected areas continue outside the image"));
176       g_object_set_data (G_OBJECT (dialog), "edge-lock-toggle", button);
177       gimp_help_set_help_data (button,
178                                _("When feathering, act as if selected areas "
179                                  "continued outside the image."),
180                                NULL);
181       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
182                                     config->selection_feather_edge_lock);
183       gtk_box_pack_start (GTK_BOX (GIMP_QUERY_BOX_VBOX (dialog)), button,
184                           FALSE, FALSE, 0);
185       gtk_widget_show (button);
186 
187       dialogs_attach_dialog (G_OBJECT (image), FEATHER_DIALOG_KEY, dialog);
188     }
189 
190   gtk_window_present (GTK_WINDOW (dialog));
191 }
192 
193 void
select_sharpen_cmd_callback(GimpAction * action,GVariant * value,gpointer data)194 select_sharpen_cmd_callback (GimpAction *action,
195                              GVariant   *value,
196                              gpointer    data)
197 {
198   GimpImage *image;
199   return_if_no_image (image, data);
200 
201   gimp_channel_sharpen (gimp_image_get_mask (image), TRUE);
202   gimp_image_flush (image);
203 }
204 
205 void
select_shrink_cmd_callback(GimpAction * action,GVariant * value,gpointer data)206 select_shrink_cmd_callback (GimpAction *action,
207                             GVariant   *value,
208                             gpointer    data)
209 {
210   GimpDisplay *display;
211   GimpImage   *image;
212   GtkWidget   *dialog;
213   return_if_no_display (display, data);
214 
215   image = gimp_display_get_image (display);
216 
217 #define SHRINK_DIALOG_KEY "gimp-selection-shrink-dialog"
218 
219   dialog = dialogs_get_dialog (G_OBJECT (image), SHRINK_DIALOG_KEY);
220 
221   if (! dialog)
222     {
223       GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
224       GtkWidget        *button;
225       gint              width;
226       gint              height;
227       gint              max_value;
228       gdouble           xres;
229       gdouble           yres;
230 
231       gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)),
232                         NULL, NULL, &width, &height);
233       max_value = MIN (width, height) / 2;
234 
235       gimp_image_get_resolution (image, &xres, &yres);
236 
237       dialog = gimp_query_size_box (_("Shrink Selection"),
238                                     GTK_WIDGET (gimp_display_get_shell (display)),
239                                     gimp_standard_help_func,
240                                     GIMP_HELP_SELECTION_SHRINK,
241                                     _("Shrink selection by"),
242                                     config->selection_shrink_radius,
243                                     1, max_value, 0,
244                                     gimp_display_get_shell (display)->unit,
245                                     MIN (xres, yres),
246                                     FALSE,
247                                     G_OBJECT (image), "disconnect",
248                                     select_shrink_callback, image);
249 
250       /* Edge lock button */
251       button = gtk_check_button_new_with_mnemonic (_("_Selected areas continue outside the image"));
252       g_object_set_data (G_OBJECT (dialog), "edge-lock-toggle", button);
253       gimp_help_set_help_data (button,
254                                _("When shrinking, act as if selected areas "
255                                  "continued outside the image."),
256                                NULL);
257       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
258                                     config->selection_shrink_edge_lock);
259       gtk_box_pack_start (GTK_BOX (GIMP_QUERY_BOX_VBOX (dialog)), button,
260                           FALSE, FALSE, 0);
261       gtk_widget_show (button);
262 
263       dialogs_attach_dialog (G_OBJECT (image), SHRINK_DIALOG_KEY, dialog);
264     }
265 
266   gtk_window_present (GTK_WINDOW (dialog));
267 }
268 
269 void
select_grow_cmd_callback(GimpAction * action,GVariant * value,gpointer data)270 select_grow_cmd_callback (GimpAction *action,
271                           GVariant   *value,
272                           gpointer    data)
273 {
274   GimpDisplay *display;
275   GimpImage   *image;
276   GtkWidget   *dialog;
277   return_if_no_display (display, data);
278 
279   image = gimp_display_get_image (display);
280 
281 #define GROW_DIALOG_KEY "gimp-selection-grow-dialog"
282 
283   dialog = dialogs_get_dialog (G_OBJECT (image), GROW_DIALOG_KEY);
284 
285   if (! dialog)
286     {
287       GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
288       gint              width;
289       gint              height;
290       gint              max_value;
291       gdouble           xres;
292       gdouble           yres;
293 
294       width  = gimp_image_get_width  (image);
295       height = gimp_image_get_height (image);
296       max_value = MAX (width, height);
297 
298       gimp_image_get_resolution (image, &xres, &yres);
299 
300       dialog = gimp_query_size_box (_("Grow Selection"),
301                                     GTK_WIDGET (gimp_display_get_shell (display)),
302                                     gimp_standard_help_func,
303                                     GIMP_HELP_SELECTION_GROW,
304                                     _("Grow selection by"),
305                                     config->selection_grow_radius,
306                                     1, max_value, 0,
307                                     gimp_display_get_shell (display)->unit,
308                                     MIN (xres, yres),
309                                     FALSE,
310                                     G_OBJECT (image), "disconnect",
311                                     select_grow_callback, image);
312 
313       dialogs_attach_dialog (G_OBJECT (image), GROW_DIALOG_KEY, dialog);
314     }
315 
316   gtk_window_present (GTK_WINDOW (dialog));
317 }
318 
319 void
select_border_cmd_callback(GimpAction * action,GVariant * value,gpointer data)320 select_border_cmd_callback (GimpAction *action,
321                             GVariant   *value,
322                             gpointer    data)
323 {
324   GimpDisplay *display;
325   GimpImage   *image;
326   GtkWidget   *dialog;
327   return_if_no_display (display, data);
328 
329   image = gimp_display_get_image (display);
330 
331 #define BORDER_DIALOG_KEY "gimp-selection-border-dialog"
332 
333   dialog = dialogs_get_dialog (G_OBJECT (image), BORDER_DIALOG_KEY);
334 
335   if (! dialog)
336     {
337       GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
338       GtkWidget        *combo;
339       GtkWidget        *button;
340       gint              width;
341       gint              height;
342       gint              max_value;
343       gdouble           xres;
344       gdouble           yres;
345 
346       gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)),
347                         NULL, NULL, &width, &height);
348       max_value = MIN (width, height) / 2;
349 
350       gimp_image_get_resolution (image, &xres, &yres);
351 
352       dialog = gimp_query_size_box (_("Border Selection"),
353                                     GTK_WIDGET (gimp_display_get_shell (display)),
354                                     gimp_standard_help_func,
355                                     GIMP_HELP_SELECTION_BORDER,
356                                     _("Border selection by"),
357                                     config->selection_border_radius,
358                                     1, max_value, 0,
359                                     gimp_display_get_shell (display)->unit,
360                                     MIN (xres, yres),
361                                     FALSE,
362                                     G_OBJECT (image), "disconnect",
363                                     select_border_callback, image);
364 
365       /* Border style combo */
366       combo = gimp_enum_combo_box_new (GIMP_TYPE_CHANNEL_BORDER_STYLE);
367       gimp_int_combo_box_set_label (GIMP_INT_COMBO_BOX (combo),
368                                     _("Border style"));
369 
370       gtk_box_pack_start (GTK_BOX (GIMP_QUERY_BOX_VBOX (dialog)), combo,
371                           FALSE, FALSE, 0);
372 
373       g_object_set_data (G_OBJECT (dialog), "border-style-combo", combo);
374       gimp_int_combo_box_set_active (GIMP_INT_COMBO_BOX (combo),
375                                      config->selection_border_style);
376       gtk_widget_show (combo);
377 
378       /* Edge lock button */
379       button = gtk_check_button_new_with_mnemonic (_("_Selected areas continue outside the image"));
380       g_object_set_data (G_OBJECT (dialog), "edge-lock-toggle", button);
381       gimp_help_set_help_data (button,
382                                _("When bordering, act as if selected areas "
383                                  "continued outside the image."),
384                                NULL);
385       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
386                                     config->selection_border_edge_lock);
387       gtk_box_pack_start (GTK_BOX (GIMP_QUERY_BOX_VBOX (dialog)), button,
388                           FALSE, FALSE, 0);
389       gtk_widget_show (button);
390 
391       dialogs_attach_dialog (G_OBJECT (image), BORDER_DIALOG_KEY, dialog);
392     }
393 
394   gtk_window_present (GTK_WINDOW (dialog));
395 }
396 
397 void
select_flood_cmd_callback(GimpAction * action,GVariant * value,gpointer data)398 select_flood_cmd_callback (GimpAction *action,
399                            GVariant   *value,
400                            gpointer    data)
401 {
402   GimpImage *image;
403   return_if_no_image (image, data);
404 
405   gimp_channel_flood (gimp_image_get_mask (image), TRUE);
406   gimp_image_flush (image);
407 }
408 
409 void
select_save_cmd_callback(GimpAction * action,GVariant * value,gpointer data)410 select_save_cmd_callback (GimpAction *action,
411                           GVariant   *value,
412                           gpointer    data)
413 {
414   GimpImage   *image;
415   GimpChannel *channel;
416   GtkWidget   *widget;
417   return_if_no_image (image, data);
418   return_if_no_widget (widget, data);
419 
420   channel = GIMP_CHANNEL (gimp_item_duplicate (GIMP_ITEM (gimp_image_get_mask (image)),
421                                                GIMP_TYPE_CHANNEL));
422 
423   /*  saved selections are not visible by default  */
424   gimp_item_set_visible (GIMP_ITEM (channel), FALSE, FALSE);
425 
426   gimp_image_add_channel (image, channel,
427                           GIMP_IMAGE_ACTIVE_PARENT, -1, TRUE);
428   gimp_image_flush (image);
429 
430   gimp_window_strategy_show_dockable_dialog (GIMP_WINDOW_STRATEGY (gimp_get_window_strategy (image->gimp)),
431                                              image->gimp,
432                                              gimp_dialog_factory_get_singleton (),
433                                              gtk_widget_get_screen (widget),
434                                              gimp_widget_get_monitor (widget),
435                                              "gimp-channel-list");
436 }
437 
438 void
select_fill_cmd_callback(GimpAction * action,GVariant * value,gpointer data)439 select_fill_cmd_callback (GimpAction *action,
440                           GVariant   *value,
441                           gpointer    data)
442 {
443   GimpImage *image;
444   return_if_no_image (image, data);
445 
446   items_fill_cmd_callback (action,
447                            image, GIMP_ITEM (gimp_image_get_mask (image)),
448                            "gimp-selection-fill-dialog",
449                            _("Fill Selection Outline"),
450                            GIMP_ICON_TOOL_BUCKET_FILL,
451                            GIMP_HELP_SELECTION_FILL,
452                            data);
453 }
454 
455 void
select_fill_last_vals_cmd_callback(GimpAction * action,GVariant * value,gpointer data)456 select_fill_last_vals_cmd_callback (GimpAction *action,
457                                     GVariant   *value,
458                                     gpointer    data)
459 {
460   GimpImage *image;
461   return_if_no_image (image, data);
462 
463   items_fill_last_vals_cmd_callback (action,
464                                      image,
465                                      GIMP_ITEM (gimp_image_get_mask (image)),
466                                      data);
467 }
468 
469 void
select_stroke_cmd_callback(GimpAction * action,GVariant * value,gpointer data)470 select_stroke_cmd_callback (GimpAction *action,
471                             GVariant   *value,
472                             gpointer    data)
473 {
474   GimpImage *image;
475   return_if_no_image (image, data);
476 
477   items_stroke_cmd_callback (action,
478                              image, GIMP_ITEM (gimp_image_get_mask (image)),
479                              "gimp-selection-stroke-dialog",
480                              _("Stroke Selection"),
481                              GIMP_ICON_SELECTION_STROKE,
482                              GIMP_HELP_SELECTION_STROKE,
483                              data);
484 }
485 
486 void
select_stroke_last_vals_cmd_callback(GimpAction * action,GVariant * value,gpointer data)487 select_stroke_last_vals_cmd_callback (GimpAction *action,
488                                       GVariant   *value,
489                                       gpointer    data)
490 {
491   GimpImage *image;
492   return_if_no_image (image, data);
493 
494   items_stroke_last_vals_cmd_callback (action,
495                                        image,
496                                        GIMP_ITEM (gimp_image_get_mask (image)),
497                                        data);
498 }
499 
500 
501 /*  private functions  */
502 
503 static void
select_feather_callback(GtkWidget * widget,gdouble size,GimpUnit unit,gpointer data)504 select_feather_callback (GtkWidget *widget,
505                          gdouble    size,
506                          GimpUnit   unit,
507                          gpointer   data)
508 {
509   GimpImage        *image  = GIMP_IMAGE (data);
510   GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
511   GtkWidget        *button;
512   gdouble           radius_x;
513   gdouble           radius_y;
514 
515   button = g_object_get_data (G_OBJECT (widget), "edge-lock-toggle");
516 
517   g_object_set (config,
518                 "selection-feather-radius", size,
519                 "selection-feather-edge-lock",
520                 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)),
521                 NULL);
522 
523   radius_x = config->selection_feather_radius;
524   radius_y = config->selection_feather_radius;
525 
526   if (unit != GIMP_UNIT_PIXEL)
527     {
528       gdouble xres;
529       gdouble yres;
530       gdouble factor;
531 
532       gimp_image_get_resolution (image, &xres, &yres);
533 
534       factor = (MAX (xres, yres) /
535                 MIN (xres, yres));
536 
537       if (xres == MIN (xres, yres))
538         radius_y *= factor;
539       else
540         radius_x *= factor;
541     }
542 
543   gimp_channel_feather (gimp_image_get_mask (image), radius_x, radius_y,
544                         config->selection_feather_edge_lock,
545                         TRUE);
546   gimp_image_flush (image);
547 }
548 
549 static void
select_border_callback(GtkWidget * widget,gdouble size,GimpUnit unit,gpointer data)550 select_border_callback (GtkWidget *widget,
551                         gdouble    size,
552                         GimpUnit   unit,
553                         gpointer   data)
554 {
555   GimpImage        *image  = GIMP_IMAGE (data);
556   GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
557   GtkWidget        *combo;
558   GtkWidget        *button;
559   gdouble           radius_x;
560   gdouble           radius_y;
561   gint              border_style;
562 
563   combo  = g_object_get_data (G_OBJECT (widget), "border-style-combo");
564   button = g_object_get_data (G_OBJECT (widget), "edge-lock-toggle");
565 
566   gimp_int_combo_box_get_active (GIMP_INT_COMBO_BOX (combo), &border_style);
567 
568   g_object_set (config,
569                 "selection-border-radius", size,
570                 "selection-border-style",  border_style,
571                 "selection-border-edge-lock",
572                 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)),
573                 NULL);
574 
575   radius_x = ROUND (config->selection_border_radius);
576   radius_y = ROUND (config->selection_border_radius);
577 
578   if (unit != GIMP_UNIT_PIXEL)
579     {
580       gdouble xres;
581       gdouble yres;
582       gdouble factor;
583 
584       gimp_image_get_resolution (image, &xres, &yres);
585 
586       factor = (MAX (xres, yres) /
587                 MIN (xres, yres));
588 
589       if (xres == MIN (xres, yres))
590         radius_y *= factor;
591       else
592         radius_x *= factor;
593     }
594 
595   gimp_channel_border (gimp_image_get_mask (image), radius_x, radius_y,
596                        config->selection_border_style,
597                        config->selection_border_edge_lock,
598                        TRUE);
599   gimp_image_flush (image);
600 }
601 
602 static void
select_grow_callback(GtkWidget * widget,gdouble size,GimpUnit unit,gpointer data)603 select_grow_callback (GtkWidget *widget,
604                       gdouble    size,
605                       GimpUnit   unit,
606                       gpointer   data)
607 {
608   GimpImage        *image  = GIMP_IMAGE (data);
609   GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
610   gdouble           radius_x;
611   gdouble           radius_y;
612 
613   g_object_set (config,
614                 "selection-grow-radius", size,
615                 NULL);
616 
617   radius_x = ROUND (config->selection_grow_radius);
618   radius_y = ROUND (config->selection_grow_radius);
619 
620   if (unit != GIMP_UNIT_PIXEL)
621     {
622       gdouble xres;
623       gdouble yres;
624       gdouble factor;
625 
626       gimp_image_get_resolution (image, &xres, &yres);
627 
628       factor = (MAX (xres, yres) /
629                 MIN (xres, yres));
630 
631       if (xres == MIN (xres, yres))
632         radius_y *= factor;
633       else
634         radius_x *= factor;
635     }
636 
637   gimp_channel_grow (gimp_image_get_mask (image), radius_x, radius_y, TRUE);
638   gimp_image_flush (image);
639 }
640 
641 static void
select_shrink_callback(GtkWidget * widget,gdouble size,GimpUnit unit,gpointer data)642 select_shrink_callback (GtkWidget *widget,
643                         gdouble    size,
644                         GimpUnit   unit,
645                         gpointer   data)
646 {
647   GimpImage        *image  = GIMP_IMAGE (data);
648   GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config);
649   GtkWidget        *button;
650   gint              radius_x;
651   gint              radius_y;
652 
653   button = g_object_get_data (G_OBJECT (widget), "edge-lock-toggle");
654 
655   g_object_set (config,
656                 "selection-shrink-radius", size,
657                 "selection-shrink-edge-lock",
658                 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)),
659                 NULL);
660 
661   radius_x = ROUND (config->selection_shrink_radius);
662   radius_y = ROUND (config->selection_shrink_radius);
663 
664   if (unit != GIMP_UNIT_PIXEL)
665     {
666       gdouble xres;
667       gdouble yres;
668       gdouble factor;
669 
670       gimp_image_get_resolution (image, &xres, &yres);
671 
672       factor = (MAX (xres, yres) /
673                 MIN (xres, yres));
674 
675       if (xres == MIN (xres, yres))
676         radius_y *= factor;
677       else
678         radius_x *= factor;
679     }
680 
681   gimp_channel_shrink (gimp_image_get_mask (image), radius_x, radius_y,
682                        config->selection_shrink_edge_lock,
683                        TRUE);
684   gimp_image_flush (image);
685 }
686