app: fix undo when resizing a group layer with a mask

Override GimpItem::resize(), instead of GimpLayer::resize(), when
resizing a group layer, so that GimpLayer doesn't try to resize the
mask.  Instead, the let the usual mask resizing logic in
GimpGroupLayer handle that.

Also, make sure that the mask is properly restored upon undo when
group resizing is suspended outside of any mask-suspension block,
which can happen when resizing the group.
This commit is contained in:
Ell 2018-02-05 14:53:38 -05:00
parent 17fb644787
commit e7a2624a85
3 changed files with 163 additions and 90 deletions

View File

@ -33,6 +33,7 @@
#include "gegl/gimp-babl.h"
#include "gimpgrouplayer.h"
#include "gimpgrouplayerundo.h"
#include "gimpimage.h"
#include "gimpimage-undo.h"
#include "gimpimage-undo-push.h"
@ -109,6 +110,13 @@ 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 void gimp_group_layer_resize (GimpItem *item,
GimpContext *context,
GimpFillType fill_type,
gint new_width,
gint new_height,
gint offset_x,
gint offset_y);
static gint64 gimp_group_layer_estimate_memsize (GimpDrawable *drawable,
GimpComponentType component_type,
@ -125,13 +133,6 @@ static void gimp_group_layer_scale (GimpLayer *layer,
gint new_offset_y,
GimpInterpolationType interp_type,
GimpProgress *progress);
static void gimp_group_layer_resize (GimpLayer *layer,
GimpContext *context,
GimpFillType fill_type,
gint new_width,
gint new_height,
gint offset_x,
gint offset_y);
static void gimp_group_layer_flip (GimpLayer *layer,
GimpContext *context,
GimpOrientationType flip_type,
@ -268,6 +269,7 @@ gimp_group_layer_class_init (GimpGroupLayerClass *klass)
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->resize = gimp_group_layer_resize;
item_class->default_name = _("Layer Group");
item_class->rename_desc = C_("undo-type", "Rename Layer Group");
@ -287,7 +289,6 @@ gimp_group_layer_class_init (GimpGroupLayerClass *klass)
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;
layer_class->flip = gimp_group_layer_flip;
layer_class->rotate = gimp_group_layer_rotate;
layer_class->transform = gimp_group_layer_transform;
@ -625,6 +626,82 @@ gimp_group_layer_end_move (GimpItem *item,
private->moving--;
}
static void
gimp_group_layer_resize (GimpItem *item,
GimpContext *context,
GimpFillType fill_type,
gint new_width,
gint new_height,
gint offset_x,
gint offset_y)
{
GimpGroupLayer *group = GIMP_GROUP_LAYER (item);
GimpGroupLayerPrivate *private = GET_PRIVATE (item);
GList *list;
gint x, y;
/* we implement GimpItem::resize(), instead of GimpLayer::resize(), so that
* GimpLayer doesn't resize the mask. instead, we temporarily decrement
* private->moving, so that mask resizing is handled by
* gimp_group_layer_update_size().
*/
g_return_if_fail (private->moving > 0);
private->moving--;
x = gimp_item_get_offset_x (item) - offset_x;
y = gimp_item_get_offset_y (item) - offset_y;
gimp_group_layer_suspend_resize (group, TRUE);
list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children));
while (list)
{
GimpItem *child = list->data;
gint child_width;
gint child_height;
gint child_x;
gint child_y;
list = g_list_next (list);
if (gimp_rectangle_intersect (x,
y,
new_width,
new_height,
gimp_item_get_offset_x (child),
gimp_item_get_offset_y (child),
gimp_item_get_width (child),
gimp_item_get_height (child),
&child_x,
&child_y,
&child_width,
&child_height))
{
gint child_offset_x = gimp_item_get_offset_x (child) - child_x;
gint child_offset_y = gimp_item_get_offset_y (child) - child_y;
gimp_item_resize (child, context, fill_type,
child_width, child_height,
child_offset_x, child_offset_y);
}
else if (gimp_item_is_attached (item))
{
gimp_image_remove_layer (gimp_item_get_image (item),
GIMP_LAYER (child),
TRUE, NULL);
}
else
{
gimp_container_remove (private->children, GIMP_OBJECT (child));
}
}
gimp_group_layer_resume_resize (group, TRUE);
private->moving++;
}
static gint64
gimp_group_layer_estimate_memsize (GimpDrawable *drawable,
GimpComponentType component_type,
@ -764,72 +841,6 @@ gimp_group_layer_scale (GimpLayer *layer,
gimp_group_layer_resume_resize (group, TRUE);
}
static void
gimp_group_layer_resize (GimpLayer *layer,
GimpContext *context,
GimpFillType fill_type,
gint new_width,
gint new_height,
gint offset_x,
gint offset_y)
{
GimpGroupLayer *group = GIMP_GROUP_LAYER (layer);
GimpGroupLayerPrivate *private = GET_PRIVATE (layer);
GList *list;
gint x, y;
x = gimp_item_get_offset_x (GIMP_ITEM (group)) - offset_x;
y = gimp_item_get_offset_y (GIMP_ITEM (group)) - offset_y;
gimp_group_layer_suspend_resize (group, TRUE);
list = gimp_item_stack_get_item_iter (GIMP_ITEM_STACK (private->children));
while (list)
{
GimpItem *child = list->data;
gint child_width;
gint child_height;
gint child_x;
gint child_y;
list = g_list_next (list);
if (gimp_rectangle_intersect (x,
y,
new_width,
new_height,
gimp_item_get_offset_x (child),
gimp_item_get_offset_y (child),
gimp_item_get_width (child),
gimp_item_get_height (child),
&child_x,
&child_y,
&child_width,
&child_height))
{
gint child_offset_x = gimp_item_get_offset_x (child) - child_x;
gint child_offset_y = gimp_item_get_offset_y (child) - child_y;
gimp_item_resize (child, context, fill_type,
child_width, child_height,
child_offset_x, child_offset_y);
}
else if (gimp_item_is_attached (GIMP_ITEM (group)))
{
gimp_image_remove_layer (gimp_item_get_image (GIMP_ITEM (group)),
GIMP_LAYER (child),
TRUE, NULL);
}
else
{
gimp_container_remove (private->children, GIMP_OBJECT (child));
}
}
gimp_group_layer_resume_resize (group, TRUE);
}
static void
gimp_group_layer_flip (GimpLayer *layer,
GimpContext *context,
@ -1390,6 +1401,10 @@ gimp_group_layer_resume_resize (GimpGroupLayer *group,
{
GimpGroupLayerPrivate *private;
GimpItem *item;
GimpItem *mask = NULL;
GeglBuffer *mask_buffer;
GeglRectangle mask_bounds;
GimpUndo *undo;
g_return_if_fail (GIMP_IS_GROUP_LAYER (group));
@ -1403,14 +1418,62 @@ gimp_group_layer_resume_resize (GimpGroupLayer *group,
push_undo = FALSE;
if (push_undo)
gimp_image_undo_push_group_layer_resume_resize (gimp_item_get_image (item),
NULL, group);
{
undo =
gimp_image_undo_push_group_layer_resume_resize (gimp_item_get_image (item),
NULL, group);
/* if there were any {suspend,resume}_mask() calls during the time the
* group's size was suspended, the resume_mask() calls will not have seen
* any changes to the mask, and will therefore won't restore the mask
* during undo. if the group's bounding box did change while resize was
* suspended, and if there are no other {suspend,resume}_mask() blocks
* that will see the resized mask, we have to restore the mask during the
* resume_resize() undo.
*
* we ref the mask buffer here, and compare it to the mask buffer after
* updating the size.
*/
if (private->suspend_resize == 1 && private->suspend_mask == 0)
{
mask = GIMP_ITEM (gimp_layer_get_mask (GIMP_LAYER (group)));
if (mask)
{
mask_buffer =
g_object_ref (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);
}
}
}
private->suspend_resize--;
if (private->suspend_resize == 0)
{
gimp_group_layer_update_size (group);
if (mask)
{
/* if the mask changed, make sure it's restored during undo, as per
* the comment above.
*/
if (gimp_drawable_get_buffer (GIMP_DRAWABLE (mask)) != mask_buffer)
{
g_return_if_fail (undo != NULL);
GIMP_GROUP_LAYER_UNDO (undo)->mask_buffer = mask_buffer;
GIMP_GROUP_LAYER_UNDO (undo)->mask_bounds = mask_bounds;
}
else
{
g_object_unref (mask_buffer);
}
}
}
}

View File

@ -85,13 +85,12 @@ gimp_group_layer_undo_constructed (GObject *object)
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);
group_layer_undo->mask_buffer =
_gimp_group_layer_get_suspended_mask(group,
&group_layer_undo->mask_bounds);
if (group_layer_undo->prev_suspended_mask_buffer)
g_object_ref (group_layer_undo->prev_suspended_mask_buffer);
if (group_layer_undo->mask_buffer)
g_object_ref (group_layer_undo->mask_buffer);
break;
case GIMP_UNDO_GROUP_LAYER_CONVERT:
@ -112,8 +111,7 @@ gimp_group_layer_undo_get_memsize (GimpObject *object,
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);
memsize += gimp_gegl_buffer_get_memsize (group_layer_undo->mask_buffer);
return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
gui_size);
@ -149,6 +147,18 @@ gimp_group_layer_undo_pop (GimpUndo *undo,
/* suspend group layer auto-resizing */
gimp_group_layer_suspend_resize (group, FALSE);
if (undo->undo_type == GIMP_UNDO_GROUP_LAYER_RESUME_RESIZE &&
group_layer_undo->mask_buffer)
{
GimpLayerMask *mask = gimp_layer_get_mask (GIMP_LAYER (group));
gimp_drawable_set_buffer_full (GIMP_DRAWABLE (mask),
FALSE, NULL,
group_layer_undo->mask_buffer,
group_layer_undo->mask_bounds.x,
group_layer_undo->mask_bounds.y);
}
}
break;
@ -170,12 +180,12 @@ gimp_group_layer_undo_pop (GimpUndo *undo,
gimp_group_layer_suspend_mask (group, FALSE);
if (undo->undo_type == GIMP_UNDO_GROUP_LAYER_RESUME_MASK &&
group_layer_undo->prev_suspended_mask_buffer)
group_layer_undo->mask_buffer)
{
_gimp_group_layer_set_suspended_mask (
group,
group_layer_undo->prev_suspended_mask_buffer,
&group_layer_undo->prev_suspended_mask_bounds);
group_layer_undo->mask_buffer,
&group_layer_undo->mask_bounds);
}
}
break;
@ -216,7 +226,7 @@ gimp_group_layer_undo_free (GimpUndo *undo,
{
GimpGroupLayerUndo *group_layer_undo = GIMP_GROUP_LAYER_UNDO (undo);
g_clear_object (&group_layer_undo->prev_suspended_mask_buffer);
g_clear_object (&group_layer_undo->mask_buffer);
GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode);
}

View File

@ -37,8 +37,8 @@ struct _GimpGroupLayerUndo
{
GimpItemUndo parent_instance;
GeglBuffer *prev_suspended_mask_buffer;
GeglRectangle prev_suspended_mask_bounds;
GeglBuffer *mask_buffer;
GeglRectangle mask_bounds;
GimpImageBaseType prev_type;
GimpPrecision prev_precision;