1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * GimpForegroundSelectTool
5  * Copyright (C) 2005  Sven Neumann <sven@gimp.org>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <string.h>
24 
25 #include <gegl.h>
26 #include <gtk/gtk.h>
27 #include <gdk/gdkkeysyms.h>
28 
29 #include "libgimpmath/gimpmath.h"
30 #include "libgimpbase/gimpbase.h"
31 #include "libgimpcolor/gimpcolor.h"
32 #include "libgimpwidgets/gimpwidgets.h"
33 
34 #include "tools-types.h"
35 
36 #include "config/gimpguiconfig.h"
37 
38 #include "gegl/gimp-gegl-loops.h"
39 #include "gegl/gimp-gegl-mask.h"
40 #include "gegl/gimp-gegl-utils.h"
41 
42 #include "core/gimp.h"
43 #include "core/gimpchannel-select.h"
44 #include "core/gimpdrawable-foreground-extract.h"
45 #include "core/gimperror.h"
46 #include "core/gimpimage.h"
47 #include "core/gimplayer.h"
48 #include "core/gimplayermask.h"
49 #include "core/gimpprogress.h"
50 #include "core/gimpscanconvert.h"
51 
52 #include "widgets/gimphelp-ids.h"
53 #include "widgets/gimpwidgets-utils.h"
54 
55 #include "display/gimpcanvasitem.h"
56 #include "display/gimpcanvasbufferpreview.h"
57 #include "display/gimpdisplay.h"
58 #include "display/gimpdisplayshell.h"
59 #include "display/gimptoolgui.h"
60 
61 #include "gimpforegroundselecttool.h"
62 #include "gimpforegroundselectoptions.h"
63 #include "gimptoolcontrol.h"
64 
65 #include "gimp-intl.h"
66 
67 
68 #define FAR_OUTSIDE -10000
69 
70 
71 typedef struct _StrokeUndo StrokeUndo;
72 
73 struct _StrokeUndo
74 {
75   GeglBuffer          *saved_trimap;
76   gint                 trimap_x;
77   gint                 trimap_y;
78   GimpMattingDrawMode  draw_mode;
79   gint                 stroke_width;
80 };
81 
82 
83 static void   gimp_foreground_select_tool_finalize       (GObject          *object);
84 
85 static gboolean  gimp_foreground_select_tool_initialize  (GimpTool         *tool,
86                                                           GimpDisplay      *display,
87                                                           GError          **error);
88 static void   gimp_foreground_select_tool_control        (GimpTool         *tool,
89                                                           GimpToolAction    action,
90                                                           GimpDisplay      *display);
91 static void   gimp_foreground_select_tool_button_press   (GimpTool         *tool,
92                                                           const GimpCoords *coords,
93                                                           guint32           time,
94                                                           GdkModifierType   state,
95                                                           GimpButtonPressType press_type,
96                                                           GimpDisplay      *display);
97 static void   gimp_foreground_select_tool_button_release (GimpTool         *tool,
98                                                           const GimpCoords *coords,
99                                                           guint32           time,
100                                                           GdkModifierType   state,
101                                                           GimpButtonReleaseType release_type,
102                                                           GimpDisplay      *display);
103 static void   gimp_foreground_select_tool_motion         (GimpTool         *tool,
104                                                           const GimpCoords *coords,
105                                                           guint32           time,
106                                                           GdkModifierType   state,
107                                                           GimpDisplay      *display);
108 static gboolean  gimp_foreground_select_tool_key_press   (GimpTool         *tool,
109                                                           GdkEventKey      *kevent,
110                                                           GimpDisplay      *display);
111 static void   gimp_foreground_select_tool_modifier_key   (GimpTool         *tool,
112                                                           GdkModifierType   key,
113                                                           gboolean          press,
114                                                           GdkModifierType   state,
115                                                           GimpDisplay      *display);
116 static void   gimp_foreground_select_tool_active_modifier_key
117                                                          (GimpTool         *tool,
118                                                           GdkModifierType   key,
119                                                           gboolean          press,
120                                                           GdkModifierType   state,
121                                                           GimpDisplay      *display);
122 static void   gimp_foreground_select_tool_oper_update    (GimpTool         *tool,
123                                                           const GimpCoords *coords,
124                                                           GdkModifierType   state,
125                                                           gboolean          proximity,
126                                                           GimpDisplay      *display);
127 static void   gimp_foreground_select_tool_cursor_update  (GimpTool         *tool,
128                                                           const GimpCoords *coords,
129                                                           GdkModifierType   state,
130                                                           GimpDisplay      *display);
131 static const gchar * gimp_foreground_select_tool_can_undo
132                                                          (GimpTool         *tool,
133                                                           GimpDisplay      *display);
134 static const gchar * gimp_foreground_select_tool_can_redo
135                                                          (GimpTool         *tool,
136                                                           GimpDisplay      *display);
137 static gboolean   gimp_foreground_select_tool_undo       (GimpTool         *tool,
138                                                           GimpDisplay      *display);
139 static gboolean   gimp_foreground_select_tool_redo       (GimpTool         *tool,
140                                                           GimpDisplay      *display);
141 static void   gimp_foreground_select_tool_options_notify (GimpTool         *tool,
142                                                           GimpToolOptions  *options,
143                                                           const GParamSpec *pspec);
144 
145 static void   gimp_foreground_select_tool_draw           (GimpDrawTool     *draw_tool);
146 
147 static void   gimp_foreground_select_tool_confirm        (GimpPolygonSelectTool *poly_sel,
148                                                           GimpDisplay           *display);
149 
150 static void   gimp_foreground_select_tool_halt           (GimpForegroundSelectTool *fg_select);
151 static void   gimp_foreground_select_tool_commit         (GimpForegroundSelectTool *fg_select);
152 
153 static void   gimp_foreground_select_tool_set_trimap     (GimpForegroundSelectTool *fg_select);
154 static void   gimp_foreground_select_tool_set_preview    (GimpForegroundSelectTool *fg_select);
155 static void   gimp_foreground_select_tool_preview        (GimpForegroundSelectTool *fg_select);
156 
157 static void   gimp_foreground_select_tool_stroke_paint   (GimpForegroundSelectTool *fg_select);
158 static void   gimp_foreground_select_tool_cancel_paint   (GimpForegroundSelectTool *fg_select);
159 
160 static void   gimp_foreground_select_tool_response       (GimpToolGui              *gui,
161                                                           gint                      response_id,
162                                                           GimpForegroundSelectTool *fg_select);
163 static void   gimp_foreground_select_tool_preview_toggled(GtkToggleButton          *button,
164                                                           GimpForegroundSelectTool *fg_select);
165 
166 static void   gimp_foreground_select_tool_update_gui     (GimpForegroundSelectTool *fg_select);
167 
168 static StrokeUndo * gimp_foreground_select_undo_new      (GeglBuffer               *trimap,
169                                                           GArray                   *stroke,
170                                                           GimpMattingDrawMode       draw_mode,
171                                                           gint                      stroke_width);
172 static void         gimp_foreground_select_undo_pop      (StrokeUndo               *undo,
173                                                           GeglBuffer               *trimap);
174 static void         gimp_foreground_select_undo_free     (StrokeUndo               *undo);
175 
176 
G_DEFINE_TYPE(GimpForegroundSelectTool,gimp_foreground_select_tool,GIMP_TYPE_POLYGON_SELECT_TOOL)177 G_DEFINE_TYPE (GimpForegroundSelectTool, gimp_foreground_select_tool,
178                GIMP_TYPE_POLYGON_SELECT_TOOL)
179 
180 #define parent_class gimp_foreground_select_tool_parent_class
181 
182 
183 void
184 gimp_foreground_select_tool_register (GimpToolRegisterCallback  callback,
185                                       gpointer                  data)
186 {
187   (* callback) (GIMP_TYPE_FOREGROUND_SELECT_TOOL,
188                 GIMP_TYPE_FOREGROUND_SELECT_OPTIONS,
189                 gimp_foreground_select_options_gui,
190                 GIMP_CONTEXT_PROP_MASK_FOREGROUND |
191                 GIMP_CONTEXT_PROP_MASK_BACKGROUND,
192                 "gimp-foreground-select-tool",
193                 _("Foreground Select"),
194                 _("Foreground Select Tool: Select a region containing foreground objects"),
195                 N_("F_oreground Select"), NULL,
196                 NULL, GIMP_HELP_TOOL_FOREGROUND_SELECT,
197                 GIMP_ICON_TOOL_FOREGROUND_SELECT,
198                 data);
199 }
200 
201 static void
gimp_foreground_select_tool_class_init(GimpForegroundSelectToolClass * klass)202 gimp_foreground_select_tool_class_init (GimpForegroundSelectToolClass *klass)
203 {
204   GObjectClass               *object_class    = G_OBJECT_CLASS (klass);
205   GimpToolClass              *tool_class      = GIMP_TOOL_CLASS (klass);
206   GimpDrawToolClass          *draw_tool_class = GIMP_DRAW_TOOL_CLASS (klass);
207   GimpPolygonSelectToolClass *polygon_select_tool_class;
208 
209   polygon_select_tool_class = GIMP_POLYGON_SELECT_TOOL_CLASS (klass);
210 
211   object_class->finalize             = gimp_foreground_select_tool_finalize;
212 
213   tool_class->initialize             = gimp_foreground_select_tool_initialize;
214   tool_class->control                = gimp_foreground_select_tool_control;
215   tool_class->button_press           = gimp_foreground_select_tool_button_press;
216   tool_class->button_release         = gimp_foreground_select_tool_button_release;
217   tool_class->motion                 = gimp_foreground_select_tool_motion;
218   tool_class->key_press              = gimp_foreground_select_tool_key_press;
219   tool_class->modifier_key           = gimp_foreground_select_tool_modifier_key;
220   tool_class->active_modifier_key    = gimp_foreground_select_tool_active_modifier_key;
221   tool_class->oper_update            = gimp_foreground_select_tool_oper_update;
222   tool_class->cursor_update          = gimp_foreground_select_tool_cursor_update;
223   tool_class->can_undo               = gimp_foreground_select_tool_can_undo;
224   tool_class->can_redo               = gimp_foreground_select_tool_can_redo;
225   tool_class->undo                   = gimp_foreground_select_tool_undo;
226   tool_class->redo                   = gimp_foreground_select_tool_redo;
227   tool_class->options_notify         = gimp_foreground_select_tool_options_notify;
228 
229   draw_tool_class->draw              = gimp_foreground_select_tool_draw;
230 
231   polygon_select_tool_class->confirm = gimp_foreground_select_tool_confirm;
232 }
233 
234 static void
gimp_foreground_select_tool_init(GimpForegroundSelectTool * fg_select)235 gimp_foreground_select_tool_init (GimpForegroundSelectTool *fg_select)
236 {
237   GimpTool *tool = GIMP_TOOL (fg_select);
238 
239   gimp_tool_control_set_motion_mode (tool->control, GIMP_MOTION_MODE_EXACT);
240   gimp_tool_control_set_scroll_lock (tool->control, FALSE);
241   gimp_tool_control_set_preserve    (tool->control, FALSE);
242   gimp_tool_control_set_dirty_mask  (tool->control,
243                                      GIMP_DIRTY_IMAGE_SIZE |
244                                      GIMP_DIRTY_ACTIVE_DRAWABLE);
245   gimp_tool_control_set_precision   (tool->control,
246                                      GIMP_CURSOR_PRECISION_SUBPIXEL);
247   gimp_tool_control_set_tool_cursor (tool->control,
248                                      GIMP_TOOL_CURSOR_FREE_SELECT);
249 
250   gimp_tool_control_set_action_size (tool->control,
251                                      "tools/tools-foreground-select-brush-size-set");
252 
253   fg_select->state = MATTING_STATE_FREE_SELECT;
254   fg_select->grayscale_preview = NULL;
255 }
256 
257 static void
gimp_foreground_select_tool_finalize(GObject * object)258 gimp_foreground_select_tool_finalize (GObject *object)
259 {
260   GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (object);
261 
262   g_clear_object (&fg_select->gui);
263   fg_select->preview_toggle = NULL;
264 
265   if (fg_select->stroke)
266     g_warning ("%s: stroke should be NULL at this point", G_STRLOC);
267 
268   if (fg_select->mask)
269     g_warning ("%s: mask should be NULL at this point", G_STRLOC);
270 
271   if (fg_select->trimap)
272     g_warning ("%s: mask should be NULL at this point", G_STRLOC);
273 
274   G_OBJECT_CLASS (parent_class)->finalize (object);
275 }
276 
277 static gboolean
gimp_foreground_select_tool_initialize(GimpTool * tool,GimpDisplay * display,GError ** error)278 gimp_foreground_select_tool_initialize (GimpTool     *tool,
279                                         GimpDisplay  *display,
280                                         GError      **error)
281 {
282   GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (tool);
283   GimpGuiConfig            *config    = GIMP_GUI_CONFIG (display->gimp->config);
284   GimpImage                *image     = gimp_display_get_image (display);
285   GimpDrawable             *drawable  = gimp_image_get_active_drawable (image);
286   GimpDisplayShell         *shell     = gimp_display_get_shell (display);
287 
288   if (! drawable)
289     return FALSE;
290 
291   if (! gimp_item_is_visible (GIMP_ITEM (drawable)) &&
292       ! config->edit_non_visible)
293     {
294       g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
295                            _("The active layer is not visible."));
296       return FALSE;
297     }
298 
299   tool->display = display;
300 
301   /*  enable double click for the FreeSelectTool, because it may have been
302    *  disabled if the tool has switched to MATTING_STATE_PAINT_TRIMAP,
303    *  in gimp_foreground_select_tool_set_trimap().
304    */
305   gimp_tool_control_set_wants_double_click (tool->control, TRUE);
306 
307   fg_select->state = MATTING_STATE_FREE_SELECT;
308 
309   if (! fg_select->gui)
310     {
311       fg_select->gui =
312         gimp_tool_gui_new (tool->tool_info,
313                            NULL,
314                            _("Dialog for foreground select"),
315                            NULL, NULL,
316                            gtk_widget_get_screen (GTK_WIDGET (shell)),
317                            gimp_widget_get_monitor (GTK_WIDGET (shell)),
318                            TRUE,
319 
320                            _("_Cancel"), GTK_RESPONSE_CANCEL,
321                            _("_Select"), GTK_RESPONSE_APPLY,
322 
323                            NULL);
324 
325       gimp_tool_gui_set_auto_overlay (fg_select->gui, TRUE);
326 
327       g_signal_connect (fg_select->gui, "response",
328                         G_CALLBACK (gimp_foreground_select_tool_response),
329                         fg_select);
330 
331       fg_select->preview_toggle =
332         gtk_check_button_new_with_mnemonic (_("_Preview mask"));
333       gtk_box_pack_start (GTK_BOX (gimp_tool_gui_get_vbox (fg_select->gui)),
334                           fg_select->preview_toggle, FALSE, FALSE, 0);
335       gtk_widget_show (fg_select->preview_toggle);
336 
337       g_signal_connect (fg_select->preview_toggle, "toggled",
338                         G_CALLBACK (gimp_foreground_select_tool_preview_toggled),
339                         fg_select);
340     }
341 
342   gimp_tool_gui_set_description (fg_select->gui,
343                                  _("Select foreground pixels"));
344 
345   gimp_tool_gui_set_response_sensitive (fg_select->gui, GTK_RESPONSE_APPLY,
346                                         FALSE);
347   gtk_widget_set_sensitive (fg_select->preview_toggle, FALSE);
348 
349   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fg_select->preview_toggle),
350                                 FALSE);
351 
352   gimp_tool_gui_set_shell (fg_select->gui, shell);
353   gimp_tool_gui_set_viewable (fg_select->gui, GIMP_VIEWABLE (drawable));
354 
355   gimp_tool_gui_show (fg_select->gui);
356 
357   return TRUE;
358 }
359 
360 static void
gimp_foreground_select_tool_control(GimpTool * tool,GimpToolAction action,GimpDisplay * display)361 gimp_foreground_select_tool_control (GimpTool       *tool,
362                                      GimpToolAction  action,
363                                      GimpDisplay    *display)
364 {
365   GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (tool);
366 
367   switch (action)
368     {
369     case GIMP_TOOL_ACTION_PAUSE:
370     case GIMP_TOOL_ACTION_RESUME:
371       break;
372 
373     case GIMP_TOOL_ACTION_HALT:
374       gimp_foreground_select_tool_halt (fg_select);
375       break;
376 
377     case GIMP_TOOL_ACTION_COMMIT:
378       gimp_foreground_select_tool_commit (fg_select);
379       break;
380     }
381 
382   GIMP_TOOL_CLASS (parent_class)->control (tool, action, display);
383 }
384 
385 static void
gimp_foreground_select_tool_button_press(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonPressType press_type,GimpDisplay * display)386 gimp_foreground_select_tool_button_press (GimpTool            *tool,
387                                           const GimpCoords    *coords,
388                                           guint32              time,
389                                           GdkModifierType      state,
390                                           GimpButtonPressType  press_type,
391                                           GimpDisplay         *display)
392 {
393   GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (tool);
394   GimpDrawTool             *draw_tool = GIMP_DRAW_TOOL (tool);
395 
396   if (fg_select->state == MATTING_STATE_FREE_SELECT)
397     {
398       GIMP_TOOL_CLASS (parent_class)->button_press (tool, coords, time, state,
399                                                     press_type, display);
400     }
401   else
402     {
403       GimpVector2 point = gimp_vector2_new (coords->x, coords->y);
404 
405       gimp_draw_tool_pause (draw_tool);
406 
407       if (gimp_draw_tool_is_active (draw_tool) && draw_tool->display != display)
408         gimp_draw_tool_stop (draw_tool);
409 
410       gimp_tool_control_activate (tool->control);
411 
412       fg_select->last_coords = *coords;
413 
414       g_return_if_fail (fg_select->stroke == NULL);
415       fg_select->stroke = g_array_new (FALSE, FALSE, sizeof (GimpVector2));
416 
417       g_array_append_val (fg_select->stroke, point);
418 
419       if (! gimp_draw_tool_is_active (draw_tool))
420         gimp_draw_tool_start (draw_tool, display);
421 
422       gimp_draw_tool_resume (draw_tool);
423     }
424 }
425 
426 static void
gimp_foreground_select_tool_button_release(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpButtonReleaseType release_type,GimpDisplay * display)427 gimp_foreground_select_tool_button_release (GimpTool              *tool,
428                                             const GimpCoords      *coords,
429                                             guint32                time,
430                                             GdkModifierType        state,
431                                             GimpButtonReleaseType  release_type,
432                                             GimpDisplay           *display)
433 {
434   GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (tool);
435 
436   if (fg_select->state == MATTING_STATE_FREE_SELECT)
437     {
438       GIMP_TOOL_CLASS (parent_class)->button_release (tool, coords, time, state,
439                                                       release_type, display);
440     }
441   else
442     {
443       gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
444 
445       gimp_tool_control_halt (tool->control);
446 
447       if (release_type == GIMP_BUTTON_RELEASE_CANCEL)
448         {
449           gimp_foreground_select_tool_cancel_paint (fg_select);
450         }
451       else
452         {
453           gimp_foreground_select_tool_stroke_paint (fg_select);
454 
455           if (fg_select->state == MATTING_STATE_PREVIEW_MASK)
456             gimp_foreground_select_tool_preview (fg_select);
457           else
458             gimp_foreground_select_tool_set_trimap (fg_select);
459         }
460 
461       gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
462     }
463 }
464 
465 static void
gimp_foreground_select_tool_motion(GimpTool * tool,const GimpCoords * coords,guint32 time,GdkModifierType state,GimpDisplay * display)466 gimp_foreground_select_tool_motion (GimpTool         *tool,
467                                     const GimpCoords *coords,
468                                     guint32           time,
469                                     GdkModifierType   state,
470                                     GimpDisplay      *display)
471 {
472   GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (tool);
473 
474   if (fg_select->state == MATTING_STATE_FREE_SELECT)
475     {
476       GIMP_TOOL_CLASS (parent_class)->motion (tool, coords, time, state,
477                                               display);
478     }
479   else
480     {
481       GimpVector2 *last = &g_array_index (fg_select->stroke,
482                                           GimpVector2,
483                                           fg_select->stroke->len - 1);
484 
485       gimp_draw_tool_pause (GIMP_DRAW_TOOL (tool));
486 
487       fg_select->last_coords = *coords;
488 
489       if (last->x != (gint) coords->x || last->y != (gint) coords->y)
490         {
491           GimpVector2 point = gimp_vector2_new (coords->x, coords->y);
492 
493           g_array_append_val (fg_select->stroke, point);
494         }
495 
496       gimp_draw_tool_resume (GIMP_DRAW_TOOL (tool));
497     }
498 }
499 
500 static gboolean
gimp_foreground_select_tool_key_press(GimpTool * tool,GdkEventKey * kevent,GimpDisplay * display)501 gimp_foreground_select_tool_key_press (GimpTool    *tool,
502                                        GdkEventKey *kevent,
503                                        GimpDisplay *display)
504 {
505   GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (tool);
506 
507   if (fg_select->state == MATTING_STATE_FREE_SELECT)
508     {
509       return GIMP_TOOL_CLASS (parent_class)->key_press (tool, kevent, display);
510     }
511   else
512     {
513       if (display != tool->display)
514         return FALSE;
515 
516       switch (kevent->keyval)
517         {
518         case GDK_KEY_Return:
519         case GDK_KEY_KP_Enter:
520         case GDK_KEY_ISO_Enter:
521           if (fg_select->state == MATTING_STATE_PAINT_TRIMAP)
522             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fg_select->preview_toggle),
523                                           TRUE);
524           else
525             gimp_foreground_select_tool_response (fg_select->gui,
526                                                   GTK_RESPONSE_APPLY, fg_select);
527           return TRUE;
528 
529         case GDK_KEY_Escape:
530           if (fg_select->state == MATTING_STATE_PAINT_TRIMAP)
531             gimp_foreground_select_tool_response (fg_select->gui,
532                                                   GTK_RESPONSE_CANCEL, fg_select);
533           else
534             gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fg_select->preview_toggle),
535                                           FALSE);
536           return TRUE;
537 
538         default:
539           return FALSE;
540         }
541     }
542 }
543 
544 static void
gimp_foreground_select_tool_modifier_key(GimpTool * tool,GdkModifierType key,gboolean press,GdkModifierType state,GimpDisplay * display)545 gimp_foreground_select_tool_modifier_key (GimpTool        *tool,
546                                           GdkModifierType  key,
547                                           gboolean         press,
548                                           GdkModifierType  state,
549                                           GimpDisplay     *display)
550 {
551   GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (tool);
552 
553   if (fg_select->state == MATTING_STATE_FREE_SELECT)
554     {
555       GIMP_TOOL_CLASS (parent_class)->modifier_key (tool, key, press, state,
556                                                     display);
557     }
558   else
559     {
560 #if 0
561       if (key == gimp_get_toggle_behavior_mask ())
562         {
563           GimpForegroundSelectOptions *options;
564 
565           options = GIMP_FOREGROUND_SELECT_TOOL_GET_OPTIONS (tool);
566 
567           g_object_set (options,
568                         "background", ! options->background,
569                         NULL);
570         }
571 #endif
572     }
573 }
574 
575 static void
gimp_foreground_select_tool_active_modifier_key(GimpTool * tool,GdkModifierType key,gboolean press,GdkModifierType state,GimpDisplay * display)576 gimp_foreground_select_tool_active_modifier_key (GimpTool        *tool,
577                                                  GdkModifierType  key,
578                                                  gboolean         press,
579                                                  GdkModifierType  state,
580                                                  GimpDisplay     *display)
581 {
582   GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (tool);
583 
584   if (fg_select->state == MATTING_STATE_FREE_SELECT)
585     {
586       GIMP_TOOL_CLASS (parent_class)->active_modifier_key (tool, key, press,
587                                                            state, display);
588     }
589 }
590 
591 static void
gimp_foreground_select_tool_oper_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,gboolean proximity,GimpDisplay * display)592 gimp_foreground_select_tool_oper_update (GimpTool         *tool,
593                                          const GimpCoords *coords,
594                                          GdkModifierType   state,
595                                          gboolean          proximity,
596                                          GimpDisplay      *display)
597 {
598   GimpForegroundSelectTool    *fg_select    = GIMP_FOREGROUND_SELECT_TOOL (tool);
599   GimpForegroundSelectOptions *options;
600   const gchar                 *status_stage = NULL;
601   const gchar                 *status_mode  = NULL;
602 
603   options = GIMP_FOREGROUND_SELECT_TOOL_GET_OPTIONS (fg_select);
604 
605   GIMP_TOOL_CLASS (parent_class)->oper_update (tool, coords, state, proximity,
606                                                display);
607 
608   if (fg_select->state == MATTING_STATE_FREE_SELECT)
609     {
610       if (GIMP_SELECTION_TOOL (tool)->function == SELECTION_SELECT)
611         {
612           gint n_points;
613 
614           gimp_polygon_select_tool_get_points (GIMP_POLYGON_SELECT_TOOL (tool),
615                                                NULL, &n_points);
616 
617           if (n_points > 2)
618             {
619               status_mode = _("Roughly outline the object to extract");
620               status_stage = _("press Enter to refine.");
621             }
622           else
623             {
624               status_stage = _("Roughly outline the object to extract");
625             }
626         }
627     }
628   else
629     {
630       GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (tool);
631 
632       gimp_draw_tool_pause (draw_tool);
633 
634       if (proximity)
635         {
636           fg_select->last_coords = *coords;
637         }
638       else
639         {
640           fg_select->last_coords.x = FAR_OUTSIDE;
641           fg_select->last_coords.y = FAR_OUTSIDE;
642         }
643 
644       gimp_draw_tool_resume (draw_tool);
645 
646       if (options->draw_mode == GIMP_MATTING_DRAW_MODE_FOREGROUND)
647         status_mode = _("Selecting foreground");
648       else if (options->draw_mode == GIMP_MATTING_DRAW_MODE_BACKGROUND)
649         status_mode = _("Selecting background");
650       else
651         status_mode = _("Selecting unknown");
652 
653       if (fg_select->state == MATTING_STATE_PAINT_TRIMAP)
654         status_stage = _("press Enter to preview.");
655       else
656         status_stage = _("press Escape to exit preview or Enter to apply.");
657     }
658 
659   if (proximity && status_stage)
660     {
661       if (status_mode)
662         gimp_tool_replace_status (tool, display, "%s, %s", status_mode, status_stage);
663       else
664         gimp_tool_replace_status (tool, display, "%s", status_stage);
665     }
666 }
667 
668 static void
gimp_foreground_select_tool_cursor_update(GimpTool * tool,const GimpCoords * coords,GdkModifierType state,GimpDisplay * display)669 gimp_foreground_select_tool_cursor_update (GimpTool         *tool,
670                                            const GimpCoords *coords,
671                                            GdkModifierType   state,
672                                            GimpDisplay      *display)
673 {
674   GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (tool);
675 
676   if (fg_select->state == MATTING_STATE_PAINT_TRIMAP)
677     {
678       switch (GIMP_SELECTION_TOOL (tool)->function)
679         {
680         case SELECTION_MOVE_MASK:
681         case SELECTION_MOVE:
682         case SELECTION_MOVE_COPY:
683         case SELECTION_ANCHOR:
684           return;
685         default:
686           break;
687         }
688     }
689 
690   GIMP_TOOL_CLASS (parent_class)->cursor_update (tool, coords, state, display);
691 }
692 
693 static const gchar *
gimp_foreground_select_tool_can_undo(GimpTool * tool,GimpDisplay * display)694 gimp_foreground_select_tool_can_undo (GimpTool    *tool,
695                                       GimpDisplay *display)
696 {
697   GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (tool);
698 
699   if (fg_select->undo_stack)
700     {
701       StrokeUndo  *undo = fg_select->undo_stack->data;
702       const gchar *desc;
703 
704       if (gimp_enum_get_value (GIMP_TYPE_MATTING_DRAW_MODE, undo->draw_mode,
705                                NULL, NULL, &desc, NULL))
706         {
707           return desc;
708         }
709     }
710 
711   return NULL;
712 }
713 
714 static const gchar *
gimp_foreground_select_tool_can_redo(GimpTool * tool,GimpDisplay * display)715 gimp_foreground_select_tool_can_redo (GimpTool    *tool,
716                                       GimpDisplay *display)
717 {
718   GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (tool);
719 
720   if (fg_select->redo_stack)
721     {
722       StrokeUndo  *undo = fg_select->redo_stack->data;
723       const gchar *desc;
724 
725       if (gimp_enum_get_value (GIMP_TYPE_MATTING_DRAW_MODE, undo->draw_mode,
726                                NULL, NULL, &desc, NULL))
727         {
728           return desc;
729         }
730     }
731 
732   return NULL;
733 }
734 
735 static gboolean
gimp_foreground_select_tool_undo(GimpTool * tool,GimpDisplay * display)736 gimp_foreground_select_tool_undo (GimpTool    *tool,
737                                   GimpDisplay *display)
738 {
739   GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (tool);
740   StrokeUndo               *undo      = fg_select->undo_stack->data;
741 
742   gimp_foreground_select_undo_pop (undo, fg_select->trimap);
743 
744   fg_select->undo_stack = g_list_remove (fg_select->undo_stack, undo);
745   fg_select->redo_stack = g_list_prepend (fg_select->redo_stack, undo);
746 
747   if (fg_select->state == MATTING_STATE_PREVIEW_MASK)
748     gimp_foreground_select_tool_preview (fg_select);
749   else
750     gimp_foreground_select_tool_set_trimap (fg_select);
751 
752   return TRUE;
753 }
754 
755 static gboolean
gimp_foreground_select_tool_redo(GimpTool * tool,GimpDisplay * display)756 gimp_foreground_select_tool_redo (GimpTool    *tool,
757                                   GimpDisplay *display)
758 {
759   GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (tool);
760   StrokeUndo               *undo      = fg_select->redo_stack->data;
761 
762   gimp_foreground_select_undo_pop (undo, fg_select->trimap);
763 
764   fg_select->redo_stack = g_list_remove (fg_select->redo_stack, undo);
765   fg_select->undo_stack = g_list_prepend (fg_select->undo_stack, undo);
766 
767   if (fg_select->state == MATTING_STATE_PREVIEW_MASK)
768     gimp_foreground_select_tool_preview (fg_select);
769   else
770     gimp_foreground_select_tool_set_trimap (fg_select);
771 
772   return TRUE;
773 }
774 
775 static void
gimp_foreground_select_tool_options_notify(GimpTool * tool,GimpToolOptions * options,const GParamSpec * pspec)776 gimp_foreground_select_tool_options_notify (GimpTool         *tool,
777                                             GimpToolOptions  *options,
778                                             const GParamSpec *pspec)
779 {
780   GimpForegroundSelectTool    *fg_select = GIMP_FOREGROUND_SELECT_TOOL (tool);
781   GimpForegroundSelectOptions *fg_options;
782 
783   fg_options = GIMP_FOREGROUND_SELECT_OPTIONS (options);
784 
785   if (! tool->display)
786     return;
787 
788   if (! strcmp (pspec->name, "mask-color") ||
789       ! strcmp (pspec->name, "preview-mode"))
790     {
791       if (fg_select->state == MATTING_STATE_PAINT_TRIMAP)
792         {
793           gimp_foreground_select_tool_set_trimap (fg_select);
794         }
795       else if (fg_select->state == MATTING_STATE_PREVIEW_MASK)
796         {
797           gimp_foreground_select_tool_set_preview (fg_select);
798         }
799     }
800   else if (! strcmp (pspec->name, "engine"))
801     {
802       if (fg_select->state == MATTING_STATE_PREVIEW_MASK)
803         {
804           gimp_foreground_select_tool_preview (fg_select);
805         }
806     }
807   else if (! strcmp (pspec->name, "iterations"))
808     {
809       if (fg_options->engine == GIMP_MATTING_ENGINE_GLOBAL &&
810           fg_select->state   == MATTING_STATE_PREVIEW_MASK)
811         {
812           gimp_foreground_select_tool_preview (fg_select);
813         }
814     }
815   else if (! strcmp (pspec->name, "levels") ||
816            ! strcmp (pspec->name, "active-levels"))
817     {
818       if (fg_options->engine == GIMP_MATTING_ENGINE_LEVIN &&
819           fg_select->state   == MATTING_STATE_PREVIEW_MASK)
820         {
821           gimp_foreground_select_tool_preview (fg_select);
822         }
823     }
824 }
825 
826 static void
gimp_foreground_select_tool_get_area(GeglBuffer * mask,gint * x1,gint * y1,gint * x2,gint * y2)827 gimp_foreground_select_tool_get_area (GeglBuffer *mask,
828                                       gint       *x1,
829                                       gint       *y1,
830                                       gint       *x2,
831                                       gint       *y2)
832 {
833   gint width;
834   gint height;
835 
836   gimp_gegl_mask_bounds (mask, x1, y1, x2, y2);
837 
838   width  = *x2 - *x1;
839   height = *y2 - *y1;
840 
841   *x1 = MAX (*x1 - width  / 2, 0);
842   *y1 = MAX (*y1 - height / 2, 0);
843   *x2 = MIN (*x2 + width  / 2, gimp_item_get_width  (GIMP_ITEM (mask)));
844   *y2 = MIN (*y2 + height / 2, gimp_item_get_height (GIMP_ITEM (mask)));
845 }
846 
847 static void
gimp_foreground_select_tool_draw(GimpDrawTool * draw_tool)848 gimp_foreground_select_tool_draw (GimpDrawTool *draw_tool)
849 {
850   GimpTool                    *tool      = GIMP_TOOL (draw_tool);
851   GimpForegroundSelectTool    *fg_select = GIMP_FOREGROUND_SELECT_TOOL (tool);
852   GimpForegroundSelectOptions *options;
853 
854   options = GIMP_FOREGROUND_SELECT_TOOL_GET_OPTIONS (tool);
855 
856   if (fg_select->state == MATTING_STATE_FREE_SELECT)
857     {
858       GIMP_DRAW_TOOL_CLASS (parent_class)->draw (draw_tool);
859       return;
860     }
861   else
862     {
863       gint    x      = fg_select->last_coords.x;
864       gint    y      = fg_select->last_coords.y;
865       gdouble radius = options->stroke_width / 2.0f;
866 
867       if (fg_select->stroke)
868         {
869           GimpDisplayShell *shell = gimp_display_get_shell (draw_tool->display);
870 
871           gimp_draw_tool_add_pen (draw_tool,
872                                   (const GimpVector2 *) fg_select->stroke->data,
873                                   fg_select->stroke->len,
874                                   GIMP_CONTEXT (options),
875                                   GIMP_ACTIVE_COLOR_FOREGROUND,
876                                   options->stroke_width * shell->scale_y);
877         }
878 
879       /*  warn if the user is drawing outside of the working area  */
880       if (FALSE)
881         {
882           gint x1, y1;
883           gint x2, y2;
884 
885           gimp_foreground_select_tool_get_area (fg_select->mask,
886                                                 &x1, &y1, &x2, &y2);
887 
888           if (x < x1 + radius || x > x2 - radius ||
889               y < y1 + radius || y > y2 - radius)
890             {
891               gimp_draw_tool_add_rectangle (draw_tool, FALSE,
892                                             x1, y1,
893                                             x2 - x1, y2 - y1);
894             }
895         }
896 
897       if (x > FAR_OUTSIDE && y > FAR_OUTSIDE)
898         gimp_draw_tool_add_arc (draw_tool, FALSE,
899                                 x - radius, y - radius,
900                                 2 * radius, 2 * radius,
901                                 0.0, 2.0 * G_PI);
902 
903       if (fg_select->grayscale_preview)
904         gimp_draw_tool_add_preview (draw_tool, fg_select->grayscale_preview);
905     }
906 }
907 
908 static void
gimp_foreground_select_tool_confirm(GimpPolygonSelectTool * poly_sel,GimpDisplay * display)909 gimp_foreground_select_tool_confirm (GimpPolygonSelectTool *poly_sel,
910                                      GimpDisplay           *display)
911 {
912   GimpForegroundSelectTool *fg_select = GIMP_FOREGROUND_SELECT_TOOL (poly_sel);
913   GimpImage                *image     = gimp_display_get_image (display);
914   GimpDrawable             *drawable  = gimp_image_get_active_drawable (image);
915   GimpItem                 *item      = GIMP_ITEM (drawable);
916 
917   if (drawable && fg_select->state == MATTING_STATE_FREE_SELECT)
918     {
919       GimpScanConvert   *scan_convert = gimp_scan_convert_new ();
920       const GimpVector2 *points;
921       gint               n_points;
922 
923       gimp_polygon_select_tool_get_points (poly_sel, &points, &n_points);
924 
925       gimp_scan_convert_add_polyline (scan_convert, n_points, points, TRUE);
926 
927       fg_select->trimap =
928         gegl_buffer_new (GEGL_RECTANGLE (gimp_item_get_offset_x (item),
929                                          gimp_item_get_offset_y (item),
930                                          gimp_item_get_width    (item),
931                                          gimp_item_get_height   (item)),
932                          gimp_image_get_mask_format (image));
933 
934       gimp_scan_convert_render_value (scan_convert, fg_select->trimap,
935                                       0, 0, 0.5);
936       gimp_scan_convert_free (scan_convert);
937 
938       fg_select->grayscale_preview =
939           gimp_canvas_buffer_preview_new (gimp_display_get_shell (display),
940                                           fg_select->trimap);
941 
942       gimp_foreground_select_tool_set_trimap (fg_select);
943     }
944 }
945 
946 static void
gimp_foreground_select_tool_halt(GimpForegroundSelectTool * fg_select)947 gimp_foreground_select_tool_halt (GimpForegroundSelectTool *fg_select)
948 {
949   GimpTool     *tool = GIMP_TOOL (fg_select);
950   GimpDrawTool *draw_tool = GIMP_DRAW_TOOL (fg_select);
951 
952   if (draw_tool->preview)
953     {
954       gimp_draw_tool_remove_preview (draw_tool, fg_select->grayscale_preview);
955     }
956 
957   g_clear_object (&fg_select->grayscale_preview);
958   g_clear_object (&fg_select->trimap);
959   g_clear_object (&fg_select->mask);
960 
961   if (fg_select->undo_stack)
962     {
963       g_list_free_full (fg_select->undo_stack,
964                         (GDestroyNotify) gimp_foreground_select_undo_free);
965       fg_select->undo_stack = NULL;
966     }
967 
968   if (fg_select->redo_stack)
969     {
970       g_list_free_full (fg_select->redo_stack,
971                         (GDestroyNotify) gimp_foreground_select_undo_free);
972       fg_select->redo_stack = NULL;
973     }
974 
975   if (tool->display)
976     gimp_display_shell_set_mask (gimp_display_get_shell (tool->display),
977                                  NULL, 0, 0, NULL, FALSE);
978 
979   gimp_tool_control_set_tool_cursor        (tool->control,
980                                             GIMP_TOOL_CURSOR_FREE_SELECT);
981   gimp_tool_control_set_toggle_tool_cursor (tool->control,
982                                             GIMP_TOOL_CURSOR_FREE_SELECT);
983 
984   gimp_tool_control_set_toggled (tool->control, FALSE);
985 
986   /*  set precision to SUBPIXEL, because it may have been changed to
987    *  PIXEL_CENTER if the tool has switched to MATTING_STATE_PAINT_TRIMAP,
988    *  in gimp_foreground_select_tool_set_trimap().
989    */
990   gimp_tool_control_set_precision (tool->control,
991                                    GIMP_CURSOR_PRECISION_SUBPIXEL);
992 
993   fg_select->state = MATTING_STATE_FREE_SELECT;
994 
995   /*  update the undo actions / menu items  */
996   if (tool->display)
997     gimp_image_flush (gimp_display_get_image (tool->display));
998 
999   tool->display  = NULL;
1000   tool->drawable = NULL;
1001 
1002   if (fg_select->gui)
1003     gimp_tool_gui_hide (fg_select->gui);
1004 }
1005 
1006 static void
gimp_foreground_select_tool_commit(GimpForegroundSelectTool * fg_select)1007 gimp_foreground_select_tool_commit (GimpForegroundSelectTool *fg_select)
1008 {
1009   GimpTool             *tool    = GIMP_TOOL (fg_select);
1010   GimpSelectionOptions *options = GIMP_SELECTION_TOOL_GET_OPTIONS (fg_select);
1011 
1012   if (tool->display && fg_select->state != MATTING_STATE_FREE_SELECT)
1013     {
1014       GimpImage *image = gimp_display_get_image (tool->display);
1015 
1016       if (fg_select->state != MATTING_STATE_PREVIEW_MASK)
1017         gimp_foreground_select_tool_preview (fg_select);
1018 
1019       gimp_channel_select_buffer (gimp_image_get_mask (image),
1020                                   C_("command", "Foreground Select"),
1021                                   fg_select->mask,
1022                                   0, /* x offset */
1023                                   0, /* y offset */
1024                                   options->operation,
1025                                   options->feather,
1026                                   options->feather_radius,
1027                                   options->feather_radius);
1028 
1029       gimp_image_flush (image);
1030     }
1031 }
1032 
1033 static void
gimp_foreground_select_tool_set_trimap(GimpForegroundSelectTool * fg_select)1034 gimp_foreground_select_tool_set_trimap (GimpForegroundSelectTool *fg_select)
1035 {
1036   GimpTool                    *tool = GIMP_TOOL (fg_select);
1037   GimpForegroundSelectOptions *options;
1038 
1039   g_return_if_fail (fg_select->trimap != NULL);
1040 
1041   options = GIMP_FOREGROUND_SELECT_TOOL_GET_OPTIONS (tool);
1042 
1043   gimp_polygon_select_tool_halt (GIMP_POLYGON_SELECT_TOOL (fg_select));
1044 
1045   if (options->preview_mode == GIMP_MATTING_PREVIEW_MODE_ON_COLOR)
1046     {
1047       if (fg_select->grayscale_preview)
1048         gimp_canvas_item_set_visible (fg_select->grayscale_preview, FALSE);
1049 
1050       gimp_display_shell_set_mask (gimp_display_get_shell (tool->display),
1051                                    fg_select->trimap, 0, 0,
1052                                    &options->mask_color, TRUE);
1053     }
1054   else
1055     {
1056       gimp_display_shell_set_mask (gimp_display_get_shell (tool->display),
1057                                    NULL, 0, 0, NULL, FALSE);
1058 
1059       if (fg_select->grayscale_preview)
1060         {
1061           g_object_set (fg_select->grayscale_preview, "buffer",
1062                         fg_select->trimap, NULL);
1063 
1064           gimp_canvas_item_set_visible (fg_select->grayscale_preview, TRUE);
1065         }
1066     }
1067 
1068   gimp_tool_control_set_tool_cursor        (tool->control,
1069                                             GIMP_TOOL_CURSOR_PAINTBRUSH);
1070   gimp_tool_control_set_toggle_tool_cursor (tool->control,
1071                                             GIMP_TOOL_CURSOR_PAINTBRUSH);
1072 
1073   gimp_tool_control_set_toggled (tool->control, FALSE);
1074 
1075   /* disable double click in paint trimap state */
1076   gimp_tool_control_set_wants_double_click (tool->control, FALSE);
1077 
1078   /* set precision to PIXEL_CENTER in paint trimap state */
1079   gimp_tool_control_set_precision (tool->control,
1080                                    GIMP_CURSOR_PRECISION_PIXEL_CENTER);
1081 
1082   fg_select->state = MATTING_STATE_PAINT_TRIMAP;
1083 
1084   gimp_foreground_select_tool_update_gui (fg_select);
1085 }
1086 
1087 static void
gimp_foreground_select_tool_set_preview(GimpForegroundSelectTool * fg_select)1088 gimp_foreground_select_tool_set_preview (GimpForegroundSelectTool *fg_select)
1089 {
1090 
1091   GimpTool                    *tool = GIMP_TOOL (fg_select);
1092   GimpForegroundSelectOptions *options;
1093 
1094   g_return_if_fail (fg_select->mask != NULL);
1095 
1096   options = GIMP_FOREGROUND_SELECT_TOOL_GET_OPTIONS (tool);
1097 
1098   if (options->preview_mode == GIMP_MATTING_PREVIEW_MODE_ON_COLOR)
1099     {
1100       if (fg_select->grayscale_preview)
1101         gimp_canvas_item_set_visible (fg_select->grayscale_preview, FALSE);
1102 
1103       gimp_display_shell_set_mask (gimp_display_get_shell (tool->display),
1104                                    fg_select->mask, 0, 0,
1105                                    &options->mask_color, TRUE);
1106     }
1107   else
1108     {
1109       gimp_display_shell_set_mask (gimp_display_get_shell (tool->display),
1110                                    NULL, 0, 0, NULL, FALSE);
1111 
1112       if (fg_select->grayscale_preview)
1113         {
1114           g_object_set (fg_select->grayscale_preview, "buffer",
1115                     fg_select->mask, NULL);
1116           gimp_canvas_item_set_visible (fg_select->grayscale_preview, TRUE);
1117         }
1118     }
1119 
1120   gimp_tool_control_set_tool_cursor        (tool->control,
1121                                             GIMP_TOOL_CURSOR_PAINTBRUSH);
1122   gimp_tool_control_set_toggle_tool_cursor (tool->control,
1123                                             GIMP_TOOL_CURSOR_PAINTBRUSH);
1124 
1125   gimp_tool_control_set_toggled (tool->control, FALSE);
1126 
1127   fg_select->state = MATTING_STATE_PREVIEW_MASK;
1128 
1129   gimp_foreground_select_tool_update_gui (fg_select);
1130 }
1131 
1132 static void
gimp_foreground_select_tool_preview(GimpForegroundSelectTool * fg_select)1133 gimp_foreground_select_tool_preview (GimpForegroundSelectTool *fg_select)
1134 {
1135   GimpTool                    *tool     = GIMP_TOOL (fg_select);
1136   GimpForegroundSelectOptions *options;
1137   GimpImage                   *image    = gimp_display_get_image (tool->display);
1138   GimpDrawable                *drawable = gimp_image_get_active_drawable (image);
1139 
1140   options  = GIMP_FOREGROUND_SELECT_TOOL_GET_OPTIONS (tool);
1141 
1142   g_clear_object (&fg_select->mask);
1143 
1144   fg_select->mask = gimp_drawable_foreground_extract (drawable,
1145                                                       options->engine,
1146                                                       options->iterations,
1147                                                       options->levels,
1148                                                       options->active_levels,
1149                                                       fg_select->trimap,
1150                                                       GIMP_PROGRESS (fg_select));
1151 
1152   gimp_foreground_select_tool_set_preview (fg_select);
1153 }
1154 
1155 static void
gimp_foreground_select_tool_stroke_paint(GimpForegroundSelectTool * fg_select)1156 gimp_foreground_select_tool_stroke_paint (GimpForegroundSelectTool *fg_select)
1157 {
1158   GimpForegroundSelectOptions *options;
1159   GimpScanConvert             *scan_convert;
1160   StrokeUndo                  *undo;
1161   gint                         width;
1162   gdouble                      opacity;
1163 
1164   options = GIMP_FOREGROUND_SELECT_TOOL_GET_OPTIONS (fg_select);
1165 
1166   g_return_if_fail (fg_select->stroke != NULL);
1167 
1168   width = ROUND ((gdouble) options->stroke_width);
1169 
1170   if (fg_select->redo_stack)
1171     {
1172       g_list_free_full (fg_select->redo_stack,
1173                         (GDestroyNotify) gimp_foreground_select_undo_free);
1174       fg_select->redo_stack = NULL;
1175     }
1176 
1177   undo = gimp_foreground_select_undo_new (fg_select->trimap,
1178                                           fg_select->stroke,
1179                                           options->draw_mode, width);
1180   if (! undo)
1181     {
1182       g_array_free (fg_select->stroke, TRUE);
1183       fg_select->stroke = NULL;
1184       return;
1185     }
1186 
1187   fg_select->undo_stack = g_list_prepend (fg_select->undo_stack, undo);
1188 
1189   scan_convert = gimp_scan_convert_new ();
1190 
1191   if (fg_select->stroke->len == 1)
1192     {
1193       GimpVector2 points[2];
1194 
1195       points[0] = points[1] = ((GimpVector2 *) fg_select->stroke->data)[0];
1196 
1197       points[1].x += 0.01;
1198       points[1].y += 0.01;
1199 
1200       gimp_scan_convert_add_polyline (scan_convert, 2, points, FALSE);
1201     }
1202   else
1203     {
1204       gimp_scan_convert_add_polyline (scan_convert,
1205                                       fg_select->stroke->len,
1206                                       (GimpVector2 *) fg_select->stroke->data,
1207                                       FALSE);
1208     }
1209 
1210   gimp_scan_convert_stroke (scan_convert,
1211                             width,
1212                             GIMP_JOIN_ROUND, GIMP_CAP_ROUND, 10.0,
1213                             0.0, NULL);
1214 
1215   if (options->draw_mode == GIMP_MATTING_DRAW_MODE_FOREGROUND)
1216     opacity = 1.0;
1217   else if (options->draw_mode == GIMP_MATTING_DRAW_MODE_BACKGROUND)
1218     opacity = 0.0;
1219   else
1220     opacity = 0.5;
1221 
1222   gimp_scan_convert_compose_value (scan_convert, fg_select->trimap,
1223                                    0, 0,
1224                                    opacity);
1225 
1226   gimp_scan_convert_free (scan_convert);
1227 
1228   g_array_free (fg_select->stroke, TRUE);
1229   fg_select->stroke = NULL;
1230 
1231   /*  update the undo actions / menu items  */
1232   gimp_image_flush (gimp_display_get_image (GIMP_TOOL (fg_select)->display));
1233 }
1234 
1235 static void
gimp_foreground_select_tool_cancel_paint(GimpForegroundSelectTool * fg_select)1236 gimp_foreground_select_tool_cancel_paint (GimpForegroundSelectTool *fg_select)
1237 {
1238   g_return_if_fail (fg_select->stroke != NULL);
1239 
1240   g_array_free (fg_select->stroke, TRUE);
1241   fg_select->stroke = NULL;
1242 }
1243 
1244 static void
gimp_foreground_select_tool_response(GimpToolGui * gui,gint response_id,GimpForegroundSelectTool * fg_select)1245 gimp_foreground_select_tool_response (GimpToolGui              *gui,
1246                                       gint                      response_id,
1247                                       GimpForegroundSelectTool *fg_select)
1248 {
1249   GimpTool *tool = GIMP_TOOL (fg_select);
1250 
1251   switch (response_id)
1252     {
1253     case GTK_RESPONSE_APPLY:
1254       gimp_tool_control (tool, GIMP_TOOL_ACTION_COMMIT, tool->display);
1255       break;
1256 
1257     default:
1258       gimp_tool_control (tool, GIMP_TOOL_ACTION_HALT, tool->display);
1259       break;
1260     }
1261 }
1262 
1263 static void
gimp_foreground_select_tool_preview_toggled(GtkToggleButton * button,GimpForegroundSelectTool * fg_select)1264 gimp_foreground_select_tool_preview_toggled (GtkToggleButton          *button,
1265                                              GimpForegroundSelectTool *fg_select)
1266 {
1267   if (fg_select->state != MATTING_STATE_FREE_SELECT)
1268     {
1269       if (gtk_toggle_button_get_active (button))
1270         {
1271           if (fg_select->state == MATTING_STATE_PAINT_TRIMAP)
1272             gimp_foreground_select_tool_preview (fg_select);
1273         }
1274       else
1275         {
1276           if (fg_select->state == MATTING_STATE_PREVIEW_MASK)
1277             gimp_foreground_select_tool_set_trimap (fg_select);
1278         }
1279     }
1280 }
1281 
1282 static void
gimp_foreground_select_tool_update_gui(GimpForegroundSelectTool * fg_select)1283 gimp_foreground_select_tool_update_gui (GimpForegroundSelectTool *fg_select)
1284 {
1285   if (fg_select->state == MATTING_STATE_PAINT_TRIMAP)
1286     {
1287       gimp_tool_gui_set_description (fg_select->gui, _("Paint mask"));
1288     }
1289   else if (fg_select->state == MATTING_STATE_PREVIEW_MASK)
1290     {
1291       gimp_tool_gui_set_description (fg_select->gui, _("Preview"));
1292     }
1293 
1294   gimp_tool_gui_set_response_sensitive (fg_select->gui, GTK_RESPONSE_APPLY,
1295                                         TRUE);
1296   gtk_widget_set_sensitive (fg_select->preview_toggle, TRUE);
1297 }
1298 
1299 static StrokeUndo *
gimp_foreground_select_undo_new(GeglBuffer * trimap,GArray * stroke,GimpMattingDrawMode draw_mode,gint stroke_width)1300 gimp_foreground_select_undo_new (GeglBuffer          *trimap,
1301                                  GArray              *stroke,
1302                                  GimpMattingDrawMode draw_mode,
1303                                  gint                stroke_width)
1304 
1305 {
1306   StrokeUndo          *undo;
1307   const GeglRectangle *extent;
1308   gint                 x1, y1, x2, y2;
1309   gint                 width, height;
1310   gint                 i;
1311 
1312   extent = gegl_buffer_get_extent (trimap);
1313 
1314   x1 = G_MAXINT;
1315   y1 = G_MAXINT;
1316   x2 = G_MININT;
1317   y2 = G_MININT;
1318 
1319   for (i = 0; i < stroke->len; i++)
1320     {
1321       GimpVector2 *point = &g_array_index (stroke, GimpVector2, i);
1322 
1323       x1 = MIN (x1, floor (point->x));
1324       y1 = MIN (y1, floor (point->y));
1325       x2 = MAX (x2, ceil (point->x));
1326       y2 = MAX (y2, ceil (point->y));
1327     }
1328 
1329   x1 -= (stroke_width + 1) / 2;
1330   y1 -= (stroke_width + 1) / 2;
1331   x2 += (stroke_width + 1) / 2;
1332   y2 += (stroke_width + 1) / 2;
1333 
1334   x1 = MAX (x1, extent->x);
1335   y1 = MAX (y1, extent->y);
1336   x2 = MIN (x2, extent->x + extent->width);
1337   y2 = MIN (y2, extent->x + extent->height);
1338 
1339   width  = x2 - x1;
1340   height = y2 - y1;
1341 
1342   if (width <= 0 || height <= 0)
1343     return NULL;
1344 
1345   undo = g_slice_new0 (StrokeUndo);
1346   undo->saved_trimap = gegl_buffer_new (GEGL_RECTANGLE (x1, y1, width, height),
1347                                         gegl_buffer_get_format (trimap));
1348 
1349   gimp_gegl_buffer_copy (
1350     trimap,             GEGL_RECTANGLE (x1, y1, width, height),
1351     GEGL_ABYSS_NONE,
1352     undo->saved_trimap, NULL);
1353 
1354   undo->trimap_x = x1;
1355   undo->trimap_y = y1;
1356 
1357   undo->draw_mode    = draw_mode;
1358   undo->stroke_width = stroke_width;
1359 
1360   return undo;
1361 }
1362 
1363 static void
gimp_foreground_select_undo_pop(StrokeUndo * undo,GeglBuffer * trimap)1364 gimp_foreground_select_undo_pop (StrokeUndo *undo,
1365                                  GeglBuffer *trimap)
1366 {
1367   GeglBuffer *buffer;
1368   gint        width, height;
1369 
1370   buffer = gimp_gegl_buffer_dup (undo->saved_trimap);
1371 
1372   width  = gegl_buffer_get_width  (buffer);
1373   height = gegl_buffer_get_height (buffer);
1374 
1375   gimp_gegl_buffer_copy (trimap,
1376                          GEGL_RECTANGLE (undo->trimap_x, undo->trimap_y,
1377                                          width, height),
1378                          GEGL_ABYSS_NONE,
1379                          undo->saved_trimap, NULL);
1380 
1381   gimp_gegl_buffer_copy (buffer,
1382                          GEGL_RECTANGLE (undo->trimap_x, undo->trimap_y,
1383                                          width, height),
1384                          GEGL_ABYSS_NONE,
1385                          trimap, NULL);
1386 
1387   g_object_unref (buffer);
1388 }
1389 
1390 static void
gimp_foreground_select_undo_free(StrokeUndo * undo)1391 gimp_foreground_select_undo_free (StrokeUndo *undo)
1392 {
1393   g_clear_object (&undo->saved_trimap);
1394 
1395   g_slice_free (StrokeUndo, undo);
1396 }
1397