[gimp] Bug 51112 - Support layer masks on layer groups



commit 36dec4e6b077851a3ced62e18149d68e4081c060
Author: Ell <ell_se yahoo com>
Date:   Mon Feb 5 11:19:18 2018 -0500

    Bug 51112 - Support layer masks on layer groups
    
    Add layer-mask support for group layers.  Group-layer masks work
    similarly to ordinary-layer masks, with the following
    considerations:
    
    The group's mask size is the same as group's size (i.e., the
    bounding box of its children) at all times.  When the group's size
    changes, the mask is cropped to the new size -- areas of the mask
    that fall outside of the new bounds are discarded and their data is
    lost (sans undo), and newly added areas are filled with black (and
    hence are transparent by default).
    
    The new gimp_group_layer_{suspend,resume}_mask() functions can be
    used to modify this behavior.  Between the outermost pair of
    suspend/resume calls, the old mask data is remembered, and is used
    to fill the newly added areas while cropping the mask when the
    group is resized.  We override GimpItem::{start,end}_move() for
    GimpLayer, to call these functions (suspend() in start_move(), and
    resume() in end_move()) for each of the layer's ancestors.
    
    As a result, while moving a layer, or a set of layers, atomically,
    such as while dragging with the move tool, or moving linked layers,
    the ancestors' mask data is not lost, and is only discarded at the
    end of the operation.
    
    This commit also takes care of properly handling undo for group-
    layer mask crops, properly invalidating the image when the group
    layer's mask is shown, and enabling the mask actions for group
    layers (obviously :).

 app/actions/layers-actions.c   |    6 +-
 app/core/core-enums.c          |   12 ++-
 app/core/core-enums.h          |    6 +-
 app/core/gimpgrouplayer.c      |  274 ++++++++++++++++++++++++++++++++++++++--
 app/core/gimpgrouplayer.h      |   26 +++-
 app/core/gimpgrouplayerundo.c  |  102 +++++++++++++--
 app/core/gimpgrouplayerundo.h  |    3 +
 app/core/gimpimage-undo-push.c |   48 ++++++--
 app/core/gimpimage-undo-push.h |   14 ++-
 app/core/gimplayer.c           |   35 +++++
 10 files changed, 477 insertions(+), 49 deletions(-)
---
diff --git a/app/actions/layers-actions.c b/app/actions/layers-actions.c
index 34c1f15..a6369a7 100644
--- a/app/actions/layers-actions.c
+++ b/app/actions/layers-actions.c
@@ -975,9 +975,9 @@ layers_actions_update (GimpActionGroup *group,
   SET_SENSITIVE ("layers-composite-mode-src-in",   layer && cm_mutable);
   SET_SENSITIVE ("layers-composite-mode-dst-atop", layer && cm_mutable);
 
-  SET_SENSITIVE ("layers-mask-add",             layer && !fs && !ac && !mask && !children);
-  SET_SENSITIVE ("layers-mask-add-button",      layer && !fs && !ac && !children);
-  SET_SENSITIVE ("layers-mask-add-last-values", layer && !fs && !ac && !mask && !children);
+  SET_SENSITIVE ("layers-mask-add",             layer && !fs && !ac && !mask);
+  SET_SENSITIVE ("layers-mask-add-button",      layer && !fs && !ac);
+  SET_SENSITIVE ("layers-mask-add-last-values", layer && !fs && !ac && !mask);
 
   SET_SENSITIVE ("layers-mask-apply",  writable && !fs && !ac &&  mask && !children);
   SET_SENSITIVE ("layers-mask-delete", layer    && !fs && !ac &&  mask);
diff --git a/app/core/core-enums.c b/app/core/core-enums.c
index 20c3a32..83076d5 100644
--- a/app/core/core-enums.c
+++ b/app/core/core-enums.c
@@ -793,8 +793,10 @@ gimp_undo_type_get_type (void)
     { GIMP_UNDO_LAYER_MODE, "GIMP_UNDO_LAYER_MODE", "layer-mode" },
     { GIMP_UNDO_LAYER_OPACITY, "GIMP_UNDO_LAYER_OPACITY", "layer-opacity" },
     { GIMP_UNDO_LAYER_LOCK_ALPHA, "GIMP_UNDO_LAYER_LOCK_ALPHA", "layer-lock-alpha" },
-    { GIMP_UNDO_GROUP_LAYER_SUSPEND, "GIMP_UNDO_GROUP_LAYER_SUSPEND", "group-layer-suspend" },
-    { GIMP_UNDO_GROUP_LAYER_RESUME, "GIMP_UNDO_GROUP_LAYER_RESUME", "group-layer-resume" },
+    { GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE, "GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE", 
"group-layer-suspend-resize" },
+    { GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE, "GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE", 
"group-layer-resume-resize" },
+    { GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK, "GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK", "group-layer-suspend-mask" },
+    { GIMP_UNDO_GROUP_LAYER_RESUME_MASK, "GIMP_UNDO_GROUP_LAYER_RESUME_MASK", "group-layer-resume-mask" },
     { GIMP_UNDO_GROUP_LAYER_CONVERT, "GIMP_UNDO_GROUP_LAYER_CONVERT", "group-layer-convert" },
     { GIMP_UNDO_TEXT_LAYER, "GIMP_UNDO_TEXT_LAYER", "text-layer" },
     { GIMP_UNDO_TEXT_LAYER_MODIFIED, "GIMP_UNDO_TEXT_LAYER_MODIFIED", "text-layer-modified" },
@@ -886,8 +888,10 @@ gimp_undo_type_get_type (void)
     { GIMP_UNDO_LAYER_MODE, NC_("undo-type", "Set layer mode"), NULL },
     { GIMP_UNDO_LAYER_OPACITY, NC_("undo-type", "Set layer opacity"), NULL },
     { GIMP_UNDO_LAYER_LOCK_ALPHA, NC_("undo-type", "Lock/Unlock alpha channel"), NULL },
-    { GIMP_UNDO_GROUP_LAYER_SUSPEND, NC_("undo-type", "Suspend group layer resize"), NULL },
-    { GIMP_UNDO_GROUP_LAYER_RESUME, NC_("undo-type", "Resume group layer resize"), NULL },
+    { GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE, NC_("undo-type", "Suspend group layer resize"), NULL },
+    { GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE, NC_("undo-type", "Resume group layer resize"), NULL },
+    { GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK, NC_("undo-type", "Suspend group layer mask"), NULL },
+    { GIMP_UNDO_GROUP_LAYER_RESUME_MASK, NC_("undo-type", "Resume group layer mask"), NULL },
     { GIMP_UNDO_GROUP_LAYER_CONVERT, NC_("undo-type", "Convert group layer"), NULL },
     { GIMP_UNDO_TEXT_LAYER, NC_("undo-type", "Text layer"), NULL },
     { GIMP_UNDO_TEXT_LAYER_MODIFIED, NC_("undo-type", "Text layer modification"), NULL },
diff --git a/app/core/core-enums.h b/app/core/core-enums.h
index 51db731..9cd2cdb 100644
--- a/app/core/core-enums.h
+++ b/app/core/core-enums.h
@@ -410,8 +410,10 @@ typedef enum /*< pdb-skip >*/
   GIMP_UNDO_LAYER_MODE,               /*< desc="Set layer mode"              >*/
   GIMP_UNDO_LAYER_OPACITY,            /*< desc="Set layer opacity"           >*/
   GIMP_UNDO_LAYER_LOCK_ALPHA,         /*< desc="Lock/Unlock alpha channel"   >*/
-  GIMP_UNDO_GROUP_LAYER_SUSPEND,      /*< desc="Suspend group layer resize"  >*/
-  GIMP_UNDO_GROUP_LAYER_RESUME,       /*< desc="Resume group layer resize"   >*/
+  GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE,/*< desc="Suspend group layer resize" >*/
+  GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE,/*< desc="Resume group layer resize"   >*/
+  GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK, /*< desc="Suspend group layer mask"    >*/
+  GIMP_UNDO_GROUP_LAYER_RESUME_MASK,  /*< desc="Resume group layer mask"     >*/
   GIMP_UNDO_GROUP_LAYER_CONVERT,      /*< desc="Convert group layer"         >*/
   GIMP_UNDO_TEXT_LAYER,               /*< desc="Text layer"                  >*/
   GIMP_UNDO_TEXT_LAYER_MODIFIED,      /*< desc="Text layer modification"     >*/
diff --git a/app/core/gimpgrouplayer.c b/app/core/gimpgrouplayer.c
index a7746f8..0a045c6 100644
--- a/app/core/gimpgrouplayer.c
+++ b/app/core/gimpgrouplayer.c
@@ -34,6 +34,7 @@
 
 #include "gimpgrouplayer.h"
 #include "gimpimage.h"
+#include "gimpimage-undo.h"
 #include "gimpimage-undo-push.h"
 #include "gimplayerstack.h"
 #include "gimppickable.h"
@@ -54,6 +55,10 @@ struct _GimpGroupLayerPrivate
   GeglNode       *graph;
   GeglNode       *offset_node;
   gint            suspend_resize;
+  gint            suspend_mask;
+  GeglBuffer     *suspended_mask_buffer;
+  GeglRectangle   suspended_mask_bounds;
+  gint            moving;
   gboolean        expanded;
   gboolean        pass_through;
 
@@ -100,6 +105,10 @@ static GimpItem      * gimp_group_layer_duplicate    (GimpItem        *item,
 static void            gimp_group_layer_convert      (GimpItem        *item,
                                                       GimpImage       *dest_image,
                                                       GType            old_type);
+static void            gimp_group_layer_start_move   (GimpItem        *item,
+                                                      gboolean         push_undo);
+static void            gimp_group_layer_end_move     (GimpItem        *item,
+                                                      gboolean         push_undo);
 
 static gint64      gimp_group_layer_estimate_memsize (GimpDrawable      *drawable,
                                                       GimpComponentType  component_type,
@@ -257,6 +266,8 @@ gimp_group_layer_class_init (GimpGroupLayerClass *klass)
   item_class->is_position_locked         = gimp_group_layer_is_position_locked;
   item_class->duplicate                  = gimp_group_layer_duplicate;
   item_class->convert                    = gimp_group_layer_convert;
+  item_class->start_move                 = gimp_group_layer_start_move;
+  item_class->end_move                   = gimp_group_layer_end_move;
 
   item_class->default_name               = _("Layer Group");
   item_class->rename_desc                = C_("undo-type", "Rename Layer Group");
@@ -590,6 +601,30 @@ gimp_group_layer_convert (GimpItem  *item,
   GIMP_ITEM_CLASS (parent_class)->convert (item, dest_image, old_type);
 }
 
+static void
+gimp_group_layer_start_move (GimpItem *item,
+                             gboolean  push_undo)
+{
+  GimpGroupLayerPrivate *private = GET_PRIVATE (item);
+
+  private->moving++;
+
+  if (GIMP_ITEM_CLASS (parent_class)->start_move)
+    GIMP_ITEM_CLASS (parent_class)->start_move (item, push_undo);
+}
+
+static void
+gimp_group_layer_end_move (GimpItem *item,
+                           gboolean  push_undo)
+{
+  GimpGroupLayerPrivate *private = GET_PRIVATE (item);
+
+  if (GIMP_ITEM_CLASS (parent_class)->end_move)
+    GIMP_ITEM_CLASS (parent_class)->end_move (item, push_undo);
+
+  private->moving--;
+}
+
 static gint64
 gimp_group_layer_estimate_memsize (GimpDrawable      *drawable,
                                    GimpComponentType  component_type,
@@ -1041,6 +1076,8 @@ gimp_group_layer_excludes_backdrop_changed (GimpLayer *layer)
 static void
 gimp_group_layer_mask_changed (GimpLayer *layer)
 {
+  g_warn_if_fail (GET_PRIVATE (layer)->suspend_mask == 0);
+
   gimp_layer_update_effective_mode (layer);
 
   if (GIMP_LAYER_CLASS (parent_class)->mask_changed)
@@ -1341,8 +1378,8 @@ gimp_group_layer_suspend_resize (GimpGroupLayer *group,
     push_undo = FALSE;
 
   if (push_undo)
-    gimp_image_undo_push_group_layer_suspend (gimp_item_get_image (item),
-                                              NULL, group);
+    gimp_image_undo_push_group_layer_suspend_resize (gimp_item_get_image (item),
+                                                     NULL, group);
 
   GET_PRIVATE (group)->suspend_resize++;
 }
@@ -1366,8 +1403,8 @@ gimp_group_layer_resume_resize (GimpGroupLayer *group,
     push_undo = FALSE;
 
   if (push_undo)
-    gimp_image_undo_push_group_layer_resume (gimp_item_get_image (item),
-                                             NULL, group);
+    gimp_image_undo_push_group_layer_resume_resize (gimp_item_get_image (item),
+                                                    NULL, group);
 
   private->suspend_resize--;
 
@@ -1377,6 +1414,129 @@ gimp_group_layer_resume_resize (GimpGroupLayer *group,
     }
 }
 
+void
+gimp_group_layer_suspend_mask (GimpGroupLayer *group,
+                               gboolean        push_undo)
+{
+  GimpGroupLayerPrivate *private;
+  GimpItem              *item;
+
+  g_return_if_fail (GIMP_IS_GROUP_LAYER (group));
+
+  private = GET_PRIVATE (group);
+  item    = GIMP_ITEM (group);
+
+  if (! gimp_item_is_attached (item))
+    push_undo = FALSE;
+
+  if (push_undo)
+    gimp_image_undo_push_group_layer_suspend_mask (gimp_item_get_image (item),
+                                                   NULL, group);
+
+  if (private->suspend_mask == 0)
+    {
+      if (gimp_layer_get_mask (GIMP_LAYER (group)))
+        {
+          GimpItem *mask = GIMP_ITEM (gimp_layer_get_mask (GIMP_LAYER (group)));
+
+          private->suspended_mask_buffer =
+            g_object_ref (gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)));
+
+          private->suspended_mask_bounds.x      = gimp_item_get_offset_x (mask);
+          private->suspended_mask_bounds.y      = gimp_item_get_offset_y (mask);
+          private->suspended_mask_bounds.width  = gimp_item_get_width    (mask);
+          private->suspended_mask_bounds.height = gimp_item_get_height   (mask);
+        }
+      else
+        {
+          private->suspended_mask_buffer = NULL;
+        }
+    }
+
+  private->suspend_mask++;
+}
+
+void
+gimp_group_layer_resume_mask (GimpGroupLayer *group,
+                              gboolean        push_undo)
+{
+  GimpGroupLayerPrivate *private;
+  GimpItem              *item;
+
+  g_return_if_fail (GIMP_IS_GROUP_LAYER (group));
+
+  private = GET_PRIVATE (group);
+
+  g_return_if_fail (private->suspend_mask > 0);
+
+  item = GIMP_ITEM (group);
+
+  if (! gimp_item_is_attached (item))
+    push_undo = FALSE;
+
+  if (push_undo)
+    gimp_image_undo_push_group_layer_resume_mask (gimp_item_get_image (item),
+                                                  NULL, group);
+
+  private->suspend_mask--;
+
+  if (private->suspend_mask == 0)
+    g_clear_object (&private->suspended_mask_buffer);
+}
+
+
+/*  protected functions  */
+
+void
+_gimp_group_layer_set_suspended_mask (GimpGroupLayer      *group,
+                                      GeglBuffer          *buffer,
+                                      const GeglRectangle *bounds)
+{
+  GimpGroupLayerPrivate *private;
+
+  g_return_if_fail (GIMP_IS_GROUP_LAYER (group));
+  g_return_if_fail (buffer != NULL);
+  g_return_if_fail (bounds != NULL);
+
+  private = GET_PRIVATE (group);
+
+  g_return_if_fail (private->suspend_mask > 0);
+
+  g_object_ref (buffer);
+
+  g_clear_object (&private->suspended_mask_buffer);
+
+  private->suspended_mask_buffer = buffer;
+  private->suspended_mask_bounds = *bounds;
+}
+
+GeglBuffer *
+_gimp_group_layer_get_suspended_mask (GimpGroupLayer *group,
+                                      GeglRectangle  *bounds)
+{
+  GimpGroupLayerPrivate *private;
+  GimpLayerMask         *mask;
+
+  g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL);
+  g_return_val_if_fail (bounds != NULL, NULL);
+
+  private = GET_PRIVATE (group);
+  mask    = gimp_layer_get_mask (GIMP_LAYER (group));
+
+  g_return_val_if_fail (private->suspend_mask > 0, NULL);
+
+  if (mask &&
+      gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)) !=
+      private->suspended_mask_buffer)
+    {
+      *bounds = private->suspended_mask_bounds;
+
+      return private->suspended_mask_buffer;
+    }
+
+  return NULL;
+}
+
 
 /*  private functions  */
 
@@ -1501,6 +1661,8 @@ gimp_group_layer_update_size (GimpGroupLayer *group)
   gint                   width      = 1;
   gint                   height     = 1;
   gboolean               first      = TRUE;
+  gboolean               size_changed;
+  gboolean               resize_mask ;
   GList                 *list;
 
   for (list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children));
@@ -1540,11 +1702,93 @@ gimp_group_layer_update_size (GimpGroupLayer *group)
         }
     }
 
-  if (private->reallocate_projection ||
-      x      != old_x                ||
-      y      != old_y                ||
-      width  != old_width            ||
-      height != old_height)
+  size_changed = (x      != old_x     ||
+                  y      != old_y     ||
+                  width  != old_width ||
+                  height != old_height);
+
+  resize_mask = gimp_layer_get_mask (GIMP_LAYER (group)) && size_changed;
+
+  /* if we show the mask, invalidate the old mask area */
+  if (resize_mask && gimp_layer_get_show_mask (GIMP_LAYER (group)))
+    {
+      GimpItem *mask = GIMP_ITEM (gimp_layer_get_mask (GIMP_LAYER (group)));
+
+      gimp_drawable_update (GIMP_DRAWABLE (group),
+                            gimp_item_get_offset_x (mask) - old_x,
+                            gimp_item_get_offset_y (mask) - old_y,
+                            gimp_item_get_width    (mask),
+                            gimp_item_get_height   (mask));
+    }
+
+  /* resize the mask if not moving (in which case, GimpLayer takes care of the
+   * mask)
+   */
+  if (resize_mask && ! private->moving)
+    {
+      GimpItem      *mask = GIMP_ITEM (gimp_layer_get_mask (GIMP_LAYER (group)));
+      GeglBuffer    *buffer;
+      GeglBuffer    *mask_buffer;
+      GeglRectangle  mask_bounds;
+      GeglRectangle  copy_bounds;
+      gboolean       intersect;
+
+      buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height),
+                                gimp_drawable_get_format (GIMP_DRAWABLE (mask)));
+
+      if (private->suspended_mask_buffer)
+        {
+          /* copy the suspended mask into the new mask */
+          mask_buffer = private->suspended_mask_buffer;
+          mask_bounds = private->suspended_mask_bounds;
+        }
+      else
+        {
+          /* copy the old mask into the new mask */
+          mask_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (mask));
+
+          mask_bounds.x      = gimp_item_get_offset_x (mask);
+          mask_bounds.y      = gimp_item_get_offset_y (mask);
+          mask_bounds.width  = gimp_item_get_width    (mask);
+          mask_bounds.height = gimp_item_get_height   (mask);
+        }
+
+      intersect = gimp_rectangle_intersect (mask_bounds.x,
+                                            mask_bounds.y,
+                                            mask_bounds.width,
+                                            mask_bounds.height,
+                                            x,
+                                            y,
+                                            width,
+                                            height,
+                                            &copy_bounds.x,
+                                            &copy_bounds.y,
+                                            &copy_bounds.width,
+                                            &copy_bounds.height);
+
+      if (intersect)
+        {
+          gegl_buffer_copy (mask_buffer,
+                            GEGL_RECTANGLE (copy_bounds.x - mask_bounds.x,
+                                            copy_bounds.y - mask_bounds.y,
+                                            copy_bounds.width,
+                                            copy_bounds.height),
+                            GEGL_ABYSS_NONE,
+                            buffer,
+                            GEGL_RECTANGLE (copy_bounds.x - x,
+                                            copy_bounds.y - y,
+                                            copy_bounds.width,
+                                            copy_bounds.height));
+        }
+
+      gimp_drawable_set_buffer_full (GIMP_DRAWABLE (mask),
+                                     FALSE, NULL,
+                                     buffer, x, y);
+
+      g_object_unref (buffer);
+    }
+
+  if (private->reallocate_projection || size_changed)
     {
       /* if the graph is already constructed, set the offset node's
        * coordinates first, so the graph is in the right state when
@@ -1603,6 +1847,18 @@ gimp_group_layer_update_size (GimpGroupLayer *group)
           gimp_group_layer_flush (group);
         }
     }
+
+  /* if we show the mask, invalidate the new mask area */
+  if (resize_mask && gimp_layer_get_show_mask (GIMP_LAYER (group)))
+    {
+      GimpItem *mask = GIMP_ITEM (gimp_layer_get_mask (GIMP_LAYER (group)));
+
+      gimp_drawable_update (GIMP_DRAWABLE (group),
+                            gimp_item_get_offset_x (mask) - x,
+                            gimp_item_get_offset_y (mask) - y,
+                            gimp_item_get_width    (mask),
+                            gimp_item_get_height   (mask));
+    }
 }
 
 static void
diff --git a/app/core/gimpgrouplayer.h b/app/core/gimpgrouplayer.h
index db0169f..6603fe6 100644
--- a/app/core/gimpgrouplayer.h
+++ b/app/core/gimpgrouplayer.h
@@ -46,16 +46,28 @@ struct _GimpGroupLayerClass
 };
 
 
-GType            gimp_group_layer_get_type       (void) G_GNUC_CONST;
+GType            gimp_group_layer_get_type            (void) G_GNUC_CONST;
 
-GimpLayer      * gimp_group_layer_new            (GimpImage      *image);
+GimpLayer      * gimp_group_layer_new                 (GimpImage           *image);
 
-GimpProjection * gimp_group_layer_get_projection (GimpGroupLayer *group);
+GimpProjection * gimp_group_layer_get_projection      (GimpGroupLayer      *group);
 
-void             gimp_group_layer_suspend_resize (GimpGroupLayer *group,
-                                                  gboolean        push_undo);
-void             gimp_group_layer_resume_resize  (GimpGroupLayer *group,
-                                                  gboolean        push_undo);
+void             gimp_group_layer_suspend_resize      (GimpGroupLayer      *group,
+                                                       gboolean             push_undo);
+void             gimp_group_layer_resume_resize       (GimpGroupLayer      *group,
+                                                       gboolean             push_undo);
+
+void             gimp_group_layer_suspend_mask        (GimpGroupLayer      *group,
+                                                       gboolean             push_undo);
+void             gimp_group_layer_resume_mask         (GimpGroupLayer      *group,
+                                                       gboolean             push_undo);
+
+
+void             _gimp_group_layer_set_suspended_mask (GimpGroupLayer      *group,
+                                                       GeglBuffer          *buffer,
+                                                       const GeglRectangle *bounds);
+GeglBuffer     * _gimp_group_layer_get_suspended_mask (GimpGroupLayer      *group,
+                                                       GeglRectangle       *bounds);
 
 
 #endif /* __GIMP_GROUP_LAYER_H__ */
diff --git a/app/core/gimpgrouplayerundo.c b/app/core/gimpgrouplayerundo.c
index 6c30c04..63aa02c 100644
--- a/app/core/gimpgrouplayerundo.c
+++ b/app/core/gimpgrouplayerundo.c
@@ -22,16 +22,22 @@
 
 #include "core-types.h"
 
+#include "gimp-memsize.h"
 #include "gimpimage.h"
 #include "gimpgrouplayer.h"
 #include "gimpgrouplayerundo.h"
 
 
-static void   gimp_group_layer_undo_constructed (GObject             *object);
+static void     gimp_group_layer_undo_constructed (GObject             *object);
 
-static void   gimp_group_layer_undo_pop         (GimpUndo            *undo,
-                                                 GimpUndoMode         undo_mode,
-                                                 GimpUndoAccumulator *accum);
+static gint64   gimp_group_layer_undo_get_memsize (GimpObject          *object,
+                                                   gint64              *gui_size);
+
+static void     gimp_group_layer_undo_pop         (GimpUndo            *undo,
+                                                   GimpUndoMode         undo_mode,
+                                                   GimpUndoAccumulator *accum);
+static void     gimp_group_layer_undo_free        (GimpUndo            *undo,
+                                                   GimpUndoMode         undo_mode);
 
 
 G_DEFINE_TYPE (GimpGroupLayerUndo, gimp_group_layer_undo, GIMP_TYPE_ITEM_UNDO)
@@ -42,12 +48,16 @@ G_DEFINE_TYPE (GimpGroupLayerUndo, gimp_group_layer_undo, GIMP_TYPE_ITEM_UNDO)
 static void
 gimp_group_layer_undo_class_init (GimpGroupLayerUndoClass *klass)
 {
-  GObjectClass  *object_class = G_OBJECT_CLASS (klass);
-  GimpUndoClass *undo_class   = GIMP_UNDO_CLASS (klass);
+  GObjectClass    *object_class      = G_OBJECT_CLASS (klass);
+  GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
+  GimpUndoClass   *undo_class        = GIMP_UNDO_CLASS (klass);
+
+  object_class->constructed      = gimp_group_layer_undo_constructed;
 
-  object_class->constructed   = gimp_group_layer_undo_constructed;
+  gimp_object_class->get_memsize = gimp_group_layer_undo_get_memsize;
 
-  undo_class->pop             = gimp_group_layer_undo_pop;
+  undo_class->pop                = gimp_group_layer_undo_pop;
+  undo_class->free               = gimp_group_layer_undo_free;
 }
 
 static void
@@ -69,8 +79,19 @@ gimp_group_layer_undo_constructed (GObject *object)
 
   switch (GIMP_UNDO (object)->undo_type)
     {
-    case GIMP_UNDO_GROUP_LAYER_SUSPEND:
-    case GIMP_UNDO_GROUP_LAYER_RESUME:
+    case GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE:
+    case GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE:
+    case GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK:
+      break;
+
+    case GIMP_UNDO_GROUP_LAYER_RESUME_MASK:
+      group_layer_undo->prev_suspended_mask_buffer =
+        _gimp_group_layer_get_suspended_mask(
+          group,
+          &group_layer_undo->prev_suspended_mask_bounds);
+
+      if (group_layer_undo->prev_suspended_mask_buffer)
+        g_object_ref (group_layer_undo->prev_suspended_mask_buffer);
       break;
 
     case GIMP_UNDO_GROUP_LAYER_CONVERT:
@@ -84,6 +105,20 @@ gimp_group_layer_undo_constructed (GObject *object)
     }
 }
 
+static gint64
+gimp_group_layer_undo_get_memsize (GimpObject *object,
+                                   gint64     *gui_size)
+{
+  GimpGroupLayerUndo *group_layer_undo = GIMP_GROUP_LAYER_UNDO (object);
+  gint64              memsize       = 0;
+
+  memsize +=
+    gimp_gegl_buffer_get_memsize (group_layer_undo->prev_suspended_mask_buffer);
+
+  return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
+                                                                  gui_size);
+}
+
 static void
 gimp_group_layer_undo_pop (GimpUndo            *undo,
                            GimpUndoMode         undo_mode,
@@ -98,12 +133,12 @@ gimp_group_layer_undo_pop (GimpUndo            *undo,
 
   switch (undo->undo_type)
     {
-    case GIMP_UNDO_GROUP_LAYER_SUSPEND:
-    case GIMP_UNDO_GROUP_LAYER_RESUME:
+    case GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE:
+    case GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE:
       if ((undo_mode       == GIMP_UNDO_MODE_UNDO &&
-           undo->undo_type == GIMP_UNDO_GROUP_LAYER_SUSPEND) ||
+           undo->undo_type == GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE) ||
           (undo_mode       == GIMP_UNDO_MODE_REDO &&
-           undo->undo_type == GIMP_UNDO_GROUP_LAYER_RESUME))
+           undo->undo_type == GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE))
         {
           /*  resume group layer auto-resizing  */
 
@@ -117,6 +152,34 @@ gimp_group_layer_undo_pop (GimpUndo            *undo,
         }
       break;
 
+    case GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK:
+    case GIMP_UNDO_GROUP_LAYER_RESUME_MASK:
+      if ((undo_mode       == GIMP_UNDO_MODE_UNDO &&
+           undo->undo_type == GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK) ||
+          (undo_mode       == GIMP_UNDO_MODE_REDO &&
+           undo->undo_type == GIMP_UNDO_GROUP_LAYER_RESUME_MASK))
+        {
+          /*  resume group layer mask auto-resizing  */
+
+          gimp_group_layer_resume_mask (group, FALSE);
+        }
+      else
+        {
+          /*  suspend group layer mask auto-resizing  */
+
+          gimp_group_layer_suspend_mask (group, FALSE);
+
+          if (undo->undo_type == GIMP_UNDO_GROUP_LAYER_RESUME_MASK &&
+              group_layer_undo->prev_suspended_mask_buffer)
+            {
+              _gimp_group_layer_set_suspended_mask (
+                group,
+                group_layer_undo->prev_suspended_mask_buffer,
+                &group_layer_undo->prev_suspended_mask_bounds);
+            }
+        }
+      break;
+
     case GIMP_UNDO_GROUP_LAYER_CONVERT:
       {
         GimpImageBaseType type;
@@ -146,3 +209,14 @@ gimp_group_layer_undo_pop (GimpUndo            *undo,
       g_return_if_reached ();
     }
 }
+
+static void
+gimp_group_layer_undo_free (GimpUndo     *undo,
+                            GimpUndoMode  undo_mode)
+{
+  GimpGroupLayerUndo *group_layer_undo = GIMP_GROUP_LAYER_UNDO (undo);
+
+  g_clear_object (&group_layer_undo->prev_suspended_mask_buffer);
+
+  GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode);
+}
diff --git a/app/core/gimpgrouplayerundo.h b/app/core/gimpgrouplayerundo.h
index 61502b2..f4dd039 100644
--- a/app/core/gimpgrouplayerundo.h
+++ b/app/core/gimpgrouplayerundo.h
@@ -37,6 +37,9 @@ struct _GimpGroupLayerUndo
 {
   GimpItemUndo       parent_instance;
 
+  GeglBuffer        *prev_suspended_mask_buffer;
+  GeglRectangle      prev_suspended_mask_bounds;
+
   GimpImageBaseType  prev_type;
   GimpPrecision      prev_precision;
   gboolean           prev_has_alpha;
diff --git a/app/core/gimpimage-undo-push.c b/app/core/gimpimage-undo-push.c
index 670cc42..77c55fd 100644
--- a/app/core/gimpimage-undo-push.c
+++ b/app/core/gimpimage-undo-push.c
@@ -608,32 +608,64 @@ gimp_image_undo_push_layer_lock_alpha (GimpImage   *image,
 /***********************/
 
 GimpUndo *
-gimp_image_undo_push_group_layer_suspend (GimpImage      *image,
-                                          const gchar    *undo_desc,
-                                          GimpGroupLayer *group)
+gimp_image_undo_push_group_layer_suspend_resize (GimpImage      *image,
+                                                 const gchar    *undo_desc,
+                                                 GimpGroupLayer *group)
+{
+  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
+  g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL);
+  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (group)), NULL);
+
+  return gimp_image_undo_push (image, GIMP_TYPE_GROUP_LAYER_UNDO,
+                               GIMP_UNDO_GROUP_LAYER_SUSPEND_RESIZE, undo_desc,
+                               GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE,
+                               "item",  group,
+                               NULL);
+}
+
+GimpUndo *
+gimp_image_undo_push_group_layer_resume_resize (GimpImage      *image,
+                                                const gchar    *undo_desc,
+                                                GimpGroupLayer *group)
+{
+  g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
+  g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL);
+  g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (group)), NULL);
+
+  return gimp_image_undo_push (image, GIMP_TYPE_GROUP_LAYER_UNDO,
+                               GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE, undo_desc,
+                               GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE,
+                               "item",  group,
+                               NULL);
+}
+
+GimpUndo *
+gimp_image_undo_push_group_layer_suspend_mask (GimpImage      *image,
+                                               const gchar    *undo_desc,
+                                               GimpGroupLayer *group)
 {
   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
   g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL);
   g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (group)), NULL);
 
   return gimp_image_undo_push (image, GIMP_TYPE_GROUP_LAYER_UNDO,
-                               GIMP_UNDO_GROUP_LAYER_SUSPEND, undo_desc,
+                               GIMP_UNDO_GROUP_LAYER_SUSPEND_MASK, undo_desc,
                                GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE,
                                "item",  group,
                                NULL);
 }
 
 GimpUndo *
-gimp_image_undo_push_group_layer_resume (GimpImage      *image,
-                                         const gchar    *undo_desc,
-                                         GimpGroupLayer *group)
+gimp_image_undo_push_group_layer_resume_mask (GimpImage      *image,
+                                              const gchar    *undo_desc,
+                                              GimpGroupLayer *group)
 {
   g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
   g_return_val_if_fail (GIMP_IS_GROUP_LAYER (group), NULL);
   g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (group)), NULL);
 
   return gimp_image_undo_push (image, GIMP_TYPE_GROUP_LAYER_UNDO,
-                               GIMP_UNDO_GROUP_LAYER_RESUME, undo_desc,
+                               GIMP_UNDO_GROUP_LAYER_RESUME_MASK, undo_desc,
                                GIMP_DIRTY_ITEM | GIMP_DIRTY_DRAWABLE,
                                "item",  group,
                                NULL);
diff --git a/app/core/gimpimage-undo-push.h b/app/core/gimpimage-undo-push.h
index 57bc970..d2c1530 100644
--- a/app/core/gimpimage-undo-push.h
+++ b/app/core/gimpimage-undo-push.h
@@ -145,10 +145,20 @@ GimpUndo * gimp_image_undo_push_layer_lock_alpha    (GimpImage     *image,
 
 /*  group layer undos  */
 
-GimpUndo * gimp_image_undo_push_group_layer_suspend (GimpImage      *image,
+GimpUndo *
+    gimp_image_undo_push_group_layer_suspend_resize (GimpImage      *image,
                                                      const gchar    *undo_desc,
                                                      GimpGroupLayer *group);
-GimpUndo * gimp_image_undo_push_group_layer_resume  (GimpImage      *image,
+GimpUndo *
+     gimp_image_undo_push_group_layer_resume_resize (GimpImage      *image,
+                                                     const gchar    *undo_desc,
+                                                     GimpGroupLayer *group);
+GimpUndo *
+      gimp_image_undo_push_group_layer_suspend_mask (GimpImage      *image,
+                                                     const gchar    *undo_desc,
+                                                     GimpGroupLayer *group);
+GimpUndo *
+       gimp_image_undo_push_group_layer_resume_mask (GimpImage      *image,
                                                      const gchar    *undo_desc,
                                                      GimpGroupLayer *group);
 GimpUndo * gimp_image_undo_push_group_layer_convert (GimpImage      *image,
diff --git a/app/core/gimplayer.c b/app/core/gimplayer.c
index fe33e92..ed971be 100644
--- a/app/core/gimplayer.c
+++ b/app/core/gimplayer.c
@@ -44,6 +44,7 @@
 #include "gimpcontainer.h"
 #include "gimpdrawable-floating-selection.h"
 #include "gimperror.h"
+#include "gimpgrouplayer.h"
 #include "gimpimage-undo-push.h"
 #include "gimpimage-undo.h"
 #include "gimpimage.h"
@@ -128,6 +129,10 @@ static gboolean   gimp_layer_rename             (GimpItem           *item,
                                                  const gchar        *new_name,
                                                  const gchar        *undo_desc,
                                                  GError            **error);
+static void       gimp_layer_start_move         (GimpItem           *item,
+                                                 gboolean            push_undo);
+static void       gimp_layer_end_move           (GimpItem           *item,
+                                                 gboolean            push_undo);
 static void       gimp_layer_translate          (GimpItem           *item,
                                                  gint                offset_x,
                                                  gint                offset_y,
@@ -421,6 +426,8 @@ gimp_layer_class_init (GimpLayerClass *klass)
   item_class->duplicate               = gimp_layer_duplicate;
   item_class->convert                 = gimp_layer_convert;
   item_class->rename                  = gimp_layer_rename;
+  item_class->start_move              = gimp_layer_start_move;
+  item_class->end_move                = gimp_layer_end_move;
   item_class->translate               = gimp_layer_translate;
   item_class->scale                   = gimp_layer_scale;
   item_class->resize                  = gimp_layer_resize;
@@ -1050,6 +1057,34 @@ gimp_layer_rename (GimpItem     *item,
 }
 
 static void
+gimp_layer_start_move (GimpItem *item,
+                       gboolean  push_undo)
+{
+  GimpLayer *layer = GIMP_LAYER (item);
+
+  /* suspend mask cropping for all of the layer's ancestors */
+  while ((layer = gimp_layer_get_parent (layer)))
+    gimp_group_layer_suspend_mask (GIMP_GROUP_LAYER (layer), push_undo);
+
+  if (GIMP_ITEM_CLASS (parent_class)->start_move)
+    GIMP_ITEM_CLASS (parent_class)->start_move (item, push_undo);
+}
+
+static void
+gimp_layer_end_move (GimpItem *item,
+                     gboolean  push_undo)
+{
+  GimpLayer *layer = GIMP_LAYER (item);
+
+  if (GIMP_ITEM_CLASS (parent_class)->end_move)
+    GIMP_ITEM_CLASS (parent_class)->end_move (item, push_undo);
+
+  /* resume mask cropping for all of the layer's ancestors */
+  while ((layer = gimp_layer_get_parent (layer)))
+    gimp_group_layer_resume_mask (GIMP_GROUP_LAYER (layer), push_undo);
+}
+
+static void
 gimp_layer_translate (GimpItem *item,
                       gint      offset_x,
                       gint      offset_y,


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