[gimp/wip/passthrough: 10/10] app: move bottom-layer special casing to GimpOperationLayerMode



commit 5378493eaba360595a5c13fcf62aa7f47f9162d8
Author: Ell <ell_se yahoo com>
Date:   Fri Apr 21 19:42:04 2017 -0400

    app: move bottom-layer special casing to GimpOperationLayerMode
    
    GimpFilter's is_last_node field only reflects the item's position
    within the parent stack.  When a layer is contained in a pass-
    through group, it can be the last layer of the group, while not
    being the last layer in the graph as a whole (paticularly, if
    there are visible layers below the group).  In fact, when we have
    nested pass-through groups, whether or not a layer is the last
    node depends on which group we're considering as the root (since
    we exclude the backdrop from the group's projection, resulting in
    different graphs for different groups).
    
    Instead of rolling our own graph traversal, just move the relevant
    logic to GimpOperationLayerMode, and let GEGL do the work for us.
    At processing time, we can tell if we're the last node by checking
    if we have any input.
    
    For this to work, GimpOperationLayerMode's process() function needs
    to have control over what's going on.  Replace the derived op
    classes, which override process(), with a call to the layer mode's
    function (as per gimp_layer_mode_get_function()) in
    GimpOperationLayerMode's process() function.  (Well, actually, this
    commit keeps the ops around, and just hacks around them in
    gimp_layer_mode_get_operation(), because laziness :P)
    
    Keep using the layer's is_last_node property to do the invalidation.

 app/core/gimpimage-merge.c                         |   17 +--
 app/core/gimplayer.c                               |   22 +---
 app/operations/layer-modes/gimp-layer-modes.c      |   43 ++++---
 app/operations/layer-modes/gimp-layer-modes.h      |    2 +
 .../layer-modes/gimpoperationlayermode.c           |  153 +++++++++++++++++---
 .../layer-modes/gimpoperationlayermode.h           |    3 +-
 6 files changed, 170 insertions(+), 70 deletions(-)
---
diff --git a/app/core/gimpimage-merge.c b/app/core/gimpimage-merge.c
index a6f8675..3298d07 100644
--- a/app/core/gimpimage-merge.c
+++ b/app/core/gimpimage-merge.c
@@ -610,25 +610,11 @@ gimp_image_merge_layers (GimpImage     *image,
 
       gimp_item_get_offset (GIMP_ITEM (layer), &off_x, &off_y);
 
-      /* MODE_DISSOLVE is special since it is the only mode that does not
-       *  work on the projection with the lower layer, but only locally on
-       *  the layers alpha channel.
-       */
       mode            = gimp_layer_get_mode (layer);
       blend_space     = gimp_layer_get_blend_space (layer);
       composite_space = gimp_layer_get_composite_space (layer);
       composite_mode  = gimp_layer_get_composite_mode (layer);
 
-      if (layer == bottom_layer)
-        {
-          if (mode != GIMP_LAYER_MODE_DISSOLVE)
-            mode          = GIMP_LAYER_MODE_NORMAL_LEGACY;
-
-          blend_space     = GIMP_LAYER_COLOR_SPACE_AUTO;
-          composite_space = GIMP_LAYER_COLOR_SPACE_AUTO;
-          composite_mode  = GIMP_LAYER_COMPOSITE_AUTO;
-        }
-
       merge_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (merge_layer));
       layer_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
 
@@ -647,7 +633,8 @@ gimp_image_merge_layers (GimpImage     *image,
                                            - (y1 - off_y));
         }
 
-      gimp_applicator_set_src_buffer (applicator, merge_buffer);
+      if (layer != bottom_layer)
+        gimp_applicator_set_src_buffer (applicator, merge_buffer);
       gimp_applicator_set_dest_buffer (applicator, merge_buffer);
 
       gimp_applicator_set_apply_buffer (applicator, layer_buffer);
diff --git a/app/core/gimplayer.c b/app/core/gimplayer.c
index 8f00663..85ce26c 100644
--- a/app/core/gimplayer.c
+++ b/app/core/gimplayer.c
@@ -596,24 +596,10 @@ gimp_layer_update_mode_node (GimpLayer *layer)
     }
   else
     {
-      if (gimp_filter_get_is_last_node (GIMP_FILTER (layer)))
-        {
-          if (layer->mode != GIMP_LAYER_MODE_DISSOLVE)
-            visible_mode          = GIMP_LAYER_MODE_NORMAL_LEGACY;
-          else
-            visible_mode          = GIMP_LAYER_MODE_DISSOLVE;
-
-          visible_blend_space     = GIMP_LAYER_COLOR_SPACE_AUTO;
-          visible_composite_space = GIMP_LAYER_COLOR_SPACE_AUTO;
-          visible_composite_mode  = GIMP_LAYER_COMPOSITE_AUTO;
-        }
-      else
-        {
-          visible_mode            = layer->mode;
-          visible_blend_space     = layer->blend_space;
-          visible_composite_space = layer->composite_space;
-          visible_composite_mode  = layer->composite_mode;
-        }
+      visible_mode            = layer->mode;
+      visible_blend_space     = layer->blend_space;
+      visible_composite_space = layer->composite_space;
+      visible_composite_mode  = layer->composite_mode;
     }
 
   gimp_gegl_mode_node_set_mode (mode_node,
diff --git a/app/operations/layer-modes/gimp-layer-modes.c b/app/operations/layer-modes/gimp-layer-modes.c
index 12a47e3..80e234b 100644
--- a/app/operations/layer-modes/gimp-layer-modes.c
+++ b/app/operations/layer-modes/gimp-layer-modes.c
@@ -60,15 +60,16 @@ typedef struct _GimpLayerModeInfo GimpLayerModeInfo;
 
 struct _GimpLayerModeInfo
 {
-  GimpLayerMode           layer_mode;
-  const gchar            *op_name;
-  GimpLayerModeFunc       function;
-  GimpLayerModeFlags      flags;
-  GimpLayerModeContext    context;
-  GimpLayerCompositeMode  paint_composite_mode;
-  GimpLayerCompositeMode  composite_mode;
-  GimpLayerColorSpace     composite_space;
-  GimpLayerColorSpace     blend_space;
+  GimpLayerMode            layer_mode;
+  const gchar             *op_name;
+  GimpLayerModeFunc        function;
+  GimpLayerModeFlags       flags;
+  GimpLayerModeContext     context;
+  GimpLayerCompositeMode   paint_composite_mode;
+  GimpLayerCompositeMode   composite_mode;
+  GimpLayerColorSpace      composite_space;
+  GimpLayerColorSpace      blend_space;
+  GimpLayerModeAffectMask  affect_mask;
 };
 
 
@@ -98,7 +99,8 @@ static const GimpLayerModeInfo layer_mode_infos[] =
                             GIMP_LAYER_MODE_FLAG_COMPOSITE_SPACE_IMMUTABLE,
     .context              = GIMP_LAYER_MODE_CONTEXT_ALL,
     .paint_composite_mode = GIMP_LAYER_COMPOSITE_SRC_OVER,
-    .composite_mode       = GIMP_LAYER_COMPOSITE_SRC_OVER
+    .composite_mode       = GIMP_LAYER_COMPOSITE_SRC_OVER,
+    .affect_mask          = GIMP_LAYER_MODE_AFFECT_SRC
   },
 
   { GIMP_LAYER_MODE_BEHIND_LEGACY,
@@ -869,7 +871,8 @@ static const GimpLayerModeInfo layer_mode_infos[] =
     .context              = GIMP_LAYER_MODE_CONTEXT_FADE,
     .paint_composite_mode = GIMP_LAYER_COMPOSITE_SRC_OVER,
     .composite_mode       = GIMP_LAYER_COMPOSITE_SRC_OVER,
-    .composite_space      = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR
+    .composite_space      = GIMP_LAYER_COLOR_SPACE_RGB_LINEAR,
+    .affect_mask          = GIMP_LAYER_MODE_AFFECT_DST
   },
 
   { GIMP_LAYER_MODE_ANTI_ERASE,
@@ -1276,12 +1279,7 @@ gimp_layer_mode_get_paint_composite_mode (GimpLayerMode mode)
 const gchar *
 gimp_layer_mode_get_operation (GimpLayerMode mode)
 {
-  const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
-
-  if (! info)
-    return "gimp:layer-mode";
-
-  return info->op_name;
+  return "gimp:layer-mode";
 }
 
 GimpLayerModeFunc
@@ -1295,6 +1293,17 @@ gimp_layer_mode_get_function (GimpLayerMode mode)
   return info->function;
 }
 
+GimpLayerModeAffectMask
+gimp_layer_mode_get_affect_mask (GimpLayerMode mode)
+{
+  const GimpLayerModeInfo *info = gimp_layer_mode_info (mode);
+
+  if (! info)
+    return GIMP_LAYER_MODE_AFFECT_NONE;
+
+  return info->affect_mask;
+}
+
 GimpLayerModeContext
 gimp_layer_mode_get_context (GimpLayerMode mode)
 {
diff --git a/app/operations/layer-modes/gimp-layer-modes.h b/app/operations/layer-modes/gimp-layer-modes.h
index 9b49fb6..bf8991f 100644
--- a/app/operations/layer-modes/gimp-layer-modes.h
+++ b/app/operations/layer-modes/gimp-layer-modes.h
@@ -43,6 +43,8 @@ const gchar            * gimp_layer_mode_get_operation       (GimpLayerMode
 
 GimpLayerModeFunc        gimp_layer_mode_get_function        (GimpLayerMode        mode);
 
+GimpLayerModeAffectMask  gimp_layer_mode_get_affect_mask     (GimpLayerMode        mode);
+
 GimpLayerModeContext     gimp_layer_mode_get_context         (GimpLayerMode        mode);
 
 GimpLayerMode          * gimp_layer_mode_get_context_array   (GimpLayerMode        mode,
diff --git a/app/operations/layer-modes/gimpoperationlayermode.c 
b/app/operations/layer-modes/gimpoperationlayermode.c
index 7706a76..bedeb6f 100644
--- a/app/operations/layer-modes/gimpoperationlayermode.c
+++ b/app/operations/layer-modes/gimpoperationlayermode.c
@@ -76,14 +76,24 @@ static void     gimp_operation_layer_mode_get_property (GObject                *
                                                         guint                   property_id,
                                                         GValue                 *value,
                                                         GParamSpec             *pspec);
-
+                                                       
 static void     gimp_operation_layer_mode_prepare      (GeglOperation          *operation);
-static gboolean gimp_operation_layer_mode_process      (GeglOperation          *operation,
+static gboolean
+              gimp_operation_layer_mode_parent_process (GeglOperation          *operation,
                                                         GeglOperationContext   *context,
                                                         const gchar            *output_prop,
                                                         const GeglRectangle    *result,
                                                         gint                    level);
 
+static gboolean gimp_operation_layer_mode_process      (GeglOperation          *operation,
+                                                        void                   *in,
+                                                        void                   *layer,
+                                                        void                   *mask,
+                                                        void                   *out,
+                                                        glong                   samples,
+                                                        const GeglRectangle    *roi,
+                                                        gint                    level);
+
 static GimpLayerModeAffectMask
         gimp_operation_layer_mode_real_get_affect_mask (GimpOperationLayerMode *layer_mode);
 
@@ -155,6 +165,15 @@ static inline void composite_func_src_atop_sse2     (gfloat *in,
                                                      gint    samples);
 #endif
 
+static gboolean process_layer_only (GeglOperation       *operation,
+                                    void                *in,
+                                    void                *layer,
+                                    void                *mask,
+                                    void                *out,
+                                    glong                samples,
+                                    const GeglRectangle *roi,
+                                    gint                 level);
+
 
 G_DEFINE_TYPE (GimpOperationLayerMode, gimp_operation_layer_mode,
                GEGL_TYPE_OPERATION_POINT_COMPOSER3)
@@ -193,8 +212,8 @@ gimp_operation_layer_mode_class_init (GimpOperationLayerModeClass *klass)
   object_class->get_property = gimp_operation_layer_mode_get_property;
 
   operation_class->prepare       = gimp_operation_layer_mode_prepare;
-  operation_class->process       = gimp_operation_layer_mode_process;
-  point_composer3_class->process = gimp_operation_layer_mode_process_pixels;
+  operation_class->process       = gimp_operation_layer_mode_parent_process;
+  point_composer3_class->process = gimp_operation_layer_mode_process;
 
   klass->get_affect_mask = gimp_operation_layer_mode_real_get_affect_mask;
 
@@ -352,15 +371,42 @@ static void
 gimp_operation_layer_mode_prepare (GeglOperation *operation)
 {
   GimpOperationLayerMode *self = GIMP_OPERATION_LAYER_MODE (operation);
-  const Babl             *in_format;
+  const GeglRectangle    *input_extent;
+  const Babl             *preferred_format;
   const Babl             *format;
 
-  in_format = gegl_operation_get_source_format (operation, "input");
+  self->func = gimp_layer_mode_get_function (self->layer_mode);
+
+  input_extent = gegl_operation_source_get_bounding_box (operation, "input");
+
+  /* if the input pad has data, work as usual. */
+  if (input_extent && ! gegl_rectangle_is_empty (input_extent))
+    {
+      self->is_last_node = FALSE;
+
+      preferred_format = gegl_operation_get_source_format (operation, "input");
+    }
+  /* otherwise, we're the last node (corresponding to the bottom layer).
+   * in this case, we render the layer (as if) using src-over mode.
+   */
+  else
+    {
+      self->is_last_node = TRUE;
+
+      /* if the layer mode doesn't affect the source, use a shortcut
+       * function that only applies the opacity/mask to the layer.
+       */
+      if (! (gimp_layer_mode_get_affect_mask (self->layer_mode) &
+             GIMP_LAYER_MODE_AFFECT_SRC))
+        self->func = process_layer_only;
 
-  format    = gimp_layer_mode_get_format (self->layer_mode,
-                                          self->composite_space,
-                                          self->blend_space,
-                                          in_format);
+      preferred_format = gegl_operation_get_source_format (operation, "aux");
+    }
+
+  format = gimp_layer_mode_get_format (self->layer_mode,
+                                       self->composite_space,
+                                       self->blend_space,
+                                       preferred_format);
 
   gegl_operation_set_format (operation, "input",  format);
   gegl_operation_set_format (operation, "output", format);
@@ -369,11 +415,11 @@ gimp_operation_layer_mode_prepare (GeglOperation *operation)
 }
 
 static gboolean
-gimp_operation_layer_mode_process (GeglOperation        *operation,
-                                   GeglOperationContext *context,
-                                   const gchar          *output_prop,
-                                   const GeglRectangle  *result,
-                                   gint                  level)
+gimp_operation_layer_mode_parent_process (GeglOperation        *operation,
+                                          GeglOperationContext *context,
+                                          const gchar          *output_prop,
+                                          const GeglRectangle  *result,
+                                          gint                  level)
 {
   GimpOperationLayerMode *point = GIMP_OPERATION_LAYER_MODE (operation);
   GObject                *input;
@@ -405,14 +451,16 @@ gimp_operation_layer_mode_process (GeglOperation        *operation,
   /* if there's no 'input' ... */
   if (! has_input)
     {
-      /* ... and there's 'aux', and the composite mode includes it ... */
+      /* ... and there's 'aux', and the composite mode includes it (or we're
+       * the last node, which is equivalent to src-over) ... */
       if (has_aux &&
           (point->composite_mode == GIMP_LAYER_COMPOSITE_SRC_OVER ||
-           point->composite_mode == GIMP_LAYER_COMPOSITE_DST_ATOP))
+           point->composite_mode == GIMP_LAYER_COMPOSITE_DST_ATOP ||
+           point->is_last_node))
         {
           GimpLayerModeAffectMask affect_mask;
 
-          affect_mask = gimp_operation_layer_mode_get_affect_mask (point);
+          affect_mask = gimp_layer_mode_get_affect_mask (point->layer_mode);
 
           /* ... and the op doesn't otherwise affect 'aux', or changes its
            * alpha ...
@@ -449,7 +497,7 @@ gimp_operation_layer_mode_process (GeglOperation        *operation,
         {
           GimpLayerModeAffectMask affect_mask;
 
-          affect_mask = gimp_operation_layer_mode_get_affect_mask (point);
+          affect_mask = gimp_layer_mode_get_affect_mask (point->layer_mode);
 
           /* ... and the op doesn't otherwise affect 'input' ... */
           if (! (affect_mask & GIMP_LAYER_MODE_AFFECT_DST))
@@ -494,6 +542,43 @@ gimp_operation_layer_mode_process (GeglOperation        *operation,
                                                        level);
 }
 
+static gboolean
+gimp_operation_layer_mode_process (GeglOperation       *operation,
+                                   void                *in,
+                                   void                *layer,
+                                   void                *mask,
+                                   void                *out,
+                                   glong                samples,
+                                   const GeglRectangle *roi,
+                                   gint                 level)
+{
+  GimpOperationLayerMode *point = GIMP_OPERATION_LAYER_MODE (operation);
+
+  /* if we're not the last node, or we're using the opacity/mask shortcut
+   * function, forward directly to the real process function.
+   */
+  if (! point->is_last_node || point->func == process_layer_only)
+    {
+      return point->func (operation, in, layer, mask, out, samples, roi, level);
+    }
+  /* otherwise, switch the composite mode temporarily to src-over, before
+   * handing processing over to the real process function.
+   */
+  else
+    {
+      GimpLayerCompositeMode composite_mode = point->composite_mode;
+      gboolean               result;
+
+      point->composite_mode = GIMP_LAYER_COMPOSITE_SRC_OVER;
+
+      result = point->func (operation, in, layer, mask, out, samples, roi, level);
+
+      point->composite_mode = composite_mode;
+
+      return result;
+    }
+}
+
 static GimpLayerModeAffectMask
 gimp_operation_layer_mode_real_get_affect_mask (GimpOperationLayerMode *layer_mode)
 {
@@ -545,6 +630,36 @@ gimp_operation_layer_mode_process_pixels (GeglOperation       *operation,
   return TRUE;
 }
 
+static gboolean
+process_layer_only (GeglOperation       *operation,
+                    void                *in_buf,
+                    void                *layer_buf,
+                    void                *mask_buf,
+                    void                *out_buf,
+                    glong                samples,
+                    const GeglRectangle *roi,
+                    gint                 level)
+{
+  gfloat *out    = out_buf;
+  gfloat *layer  = layer_buf;
+  gfloat *mask   = mask_buf;
+  gfloat opacity = GIMP_OPERATION_LAYER_MODE (operation)->opacity;
+
+  while (samples--)
+    {
+      memcpy (out, layer, 3 * sizeof (gfloat));
+
+      out[ALPHA] = layer[ALPHA] * opacity;
+      if (mask)
+        out[ALPHA] *= *mask++;
+
+      layer += 4;
+      out   += 4;
+    }
+
+  return TRUE;
+}
+
 
 /*  non-subtractive compositing functions.  these functions expect comp[ALPHA]
  *  to be the same as layer[ALPHA].  when in[ALPHA] or layer[ALPHA] are zero,
diff --git a/app/operations/layer-modes/gimpoperationlayermode.h 
b/app/operations/layer-modes/gimpoperationlayermode.h
index c4383ca..29a9d00 100644
--- a/app/operations/layer-modes/gimpoperationlayermode.h
+++ b/app/operations/layer-modes/gimpoperationlayermode.h
@@ -58,7 +58,8 @@ struct _GimpOperationLayerMode
   GimpLayerColorSpace          blend_space;
   GimpLayerColorSpace          composite_space;
   GimpLayerCompositeMode       composite_mode;
-  GimpBlendFunc                blend_func;
+  GimpLayerModeFunc            func;
+  gboolean                     is_last_node;
 };
 
 


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