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