From 1b64fdf52d97e21a4691bb9fd6ba80e08c1a14aa Mon Sep 17 00:00:00 2001 From: Jehan Date: Thu, 10 Nov 2022 22:41:27 +0100 Subject: [PATCH] app: pasting selection from multiple layers should still create multiple layers. This is also work-in-progress for better copy-paste handling for multi-items. Until now, I had decided that, if a selection existed, a copy would copy a merged version of the selected items. But sometimes you want to have a piece of each layer, each piece in its own layer. Also you may always merge the pasted layers afterwards. So now we will indeed create as many layers out as there are layers in. Being able to copy a merged down version of all selected layers is still an interesting feature though. We might add a dedicated "Copy Merged" action, similarly to the fact we have a "Copy Visible" (which does the same thing but for all visible layers, not specifically the selected ones). --- app/core/gimp-edit.c | 76 ++++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 20 deletions(-) diff --git a/app/core/gimp-edit.c b/app/core/gimp-edit.c index 32c8c23834..c02fc5fb76 100644 --- a/app/core/gimp-edit.c +++ b/app/core/gimp-edit.c @@ -30,6 +30,7 @@ #include "gimp-edit.h" #include "gimpbuffer.h" #include "gimpcontext.h" +#include "gimpdrawable-edit.h" #include "gimpgrouplayer.h" #include "gimpimage.h" #include "gimpimage-duplicate.h" @@ -183,38 +184,73 @@ gimp_edit_copy (GimpImage *image, /* Only accept multiple drawables for layers. */ g_return_val_if_fail (g_list_length (drawables) == 1 || drawables_are_layers, NULL); - if (drawables_are_layers && - gimp_channel_is_empty (gimp_image_get_mask (image))) + if (drawables_are_layers) { /* Special-casing the 1 layer with no selection case. * It allows us to save the whole layer with all pixels as stored, * not the rendered version of it. */ - GimpImage *clip_image; + GimpImage *clip_image; + GimpChannel *clip_selection; - clip_image = gimp_image_new_from_drawables (image->gimp, drawables, FALSE); + clip_image = gimp_image_new_from_drawables (image->gimp, drawables, TRUE); gimp_container_remove (image->gimp->images, GIMP_OBJECT (clip_image)); gimp_set_clipboard_image (image->gimp, clip_image); g_object_unref (clip_image); + clip_selection = gimp_image_get_mask (clip_image); + if (! gimp_channel_is_empty (clip_selection)) + { + GList *all_items; + GeglRectangle selection_bounds; + + gimp_item_bounds (GIMP_ITEM (clip_selection), + &selection_bounds.x, &selection_bounds.y, + &selection_bounds.width, &selection_bounds.height); + + /* Invert the selection. */ + gimp_channel_invert (clip_selection, FALSE); + all_items = gimp_image_get_layer_list (clip_image); + + for (iter = all_items; iter; iter = g_list_next (iter)) + { + GeglRectangle bounds; + gint item_x; + gint item_y; + gint x, y; + + gimp_item_get_offset (GIMP_ITEM (iter->data), &item_x, &item_y); + bounds.x = item_x; + bounds.y = item_y; + bounds.width = gimp_item_get_width (GIMP_ITEM (iter->data)); + bounds.height = gimp_item_get_height (GIMP_ITEM (iter->data)); + + /* Even if the original layer may not have an alpha channel, the + * selected data must always have one. First because a selection + * is in some way an alpha channel when we copy (we may copy part + * of a pixel, i.e. with transparency). Second because the + * selection is not necessary rectangular, unlike layers. So when + * we will clear, if we hadn't added an alpha channel, we'd end up + * with background color all over the place. + */ + gimp_layer_add_alpha (GIMP_LAYER (iter->data)); + gimp_drawable_edit_clear (GIMP_DRAWABLE (iter->data), context); + + gegl_rectangle_intersect (&bounds, &bounds, &selection_bounds); + x = MIN (item_x - selection_bounds.x, 0.0); + y = MIN (item_y - selection_bounds.y, 0.0); + + /* Finally shrink the copied layer to contents. */ + gimp_item_resize (iter->data, context, GIMP_FILL_TRANSPARENT, + bounds.width, bounds.height, x, y); + } + g_list_free (all_items); + } + /* Remove selection from the clipboard image. */ + gimp_channel_clear (clip_selection, NULL, FALSE); + return GIMP_OBJECT (gimp_get_clipboard_image (image->gimp)); } - else if (drawables_are_layers) - { - /* Copying multiple layers or specific selection, we copy the - * composited pixels as rendered. - */ - GimpImage *clip_image; - GimpBuffer *buffer; - - clip_image = gimp_image_new_from_drawables (image->gimp, drawables, TRUE); - gimp_container_remove (image->gimp->images, GIMP_OBJECT (clip_image)); - buffer = gimp_edit_copy_visible (clip_image, context, error); - - g_object_unref (clip_image); - - return buffer ? GIMP_OBJECT (buffer) : NULL; - } else { GimpBuffer *buffer;