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