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.
This commit is contained in:
Ell 2019-02-25 04:49:04 -05:00
parent 914200f3ad
commit e83d8ac4f2
9 changed files with 89 additions and 45 deletions

View File

@ -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);

View File

@ -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);
}

View File

@ -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)

View File

@ -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,

View File

@ -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
{

View File

@ -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 *

View File

@ -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);
}

View File

@ -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;

View File

@ -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;