[gimp] app: strength-reduce pass-through groups to normal groups



commit fa9a023c2707a5fc1b044905040fca972e97e2b9
Author: Ell <ell_se yahoo com>
Date:   Wed Dec 6 14:06:16 2017 -0500

    app: strength-reduce pass-through groups to normal groups
    
    Override GimpLayer::get_effective_mode() in GimpGroupLayer, to
    perform strength-reduction of pass-through groups to normal groups
    under certain conditions (see gimp_group_layer_get_effective_mode()
    for the logic.)
    
    The main motivation for this is the fact that Photoshop uses pass-
    through mode as the default mode for groups, resulting in many PSDs
    using pass-through groups generously and unnecessarily.  Since
    pass-through groups are more expensive that normal groups, reducing
    them to normal groups when possible can make a big difference.
    
    Note that, while the results of the strength-reduced composition
    are theoretically equivalent, there may be small differences in
    practice due to numerical errors, especially when using low
    precision.  This is unlikely to be an issue, but, just in case,
    allow disabling this optimization using the
    GIMP_NO_PASS_THROUGH_STRENGTH_REDUCTION environment variable.

 app/core/gimpgrouplayer.c |  207 ++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 195 insertions(+), 12 deletions(-)
---
diff --git a/app/core/gimpgrouplayer.c b/app/core/gimpgrouplayer.c
index 975969f..c124d92 100644
--- a/app/core/gimpgrouplayer.c
+++ b/app/core/gimpgrouplayer.c
@@ -151,9 +151,16 @@ static void            gimp_group_layer_convert_type (GimpLayer         *layer,
                                                       GimpProgress      *progress);
 static GeglNode   * gimp_group_layer_get_source_node (GimpDrawable      *drawable);
 
-static void            gimp_group_layer_mode_changed (GimpLayer         *layer);
+static void         gimp_group_layer_opacity_changed (GimpLayer         *layer);
+static void  gimp_group_layer_effective_mode_changed (GimpLayer         *layer);
 static void
           gimp_group_layer_excludes_backdrop_changed (GimpLayer         *layer);
+static void            gimp_group_layer_mask_changed (GimpLayer         *layer);
+static void      gimp_group_layer_get_effective_mode (GimpLayer         *layer,
+                                                      GimpLayerMode          *mode,
+                                                      GimpLayerColorSpace    *blend_space,
+                                                      GimpLayerColorSpace    *composite_space,
+                                                      GimpLayerCompositeMode *composite_mode);
 static gboolean
               gimp_group_layer_get_excludes_backdrop (GimpLayer         *layer);
 
@@ -181,6 +188,9 @@ static void            gimp_group_layer_child_resize (GimpLayer       *child,
 static void    gimp_group_layer_child_active_changed (GimpLayer       *child,
                                                       GimpGroupLayer  *group);
 static void
+       gimp_group_layer_child_effective_mode_changed (GimpLayer       *child,
+                                                      GimpGroupLayer  *group);
+static void
     gimp_group_layer_child_excludes_backdrop_changed (GimpLayer       *child,
                                                       GimpGroupLayer  *group);
 
@@ -215,6 +225,12 @@ G_DEFINE_TYPE_WITH_CODE (GimpGroupLayer, gimp_group_layer, GIMP_TYPE_LAYER,
 #define parent_class gimp_group_layer_parent_class
 
 
+/* disable pass-through groups strength-reduction to normal groups.
+ * see gimp_group_layer_get_effective_mode().
+ */
+static gboolean no_pass_through_strength_reduction = FALSE;
+
+
 static void
 gimp_group_layer_class_init (GimpGroupLayerClass *klass)
 {
@@ -254,8 +270,10 @@ gimp_group_layer_class_init (GimpGroupLayerClass *klass)
   drawable_class->estimate_memsize       = gimp_group_layer_estimate_memsize;
   drawable_class->get_source_node        = gimp_group_layer_get_source_node;
 
-  layer_class->mode_changed              = gimp_group_layer_mode_changed;
+  layer_class->opacity_changed           = gimp_group_layer_opacity_changed;
+  layer_class->effective_mode_changed    = gimp_group_layer_effective_mode_changed;
   layer_class->excludes_backdrop_changed = gimp_group_layer_excludes_backdrop_changed;
+  layer_class->mask_changed              = gimp_group_layer_mask_changed;
   layer_class->translate                 = gimp_group_layer_translate;
   layer_class->scale                     = gimp_group_layer_scale;
   layer_class->resize                    = gimp_group_layer_resize;
@@ -263,9 +281,13 @@ gimp_group_layer_class_init (GimpGroupLayerClass *klass)
   layer_class->rotate                    = gimp_group_layer_rotate;
   layer_class->transform                 = gimp_group_layer_transform;
   layer_class->convert_type              = gimp_group_layer_convert_type;
+  layer_class->get_effective_mode        = gimp_group_layer_get_effective_mode;
   layer_class->get_excludes_backdrop     = gimp_group_layer_get_excludes_backdrop;
 
   g_type_class_add_private (klass, sizeof (GimpGroupLayerPrivate));
+
+  if (g_getenv ("GIMP_NO_PASS_THROUGH_STRENGTH_REDUCTION"))
+    no_pass_through_strength_reduction = TRUE;
 }
 
 static void
@@ -314,6 +336,9 @@ gimp_group_layer_init (GimpGroupLayer *group)
   gimp_container_add_handler (private->children, "active-changed",
                               G_CALLBACK (gimp_group_layer_child_active_changed),
                               group);
+  gimp_container_add_handler (private->children, "effective-mode-changed",
+                              G_CALLBACK (gimp_group_layer_child_effective_mode_changed),
+                              group);
   gimp_container_add_handler (private->children, "excludes-backdrop-changed",
                               G_CALLBACK (gimp_group_layer_child_excludes_backdrop_changed),
                               group);
@@ -964,13 +989,25 @@ gimp_group_layer_get_source_node (GimpDrawable *drawable)
 }
 
 static void
-gimp_group_layer_mode_changed (GimpLayer *layer)
+gimp_group_layer_opacity_changed (GimpLayer *layer)
+{
+  gimp_layer_update_effective_mode (layer);
+
+  if (GIMP_LAYER_CLASS (parent_class)->opacity_changed)
+    GIMP_LAYER_CLASS (parent_class)->opacity_changed (layer);
+}
+
+static void
+gimp_group_layer_effective_mode_changed (GimpLayer *layer)
 {
   GimpGroupLayer        *group   = GIMP_GROUP_LAYER (layer);
   GimpGroupLayerPrivate *private = GET_PRIVATE (layer);
+  GimpLayerMode          mode;
   gboolean               pass_through;
 
-  pass_through = (gimp_layer_get_mode (layer) == GIMP_LAYER_MODE_PASS_THROUGH);
+  gimp_layer_get_effective_mode (layer, &mode, NULL, NULL, NULL);
+
+  pass_through = (mode == GIMP_LAYER_MODE_PASS_THROUGH);
 
   if (private->pass_through && ! pass_through)
     {
@@ -987,8 +1024,8 @@ gimp_group_layer_mode_changed (GimpLayer *layer)
   gimp_group_layer_update_source_node (group);
   gimp_group_layer_update_mode_node (group);
 
-  if (GIMP_LAYER_CLASS (parent_class)->mode_changed)
-    GIMP_LAYER_CLASS (parent_class)->mode_changed (layer);
+  if (GIMP_LAYER_CLASS (parent_class)->effective_mode_changed)
+    GIMP_LAYER_CLASS (parent_class)->effective_mode_changed (layer);
 }
 
 static void
@@ -1003,6 +1040,138 @@ gimp_group_layer_excludes_backdrop_changed (GimpLayer *layer)
     GIMP_LAYER_CLASS (parent_class)->excludes_backdrop_changed (layer);
 }
 
+static void
+gimp_group_layer_mask_changed (GimpLayer *layer)
+{
+  gimp_layer_update_effective_mode (layer);
+
+  if (GIMP_LAYER_CLASS (parent_class)->mask_changed)
+    GIMP_LAYER_CLASS (parent_class)->mask_changed (layer);
+}
+
+static void
+gimp_group_layer_get_effective_mode (GimpLayer              *layer,
+                                     GimpLayerMode          *mode,
+                                     GimpLayerColorSpace    *blend_space,
+                                     GimpLayerColorSpace    *composite_space,
+                                     GimpLayerCompositeMode *composite_mode)
+{
+  GimpGroupLayerPrivate *private = GET_PRIVATE (layer);
+
+  /* try to strength-reduce pass-through groups to normal groups, which are
+   * cheaper.
+   */
+  if (gimp_layer_get_mode (layer) == GIMP_LAYER_MODE_PASS_THROUGH &&
+      ! no_pass_through_strength_reduction)
+    {
+      /* we perform the strength-reduction if:
+       *
+       *   - the group has no active children;
+       *
+       *   or,
+       *
+       *     - the group has a single active child; or,
+       *
+       *     - the effective mode of all the active children is normal, their
+       *       effective composite mode is src-over, and their effective
+       *       blend and composite spaces are equal;
+       *
+       *   - and,
+       *
+       *     - the group's opacity is 100%, and it has no mask; or,
+       *
+       *     - the group's composite space equals the active children's
+       *       composite space.
+       */
+
+      GList    *list;
+      gboolean  reduce = TRUE;
+      gboolean  first  = TRUE;
+
+      *mode            = GIMP_LAYER_MODE_NORMAL;
+      *blend_space     = gimp_layer_get_real_blend_space (layer);
+      *composite_space = gimp_layer_get_real_composite_space (layer);
+      *composite_mode  = gimp_layer_get_real_composite_mode (layer);
+
+      for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children));
+           list;
+           list = g_list_next (list))
+        {
+          GimpLayer *child = list->data;
+
+          if (! gimp_filter_get_active (GIMP_FILTER (child)))
+            continue;
+
+          if (first)
+            {
+              gimp_layer_get_effective_mode (child,
+                                             mode,
+                                             blend_space,
+                                             composite_space,
+                                             composite_mode);
+
+              if (*mode == GIMP_LAYER_MODE_NORMAL_LEGACY)
+                *mode = GIMP_LAYER_MODE_NORMAL;
+
+              first = FALSE;
+            }
+          else
+            {
+              GimpLayerMode          other_mode;
+              GimpLayerColorSpace    other_blend_space;
+              GimpLayerColorSpace    other_composite_space;
+              GimpLayerCompositeMode other_composite_mode;
+
+              if (*mode           != GIMP_LAYER_MODE_NORMAL ||
+                  *composite_mode != GIMP_LAYER_COMPOSITE_SRC_OVER)
+                {
+                  reduce = FALSE;
+
+                  break;
+                }
+
+              gimp_layer_get_effective_mode (child,
+                                             &other_mode,
+                                             &other_blend_space,
+                                             &other_composite_space,
+                                             &other_composite_mode);
+
+              if (other_mode == GIMP_LAYER_MODE_NORMAL_LEGACY)
+                other_mode = GIMP_LAYER_MODE_NORMAL;
+
+              if (other_mode            != *mode            ||
+                  other_blend_space     != *blend_space     ||
+                  other_composite_space != *composite_space ||
+                  other_composite_mode  != *composite_mode)
+                {
+                  reduce = FALSE;
+
+                  break;
+                }
+            }
+        }
+
+      if (reduce)
+        {
+          if (first                                                  ||
+              (gimp_layer_get_opacity (layer) == GIMP_OPACITY_OPAQUE &&
+               ! gimp_layer_get_mask (layer))                        ||
+              *composite_space == gimp_layer_get_real_composite_space (layer))
+            {
+              /* strength reduction succeeded! */
+              return;
+            }
+        }
+    }
+
+  /* strength-reduction failed.  chain up. */
+  GIMP_LAYER_CLASS (parent_class)->get_effective_mode (layer,
+                                                       mode,
+                                                       blend_space,
+                                                       composite_space,
+                                                       composite_mode);
+}
+
 static gboolean
 gimp_group_layer_get_excludes_backdrop (GimpLayer *layer)
 {
@@ -1220,10 +1389,12 @@ gimp_group_layer_child_add (GimpContainer  *container,
 {
   gimp_group_layer_update (group);
 
-  if (gimp_filter_get_active (GIMP_FILTER (child)) &&
-      gimp_layer_get_excludes_backdrop (child))
+  if (gimp_filter_get_active (GIMP_FILTER (child)))
     {
-      gimp_layer_update_excludes_backdrop (GIMP_LAYER (group));
+      gimp_layer_update_effective_mode (GIMP_LAYER (group));
+
+      if (gimp_layer_get_excludes_backdrop (child))
+        gimp_layer_update_excludes_backdrop (GIMP_LAYER (group));
     }
 }
 
@@ -1234,10 +1405,12 @@ gimp_group_layer_child_remove (GimpContainer  *container,
 {
   gimp_group_layer_update (group);
 
-  if (gimp_filter_get_active (GIMP_FILTER (child)) &&
-      gimp_layer_get_excludes_backdrop (child))
+  if (gimp_filter_get_active (GIMP_FILTER (child)))
     {
-      gimp_layer_update_excludes_backdrop (GIMP_LAYER (group));
+      gimp_layer_update_effective_mode (GIMP_LAYER (group));
+
+      if (gimp_layer_get_excludes_backdrop (child))
+        gimp_layer_update_excludes_backdrop (GIMP_LAYER (group));
     }
 }
 
@@ -1260,11 +1433,21 @@ static void
 gimp_group_layer_child_active_changed (GimpLayer      *child,
                                        GimpGroupLayer *group)
 {
+  gimp_layer_update_effective_mode (GIMP_LAYER (group));
+
   if (gimp_layer_get_excludes_backdrop (child))
     gimp_layer_update_excludes_backdrop (GIMP_LAYER (group));
 }
 
 static void
+gimp_group_layer_child_effective_mode_changed (GimpLayer      *child,
+                                               GimpGroupLayer *group)
+{
+  if (gimp_filter_get_active (GIMP_FILTER (child)))
+    gimp_layer_update_effective_mode (GIMP_LAYER (group));
+}
+
+static void
 gimp_group_layer_child_excludes_backdrop_changed (GimpLayer      *child,
                                                   GimpGroupLayer *group)
 {


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