From 7a5f914866d4cccde2392ea5d8bfac919ee78f96 Mon Sep 17 00:00:00 2001 From: Michael Natterer Date: Sat, 6 Sep 2003 20:06:53 +0000 Subject: [PATCH] To optimize duplicate and/or wrong image updates away, introduced new 2003-09-06 Michael Natterer To optimize duplicate and/or wrong image updates away, introduced new policy that a child object must never explicitly update or invalidate its parent object (just like the GUI is not updated explicitly by the core): * app/core/gimpdrawable.[ch]: added new signal GimpDrawable::update(). Never update or invalidate the image when the drawable is updated or invalidated. (gimp_drawable_set_visible): don't gimp_drawable_update() the drawable since its pixels have not changed. * app/core/gimpimage.[ch]: connect to the "add" and "remove" signals of the layers and channels containers. Also connect to the "update" and "visibility_changed" signals of all drawables in these containers (optimizes away updates issued by drawables which are not yet added to the image and updates of the selection mask). Also, don't propagate updates to the image if the emitting drawable is invisible (optimizes away updates issued by invisible drawables). (gimp_image_add_layer,channel) (gimp_image_remove_layer,channel): don't update the image since that's done by our "add" and "remove" handlers now. (gimp_image_position_layer,channel): update just the image, not the drawable since its pixels have not changed. (gimp_image_real_colormap_changed) (gimp_image_set_component_visible): always call gimp_image_update() *and* gimp_viewable_invalidate_preview() to get everything updated, since update and invalidate of images are not connected. * app/core/gimpimage-undo-push.c (undo_pop_layer,channel): don't update the drawable since (a) its pixels don't change and (b) the image updates itself upon adding/removing now. (undo_pop_layer_mod): replaced gimp_image_update() by gimp_drawable_update() (just for consistency with other similar functions). * app/core/gimplayer.c: connect to "update" of the layer mask and issue updates on the layer if the mask update has any effect on the projection. (gimp_layer_create_mask): don't set the mask's offsets here since they may be different when we later add the mask to the layer. * app/core/gimplayermask.c (gimp_layer_mask_set_layer): set the mask offsets here instead. * app/core/gimpchannel.c (gimp_channel_translate): update the channel even if push_undo == FALSE. * app/paint/gimppaintcore.c (gimp_paint_core_finish) * app/tools/gimpinktool.c (ink_finish): invalidate both the drawable and the image preview since invalidating the drawable doesn't invalidate the image any more. * app/text/gimptextlayer.c (gimp_text_layer_render_now): also update the new extents of the text layer, not only the old one. (gimp_text_layer_render_layout): don't update the drawable since gimp_drawable_fill() already updated it. --- ChangeLog | 67 ++++++++++ app/core/gimpchannel.c | 35 ++--- app/core/gimpdrawable.c | 62 +++++---- app/core/gimpdrawable.h | 9 +- app/core/gimpimage-undo-push.c | 31 +---- app/core/gimpimage.c | 237 ++++++++++++++++++++++++--------- app/core/gimpimage.h | 5 + app/core/gimplayer.c | 84 +++++++++--- app/core/gimplayermask.c | 10 +- app/paint/gimpink.c | 13 +- app/paint/gimppaintcore.c | 15 ++- app/text/gimptextlayer.c | 9 +- app/tools/gimpinktool.c | 13 +- 13 files changed, 410 insertions(+), 180 deletions(-) diff --git a/ChangeLog b/ChangeLog index fa03738498..25fa600c09 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,70 @@ +2003-09-06 Michael Natterer + + To optimize duplicate and/or wrong image updates away, introduced + new policy that a child object must never explicitly update or + invalidate its parent object (just like the GUI is not updated + explicitly by the core): + + * app/core/gimpdrawable.[ch]: added new signal + GimpDrawable::update(). Never update or invalidate the image when + the drawable is updated or invalidated. + + (gimp_drawable_set_visible): don't gimp_drawable_update() the + drawable since its pixels have not changed. + + * app/core/gimpimage.[ch]: connect to the "add" and "remove" + signals of the layers and channels containers. Also connect to the + "update" and "visibility_changed" signals of all drawables in + these containers (optimizes away updates issued by drawables which + are not yet added to the image and updates of the selection + mask). Also, don't propagate updates to the image if the emitting + drawable is invisible (optimizes away updates issued by invisible + drawables). + + (gimp_image_add_layer,channel) + (gimp_image_remove_layer,channel): don't update the image since + that's done by our "add" and "remove" handlers now. + + (gimp_image_position_layer,channel): update just the image, not + the drawable since its pixels have not changed. + + (gimp_image_real_colormap_changed) + (gimp_image_set_component_visible): always call + gimp_image_update() *and* gimp_viewable_invalidate_preview() to + get everything updated, since update and invalidate of images are + not connected. + + * app/core/gimpimage-undo-push.c (undo_pop_layer,channel): don't + update the drawable since (a) its pixels don't change and (b) the + image updates itself upon adding/removing now. + + (undo_pop_layer_mod): replaced gimp_image_update() by + gimp_drawable_update() (just for consistency with other similar + functions). + + * app/core/gimplayer.c: connect to "update" of the layer mask and + issue updates on the layer if the mask update has any effect on + the projection. + (gimp_layer_create_mask): don't set the mask's offsets here since + they may be different when we later add the mask to the layer. + + * app/core/gimplayermask.c (gimp_layer_mask_set_layer): set the + mask offsets here instead. + + * app/core/gimpchannel.c (gimp_channel_translate): update the + channel even if push_undo == FALSE. + + * app/paint/gimppaintcore.c (gimp_paint_core_finish) + * app/tools/gimpinktool.c (ink_finish): invalidate both the + drawable and the image preview since invalidating the drawable + doesn't invalidate the image any more. + + * app/text/gimptextlayer.c (gimp_text_layer_render_now): also + update the new extents of the text layer, not only the old one. + + (gimp_text_layer_render_layout): don't update the drawable since + gimp_drawable_fill() already updated it. + 2003-09-06 Sven Neumann * app/vectors/gimpbezierstroke.c diff --git a/app/core/gimpchannel.c b/app/core/gimpchannel.c index af36efde85..df79d4f205 100644 --- a/app/core/gimpchannel.c +++ b/app/core/gimpchannel.c @@ -362,19 +362,15 @@ gimp_channel_translate (GimpItem *item, height = y2 - y1; if (push_undo) - { - gimp_channel_push_undo (channel, - GIMP_CHANNEL_GET_CLASS (channel)->translate_desc); - - /* update the old area */ - gimp_drawable_update (GIMP_DRAWABLE (item), - x1, y1, - x2 - x1, y2 - y1); - } + gimp_channel_push_undo (channel, + GIMP_CHANNEL_GET_CLASS (channel)->translate_desc); else - { - gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (channel)); - } + gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (channel)); + + /* update the old area */ + gimp_drawable_update (GIMP_DRAWABLE (item), + x1, y1, + x2 - x1, y2 - y1); /* make sure width and height are non-zero */ if (width != 0 && height != 0) @@ -429,16 +425,11 @@ gimp_channel_translate (GimpItem *item, channel->y2 = y2; } - if (push_undo) - { - /* update the new area */ - gimp_drawable_update (GIMP_DRAWABLE (item), - channel->x1, channel->y1, - channel->x2 - channel->x1, - channel->y2 - channel->y1); - - gimp_viewable_size_changed (GIMP_VIEWABLE (item)); - } + /* update the new area */ + gimp_drawable_update (GIMP_DRAWABLE (item), + channel->x1, channel->y1, + channel->x2 - channel->x1, + channel->y2 - channel->y1); } static void diff --git a/app/core/gimpdrawable.c b/app/core/gimpdrawable.c index 9239708cac..b7d344ac10 100644 --- a/app/core/gimpdrawable.c +++ b/app/core/gimpdrawable.c @@ -56,6 +56,7 @@ enum { + UPDATE, VISIBILITY_CHANGED, ALPHA_CHANGED, LAST_SIGNAL @@ -105,6 +106,12 @@ static void gimp_drawable_transform (GimpItem *item, GimpProgressFunc progress_callback, gpointer progress_data); +static void gimp_drawable_real_update (GimpDrawable *drawable, + gint x, + gint y, + gint width, + gint height); + /* private variables */ @@ -156,6 +163,19 @@ gimp_drawable_class_init (GimpDrawableClass *klass) parent_class = g_type_class_peek_parent (klass); + gimp_drawable_signals[UPDATE] = + g_signal_new ("update", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GimpDrawableClass, update), + NULL, NULL, + gimp_marshal_VOID__INT_INT_INT_INT, + G_TYPE_NONE, 4, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT, + G_TYPE_INT); + gimp_drawable_signals[VISIBILITY_CHANGED] = g_signal_new ("visibility_changed", G_TYPE_FROM_CLASS (klass), @@ -190,6 +210,7 @@ gimp_drawable_class_init (GimpDrawableClass *klass) item_class->rotate = gimp_drawable_rotate; item_class->transform = gimp_drawable_transform; + klass->update = gimp_drawable_real_update; klass->visibility_changed = NULL; klass->alpha_changed = NULL; klass->invalidate_boundary = NULL; @@ -247,7 +268,6 @@ static void gimp_drawable_invalidate_preview (GimpViewable *viewable) { GimpDrawable *drawable; - GimpImage *gimage; if (GIMP_VIEWABLE_CLASS (parent_class)->invalidate_preview) GIMP_VIEWABLE_CLASS (parent_class)->invalidate_preview (viewable); @@ -258,11 +278,6 @@ gimp_drawable_invalidate_preview (GimpViewable *viewable) if (drawable->preview_cache) gimp_preview_cache_invalidate (&drawable->preview_cache); - - gimage = gimp_item_get_image (GIMP_ITEM (drawable)); - - if (gimage) - gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); } static GimpItem * @@ -559,6 +574,16 @@ gimp_drawable_transform (GimpItem *item, } } +static void +gimp_drawable_real_update (GimpDrawable *drawable, + gint x, + gint y, + gint width, + gint height) +{ + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (drawable)); +} + void gimp_drawable_configure (GimpDrawable *drawable, GimpImage *gimage, @@ -595,28 +620,13 @@ void gimp_drawable_update (GimpDrawable *drawable, gint x, gint y, - gint w, - gint h) + gint width, + gint height) { - GimpItem *item; - GimpImage *gimage; - gint offset_x; - gint offset_y; - g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); - item = GIMP_ITEM (drawable); - gimage = gimp_item_get_image (item); - - g_return_if_fail (gimage != NULL); - - gimp_item_offsets (item, &offset_x, &offset_y); - x += offset_x; - y += offset_y; - - gimp_image_update (gimage, x, y, w, h); - - gimp_viewable_invalidate_preview (GIMP_VIEWABLE (drawable)); + g_signal_emit (drawable, gimp_drawable_signals[UPDATE], 0, + x, y, width, height); } void @@ -947,8 +957,6 @@ gimp_drawable_set_visible (GimpDrawable *drawable, drawable->visible = visible ? TRUE : FALSE; g_signal_emit (drawable, gimp_drawable_signals[VISIBILITY_CHANGED], 0); - - gimp_drawable_update (drawable, 0, 0, item->width, item->height); } } diff --git a/app/core/gimpdrawable.h b/app/core/gimpdrawable.h index 93a575221c..6f7bf2eb76 100644 --- a/app/core/gimpdrawable.h +++ b/app/core/gimpdrawable.h @@ -54,6 +54,11 @@ struct _GimpDrawableClass GimpItemClass parent_class; /* signals */ + void (* update) (GimpDrawable *drawable, + gint x, + gint y, + gint width, + gint height); void (* visibility_changed) (GimpDrawable *drawable); void (* alpha_changed) (GimpDrawable *drawable); @@ -78,8 +83,8 @@ void gimp_drawable_configure (GimpDrawable *drawable, void gimp_drawable_update (GimpDrawable *drawable, gint x, gint y, - gint w, - gint h); + gint width, + gint height); void gimp_drawable_push_undo (GimpDrawable *drawable, const gchar *undo_desc, diff --git a/app/core/gimpimage-undo-push.c b/app/core/gimpimage-undo-push.c index 4a7fb66cce..0b317206e7 100644 --- a/app/core/gimpimage-undo-push.c +++ b/app/core/gimpimage-undo-push.c @@ -1563,11 +1563,6 @@ undo_pop_layer (GimpUndo *undo, gimp_image_floating_selection_changed (undo->gimage); } - gimp_drawable_update (GIMP_DRAWABLE (layer), - 0, 0, - GIMP_ITEM (layer)->width, - GIMP_ITEM (layer)->height); - if (gimp_container_num_children (undo->gimage->layers) == 1 && ! gimp_drawable_has_alpha (GIMP_LIST (undo->gimage->layers)->list->data)) { @@ -1607,11 +1602,6 @@ undo_pop_layer (GimpUndo *undo, if (gimp_layer_is_floating_sel (layer)) gimp_image_floating_selection_changed (undo->gimage); - - gimp_drawable_update (GIMP_DRAWABLE (layer), - 0, 0, - GIMP_ITEM (layer)->width, - GIMP_ITEM (layer)->height); } return TRUE; @@ -1699,11 +1689,10 @@ undo_pop_layer_mod (GimpUndo *undo, layer = GIMP_LAYER (GIMP_ITEM_UNDO (undo)->item); /* Issue the first update */ - gimp_image_update (undo->gimage, - GIMP_ITEM (layer)->offset_x, - GIMP_ITEM (layer)->offset_y, - GIMP_ITEM (layer)->width, - GIMP_ITEM (layer)->height); + gimp_drawable_update (GIMP_DRAWABLE (layer), + 0, 0, + GIMP_ITEM (layer)->width, + GIMP_ITEM (layer)->height); tiles = lmu->tiles; layer_type = lmu->type; @@ -2237,12 +2226,6 @@ undo_pop_channel (GimpUndo *undo, else gimp_image_unset_active_channel (undo->gimage); } - - /* update the area */ - gimp_drawable_update (GIMP_DRAWABLE (channel), - 0, 0, - GIMP_ITEM (channel)->width, - GIMP_ITEM (channel)->height); } else { @@ -2257,12 +2240,6 @@ undo_pop_channel (GimpUndo *undo, gimp_container_insert (undo->gimage->channels, GIMP_OBJECT (channel), cu->prev_position); gimp_image_set_active_channel (undo->gimage, channel); - - /* update the area */ - gimp_drawable_update (GIMP_DRAWABLE (channel), - 0, 0, - GIMP_ITEM (channel)->width, - GIMP_ITEM (channel)->height); } return TRUE; diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c index 8cb026f2e0..79f8e3542a 100644 --- a/app/core/gimpimage.c +++ b/app/core/gimpimage.c @@ -118,6 +118,21 @@ static gchar * gimp_image_get_description (GimpViewable *viewable, static void gimp_image_real_colormap_changed (GimpImage *gimage, gint ncol); +static void gimp_image_drawable_update (GimpDrawable *drawable, + gint x, + gint y, + gint width, + gint height, + GimpImage *gimage); +static void gimp_image_drawable_visibility (GimpDrawable *drawable, + GimpImage *gimage); +static void gimp_image_drawable_add (GimpContainer *container, + GimpDrawable *drawable, + GimpImage *gimage); +static void gimp_image_drawable_remove (GimpContainer *container, + GimpDrawable *drawable, + GimpImage *gimage); + static void gimp_image_get_active_components (const GimpImage *gimage, const GimpDrawable *drawable, gboolean *active); @@ -478,6 +493,38 @@ gimp_image_init (GimpImage *gimage) GIMP_CONTAINER_POLICY_STRONG); gimage->layer_stack = NULL; + gimage->layer_update_handler = + gimp_container_add_handler (gimage->layers, "update", + G_CALLBACK (gimp_image_drawable_update), + gimage); + gimage->channel_update_handler = + gimp_container_add_handler (gimage->channels, "update", + G_CALLBACK (gimp_image_drawable_update), + gimage); + + gimage->layer_visible_handler = + gimp_container_add_handler (gimage->layers, "visibility_changed", + G_CALLBACK (gimp_image_drawable_visibility), + gimage); + gimage->channel_visible_handler = + gimp_container_add_handler (gimage->channels, "visibility_changed", + G_CALLBACK (gimp_image_drawable_visibility), + gimage); + + g_signal_connect (gimage->layers, "add", + G_CALLBACK (gimp_image_drawable_add), + gimage); + g_signal_connect (gimage->channels, "add", + G_CALLBACK (gimp_image_drawable_add), + gimage); + + g_signal_connect (gimage->layers, "remove", + G_CALLBACK (gimp_image_drawable_remove), + gimage); + g_signal_connect (gimage->channels, "remove", + G_CALLBACK (gimp_image_drawable_remove), + gimage); + gimage->active_layer = NULL; gimage->active_channel = NULL; gimage->active_vectors = NULL; @@ -507,21 +554,41 @@ gimp_image_init (GimpImage *gimage) static void gimp_image_dispose (GObject *object) { - GimpImage *gimage; - - gimage = GIMP_IMAGE (object); + GimpImage *gimage = GIMP_IMAGE (object); gimp_image_undo_free (gimage); + gimp_container_remove_handler (gimage->layers, + gimage->layer_update_handler); + gimp_container_remove_handler (gimage->channels, + gimage->channel_update_handler); + + gimp_container_remove_handler (gimage->layers, + gimage->layer_visible_handler); + gimp_container_remove_handler (gimage->channels, + gimage->channel_visible_handler); + + g_signal_handlers_disconnect_by_func (gimage->layers, + gimp_image_drawable_add, + gimage); + g_signal_handlers_disconnect_by_func (gimage->channels, + gimp_image_drawable_add, + gimage); + + g_signal_handlers_disconnect_by_func (gimage->layers, + gimp_image_drawable_remove, + gimage); + g_signal_handlers_disconnect_by_func (gimage->channels, + gimp_image_drawable_remove, + gimage); + G_OBJECT_CLASS (parent_class)->dispose (object); } static void gimp_image_finalize (GObject *object) { - GimpImage *gimage; - - gimage = GIMP_IMAGE (object); + GimpImage *gimage = GIMP_IMAGE (object); if (gimage->gimp && gimage->gimp->image_table) { @@ -763,11 +830,71 @@ gimp_image_real_colormap_changed (GimpImage *gimage, { /* A colormap alteration affects the whole image */ gimp_image_update (gimage, 0, 0, gimage->width, gimage->height); + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); gimp_image_color_hash_invalidate (gimage, ncol); } } +static void +gimp_image_drawable_update (GimpDrawable *drawable, + gint x, + gint y, + gint width, + gint height, + GimpImage *gimage) +{ + if (gimp_drawable_get_visible (drawable)) + { + gint offset_x; + gint offset_y; + + gimp_item_offsets (GIMP_ITEM (drawable), &offset_x, &offset_y); + x += offset_x; + y += offset_y; + + gimp_image_update (gimage, x, y, width, height); + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); + } +} + +static void +gimp_image_drawable_visibility (GimpDrawable *drawable, + GimpImage *gimage) +{ + GimpItem *item; + gint offset_x; + gint offset_y; + + item = GIMP_ITEM (drawable); + + gimp_item_offsets (item, &offset_x, &offset_y); + + gimp_image_update (gimage, + offset_x, offset_y, + gimp_item_width (item), + gimp_item_height (item)); + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); +} + +static void +gimp_image_drawable_add (GimpContainer *container, + GimpDrawable *drawable, + GimpImage *gimage) +{ + if (gimp_drawable_get_visible (drawable)) + gimp_image_drawable_visibility (drawable, gimage); +} + +static void +gimp_image_drawable_remove (GimpContainer *container, + GimpDrawable *drawable, + GimpImage *gimage) +{ + if (gimp_drawable_get_visible (drawable)) + gimp_image_drawable_visibility (drawable, gimage); +} + static void gimp_image_get_active_components (const GimpImage *gimage, const GimpDrawable *drawable, @@ -1236,6 +1363,7 @@ gimp_image_set_component_visible (GimpImage *gimage, channel); gimp_image_update (gimage, 0, 0, gimage->width, gimage->height); + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); } } @@ -1820,8 +1948,6 @@ gimp_image_replace_image (GimpImage *gimage, gint x1, y1, x2, y2; gint offset_x, offset_y; PixelRegion src1PR, destPR; - PixelRegion mask2PR, tempPR; - guchar *temp_data; CombinationMode operation; gboolean active_components[MAX_CHANNELS]; @@ -1887,7 +2013,9 @@ gimp_image_replace_image (GimpImage *gimage, if (mask) { - int mx, my; + PixelRegion mask2PR, tempPR; + guchar *temp_data; + gint mx, my; /* configure the mask pixel region * don't use x1 and y1 because they are in layer @@ -1902,30 +2030,29 @@ gimp_image_replace_image (GimpImage *gimage, (x2 - x1), (y2 - y1), FALSE); - tempPR.bytes = 1; - tempPR.x = 0; - tempPR.y = 0; - tempPR.w = x2 - x1; - tempPR.h = y2 - y1; + tempPR.bytes = 1; + tempPR.x = 0; + tempPR.y = 0; + tempPR.w = x2 - x1; + tempPR.h = y2 - y1; tempPR.rowstride = tempPR.w * tempPR.bytes; - temp_data = g_malloc (tempPR.h * tempPR.rowstride); - tempPR.data = temp_data; + tempPR.data = temp_data = g_malloc (tempPR.h * tempPR.rowstride); copy_region (&mask2PR, &tempPR); /* apparently, region operations can mutate some PR data. */ - tempPR.x = 0; - tempPR.y = 0; - tempPR.w = x2 - x1; - tempPR.h = y2 - y1; + tempPR.x = 0; + tempPR.y = 0; + tempPR.w = x2 - x1; + tempPR.h = y2 - y1; tempPR.data = temp_data; apply_mask_to_region (&tempPR, maskPR, OPAQUE_OPACITY); - tempPR.x = 0; - tempPR.y = 0; - tempPR.w = x2 - x1; - tempPR.h = y2 - y1; + tempPR.x = 0; + tempPR.y = 0; + tempPR.w = x2 - x1; + tempPR.h = y2 - y1; tempPR.data = temp_data; combine_regions_replace (&src1PR, src2PR, &destPR, &tempPR, NULL, @@ -2569,12 +2696,6 @@ gimp_image_add_layer (GimpImage *gimage, /* notify the layers dialog of the currently active layer */ gimp_image_set_active_layer (gimage, layer); - /* update the new layer's area */ - gimp_drawable_update (GIMP_DRAWABLE (layer), - 0, 0, - gimp_item_width (GIMP_ITEM (layer)), - gimp_item_height (GIMP_ITEM (layer))); - if (alpha_changed) gimp_image_alpha_changed (gimage); @@ -2585,8 +2706,6 @@ void gimp_image_remove_layer (GimpImage *gimage, GimpLayer *layer) { - gint x, y, w, h; - g_return_if_fail (GIMP_IS_IMAGE (gimage)); g_return_if_fail (GIMP_IS_LAYER (layer)); @@ -2630,16 +2749,8 @@ gimp_image_remove_layer (GimpImage *gimage, /* Send out REMOVED signal from layer */ gimp_item_removed (GIMP_ITEM (layer)); - gimp_item_offsets (GIMP_ITEM (layer), &x, &y); - w = gimp_item_width (GIMP_ITEM (layer)); - h = gimp_item_height (GIMP_ITEM (layer)); - g_object_unref (layer); - gimp_image_update (gimage, x, y, w, h); - - gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); - if (gimp_container_num_children (gimage->layers) == 1 && ! gimp_drawable_has_alpha (GIMP_LIST (gimage->layers)->list->data)) { @@ -2755,7 +2866,6 @@ gimp_image_position_layer (GimpImage *gimage, gboolean push_undo, const gchar *undo_desc) { - gint off_x, off_y; gint index; gint num_layers; @@ -2800,14 +2910,18 @@ gimp_image_position_layer (GimpImage *gimage, gimp_container_reorder (gimage->layers, GIMP_OBJECT (layer), new_index); - gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y); + if (gimp_drawable_get_visible (GIMP_DRAWABLE (layer))) + { + gint off_x, off_y; - gimp_image_update (gimage, - off_x, off_y, - gimp_item_width (GIMP_ITEM (layer)), - gimp_item_height (GIMP_ITEM (layer))); + gimp_item_offsets (GIMP_ITEM (layer), &off_x, &off_y); - gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); + gimp_image_update (gimage, + off_x, off_y, + gimp_item_width (GIMP_ITEM (layer)), + gimp_item_height (GIMP_ITEM (layer))); + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); + } return TRUE; } @@ -2867,13 +2981,6 @@ gimp_image_add_channel (GimpImage *gimage, /* notify this gimage of the currently active channel */ gimp_image_set_active_channel (gimage, channel); - /* if channel is visible, update the image */ - if (gimp_drawable_get_visible (GIMP_DRAWABLE (channel))) - gimp_drawable_update (GIMP_DRAWABLE (channel), - 0, 0, - gimp_item_width (GIMP_ITEM (channel)), - gimp_item_height (GIMP_ITEM (channel))); - return TRUE; } @@ -2916,10 +3023,6 @@ gimp_image_remove_channel (GimpImage *gimage, } g_object_unref (channel); - - gimp_image_update (gimage, 0, 0, gimage->width, gimage->height); - - gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); } gboolean @@ -2971,7 +3074,7 @@ gimp_image_position_channel (GimpImage *gimage, gboolean push_undo, const gchar *undo_desc) { - gint index; + gint index; gint num_channels; g_return_val_if_fail (GIMP_IS_IMAGE (gimage), FALSE); @@ -2995,10 +3098,18 @@ gimp_image_position_channel (GimpImage *gimage, gimp_container_reorder (gimage->channels, GIMP_OBJECT (channel), new_index); - gimp_drawable_update (GIMP_DRAWABLE (channel), - 0, 0, - gimp_item_width (GIMP_ITEM (channel)), - gimp_item_height (GIMP_ITEM (channel))); + if (gimp_drawable_get_visible (GIMP_DRAWABLE (channel))) + { + gint off_x, off_y; + + gimp_item_offsets (GIMP_ITEM (channel), &off_x, &off_y); + + gimp_image_update (gimage, + off_x, off_y, + gimp_item_width (GIMP_ITEM (channel)), + gimp_item_height (GIMP_ITEM (channel))); + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); + } return TRUE; } diff --git a/app/core/gimpimage.h b/app/core/gimpimage.h index 2375ec66a5..e6930b0e49 100644 --- a/app/core/gimpimage.h +++ b/app/core/gimpimage.h @@ -132,6 +132,11 @@ struct _GimpImage GimpContainer *vectors; /* the list of vectors */ GSList *layer_stack; /* the layers in MRU order */ + GQuark layer_update_handler; + GQuark channel_update_handler; + GQuark layer_visible_handler; + GQuark channel_visible_handler; + GimpLayer *active_layer; /* the active layer */ GimpChannel *active_channel; /* the active channel */ GimpVectors *active_vectors; /* the active vectors */ diff --git a/app/core/gimplayer.c b/app/core/gimplayer.c index 1e69c9a7fa..22d9f720ef 100644 --- a/app/core/gimplayer.c +++ b/app/core/gimplayer.c @@ -63,6 +63,7 @@ enum static void gimp_layer_class_init (GimpLayerClass *klass); static void gimp_layer_init (GimpLayer *layer); +static void gimp_layer_dispose (GObject *object); static void gimp_layer_finalize (GObject *object); static gsize gimp_layer_get_memsize (GimpObject *object, @@ -120,6 +121,13 @@ static void gimp_layer_transform_color (GimpImage *gimage, GimpDrawable *drawable, GimpImageBaseType type); +static void gimp_layer_layer_mask_update (GimpDrawable *layer_mask, + gint x, + gint y, + gint width, + gint height, + GimpLayer *layer); + static guint layer_signals[LAST_SIGNAL] = { 0 }; @@ -207,6 +215,7 @@ gimp_layer_class_init (GimpLayerClass *klass) gimp_marshal_VOID__VOID, G_TYPE_NONE, 0); + object_class->dispose = gimp_layer_dispose; object_class->finalize = gimp_layer_finalize; gimp_object_class->get_memsize = gimp_layer_get_memsize; @@ -252,6 +261,19 @@ gimp_layer_init (GimpLayer *layer) layer->fs.num_segs = 0; } +static void +gimp_layer_dispose (GObject *object) +{ + GimpLayer *layer = GIMP_LAYER (object); + + if (layer->mask) + g_signal_handlers_disconnect_by_func (layer->mask, + gimp_layer_layer_mask_update, + layer); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + static void gimp_layer_finalize (GObject *object) { @@ -266,7 +288,8 @@ gimp_layer_finalize (GObject *object) if (layer->fs.segs) { g_free (layer->fs.segs); - layer->fs.segs = NULL; + layer->fs.segs = NULL; + layer->fs.num_segs = 0; } /* free the floating selection if it exists */ @@ -303,9 +326,7 @@ gimp_layer_get_memsize (GimpObject *object, static void gimp_layer_invalidate_preview (GimpViewable *viewable) { - GimpLayer *layer; - - layer = GIMP_LAYER (viewable); + GimpLayer *layer = GIMP_LAYER (viewable); if (GIMP_VIEWABLE_CLASS (parent_class)->invalidate_preview) GIMP_VIEWABLE_CLASS (parent_class)->invalidate_preview (viewable); @@ -749,9 +770,25 @@ gimp_layer_transform_color (GimpImage *gimage, } } -/**************************/ -/* Function definitions */ -/**************************/ +static void +gimp_layer_layer_mask_update (GimpDrawable *drawable, + gint x, + gint y, + gint width, + gint height, + GimpLayer *layer) +{ + GimpLayerMask *layer_mask = GIMP_LAYER_MASK (drawable); + + if (layer_mask->apply_mask || layer_mask->show_mask) + { + gimp_drawable_update (GIMP_DRAWABLE (layer), + x, y, width, height); + } +} + + +/* public functions */ GimpLayer * gimp_layer_new (GimpImage *gimage, @@ -906,15 +943,20 @@ gimp_layer_add_mask (GimpLayer *layer, return NULL; } - layer->mask = mask; - g_object_ref (layer->mask); - + layer->mask = g_object_ref (mask); gimp_layer_mask_set_layer (mask, layer); - gimp_drawable_update (GIMP_DRAWABLE (layer), - 0, 0, - GIMP_ITEM (layer)->width, - GIMP_ITEM (layer)->height); + if (mask->apply_mask || mask->show_mask) + { + gimp_drawable_update (GIMP_DRAWABLE (layer), + 0, 0, + GIMP_ITEM (layer)->width, + GIMP_ITEM (layer)->height); + } + + g_signal_connect (mask, "update", + G_CALLBACK (gimp_layer_layer_mask_update), + layer); if (push_undo) gimp_image_undo_push_layer_mask_add (gimage, _("Add Layer Mask"), @@ -954,9 +996,6 @@ gimp_layer_create_mask (const GimpLayer *layer, g_free (mask_name); - GIMP_ITEM (mask)->offset_x = item->offset_x; - GIMP_ITEM (mask)->offset_y = item->offset_y; - switch (add_mask_type) { case GIMP_ADD_WHITE_MASK: @@ -1126,10 +1165,9 @@ gimp_layer_apply_mask (GimpLayer *layer, } /* check if applying the mask changes the projection */ - if ((mode == GIMP_MASK_APPLY && (! layer->mask->apply_mask || - layer->mask->show_mask)) || - (mode == GIMP_MASK_DISCARD && (layer->mask->apply_mask || - layer->mask->show_mask))) + if (layer->mask->show_mask || + (mode == GIMP_MASK_APPLY && ! layer->mask->apply_mask) || + (mode == GIMP_MASK_DISCARD && layer->mask->apply_mask)) { view_changed = TRUE; } @@ -1158,6 +1196,10 @@ gimp_layer_apply_mask (GimpLayer *layer, apply_mask_to_region (&srcPR, &maskPR, OPAQUE_OPACITY); } + g_signal_handlers_disconnect_by_func (layer->mask, + gimp_layer_layer_mask_update, + layer); + g_object_unref (layer->mask); layer->mask = NULL; diff --git a/app/core/gimplayermask.c b/app/core/gimplayermask.c index 11ec002dd1..35a5f3d007 100644 --- a/app/core/gimplayermask.c +++ b/app/core/gimplayermask.c @@ -170,7 +170,7 @@ gimp_layer_mask_new (GimpImage *gimage, layer_mask = g_object_new (GIMP_TYPE_LAYER_MASK, NULL); - gimp_drawable_configure (GIMP_DRAWABLE (layer_mask), + gimp_drawable_configure (GIMP_DRAWABLE (layer_mask), gimage, 0, 0, width, height, GIMP_GRAY_IMAGE, name); @@ -192,9 +192,15 @@ gimp_layer_mask_set_layer (GimpLayerMask *layer_mask, GimpLayer *layer) { g_return_if_fail (GIMP_IS_LAYER_MASK (layer_mask)); - g_return_if_fail (! layer || GIMP_IS_LAYER (layer)); + g_return_if_fail (layer == NULL || GIMP_IS_LAYER (layer)); layer_mask->layer = layer; + + if (layer) + { + GIMP_ITEM (layer_mask)->offset_x = GIMP_ITEM (layer)->offset_x; + GIMP_ITEM (layer_mask)->offset_y = GIMP_ITEM (layer)->offset_y; + } } GimpLayer * diff --git a/app/paint/gimpink.c b/app/paint/gimpink.c index ed71b210ca..04a597d176 100644 --- a/app/paint/gimpink.c +++ b/app/paint/gimpink.c @@ -704,6 +704,8 @@ static void ink_finish (GimpInkTool *ink_tool, GimpDrawable *drawable) { + GimpImage *gimage; + gimp_drawable_push_undo (drawable, _("Ink"), ink_tool->x1, ink_tool->y1, ink_tool->x2, ink_tool->y2, @@ -712,10 +714,13 @@ ink_finish (GimpInkTool *ink_tool, tile_manager_unref (undo_tiles); undo_tiles = NULL; - /* invalidate the drawable--have to do it here, because + gimage = gimp_item_get_image (GIMP_ITEM (drawable)); + + /* invalidate the previews -- have to do it here, because * it is not done during the actual painting. */ gimp_viewable_invalidate_preview (GIMP_VIEWABLE (drawable)); + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); } static void @@ -1029,9 +1034,9 @@ ink_paste (GimpInkTool *ink_tool, ink_tool->x2 = MAX (ink_tool->x2, (canvas_buf->x + canvas_buf->width)); ink_tool->y2 = MAX (ink_tool->y2, (canvas_buf->y + canvas_buf->height)); - /* Update the gimage--it is important to call gimp_image_update - * instead of drawable_update because we don't want the drawable - * preview to be constantly invalidated + /* Update the gimage -- it is important to call gimp_image_update() + * instead of gimp_drawable_update() because we don't want the + * drawable and image previews to be constantly invalidated */ gimp_item_offsets (GIMP_ITEM (drawable), &offx, &offy); gimp_image_update (gimage, diff --git a/app/paint/gimppaintcore.c b/app/paint/gimppaintcore.c index a66b7512be..18aea012aa 100644 --- a/app/paint/gimppaintcore.c +++ b/app/paint/gimppaintcore.c @@ -489,10 +489,11 @@ gimp_paint_core_finish (GimpPaintCore *core, gimp_image_undo_group_end (gimage); - /* invalidate the drawable--have to do it here, because + /* invalidate the previews -- have to do it here, because * it is not done during the actual painting. */ gimp_viewable_invalidate_preview (GIMP_VIEWABLE (drawable)); + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); } void @@ -1622,9 +1623,9 @@ gimp_paint_core_paste (GimpPaintCore *core, core->x2 = MAX (core->x2, core->canvas_buf->x + core->canvas_buf->width); core->y2 = MAX (core->y2, core->canvas_buf->y + core->canvas_buf->height); - /* Update the gimage -- It is important to call gimp_image_update - * instead of drawable_update because we don't want the drawable - * preview to be constantly invalidated + /* Update the gimage -- It is important to call gimp_image_update() + * instead of gimp_drawable_update() because we don't want the + * drawable and image previews to be constantly invalidated */ gimp_item_offsets (GIMP_ITEM (drawable), &offx, &offy); gimp_image_update (gimage, @@ -1734,9 +1735,9 @@ gimp_paint_core_replace (GimpPaintCore *core, core->x2 = MAX (core->x2, core->canvas_buf->x + core->canvas_buf->width) ; core->y2 = MAX (core->y2, core->canvas_buf->y + core->canvas_buf->height) ; - /* Update the gimage -- It is important to call gimp_image_update - * instead of drawable_update because we don't want the drawable - * preview to be constantly invalidated + /* Update the gimage -- It is important to call gimp_image_update() + * instead of gimp_drawable_update() because we don't want the + * drawable and image previews to be constantly invalidated */ gimp_item_offsets (GIMP_ITEM (drawable), &offx, &offy); gimp_image_update (gimage, diff --git a/app/text/gimptextlayer.c b/app/text/gimptextlayer.c index 658e8ac8b4..0e5d930f1b 100644 --- a/app/text/gimptextlayer.c +++ b/app/text/gimptextlayer.c @@ -443,6 +443,11 @@ gimp_text_layer_render_now (GimpTextLayer *layer) drawable->tiles = tile_manager_new (width, height, drawable->bytes); + gimp_drawable_update (drawable, + 0, 0, + gimp_item_width (item), + gimp_item_height (item)); + gimp_viewable_size_changed (GIMP_VIEWABLE (layer)); } } @@ -508,5 +513,7 @@ gimp_text_layer_render_layout (GimpTextLayer *layer, tile_manager_unref (mask); - gimp_drawable_update (drawable, 0, 0, bitmap.width, bitmap.rows); + /* no need to gimp_drawable_update() since gimp_drawable_fill() + * did that for us. + */ } diff --git a/app/tools/gimpinktool.c b/app/tools/gimpinktool.c index ed71b210ca..04a597d176 100644 --- a/app/tools/gimpinktool.c +++ b/app/tools/gimpinktool.c @@ -704,6 +704,8 @@ static void ink_finish (GimpInkTool *ink_tool, GimpDrawable *drawable) { + GimpImage *gimage; + gimp_drawable_push_undo (drawable, _("Ink"), ink_tool->x1, ink_tool->y1, ink_tool->x2, ink_tool->y2, @@ -712,10 +714,13 @@ ink_finish (GimpInkTool *ink_tool, tile_manager_unref (undo_tiles); undo_tiles = NULL; - /* invalidate the drawable--have to do it here, because + gimage = gimp_item_get_image (GIMP_ITEM (drawable)); + + /* invalidate the previews -- have to do it here, because * it is not done during the actual painting. */ gimp_viewable_invalidate_preview (GIMP_VIEWABLE (drawable)); + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage)); } static void @@ -1029,9 +1034,9 @@ ink_paste (GimpInkTool *ink_tool, ink_tool->x2 = MAX (ink_tool->x2, (canvas_buf->x + canvas_buf->width)); ink_tool->y2 = MAX (ink_tool->y2, (canvas_buf->y + canvas_buf->height)); - /* Update the gimage--it is important to call gimp_image_update - * instead of drawable_update because we don't want the drawable - * preview to be constantly invalidated + /* Update the gimage -- it is important to call gimp_image_update() + * instead of gimp_drawable_update() because we don't want the + * drawable and image previews to be constantly invalidated */ gimp_item_offsets (GIMP_ITEM (drawable), &offx, &offy); gimp_image_update (gimage,