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