From e83d8ac4f2dc0260e97a91938ae3e34608efdbf9 Mon Sep 17 00:00:00 2001 From: Ell Date: Mon, 25 Feb 2019 04:49:04 -0500 Subject: [PATCH] app: merge layers in chunks, and show progress In gimp_image_merge_layers() -- the internal function used by the various layer-merging/flattenning functions -- process the merged- layer graph in chunks, using gimp_gegl_apply_operation(), instead of in one go, using gegl_node_blit_buffer(). Processing in chunks better utilizes the cache, since it reduces the size of intermediate buffers, reducing the chances of hitting the swap when merging large images (see, for example, issue #3012.) Additionally, this allows us to show progress indication. Have the relevant gimpimage-merge functions take a GimpProgress, and pass it down to gimp_image_merge_layers(). Adapt all callers. --- app/actions/image-commands.c | 33 ++++++++----- app/actions/layers-commands.c | 9 ++-- app/core/gimpimage-merge.c | 62 +++++++++++++++++-------- app/core/gimpimage-merge.h | 5 +- app/dialogs/image-merge-layers-dialog.c | 3 +- app/dialogs/image-merge-layers-dialog.h | 3 +- app/file/file-open.c | 3 +- app/pdb/image-cmds.c | 8 ++-- pdb/groups/image.pdb | 8 ++-- 9 files changed, 89 insertions(+), 45 deletions(-) diff --git a/app/actions/image-commands.c b/app/actions/image-commands.c index a2a108de2f..75544be59c 100644 --- a/app/actions/image-commands.c +++ b/app/actions/image-commands.c @@ -166,7 +166,8 @@ static void image_merge_layers_callback (GtkWidget *dialog, GimpContext *context, GimpMergeType merge_type, gboolean merge_active_group, - gboolean discard_invisible); + gboolean discard_invisible, + gpointer user_data); /* private variables */ @@ -898,10 +899,12 @@ void image_merge_layers_cmd_callback (GtkAction *action, gpointer data) { - GtkWidget *dialog; - GimpImage *image; - GtkWidget *widget; + GtkWidget *dialog; + GimpImage *image; + GimpDisplay *display; + GtkWidget *widget; return_if_no_image (image, data); + return_if_no_display (display, data); return_if_no_widget (widget, data); #define MERGE_LAYERS_DIALOG_KEY "gimp-merge-layers-dialog" @@ -919,7 +922,7 @@ image_merge_layers_cmd_callback (GtkAction *action, config->layer_merge_active_group_only, config->layer_merge_discard_invisible, image_merge_layers_callback, - NULL); + display); dialogs_attach_dialog (G_OBJECT (image), MERGE_LAYERS_DIALOG_KEY, dialog); } @@ -931,13 +934,16 @@ void image_flatten_image_cmd_callback (GtkAction *action, gpointer data) { - GimpImage *image; - GtkWidget *widget; - GError *error = NULL; + GimpImage *image; + GimpDisplay *display; + GtkWidget *widget; + GError *error = NULL; return_if_no_image (image, data); + return_if_no_display (display, data); return_if_no_widget (widget, data); - if (! gimp_image_flatten (image, action_data_get_context (data), &error)) + if (! gimp_image_flatten (image, action_data_get_context (data), + GIMP_PROGRESS (display), &error)) { gimp_message_literal (image->gimp, G_OBJECT (widget), GIMP_MESSAGE_WARNING, @@ -1458,9 +1464,11 @@ image_merge_layers_callback (GtkWidget *dialog, GimpContext *context, GimpMergeType merge_type, gboolean merge_active_group, - gboolean discard_invisible) + gboolean discard_invisible, + gpointer user_data) { - GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GimpDialogConfig *config = GIMP_DIALOG_CONFIG (image->gimp->config); + GimpDisplay *display = user_data; g_object_set (config, "layer-merge-type", merge_type, @@ -1472,7 +1480,8 @@ image_merge_layers_callback (GtkWidget *dialog, context, config->layer_merge_type, config->layer_merge_active_group_only, - config->layer_merge_discard_invisible); + config->layer_merge_discard_invisible, + GIMP_PROGRESS (display)); gimp_image_flush (image); diff --git a/app/actions/layers-commands.c b/app/actions/layers-commands.c index 4d15d0294b..289a0f0228 100644 --- a/app/actions/layers-commands.c +++ b/app/actions/layers-commands.c @@ -577,12 +577,15 @@ void layers_merge_down_cmd_callback (GtkAction *action, gpointer data) { - GimpImage *image; - GimpLayer *layer; + GimpImage *image; + GimpLayer *layer; + GimpDisplay *display; return_if_no_layer (image, layer, data); + return_if_no_display (display, data); gimp_image_merge_down (image, layer, action_data_get_context (data), - GIMP_EXPAND_AS_NECESSARY, NULL); + GIMP_EXPAND_AS_NECESSARY, + GIMP_PROGRESS (display), NULL); gimp_image_flush (image); } diff --git a/app/core/gimpimage-merge.c b/app/core/gimpimage-merge.c index f118a8d4e9..b8dc8cc11b 100644 --- a/app/core/gimpimage-merge.c +++ b/app/core/gimpimage-merge.c @@ -48,6 +48,7 @@ #include "gimpmarshal.h" #include "gimpparasitelist.h" #include "gimppickable.h" +#include "gimpprogress.h" #include "gimpprojectable.h" #include "gimpundostack.h" @@ -58,7 +59,9 @@ static GimpLayer * gimp_image_merge_layers (GimpImage *image, GimpContainer *container, GSList *merge_list, GimpContext *context, - GimpMergeType merge_type); + GimpMergeType merge_type, + const gchar *undo_desc, + GimpProgress *progress); /* public functions */ @@ -68,7 +71,8 @@ gimp_image_merge_visible_layers (GimpImage *image, GimpContext *context, GimpMergeType merge_type, gboolean merge_active_group, - gboolean discard_invisible) + gboolean discard_invisible, + GimpProgress *progress) { GimpContainer *container; GList *list; @@ -77,6 +81,7 @@ gimp_image_merge_visible_layers (GimpImage *image, g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); if (merge_active_group) { @@ -126,13 +131,14 @@ gimp_image_merge_visible_layers (GimpImage *image, if (merge_list) { - GimpLayer *layer; + GimpLayer *layer; + const gchar *undo_desc = C_("undo-type", "Merge Visible Layers"); gimp_set_busy (image->gimp); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, - C_("undo-type", "Merge Visible Layers")); + undo_desc); /* if there's a floating selection, anchor it */ if (gimp_image_get_floating_selection (image)) @@ -140,7 +146,8 @@ gimp_image_merge_visible_layers (GimpImage *image, layer = gimp_image_merge_layers (image, container, - merge_list, context, merge_type); + merge_list, context, merge_type, + undo_desc, progress); g_slist_free (merge_list); if (invisible_list) @@ -164,9 +171,10 @@ gimp_image_merge_visible_layers (GimpImage *image, } GimpLayer * -gimp_image_flatten (GimpImage *image, - GimpContext *context, - GError **error) +gimp_image_flatten (GimpImage *image, + GimpContext *context, + GimpProgress *progress, + GError **error) { GList *list; GSList *merge_list = NULL; @@ -174,6 +182,7 @@ gimp_image_flatten (GimpImage *image, g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); for (list = gimp_image_get_layer_iter (image); @@ -191,11 +200,13 @@ gimp_image_flatten (GimpImage *image, if (merge_list) { + const gchar *undo_desc = C_("undo-type", "Flatten Image"); + gimp_set_busy (image->gimp); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, - C_("undo-type", "Flatten Image")); + undo_desc); /* if there's a floating selection, anchor it */ if (gimp_image_get_floating_selection (image)) @@ -204,7 +215,8 @@ gimp_image_flatten (GimpImage *image, layer = gimp_image_merge_layers (image, gimp_image_get_layers (image), merge_list, context, - GIMP_FLATTEN_IMAGE); + GIMP_FLATTEN_IMAGE, + undo_desc, progress); g_slist_free (merge_list); gimp_image_alpha_changed (image); @@ -226,17 +238,20 @@ gimp_image_merge_down (GimpImage *image, GimpLayer *current_layer, GimpContext *context, GimpMergeType merge_type, + GimpProgress *progress, GError **error) { - GimpLayer *layer; - GList *list; - GList *layer_list = NULL; - GSList *merge_list = NULL; + GimpLayer *layer; + GList *list; + GList *layer_list = NULL; + GSList *merge_list = NULL; + const gchar *undo_desc; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_LAYER (current_layer), NULL); g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (current_layer)), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); if (gimp_layer_is_floating_sel (current_layer)) @@ -299,15 +314,18 @@ gimp_image_merge_down (GimpImage *image, merge_list = g_slist_prepend (merge_list, current_layer); + undo_desc = C_("undo-type", "Merge Down"); + gimp_set_busy (image->gimp); gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE, - C_("undo-type", "Merge Down")); + undo_desc); layer = gimp_image_merge_layers (image, gimp_item_get_container (GIMP_ITEM (current_layer)), - merge_list, context, merge_type); + merge_list, context, merge_type, + undo_desc, progress); g_slist_free (merge_list); gimp_image_undo_group_end (image); @@ -457,7 +475,9 @@ gimp_image_merge_layers (GimpImage *image, GimpContainer *container, GSList *merge_list, GimpContext *context, - GimpMergeType merge_type) + GimpMergeType merge_type, + const gchar *undo_desc, + GimpProgress *progress) { GimpLayer *parent; gint x1, y1; @@ -478,6 +498,7 @@ gimp_image_merge_layers (GimpImage *image, g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), NULL); top_layer = merge_list->data; parent = gimp_layer_get_parent (top_layer); @@ -660,9 +681,10 @@ gimp_image_merge_layers (GimpImage *image, gegl_node_disconnect (last_node, "input"); /* Render the graph into the merge layer */ - gegl_node_blit_buffer (offset_node, - gimp_drawable_get_buffer (GIMP_DRAWABLE (merge_layer)), - NULL, 0, GEGL_ABYSS_NONE); + gimp_gegl_apply_operation (NULL, progress, undo_desc, offset_node, + gimp_drawable_get_buffer ( + GIMP_DRAWABLE (merge_layer)), + NULL, FALSE); /* Reconnect the bottom-layer node's input */ if (last_node_source) diff --git a/app/core/gimpimage-merge.h b/app/core/gimpimage-merge.h index 4b7a916561..93aab4e53a 100644 --- a/app/core/gimpimage-merge.h +++ b/app/core/gimpimage-merge.h @@ -23,17 +23,20 @@ GimpLayer * gimp_image_merge_visible_layers (GimpImage *image, GimpContext *context, GimpMergeType merge_type, gboolean merge_active_group, - gboolean discard_invisible); + gboolean discard_invisible, + GimpProgress *progress); GimpLayer * gimp_image_merge_down (GimpImage *image, GimpLayer *current_layer, GimpContext *context, GimpMergeType merge_type, + GimpProgress *progress, GError **error); GimpLayer * gimp_image_merge_group_layer (GimpImage *image, GimpGroupLayer *group); GimpLayer * gimp_image_flatten (GimpImage *image, GimpContext *context, + GimpProgress *progress, GError **error); GimpVectors * gimp_image_merge_visible_vectors (GimpImage *image, diff --git a/app/dialogs/image-merge-layers-dialog.c b/app/dialogs/image-merge-layers-dialog.c index 552d26bafa..67681eb779 100644 --- a/app/dialogs/image-merge-layers-dialog.c +++ b/app/dialogs/image-merge-layers-dialog.c @@ -182,7 +182,8 @@ image_merge_layers_dialog_response (GtkWidget *dialog, private->context, private->merge_type, private->merge_active_group, - private->discard_invisible); + private->discard_invisible, + private->user_data); } else { diff --git a/app/dialogs/image-merge-layers-dialog.h b/app/dialogs/image-merge-layers-dialog.h index e763f91cc9..ee965150ee 100644 --- a/app/dialogs/image-merge-layers-dialog.h +++ b/app/dialogs/image-merge-layers-dialog.h @@ -24,7 +24,8 @@ typedef void (* GimpMergeLayersCallback) (GtkWidget *dialog, GimpContext *context, GimpMergeType merge_type, gboolean merge_active_group, - gboolean discard_invisible); + gboolean discard_invisible, + gpointer user_data); GtkWidget * diff --git a/app/file/file-open.c b/app/file/file-open.c index 0747c7cf4a..fcb7fc3d5f 100644 --- a/app/file/file-open.c +++ b/app/file/file-open.c @@ -634,7 +634,8 @@ file_open_layers (Gimp *gimp, layer = gimp_image_merge_visible_layers (new_image, context, GIMP_CLIP_TO_IMAGE, - FALSE, FALSE); + FALSE, FALSE, + NULL); layers = g_list_prepend (NULL, layer); } diff --git a/app/pdb/image-cmds.c b/app/pdb/image-cmds.c index 2ddf89b590..17c56d737b 100644 --- a/app/pdb/image-cmds.c +++ b/app/pdb/image-cmds.c @@ -1365,7 +1365,8 @@ image_flatten_invoker (GimpProcedure *procedure, if (success) { - layer = gimp_image_flatten (image, context, error); + layer = gimp_image_flatten (image, context, + progress, error); if (! layer) success = FALSE; @@ -1400,7 +1401,8 @@ image_merge_visible_layers_invoker (GimpProcedure *procedure, if (success) { layer = gimp_image_merge_visible_layers (image, context, merge_type, - FALSE, FALSE); + FALSE, FALSE, + progress); if (! layer) success = FALSE; @@ -1439,7 +1441,7 @@ image_merge_down_invoker (GimpProcedure *procedure, if (gimp_pdb_item_is_attached (GIMP_ITEM (merge_layer), image, 0, error)) { layer = gimp_image_merge_down (image, merge_layer, context, merge_type, - error); + progress, error); if (! layer) success = FALSE; diff --git a/pdb/groups/image.pdb b/pdb/groups/image.pdb index d67ce341e3..d51079082c 100644 --- a/pdb/groups/image.pdb +++ b/pdb/groups/image.pdb @@ -740,7 +740,8 @@ HELP code => <<'CODE' { layer = gimp_image_merge_visible_layers (image, context, merge_type, - FALSE, FALSE); + FALSE, FALSE, + progress); if (! layer) success = FALSE; @@ -786,7 +787,7 @@ HELP if (gimp_pdb_item_is_attached (GIMP_ITEM (merge_layer), image, 0, error)) { layer = gimp_image_merge_down (image, merge_layer, context, merge_type, - error); + progress, error); if (! layer) success = FALSE; @@ -825,7 +826,8 @@ HELP headers => [ qw("core/gimpimage-merge.h") ], code => <<'CODE' { - layer = gimp_image_flatten (image, context, error); + layer = gimp_image_flatten (image, context, + progress, error); if (! layer) success = FALSE;