1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <cairo.h>
21 #include <gegl.h>
22 #include <gdk-pixbuf/gdk-pixbuf.h>
23 
24 #include "libgimpbase/gimpbase.h"
25 
26 #include "core-types.h"
27 
28 #include "gegl/gimpapplicator.h"
29 
30 #include "gimpchannel.h"
31 #include "gimpdrawable-floating-selection.h"
32 #include "gimpdrawable-filters.h"
33 #include "gimpdrawable-private.h"
34 #include "gimpimage.h"
35 #include "gimplayer.h"
36 
37 #include "gimp-log.h"
38 
39 #include "gimp-intl.h"
40 
41 
42 /*  local function prototypes  */
43 
44 static void    gimp_drawable_remove_fs_filter             (GimpDrawable      *drawable);
45 static void    gimp_drawable_sync_fs_filter               (GimpDrawable      *drawable);
46 
47 static void    gimp_drawable_fs_notify                    (GObject           *object,
48                                                            const GParamSpec  *pspec,
49                                                            GimpDrawable      *drawable);
50 static void    gimp_drawable_fs_lock_position_changed     (GimpDrawable      *signal_drawable,
51                                                            GimpDrawable      *drawable);
52 static void    gimp_drawable_fs_format_changed            (GimpDrawable      *signal_drawable,
53                                                            GimpDrawable      *drawable);
54 static void    gimp_drawable_fs_affect_changed            (GimpImage         *image,
55                                                            GimpChannelType    channel,
56                                                            GimpDrawable      *drawable);
57 static void    gimp_drawable_fs_mask_changed              (GimpImage         *image,
58                                                            GimpDrawable      *drawable);
59 static void    gimp_drawable_fs_visibility_changed        (GimpLayer         *fs,
60                                                            GimpDrawable      *drawable);
61 static void    gimp_drawable_fs_excludes_backdrop_changed (GimpLayer         *fs,
62                                                            GimpDrawable      *drawable);
63 static void    gimp_drawable_fs_bounding_box_changed      (GimpLayer         *fs,
64                                                            GimpDrawable      *drawable);
65 static void    gimp_drawable_fs_update                    (GimpLayer         *fs,
66                                                            gint               x,
67                                                            gint               y,
68                                                            gint               width,
69                                                            gint               height,
70                                                            GimpDrawable      *drawable);
71 
72 
73 /*  public functions  */
74 
75 GimpLayer *
gimp_drawable_get_floating_sel(GimpDrawable * drawable)76 gimp_drawable_get_floating_sel (GimpDrawable *drawable)
77 {
78   g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
79 
80   return drawable->private->floating_selection;
81 }
82 
83 void
gimp_drawable_attach_floating_sel(GimpDrawable * drawable,GimpLayer * fs)84 gimp_drawable_attach_floating_sel (GimpDrawable *drawable,
85                                    GimpLayer    *fs)
86 {
87   GimpImage *image;
88 
89   g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
90   g_return_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)));
91   g_return_if_fail (gimp_drawable_get_floating_sel (drawable) == NULL);
92   g_return_if_fail (GIMP_IS_LAYER (fs));
93 
94   GIMP_LOG (FLOATING_SELECTION, "%s", G_STRFUNC);
95 
96   image = gimp_item_get_image (GIMP_ITEM (drawable));
97 
98   drawable->private->floating_selection = fs;
99   gimp_image_set_floating_selection (image, fs);
100 
101   /*  clear the selection  */
102   gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (fs));
103 
104   gimp_item_bind_visible_to_active (GIMP_ITEM (fs), FALSE);
105   gimp_filter_set_active (GIMP_FILTER (fs), FALSE);
106 
107   _gimp_drawable_add_floating_sel_filter (drawable);
108 
109   g_signal_connect (fs, "visibility-changed",
110                     G_CALLBACK (gimp_drawable_fs_visibility_changed),
111                     drawable);
112   g_signal_connect (fs, "excludes-backdrop-changed",
113                     G_CALLBACK (gimp_drawable_fs_excludes_backdrop_changed),
114                     drawable);
115   g_signal_connect (fs, "bounding-box-changed",
116                     G_CALLBACK (gimp_drawable_fs_bounding_box_changed),
117                     drawable);
118   g_signal_connect (fs, "update",
119                     G_CALLBACK (gimp_drawable_fs_update),
120                     drawable);
121 
122   gimp_drawable_fs_update (fs,
123                            0, 0,
124                            gimp_item_get_width  (GIMP_ITEM (fs)),
125                            gimp_item_get_height (GIMP_ITEM (fs)),
126                            drawable);
127 }
128 
129 void
gimp_drawable_detach_floating_sel(GimpDrawable * drawable)130 gimp_drawable_detach_floating_sel (GimpDrawable *drawable)
131 {
132   GimpImage *image;
133   GimpLayer *fs;
134 
135   g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
136   g_return_if_fail (gimp_drawable_get_floating_sel (drawable) != NULL);
137 
138   GIMP_LOG (FLOATING_SELECTION, "%s", G_STRFUNC);
139 
140   image = gimp_item_get_image (GIMP_ITEM (drawable));
141   fs    = drawable->private->floating_selection;
142 
143   gimp_drawable_remove_fs_filter (drawable);
144 
145   g_signal_handlers_disconnect_by_func (fs,
146                                         gimp_drawable_fs_visibility_changed,
147                                         drawable);
148   g_signal_handlers_disconnect_by_func (fs,
149                                         gimp_drawable_fs_excludes_backdrop_changed,
150                                         drawable);
151   g_signal_handlers_disconnect_by_func (fs,
152                                         gimp_drawable_fs_bounding_box_changed,
153                                         drawable);
154   g_signal_handlers_disconnect_by_func (fs,
155                                         gimp_drawable_fs_update,
156                                         drawable);
157 
158   gimp_drawable_fs_update (fs,
159                            0, 0,
160                            gimp_item_get_width  (GIMP_ITEM (fs)),
161                            gimp_item_get_height (GIMP_ITEM (fs)),
162                            drawable);
163 
164   gimp_item_bind_visible_to_active (GIMP_ITEM (fs), TRUE);
165 
166   /*  clear the selection  */
167   gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (fs));
168 
169   gimp_image_set_floating_selection (image, NULL);
170   drawable->private->floating_selection = NULL;
171 }
172 
173 GimpFilter *
gimp_drawable_get_floating_sel_filter(GimpDrawable * drawable)174 gimp_drawable_get_floating_sel_filter (GimpDrawable *drawable)
175 {
176   g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
177   g_return_val_if_fail (gimp_drawable_get_floating_sel (drawable) != NULL, NULL);
178 
179   /* Ensure that the graph is construced before the filter is used.
180    * Otherwise, we rely on the projection to cause the graph to be
181    * constructed, which fails for images that aren't displayed.
182    */
183   gimp_filter_get_node (GIMP_FILTER (drawable));
184 
185   return drawable->private->fs_filter;
186 }
187 
188 
189 /*  private functions  */
190 
191 void
_gimp_drawable_add_floating_sel_filter(GimpDrawable * drawable)192 _gimp_drawable_add_floating_sel_filter (GimpDrawable *drawable)
193 {
194   GimpDrawablePrivate *private = drawable->private;
195   GimpImage           *image   = gimp_item_get_image (GIMP_ITEM (drawable));
196   GimpLayer           *fs      = gimp_drawable_get_floating_sel (drawable);
197   GeglNode            *node;
198   GeglNode            *fs_source;
199 
200   if (! private->source_node)
201     return;
202 
203   private->fs_filter = gimp_filter_new (_("Floating Selection"));
204   gimp_viewable_set_icon_name (GIMP_VIEWABLE (private->fs_filter),
205                                "gimp-floating-selection");
206 
207   node = gimp_filter_get_node (private->fs_filter);
208 
209   fs_source = gimp_drawable_get_source_node (GIMP_DRAWABLE (fs));
210 
211   /* rip the fs' source node out of its graph */
212   if (fs->layer_offset_node)
213     {
214       gegl_node_disconnect (fs->layer_offset_node, "input");
215       gegl_node_remove_child (gimp_filter_get_node (GIMP_FILTER (fs)),
216                               fs_source);
217     }
218 
219   gegl_node_add_child (node, fs_source);
220 
221   private->fs_applicator = gimp_applicator_new (node);
222 
223   gimp_filter_set_applicator (private->fs_filter, private->fs_applicator);
224 
225   gimp_applicator_set_cache (private->fs_applicator, TRUE);
226 
227   private->fs_crop_node = gegl_node_new_child (node,
228                                                "operation", "gegl:nop",
229                                                NULL);
230 
231   gegl_node_connect_to (fs_source,             "output",
232                         private->fs_crop_node, "input");
233   gegl_node_connect_to (private->fs_crop_node, "output",
234                         node,                  "aux");
235 
236   gimp_drawable_add_filter (drawable, private->fs_filter);
237 
238   g_signal_connect (fs, "notify",
239                     G_CALLBACK (gimp_drawable_fs_notify),
240                     drawable);
241   g_signal_connect (drawable, "notify::offset-x",
242                     G_CALLBACK (gimp_drawable_fs_notify),
243                     drawable);
244   g_signal_connect (drawable, "notify::offset-y",
245                     G_CALLBACK (gimp_drawable_fs_notify),
246                     drawable);
247   g_signal_connect (drawable, "lock-position-changed",
248                     G_CALLBACK (gimp_drawable_fs_lock_position_changed),
249                     drawable);
250   g_signal_connect (drawable, "format-changed",
251                     G_CALLBACK (gimp_drawable_fs_format_changed),
252                     drawable);
253   g_signal_connect (image, "component-active-changed",
254                     G_CALLBACK (gimp_drawable_fs_affect_changed),
255                     drawable);
256   g_signal_connect (image, "mask-changed",
257                     G_CALLBACK (gimp_drawable_fs_mask_changed),
258                     drawable);
259 
260   gimp_drawable_sync_fs_filter (drawable);
261 }
262 
263 
264 /*  private functions  */
265 
266 static void
gimp_drawable_remove_fs_filter(GimpDrawable * drawable)267 gimp_drawable_remove_fs_filter (GimpDrawable *drawable)
268 {
269   GimpDrawablePrivate *private = drawable->private;
270   GimpImage           *image   = gimp_item_get_image (GIMP_ITEM (drawable));
271   GimpLayer           *fs      = gimp_drawable_get_floating_sel (drawable);
272 
273   if (private->fs_filter)
274     {
275       GeglNode *node;
276       GeglNode *fs_source;
277 
278       g_signal_handlers_disconnect_by_func (fs,
279                                             gimp_drawable_fs_notify,
280                                             drawable);
281       g_signal_handlers_disconnect_by_func (drawable,
282                                             gimp_drawable_fs_notify,
283                                             drawable);
284       g_signal_handlers_disconnect_by_func (drawable,
285                                             gimp_drawable_fs_lock_position_changed,
286                                             drawable);
287       g_signal_handlers_disconnect_by_func (drawable,
288                                             gimp_drawable_fs_format_changed,
289                                             drawable);
290       g_signal_handlers_disconnect_by_func (image,
291                                             gimp_drawable_fs_affect_changed,
292                                             drawable);
293       g_signal_handlers_disconnect_by_func (image,
294                                             gimp_drawable_fs_mask_changed,
295                                             drawable);
296 
297       gimp_drawable_remove_filter (drawable, private->fs_filter);
298 
299       node = gimp_filter_get_node (private->fs_filter);
300 
301       fs_source = gimp_drawable_get_source_node (GIMP_DRAWABLE (fs));
302 
303       gegl_node_remove_child (node, fs_source);
304 
305       /* plug the fs' source node back into its graph */
306       if (fs->layer_offset_node)
307         {
308           gegl_node_add_child (gimp_filter_get_node (GIMP_FILTER (fs)),
309                                fs_source);
310           gegl_node_connect_to (fs_source,             "output",
311                                 fs->layer_offset_node, "input");
312         }
313 
314       g_clear_object (&private->fs_filter);
315       g_clear_object (&private->fs_applicator);
316 
317       private->fs_crop_node = NULL;
318 
319       gimp_drawable_update_bounding_box (drawable);
320     }
321 }
322 
323 static void
gimp_drawable_sync_fs_filter(GimpDrawable * drawable)324 gimp_drawable_sync_fs_filter (GimpDrawable *drawable)
325 {
326   GimpDrawablePrivate *private = drawable->private;
327   GimpImage           *image   = gimp_item_get_image (GIMP_ITEM (drawable));
328   GimpChannel         *mask    = gimp_image_get_mask (image);
329   GimpLayer           *fs      = gimp_drawable_get_floating_sel (drawable);
330   gint                 off_x, off_y;
331   gint                 fs_off_x, fs_off_y;
332 
333   gimp_filter_set_active (private->fs_filter,
334                           gimp_item_get_visible (GIMP_ITEM (fs)));
335 
336   gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
337   gimp_item_get_offset (GIMP_ITEM (fs), &fs_off_x, &fs_off_y);
338 
339   if (gimp_item_get_clip (GIMP_ITEM (drawable), GIMP_TRANSFORM_RESIZE_ADJUST) ==
340       GIMP_TRANSFORM_RESIZE_CLIP ||
341       ! gimp_drawable_has_alpha (drawable))
342     {
343       gegl_node_set (
344         private->fs_crop_node,
345         "operation", "gegl:crop",
346         "x",         (gdouble) (off_x - fs_off_x),
347         "y",         (gdouble) (off_y - fs_off_y),
348         "width",     (gdouble) gimp_item_get_width  (GIMP_ITEM (drawable)),
349         "height",    (gdouble) gimp_item_get_height (GIMP_ITEM (drawable)),
350         NULL);
351     }
352   else
353     {
354       gegl_node_set (
355         private->fs_crop_node,
356         "operation", "gegl:nop",
357         NULL);
358     }
359 
360   gimp_applicator_set_apply_offset (private->fs_applicator,
361                                     fs_off_x - off_x,
362                                     fs_off_y - off_y);
363 
364   if (gimp_channel_is_empty (mask))
365     {
366       gimp_applicator_set_mask_buffer (private->fs_applicator, NULL);
367     }
368   else
369     {
370       GeglBuffer *buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
371 
372       gimp_applicator_set_mask_buffer (private->fs_applicator, buffer);
373       gimp_applicator_set_mask_offset (private->fs_applicator,
374                                        -off_x, -off_y);
375     }
376 
377   gimp_applicator_set_opacity (private->fs_applicator,
378                                gimp_layer_get_opacity (fs));
379   gimp_applicator_set_mode (private->fs_applicator,
380                             gimp_layer_get_mode (fs),
381                             gimp_layer_get_blend_space (fs),
382                             gimp_layer_get_composite_space (fs),
383                             gimp_layer_get_composite_mode (fs));
384   gimp_applicator_set_affect (private->fs_applicator,
385                               gimp_drawable_get_active_mask (drawable));
386   gimp_applicator_set_output_format (private->fs_applicator,
387                                      gimp_drawable_get_format (drawable));
388 
389   gimp_drawable_update_bounding_box (drawable);
390 }
391 
392 static void
gimp_drawable_fs_notify(GObject * object,const GParamSpec * pspec,GimpDrawable * drawable)393 gimp_drawable_fs_notify (GObject          *object,
394                          const GParamSpec *pspec,
395                          GimpDrawable     *drawable)
396 {
397   if (! strcmp (pspec->name, "offset-x")        ||
398       ! strcmp (pspec->name, "offset-y")        ||
399       ! strcmp (pspec->name, "visible")         ||
400       ! strcmp (pspec->name, "mode")            ||
401       ! strcmp (pspec->name, "blend-space")     ||
402       ! strcmp (pspec->name, "composite-space") ||
403       ! strcmp (pspec->name, "composite-mode")  ||
404       ! strcmp (pspec->name, "opacity"))
405     {
406       gimp_drawable_sync_fs_filter (drawable);
407     }
408 }
409 
410 static void
gimp_drawable_fs_lock_position_changed(GimpDrawable * signal_drawable,GimpDrawable * drawable)411 gimp_drawable_fs_lock_position_changed (GimpDrawable *signal_drawable,
412                                         GimpDrawable *drawable)
413 {
414   GimpLayer *fs = gimp_drawable_get_floating_sel (drawable);
415 
416   gimp_drawable_sync_fs_filter (drawable);
417 
418   gimp_drawable_update (GIMP_DRAWABLE (fs), 0, 0, -1, -1);
419 }
420 
421 static void
gimp_drawable_fs_format_changed(GimpDrawable * signal_drawable,GimpDrawable * drawable)422 gimp_drawable_fs_format_changed (GimpDrawable *signal_drawable,
423                                  GimpDrawable *drawable)
424 {
425   GimpLayer *fs = gimp_drawable_get_floating_sel (drawable);
426 
427   gimp_drawable_sync_fs_filter (drawable);
428 
429   gimp_drawable_update (GIMP_DRAWABLE (fs), 0, 0, -1, -1);
430 }
431 
432 static void
gimp_drawable_fs_affect_changed(GimpImage * image,GimpChannelType channel,GimpDrawable * drawable)433 gimp_drawable_fs_affect_changed (GimpImage       *image,
434                                  GimpChannelType  channel,
435                                  GimpDrawable    *drawable)
436 {
437   GimpLayer *fs = gimp_drawable_get_floating_sel (drawable);
438 
439   gimp_drawable_sync_fs_filter (drawable);
440 
441   gimp_drawable_update (GIMP_DRAWABLE (fs), 0, 0, -1, -1);
442 }
443 
444 static void
gimp_drawable_fs_mask_changed(GimpImage * image,GimpDrawable * drawable)445 gimp_drawable_fs_mask_changed (GimpImage    *image,
446                                GimpDrawable *drawable)
447 {
448   GimpLayer *fs = gimp_drawable_get_floating_sel (drawable);
449 
450   gimp_drawable_sync_fs_filter (drawable);
451 
452   gimp_drawable_update (GIMP_DRAWABLE (fs), 0, 0, -1, -1);
453 }
454 
455 static void
gimp_drawable_fs_visibility_changed(GimpLayer * fs,GimpDrawable * drawable)456 gimp_drawable_fs_visibility_changed (GimpLayer    *fs,
457                                      GimpDrawable *drawable)
458 {
459   if (gimp_layer_get_excludes_backdrop (fs))
460     gimp_drawable_update (drawable, 0, 0, -1, -1);
461   else
462     gimp_drawable_update (GIMP_DRAWABLE (fs), 0, 0, -1, -1);
463 }
464 
465 static void
gimp_drawable_fs_excludes_backdrop_changed(GimpLayer * fs,GimpDrawable * drawable)466 gimp_drawable_fs_excludes_backdrop_changed (GimpLayer    *fs,
467                                             GimpDrawable *drawable)
468 {
469   if (gimp_item_get_visible (GIMP_ITEM (fs)))
470     gimp_drawable_update (drawable, 0, 0, -1, -1);
471 }
472 
473 static void
gimp_drawable_fs_bounding_box_changed(GimpLayer * fs,GimpDrawable * drawable)474 gimp_drawable_fs_bounding_box_changed (GimpLayer    *fs,
475                                        GimpDrawable *drawable)
476 {
477   gimp_drawable_update_bounding_box (drawable);
478 }
479 
480 static void
gimp_drawable_fs_update(GimpLayer * fs,gint x,gint y,gint width,gint height,GimpDrawable * drawable)481 gimp_drawable_fs_update (GimpLayer    *fs,
482                          gint          x,
483                          gint          y,
484                          gint          width,
485                          gint          height,
486                          GimpDrawable *drawable)
487 {
488   GeglRectangle bounding_box;
489   GeglRectangle rect;
490   gint          fs_off_x, fs_off_y;
491   gint          off_x, off_y;
492 
493   gimp_item_get_offset (GIMP_ITEM (fs), &fs_off_x, &fs_off_y);
494   gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y);
495 
496   bounding_box = gimp_drawable_get_bounding_box (drawable);
497 
498   bounding_box.x += off_x;
499   bounding_box.y += off_y;
500 
501   rect.x      = x + fs_off_x;
502   rect.y      = y + fs_off_y;
503   rect.width  = width;
504   rect.height = height;
505 
506   if (gegl_rectangle_intersect (&rect, &rect, &bounding_box))
507     {
508       gimp_drawable_update (drawable,
509                             rect.x - off_x, rect.y - off_y,
510                             rect.width, rect.height);
511     }
512 }
513