[gimp/gimp-2-10] Issue #1125 - Transform tools temporarily disables layer mask



commit 5826a399b25417e190e68f2885a332275f3effdd
Author: Ell <ell_se yahoo com>
Date:   Mon Aug 6 04:17:47 2018 -0400

    Issue #1125 - Transform tools temporarily disables layer mask
    
    In GimpCanvasTransformPreview, add the necessary bits to the
    preview graph so that, when transforming a layer, the layer's
    opacity and mask are correctly applied to the preview.  Note that
    since we're still not rendering the preview as part of the image
    graph, the output is not always accurate, but it should be good
    enough in most cases.
    
    (cherry picked from commit 2ac91e0fc380a042d3492634cd9ab0d879ca404a)

 app/display/gimpcanvastransformpreview.c | 269 +++++++++++++++++++++++--------
 1 file changed, 203 insertions(+), 66 deletions(-)
---
diff --git a/app/display/gimpcanvastransformpreview.c b/app/display/gimpcanvastransformpreview.c
index a9c41d8dfb..66352b6272 100644
--- a/app/display/gimpcanvastransformpreview.c
+++ b/app/display/gimpcanvastransformpreview.c
@@ -34,6 +34,7 @@
 
 #include "core/gimpchannel.h"
 #include "core/gimpimage.h"
+#include "core/gimplayer.h"
 #include "core/gimp-transform-utils.h"
 #include "core/gimp-utils.h"
 
@@ -70,6 +71,8 @@ struct _GimpCanvasTransformPreviewPrivate
   GeglNode      *node;
   GeglNode      *source_node;
   GeglNode      *convert_format_node;
+  GeglNode      *layer_mask_source_node;
+  GeglNode      *layer_mask_opacity_node;
   GeglNode      *mask_source_node;
   GeglNode      *mask_translate_node;
   GeglNode      *mask_crop_node;
@@ -78,10 +81,12 @@ struct _GimpCanvasTransformPreviewPrivate
   GeglNode      *transform_node;
 
   GimpDrawable  *node_drawable;
+  GimpDrawable  *node_layer_mask;
   GimpDrawable  *node_mask;
   GeglRectangle  node_rect;
   gdouble        node_opacity;
   GimpMatrix3    node_matrix;
+  GeglNode      *node_output;
 };
 
 #define GET_PRIVATE(transform_preview) \
@@ -92,21 +97,26 @@ struct _GimpCanvasTransformPreviewPrivate
 
 /*  local function prototypes  */
 
-static void             gimp_canvas_transform_preview_dispose      (GObject        *object);
-static void             gimp_canvas_transform_preview_set_property (GObject        *object,
-                                                                    guint           property_id,
-                                                                    const GValue   *value,
-                                                                    GParamSpec     *pspec);
-static void             gimp_canvas_transform_preview_get_property (GObject        *object,
-                                                                    guint           property_id,
-                                                                    GValue         *value,
-                                                                    GParamSpec     *pspec);
+static void             gimp_canvas_transform_preview_dispose       (GObject                    *object);
+static void             gimp_canvas_transform_preview_set_property  (GObject                    *object,
+                                                                     guint                       property_id,
+                                                                     const GValue               *value,
+                                                                     GParamSpec                 *pspec);
+static void             gimp_canvas_transform_preview_get_property  (GObject                    *object,
+                                                                     guint                       property_id,
+                                                                     GValue                     *value,
+                                                                     GParamSpec                 *pspec);
 
-static void             gimp_canvas_transform_preview_draw         (GimpCanvasItem *item,
-                                                                    cairo_t        *cr);
-static cairo_region_t * gimp_canvas_transform_preview_get_extents  (GimpCanvasItem *item);
+static void             gimp_canvas_transform_preview_draw          (GimpCanvasItem             *item,
+                                                                     cairo_t                    *cr);
+static cairo_region_t * gimp_canvas_transform_preview_get_extents   (GimpCanvasItem             *item);
 
-static void             gimp_canvas_transform_preview_sync_node    (GimpCanvasItem *item);
+static void             gimp_canvas_transform_preview_layer_changed (GimpLayer                  *layer,
+                                                                     GimpCanvasTransformPreview 
*transform_preview);
+
+static void             gimp_canvas_transform_preview_set_drawable  (GimpCanvasTransformPreview 
*transform_preview,
+                                                                     GimpDrawable               *drawable);
+static void             gimp_canvas_transform_preview_sync_node     (GimpCanvasTransformPreview 
*transform_preview);
 
 
 G_DEFINE_TYPE (GimpCanvasTransformPreview, gimp_canvas_transform_preview,
@@ -192,10 +202,13 @@ gimp_canvas_transform_preview_init (GimpCanvasTransformPreview *transform_previe
 static void
 gimp_canvas_transform_preview_dispose (GObject *object)
 {
-  GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (object);
+  GimpCanvasTransformPreview        *transform_preview = GIMP_CANVAS_TRANSFORM_PREVIEW (object);
+  GimpCanvasTransformPreviewPrivate *private           = GET_PRIVATE (object);
 
   g_clear_object (&private->node);
 
+  gimp_canvas_transform_preview_set_drawable (transform_preview, NULL);
+
   G_OBJECT_CLASS (parent_class)->dispose (object);
 }
 
@@ -205,12 +218,14 @@ gimp_canvas_transform_preview_set_property (GObject      *object,
                                             const GValue *value,
                                             GParamSpec   *pspec)
 {
-  GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (object);
+  GimpCanvasTransformPreview        *transform_preview = GIMP_CANVAS_TRANSFORM_PREVIEW (object);
+  GimpCanvasTransformPreviewPrivate *private           = GET_PRIVATE (object);
 
   switch (property_id)
     {
     case PROP_DRAWABLE:
-      private->drawable = g_value_get_object (value); /* don't ref */
+      gimp_canvas_transform_preview_set_drawable (transform_preview,
+                                                  g_value_get_object (value));
       break;
 
     case PROP_TRANSFORM:
@@ -355,8 +370,9 @@ static void
 gimp_canvas_transform_preview_draw (GimpCanvasItem *item,
                                     cairo_t        *cr)
 {
-  GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (item);
-  GimpDisplayShell                  *shell   = gimp_canvas_item_get_shell (item);
+  GimpCanvasTransformPreview        *transform_preview = GIMP_CANVAS_TRANSFORM_PREVIEW (item);
+  GimpCanvasTransformPreviewPrivate *private           = GET_PRIVATE (item);
+  GimpDisplayShell                  *shell             = gimp_canvas_item_get_shell (item);
   cairo_rectangle_int_t              extents;
   gdouble                            clip_x1, clip_y1;
   gdouble                            clip_x2, clip_y2;
@@ -396,9 +412,9 @@ gimp_canvas_transform_preview_draw (GimpCanvasItem *item,
   surface_data   = cairo_image_surface_get_data (surface);
   surface_stride = cairo_image_surface_get_stride (surface);
 
-  gimp_canvas_transform_preview_sync_node (item);
+  gimp_canvas_transform_preview_sync_node (transform_preview);
 
-  gegl_node_blit (private->transform_node, 1.0,
+  gegl_node_blit (private->node_output, 1.0,
                   GEGL_RECTANGLE (bounds.x + shell->offset_x,
                                   bounds.y + shell->offset_y,
                                   bounds.width,
@@ -427,14 +443,61 @@ gimp_canvas_transform_preview_get_extents (GimpCanvasItem *item)
 }
 
 static void
-gimp_canvas_transform_preview_sync_node (GimpCanvasItem *item)
+gimp_canvas_transform_preview_layer_changed (GimpLayer                  *layer,
+                                             GimpCanvasTransformPreview *transform_preview)
 {
-  GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (item);
-  GimpDisplayShell                  *shell   = gimp_canvas_item_get_shell (item);
-  GimpImage                         *image   = gimp_canvas_item_get_image (item);
+  GimpCanvasItem *item = GIMP_CANVAS_ITEM (transform_preview);
+
+  gimp_canvas_item_begin_change (item);
+  gimp_canvas_item_end_change   (item);
+}
+
+static void
+gimp_canvas_transform_preview_set_drawable (GimpCanvasTransformPreview *transform_preview,
+                                            GimpDrawable               *drawable)
+{
+  GimpCanvasTransformPreviewPrivate *private = GET_PRIVATE (transform_preview);
+
+  if (private->drawable && GIMP_IS_LAYER (private->drawable))
+    {
+      g_signal_handlers_disconnect_by_func (
+        private->drawable,
+        gimp_canvas_transform_preview_layer_changed,
+        transform_preview);
+    }
+
+  g_set_object (&private->drawable, drawable);
+
+  if (drawable && GIMP_IS_LAYER (drawable))
+    {
+      g_signal_connect (drawable, "opacity-changed",
+                        G_CALLBACK (gimp_canvas_transform_preview_layer_changed),
+                        transform_preview);
+      g_signal_connect (drawable, "mask-changed",
+                        G_CALLBACK (gimp_canvas_transform_preview_layer_changed),
+                        transform_preview);
+      g_signal_connect (drawable, "apply-mask-changed",
+                        G_CALLBACK (gimp_canvas_transform_preview_layer_changed),
+                        transform_preview);
+      g_signal_connect (drawable, "show-mask-changed",
+                        G_CALLBACK (gimp_canvas_transform_preview_layer_changed),
+                        transform_preview);
+    }
+}
+
+static void
+gimp_canvas_transform_preview_sync_node (GimpCanvasTransformPreview *transform_preview)
+{
+  GimpCanvasTransformPreviewPrivate *private    = GET_PRIVATE (transform_preview);
+  GimpCanvasItem                    *item       = GIMP_CANVAS_ITEM (transform_preview);
+  GimpDisplayShell                  *shell      = gimp_canvas_item_get_shell (item);
+  GimpImage                         *image      = gimp_canvas_item_get_image (item);
+  GimpDrawable                      *drawable   = private->drawable;
+  GimpDrawable                      *layer_mask = NULL;
+  GimpDrawable                      *mask       = NULL;
+  gdouble                            opacity    = private->opacity;
   gint                               offset_x, offset_y;
   GimpMatrix3                        matrix;
-  gboolean                           has_mask;
 
   if (! private->node)
     {
@@ -450,6 +513,16 @@ gimp_canvas_transform_preview_sync_node (GimpCanvasItem *item)
                              "operation", "gegl:convert-format",
                              NULL);
 
+      private->layer_mask_source_node =
+        gegl_node_new_child (private->node,
+                             "operation", "gimp:buffer-source-validate",
+                             NULL);
+
+      private->layer_mask_opacity_node =
+        gegl_node_new_child (private->node,
+                             "operation", "gegl:opacity",
+                             NULL);
+
       private->mask_source_node =
         gegl_node_new_child (private->node,
                              "operation", "gimp:buffer-source-validate",
@@ -489,16 +562,21 @@ gimp_canvas_transform_preview_sync_node (GimpCanvasItem *item)
                            private->transform_node,
                            NULL);
 
+      gegl_node_connect_to (private->layer_mask_source_node,  "output",
+                            private->layer_mask_opacity_node, "aux");
+
       gegl_node_link_many (private->mask_source_node,
                            private->mask_translate_node,
                            private->mask_crop_node,
                            NULL);
 
-      private->node_drawable = NULL;
-      private->node_mask     = NULL;
-      private->node_rect     = *GEGL_RECTANGLE (0, 0, 0, 0);
-      private->node_opacity  = 1.0;
+      private->node_drawable   = NULL;
+      private->node_layer_mask = NULL;
+      private->node_mask       = NULL;
+      private->node_rect       = *GEGL_RECTANGLE (0, 0, 0, 0);
+      private->node_opacity    = 1.0;
       gimp_matrix3_identity (&private->node_matrix);
+      private->node_output     = private->transform_node;
     }
 
   gimp_item_get_offset (GIMP_ITEM (private->drawable), &offset_x, &offset_y);
@@ -508,48 +586,55 @@ gimp_canvas_transform_preview_sync_node (GimpCanvasItem *item)
   gimp_matrix3_mult (&private->transform, &matrix);
   gimp_matrix3_scale (&matrix, shell->scale_x, shell->scale_y);
 
-  has_mask = gimp_item_mask_bounds (GIMP_ITEM (private->drawable),
-                                    NULL, NULL, NULL, NULL);
+  if (gimp_item_mask_bounds (GIMP_ITEM (private->drawable),
+                             NULL, NULL, NULL, NULL))
+    {
+      mask = GIMP_DRAWABLE (gimp_image_get_mask (image));
+    }
 
-  if (private->drawable != private->node_drawable)
+  if (GIMP_IS_LAYER (drawable))
     {
-      private->node_drawable = private->drawable;
+      GimpLayer *layer = GIMP_LAYER (drawable);
 
+      opacity *= gimp_layer_get_opacity (layer);
+
+      layer_mask = GIMP_DRAWABLE (gimp_layer_get_mask (layer));
+
+      if (layer_mask)
+        {
+          if (gimp_layer_get_show_mask (layer) && ! mask)
+            {
+              drawable   = layer_mask;
+              layer_mask = NULL;
+            }
+          else if (! gimp_layer_get_apply_mask (layer))
+            {
+              layer_mask = NULL;
+            }
+        }
+    }
+
+  if (drawable != private->node_drawable)
+    {
       gegl_node_set (private->source_node,
-                     "buffer", gimp_drawable_get_buffer (private->drawable),
+                     "buffer", gimp_drawable_get_buffer (drawable),
                      NULL);
       gegl_node_set (private->convert_format_node,
-                     "format", gimp_drawable_get_format_with_alpha (private->drawable),
+                     "format", gimp_drawable_get_format_with_alpha (drawable),
                      NULL);
     }
 
-  if ((has_mask           || private->opacity      != 1.0) !=
-      (private->node_mask || private->node_opacity != 1.0))
+  if (layer_mask != private->node_layer_mask)
     {
-      if (has_mask || private->opacity != 1.0)
-        {
-          gegl_node_disconnect (private->convert_format_node, "input");
-
-          gegl_node_link_many (private->source_node,
-                               private->opacity_node,
-                               private->cache_node,
-                               private->transform_node,
-                               NULL);
-        }
-      else
-        {
-          gegl_node_disconnect (private->opacity_node, "input");
-
-          gegl_node_link_many (private->source_node,
-                               private->convert_format_node,
-                               private->transform_node,
-                               NULL);
-        }
+      gegl_node_set (private->layer_mask_source_node,
+                     "buffer", layer_mask ?
+                                 gimp_drawable_get_buffer (layer_mask) :
+                                 NULL,
+                     NULL);
     }
 
-  if (has_mask)
+  if (mask)
     {
-      GimpDrawable  *mask = GIMP_DRAWABLE (gimp_image_get_mask (image));
       GeglRectangle  rect;
 
       rect.x      = offset_x;
@@ -584,31 +669,83 @@ gimp_canvas_transform_preview_sync_node (GimpCanvasItem *item)
           gegl_node_connect_to (private->mask_crop_node, "output",
                                 private->opacity_node,   "aux");
         }
-
-      private->node_mask = mask;
     }
   else if (private->node_mask)
     {
-      private->node_mask = NULL;
-
       gegl_node_disconnect (private->opacity_node, "aux");
     }
 
-  if (private->opacity != private->node_opacity)
+  if (opacity != private->node_opacity)
     {
-      private->node_opacity = private->opacity;
-
       gegl_node_set (private->opacity_node,
-                     "value", private->opacity,
+                     "value", opacity,
                      NULL);
     }
 
+  if (layer_mask       != private->node_layer_mask ||
+      mask             != private->node_mask       ||
+      (opacity != 1.0) != (private->node_opacity != 1.0))
+    {
+      GeglNode *output = private->source_node;
+
+      if (layer_mask && ! mask)
+        {
+          gegl_node_link (output, private->layer_mask_opacity_node);
+          output = private->layer_mask_opacity_node;
+        }
+      else
+        {
+          gegl_node_disconnect (private->layer_mask_opacity_node, "input");
+        }
+
+      if (mask || (opacity != 1.0))
+        {
+          gegl_node_link (output, private->opacity_node);
+          output = private->opacity_node;
+        }
+      else
+        {
+          gegl_node_disconnect (private->opacity_node, "input");
+        }
+
+      if (output == private->source_node)
+        {
+          gegl_node_disconnect (private->cache_node, "input");
+
+          gegl_node_link (output, private->convert_format_node);
+          output = private->convert_format_node;
+        }
+      else
+        {
+          gegl_node_disconnect (private->convert_format_node, "input");
+
+          gegl_node_link (output, private->cache_node);
+          output = private->cache_node;
+        }
+
+      gegl_node_link (output, private->transform_node);
+      output = private->transform_node;
+
+      if (layer_mask && mask)
+        {
+          gegl_node_link (output, private->layer_mask_opacity_node);
+          output = private->layer_mask_opacity_node;
+        }
+
+      private->node_output = output;
+    }
+
   if (memcmp (&matrix, &private->node_matrix, sizeof (matrix)))
     {
       private->node_matrix = matrix;
 
       gimp_gegl_node_set_matrix (private->transform_node, &matrix);
     }
+
+  private->node_drawable   = drawable;
+  private->node_layer_mask = layer_mask;
+  private->node_mask       = mask;
+  private->node_opacity    = opacity;
 }
 
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]