1 /* GIMP - The GNU Image Manipulation Program
2 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3 *
4 * gimpcanvastransformpreview.c
5 * Copyright (C) 2011 Michael Natterer <mitch@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
28 #include "libgimpbase/gimpbase.h"
29 #include "libgimpmath/gimpmath.h"
30 #include "libgimpcolor/gimpcolor.h"
31 #include "libgimpwidgets/gimpwidgets.h"
32
33 #include "display/display-types.h"
34
35 #include "gegl/gimp-gegl-nodes.h"
36 #include "gegl/gimp-gegl-utils.h"
37 #include "gegl/gimptilehandlervalidate.h"
38
39 #include "core/gimp-transform-resize.h"
40 #include "core/gimp-transform-utils.h"
41 #include "core/gimp-utils.h"
42 #include "core/gimpchannel.h"
43 #include "core/gimpimage.h"
44 #include "core/gimplayer.h"
45 #include "core/gimppickable.h"
46
47 #include "gimpcanvas.h"
48 #include "gimpcanvastransformpreview.h"
49 #include "gimpdisplayshell.h"
50
51
52 enum
53 {
54 PROP_0,
55 PROP_PICKABLE,
56 PROP_TRANSFORM,
57 PROP_CLIP,
58 PROP_X1,
59 PROP_Y1,
60 PROP_X2,
61 PROP_Y2,
62 PROP_OPACITY
63 };
64
65
66 typedef struct _GimpCanvasTransformPreviewPrivate GimpCanvasTransformPreviewPrivate;
67
68 struct _GimpCanvasTransformPreviewPrivate
69 {
70 GimpPickable *pickable;
71 GimpMatrix3 transform;
72 GimpTransformResize clip;
73 gdouble x1, y1;
74 gdouble x2, y2;
75 gdouble opacity;
76
77 GeglNode *node;
78 GeglNode *source_node;
79 GeglNode *convert_format_node;
80 GeglNode *layer_mask_source_node;
81 GeglNode *layer_mask_opacity_node;
82 GeglNode *mask_source_node;
83 GeglNode *mask_translate_node;
84 GeglNode *mask_crop_node;
85 GeglNode *opacity_node;
86 GeglNode *cache_node;
87 GeglNode *transform_node;
88
89 GimpPickable *node_pickable;
90 GimpDrawable *node_layer_mask;
91 GimpDrawable *node_mask;
92 GeglRectangle node_rect;
93 gdouble node_opacity;
94 GimpMatrix3 node_matrix;
95 GeglNode *node_output;
96 };
97
98 #define GET_PRIVATE(transform_preview) \
99 ((GimpCanvasTransformPreviewPrivate *) gimp_canvas_transform_preview_get_instance_private ((GimpCanvasTransformPreview *) (transform_preview)))
100
101
102 /* local function prototypes */
103
104 static void gimp_canvas_transform_preview_dispose (GObject *object);
105 static void gimp_canvas_transform_preview_set_property (GObject *object,
106 guint property_id,
107 const GValue *value,
108 GParamSpec *pspec);
109 static void gimp_canvas_transform_preview_get_property (GObject *object,
110 guint property_id,
111 GValue *value,
112 GParamSpec *pspec);
113
114 static void gimp_canvas_transform_preview_draw (GimpCanvasItem *item,
115 cairo_t *cr);
116 static cairo_region_t * gimp_canvas_transform_preview_get_extents (GimpCanvasItem *item);
117
118 static void gimp_canvas_transform_preview_layer_changed (GimpLayer *layer,
119 GimpCanvasTransformPreview *transform_preview);
120
121 static void gimp_canvas_transform_preview_set_pickable (GimpCanvasTransformPreview *transform_preview,
122 GimpPickable *pickable);
123 static void gimp_canvas_transform_preview_sync_node (GimpCanvasTransformPreview *transform_preview);
124
125
G_DEFINE_TYPE_WITH_PRIVATE(GimpCanvasTransformPreview,gimp_canvas_transform_preview,GIMP_TYPE_CANVAS_ITEM)126 G_DEFINE_TYPE_WITH_PRIVATE (GimpCanvasTransformPreview,
127 gimp_canvas_transform_preview,
128 GIMP_TYPE_CANVAS_ITEM)
129
130 #define parent_class gimp_canvas_transform_preview_parent_class
131
132
133 /* private functions */
134
135
136 static void
137 gimp_canvas_transform_preview_class_init (GimpCanvasTransformPreviewClass *klass)
138 {
139 GObjectClass *object_class = G_OBJECT_CLASS (klass);
140 GimpCanvasItemClass *item_class = GIMP_CANVAS_ITEM_CLASS (klass);
141
142 object_class->dispose = gimp_canvas_transform_preview_dispose;
143 object_class->set_property = gimp_canvas_transform_preview_set_property;
144 object_class->get_property = gimp_canvas_transform_preview_get_property;
145
146 item_class->draw = gimp_canvas_transform_preview_draw;
147 item_class->get_extents = gimp_canvas_transform_preview_get_extents;
148
149 g_object_class_install_property (object_class, PROP_PICKABLE,
150 g_param_spec_object ("pickable",
151 NULL, NULL,
152 GIMP_TYPE_PICKABLE,
153 GIMP_PARAM_READWRITE));
154
155 g_object_class_install_property (object_class, PROP_TRANSFORM,
156 gimp_param_spec_matrix3 ("transform",
157 NULL, NULL,
158 NULL,
159 GIMP_PARAM_READWRITE));
160
161 g_object_class_install_property (object_class, PROP_CLIP,
162 g_param_spec_enum ("clip",
163 NULL, NULL,
164 GIMP_TYPE_TRANSFORM_RESIZE,
165 GIMP_TRANSFORM_RESIZE_ADJUST,
166 GIMP_PARAM_READWRITE));
167
168 g_object_class_install_property (object_class, PROP_X1,
169 g_param_spec_double ("x1",
170 NULL, NULL,
171 -GIMP_MAX_IMAGE_SIZE,
172 GIMP_MAX_IMAGE_SIZE,
173 0.0,
174 GIMP_PARAM_READWRITE));
175
176 g_object_class_install_property (object_class, PROP_Y1,
177 g_param_spec_double ("y1",
178 NULL, NULL,
179 -GIMP_MAX_IMAGE_SIZE,
180 GIMP_MAX_IMAGE_SIZE,
181 0.0,
182 GIMP_PARAM_READWRITE));
183
184 g_object_class_install_property (object_class, PROP_X2,
185 g_param_spec_double ("x2",
186 NULL, NULL,
187 -GIMP_MAX_IMAGE_SIZE,
188 GIMP_MAX_IMAGE_SIZE,
189 0.0,
190 GIMP_PARAM_READWRITE));
191
192 g_object_class_install_property (object_class, PROP_Y2,
193 g_param_spec_double ("y2",
194 NULL, NULL,
195 -GIMP_MAX_IMAGE_SIZE,
196 GIMP_MAX_IMAGE_SIZE,
197 0.0,
198 GIMP_PARAM_READWRITE));
199
200 g_object_class_install_property (object_class, PROP_OPACITY,
201 g_param_spec_double ("opacity",
202 NULL, NULL,
203 0.0, 1.0, 1.0,
204 GIMP_PARAM_READWRITE));
205 }
206
207 static void
gimp_canvas_transform_preview_init(GimpCanvasTransformPreview * transform_preview)208 gimp_canvas_transform_preview_init (GimpCanvasTransformPreview *transform_preview)
209 {
210 GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (transform_preview);
211
212 private->clip = GIMP_TRANSFORM_RESIZE_ADJUST;
213 private->opacity = 1.0;
214 }
215
216 static void
gimp_canvas_transform_preview_dispose(GObject * object)217 gimp_canvas_transform_preview_dispose (GObject *object)
218 {
219 GimpCanvasTransformPreview *transform_preview = GIMP_CANVAS_TRANSFORM_PREVIEW (object);
220 GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (object);
221
222 g_clear_object (&private->node);
223
224 gimp_canvas_transform_preview_set_pickable (transform_preview, NULL);
225
226 G_OBJECT_CLASS (parent_class)->dispose (object);
227 }
228
229 static void
gimp_canvas_transform_preview_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)230 gimp_canvas_transform_preview_set_property (GObject *object,
231 guint property_id,
232 const GValue *value,
233 GParamSpec *pspec)
234 {
235 GimpCanvasTransformPreview *transform_preview = GIMP_CANVAS_TRANSFORM_PREVIEW (object);
236 GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (object);
237
238 switch (property_id)
239 {
240 case PROP_PICKABLE:
241 gimp_canvas_transform_preview_set_pickable (transform_preview,
242 g_value_get_object (value));
243 break;
244
245 case PROP_TRANSFORM:
246 {
247 GimpMatrix3 *transform = g_value_get_boxed (value);
248
249 if (transform)
250 private->transform = *transform;
251 else
252 gimp_matrix3_identity (&private->transform);
253 }
254 break;
255
256 case PROP_CLIP:
257 private->clip = g_value_get_enum (value);
258 break;
259
260 case PROP_X1:
261 private->x1 = g_value_get_double (value);
262 break;
263
264 case PROP_Y1:
265 private->y1 = g_value_get_double (value);
266 break;
267
268 case PROP_X2:
269 private->x2 = g_value_get_double (value);
270 break;
271
272 case PROP_Y2:
273 private->y2 = g_value_get_double (value);
274 break;
275
276 case PROP_OPACITY:
277 private->opacity = g_value_get_double (value);
278 break;
279
280 default:
281 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
282 break;
283 }
284 }
285
286 static void
gimp_canvas_transform_preview_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)287 gimp_canvas_transform_preview_get_property (GObject *object,
288 guint property_id,
289 GValue *value,
290 GParamSpec *pspec)
291 {
292 GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (object);
293
294 switch (property_id)
295 {
296 case PROP_PICKABLE:
297 g_value_set_object (value, private->pickable);
298 break;
299
300 case PROP_TRANSFORM:
301 g_value_set_boxed (value, &private->transform);
302 break;
303
304 case PROP_CLIP:
305 g_value_set_enum (value, private->clip);
306 break;
307
308 case PROP_X1:
309 g_value_set_double (value, private->x1);
310 break;
311
312 case PROP_Y1:
313 g_value_set_double (value, private->y1);
314 break;
315
316 case PROP_X2:
317 g_value_set_double (value, private->x2);
318 break;
319
320 case PROP_Y2:
321 g_value_set_double (value, private->y2);
322 break;
323
324 case PROP_OPACITY:
325 g_value_set_double (value, private->opacity);
326 break;
327
328 default:
329 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
330 break;
331 }
332 }
333
334 static gboolean
gimp_canvas_transform_preview_transform(GimpCanvasItem * item,cairo_rectangle_int_t * extents)335 gimp_canvas_transform_preview_transform (GimpCanvasItem *item,
336 cairo_rectangle_int_t *extents)
337 {
338 GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (item);
339 gint x1, y1;
340 gint x2, y2;
341 gdouble tx1, ty1;
342 gdouble tx2, ty2;
343
344 if (! gimp_transform_resize_boundary (&private->transform,
345 private->clip,
346 private->x1, private->y1,
347 private->x2, private->y2,
348 &x1, &y1,
349 &x2, &y2))
350 {
351 return FALSE;
352 }
353
354 gimp_canvas_item_transform_xy_f (item, x1, y1, &tx1, &ty1);
355 gimp_canvas_item_transform_xy_f (item, x2, y2, &tx2, &ty2);
356
357 extents->x = (gint) floor (tx1);
358 extents->y = (gint) floor (ty1);
359 extents->width = (gint) ceil (tx2) - extents->x;
360 extents->height = (gint) ceil (ty2) - extents->y;
361
362 return TRUE;
363 }
364
365 static void
gimp_canvas_transform_preview_draw(GimpCanvasItem * item,cairo_t * cr)366 gimp_canvas_transform_preview_draw (GimpCanvasItem *item,
367 cairo_t *cr)
368 {
369 GimpCanvasTransformPreview *transform_preview = GIMP_CANVAS_TRANSFORM_PREVIEW (item);
370 GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (item);
371 GimpDisplayShell *shell = gimp_canvas_item_get_shell (item);
372 cairo_rectangle_int_t extents;
373 gdouble clip_x1, clip_y1;
374 gdouble clip_x2, clip_y2;
375 GeglRectangle bounds;
376 cairo_surface_t *surface;
377 guchar *surface_data;
378 gint surface_stride;
379
380 if (! gimp_canvas_transform_preview_transform (item, &extents))
381 return;
382
383 cairo_clip_extents (cr, &clip_x1, &clip_y1, &clip_x2, &clip_y2);
384
385 clip_x1 = floor (clip_x1);
386 clip_y1 = floor (clip_y1);
387 clip_x2 = ceil (clip_x2);
388 clip_y2 = ceil (clip_y2);
389
390 if (! gegl_rectangle_intersect (&bounds,
391 GEGL_RECTANGLE (extents.x,
392 extents.y,
393 extents.width,
394 extents.height),
395 GEGL_RECTANGLE (clip_x1,
396 clip_y1,
397 clip_x2 - clip_x1,
398 clip_y2 - clip_y1)))
399 {
400 return;
401 }
402
403 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
404 bounds.width, bounds.height);
405
406 g_return_if_fail (surface != NULL);
407
408 surface_data = cairo_image_surface_get_data (surface);
409 surface_stride = cairo_image_surface_get_stride (surface);
410
411 gimp_canvas_transform_preview_sync_node (transform_preview);
412
413 gegl_node_blit (private->node_output, 1.0,
414 GEGL_RECTANGLE (bounds.x + shell->offset_x,
415 bounds.y + shell->offset_y,
416 bounds.width,
417 bounds.height),
418 babl_format ("cairo-ARGB32"), surface_data, surface_stride,
419 GEGL_BLIT_CACHE);
420
421 cairo_surface_mark_dirty (surface);
422
423 cairo_set_source_surface (cr, surface, bounds.x, bounds.y);
424 cairo_rectangle (cr, bounds.x, bounds.y, bounds.width, bounds.height);
425 cairo_fill (cr);
426
427 cairo_surface_destroy (surface);
428 }
429
430 static cairo_region_t *
gimp_canvas_transform_preview_get_extents(GimpCanvasItem * item)431 gimp_canvas_transform_preview_get_extents (GimpCanvasItem *item)
432 {
433 cairo_rectangle_int_t rectangle;
434
435 if (gimp_canvas_transform_preview_transform (item, &rectangle))
436 return cairo_region_create_rectangle (&rectangle);
437
438 return NULL;
439 }
440
441 static void
gimp_canvas_transform_preview_layer_changed(GimpLayer * layer,GimpCanvasTransformPreview * transform_preview)442 gimp_canvas_transform_preview_layer_changed (GimpLayer *layer,
443 GimpCanvasTransformPreview *transform_preview)
444 {
445 GimpCanvasItem *item = GIMP_CANVAS_ITEM (transform_preview);
446
447 gimp_canvas_item_begin_change (item);
448 gimp_canvas_item_end_change (item);
449 }
450
451 static void
gimp_canvas_transform_preview_set_pickable(GimpCanvasTransformPreview * transform_preview,GimpPickable * pickable)452 gimp_canvas_transform_preview_set_pickable (GimpCanvasTransformPreview *transform_preview,
453 GimpPickable *pickable)
454 {
455 GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (transform_preview);
456
457 if (private->pickable && GIMP_IS_LAYER (private->pickable))
458 {
459 g_signal_handlers_disconnect_by_func (
460 private->pickable,
461 gimp_canvas_transform_preview_layer_changed,
462 transform_preview);
463 }
464
465 g_set_object (&private->pickable, pickable);
466
467 if (pickable && GIMP_IS_LAYER (pickable))
468 {
469 g_signal_connect (pickable, "opacity-changed",
470 G_CALLBACK (gimp_canvas_transform_preview_layer_changed),
471 transform_preview);
472 g_signal_connect (pickable, "mask-changed",
473 G_CALLBACK (gimp_canvas_transform_preview_layer_changed),
474 transform_preview);
475 g_signal_connect (pickable, "apply-mask-changed",
476 G_CALLBACK (gimp_canvas_transform_preview_layer_changed),
477 transform_preview);
478 g_signal_connect (pickable, "show-mask-changed",
479 G_CALLBACK (gimp_canvas_transform_preview_layer_changed),
480 transform_preview);
481 }
482 }
483
484 static void
gimp_canvas_transform_preview_sync_node(GimpCanvasTransformPreview * transform_preview)485 gimp_canvas_transform_preview_sync_node (GimpCanvasTransformPreview *transform_preview)
486 {
487 GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (transform_preview);
488 GimpCanvasItem *item = GIMP_CANVAS_ITEM (transform_preview);
489 GimpDisplayShell *shell = gimp_canvas_item_get_shell (item);
490 GimpImage *image = gimp_canvas_item_get_image (item);
491 GimpPickable *pickable = private->pickable;
492 GimpDrawable *layer_mask = NULL;
493 GimpDrawable *mask = NULL;
494 gdouble opacity = private->opacity;
495 gint offset_x = 0;
496 gint offset_y = 0;
497 GimpMatrix3 matrix;
498
499 if (! private->node)
500 {
501 private->node = gegl_node_new ();
502
503 private->source_node =
504 gegl_node_new_child (private->node,
505 "operation", "gimp:buffer-source-validate",
506 NULL);
507
508 private->convert_format_node =
509 gegl_node_new_child (private->node,
510 "operation", "gegl:convert-format",
511 NULL);
512
513 private->layer_mask_source_node =
514 gegl_node_new_child (private->node,
515 "operation", "gimp:buffer-source-validate",
516 NULL);
517
518 private->layer_mask_opacity_node =
519 gegl_node_new_child (private->node,
520 "operation", "gegl:opacity",
521 NULL);
522
523 private->mask_source_node =
524 gegl_node_new_child (private->node,
525 "operation", "gimp:buffer-source-validate",
526 NULL);
527
528 private->mask_translate_node =
529 gegl_node_new_child (private->node,
530 "operation", "gegl:translate",
531 NULL);
532
533 private->mask_crop_node =
534 gegl_node_new_child (private->node,
535 "operation", "gegl:crop",
536 "width", 0.0,
537 "height", 0.0,
538 NULL);
539
540 private->opacity_node =
541 gegl_node_new_child (private->node,
542 "operation", "gegl:opacity",
543 NULL);
544
545 private->cache_node =
546 gegl_node_new_child (private->node,
547 "operation", "gegl:cache",
548 NULL);
549
550 private->transform_node =
551 gegl_node_new_child (private->node,
552 "operation", "gegl:transform",
553 "near-z", GIMP_TRANSFORM_NEAR_Z,
554 "sampler", GIMP_INTERPOLATION_NONE,
555 NULL);
556
557 gegl_node_link_many (private->source_node,
558 private->convert_format_node,
559 private->transform_node,
560 NULL);
561
562 gegl_node_connect_to (private->layer_mask_source_node, "output",
563 private->layer_mask_opacity_node, "aux");
564
565 gegl_node_link_many (private->mask_source_node,
566 private->mask_translate_node,
567 private->mask_crop_node,
568 NULL);
569
570 private->node_pickable = NULL;
571 private->node_layer_mask = NULL;
572 private->node_mask = NULL;
573 private->node_rect = *GEGL_RECTANGLE (0, 0, 0, 0);
574 private->node_opacity = 1.0;
575 gimp_matrix3_identity (&private->node_matrix);
576 private->node_output = private->transform_node;
577 }
578
579 if (GIMP_IS_ITEM (pickable))
580 {
581 gimp_item_get_offset (GIMP_ITEM (private->pickable),
582 &offset_x, &offset_y);
583
584 if (gimp_item_mask_bounds (GIMP_ITEM (pickable),
585 NULL, NULL, NULL, NULL))
586 {
587 mask = GIMP_DRAWABLE (gimp_image_get_mask (image));
588 }
589
590 if (GIMP_IS_LAYER (pickable))
591 {
592 GimpLayer *layer = GIMP_LAYER (pickable);
593
594 opacity *= gimp_layer_get_opacity (layer);
595
596 layer_mask = GIMP_DRAWABLE (gimp_layer_get_mask (layer));
597
598 if (layer_mask)
599 {
600 if (gimp_layer_get_show_mask (layer) && ! mask)
601 {
602 pickable = GIMP_PICKABLE (layer_mask);
603 layer_mask = NULL;
604 }
605 else if (! gimp_layer_get_apply_mask (layer))
606 {
607 layer_mask = NULL;
608 }
609 }
610 }
611 }
612
613 gimp_matrix3_identity (&matrix);
614 gimp_matrix3_translate (&matrix, offset_x, offset_y);
615 gimp_matrix3_mult (&private->transform, &matrix);
616 gimp_matrix3_scale (&matrix, shell->scale_x, shell->scale_y);
617
618 if (pickable != private->node_pickable)
619 {
620 GeglBuffer *buffer;
621
622 gimp_pickable_flush (pickable);
623
624 buffer = gimp_pickable_get_buffer (pickable);
625
626 if (gimp_tile_handler_validate_get_assigned (buffer))
627 buffer = gimp_gegl_buffer_dup (buffer);
628 else
629 buffer = g_object_ref (buffer);
630
631 gegl_node_set (private->source_node,
632 "buffer", buffer,
633 NULL);
634 gegl_node_set (private->convert_format_node,
635 "format", gimp_pickable_get_format_with_alpha (pickable),
636 NULL);
637
638 g_object_unref (buffer);
639 }
640
641 if (layer_mask != private->node_layer_mask)
642 {
643 gegl_node_set (private->layer_mask_source_node,
644 "buffer", layer_mask ?
645 gimp_drawable_get_buffer (layer_mask) :
646 NULL,
647 NULL);
648 }
649
650 if (mask)
651 {
652 GeglRectangle rect;
653
654 rect.x = offset_x;
655 rect.y = offset_y;
656 rect.width = gimp_item_get_width (GIMP_ITEM (private->pickable));
657 rect.height = gimp_item_get_height (GIMP_ITEM (private->pickable));
658
659 if (mask != private->node_mask)
660 {
661 gegl_node_set (private->mask_source_node,
662 "buffer", gimp_drawable_get_buffer (mask),
663 NULL);
664 }
665
666 if (! gegl_rectangle_equal (&rect, &private->node_rect))
667 {
668 private->node_rect = rect;
669
670 gegl_node_set (private->mask_translate_node,
671 "x", (gdouble) -rect.x,
672 "y", (gdouble) -rect.y,
673 NULL);
674
675 gegl_node_set (private->mask_crop_node,
676 "width", (gdouble) rect.width,
677 "height", (gdouble) rect.height,
678 NULL);
679 }
680
681 if (! private->node_mask)
682 {
683 gegl_node_connect_to (private->mask_crop_node, "output",
684 private->opacity_node, "aux");
685 }
686 }
687 else if (private->node_mask)
688 {
689 gegl_node_disconnect (private->opacity_node, "aux");
690 }
691
692 if (opacity != private->node_opacity)
693 {
694 gegl_node_set (private->opacity_node,
695 "value", opacity,
696 NULL);
697 }
698
699 if (layer_mask != private->node_layer_mask ||
700 mask != private->node_mask ||
701 (opacity != 1.0) != (private->node_opacity != 1.0))
702 {
703 GeglNode *output = private->source_node;
704
705 if (layer_mask && ! mask)
706 {
707 gegl_node_link (output, private->layer_mask_opacity_node);
708 output = private->layer_mask_opacity_node;
709 }
710 else
711 {
712 gegl_node_disconnect (private->layer_mask_opacity_node, "input");
713 }
714
715 if (mask || (opacity != 1.0))
716 {
717 gegl_node_link (output, private->opacity_node);
718 output = private->opacity_node;
719 }
720 else
721 {
722 gegl_node_disconnect (private->opacity_node, "input");
723 }
724
725 if (output == private->source_node)
726 {
727 gegl_node_disconnect (private->cache_node, "input");
728
729 gegl_node_link (output, private->convert_format_node);
730 output = private->convert_format_node;
731 }
732 else
733 {
734 gegl_node_disconnect (private->convert_format_node, "input");
735
736 gegl_node_link (output, private->cache_node);
737 output = private->cache_node;
738 }
739
740 gegl_node_link (output, private->transform_node);
741 output = private->transform_node;
742
743 if (layer_mask && mask)
744 {
745 gegl_node_link (output, private->layer_mask_opacity_node);
746 output = private->layer_mask_opacity_node;
747 }
748
749 private->node_output = output;
750 }
751
752 if (memcmp (&matrix, &private->node_matrix, sizeof (matrix)))
753 {
754 private->node_matrix = matrix;
755
756 gimp_gegl_node_set_matrix (private->transform_node, &matrix);
757 }
758
759 private->node_pickable = pickable;
760 private->node_layer_mask = layer_mask;
761 private->node_mask = mask;
762 private->node_opacity = opacity;
763 }
764
765
766 /* public functions */
767
768
769 GimpCanvasItem *
gimp_canvas_transform_preview_new(GimpDisplayShell * shell,GimpPickable * pickable,const GimpMatrix3 * transform,gdouble x1,gdouble y1,gdouble x2,gdouble y2)770 gimp_canvas_transform_preview_new (GimpDisplayShell *shell,
771 GimpPickable *pickable,
772 const GimpMatrix3 *transform,
773 gdouble x1,
774 gdouble y1,
775 gdouble x2,
776 gdouble y2)
777 {
778 g_return_val_if_fail (GIMP_IS_DISPLAY_SHELL (shell), NULL);
779 g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL);
780 g_return_val_if_fail (transform != NULL, NULL);
781
782 return g_object_new (GIMP_TYPE_CANVAS_TRANSFORM_PREVIEW,
783 "shell", shell,
784 "pickable", pickable,
785 "transform", transform,
786 "x1", x1,
787 "y1", y1,
788 "x2", x2,
789 "y2", y2,
790 NULL);
791 }
792