From 785eef1af916da87d6c16733023d92971163466a Mon Sep 17 00:00:00 2001 From: Michael Natterer Date: Mon, 26 Jan 2009 22:05:07 +0000 Subject: [PATCH] The GEGL projection does floating selections now: 2009-01-26 Michael Natterer The GEGL projection does floating selections now: * app/core/gimpdrawable.[ch] (struct GimpDrawable): add a couple of GeglNodes which are used to create a sub-graph for this drawable's floating selection. (gimp_drawable_detach_floating_sel) (gimp_drawable_attach_floating_sel): new functions to call whenever a floating selection gets attached or detached. Change the role of the drawable's "source_node": it's no longer a direct tile source but an arbitrary graph. Add new internal function gimp_drawable_sync_source_node() which creates a sub-graph for the floating selection within the source node, and uses the new "tile_source_node" directly otherwise. Connect to "notify" of the floating selection and reconfigure its sub-graph when its properties change. This is also one more refactoring in the direction of layer trees. * app/core/gimpfloatingselundo.c * app/core/gimpimage.c * app/core/gimplayer-floating-sel.c: call the new attach/detach API whenever a floating selection is attached or detached from a drawable. This will need more refactoring i guess... svn path=/trunk/; revision=27961 --- ChangeLog | 27 ++++ app/core/gimpdrawable.c | 206 ++++++++++++++++++++++++++++-- app/core/gimpdrawable.h | 9 ++ app/core/gimpfloatingselundo.c | 7 +- app/core/gimpimage.c | 15 +-- app/core/gimplayer-floating-sel.c | 3 +- 6 files changed, 242 insertions(+), 25 deletions(-) diff --git a/ChangeLog b/ChangeLog index a4ab0869f3..68093e1437 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,30 @@ +2009-01-26 Michael Natterer + + The GEGL projection does floating selections now: + + * app/core/gimpdrawable.[ch] (struct GimpDrawable): add a couple + of GeglNodes which are used to create a sub-graph for this + drawable's floating selection. + + (gimp_drawable_detach_floating_sel) + (gimp_drawable_attach_floating_sel): new functions to call + whenever a floating selection gets attached or detached. + + Change the role of the drawable's "source_node": it's no longer a + direct tile source but an arbitrary graph. Add new internal + function gimp_drawable_sync_source_node() which creates a + sub-graph for the floating selection within the source node, and + uses the new "tile_source_node" directly otherwise. Connect to + "notify" of the floating selection and reconfigure its sub-graph + when its properties change. This is also one more refactoring in + the direction of layer trees. + + * app/core/gimpfloatingselundo.c + * app/core/gimpimage.c + * app/core/gimplayer-floating-sel.c: call the new attach/detach + API whenever a floating selection is attached or detached from a + drawable. This will need more refactoring i guess... + 2009-01-26 Michael Natterer * app/core/gimpdrawable.c (gimp_drawable_visibility_changed): diff --git a/app/core/gimpdrawable.c b/app/core/gimpdrawable.c index 36ac218b84..6a2f9bb914 100644 --- a/app/core/gimpdrawable.c +++ b/app/core/gimpdrawable.c @@ -150,6 +150,12 @@ static void gimp_drawable_real_swap_pixels (GimpDrawable *drawable, gint width, gint height); +static void gimp_drawable_sync_source_node (GimpDrawable *drawable, + gboolean detach_fs); +static void gimp_drawable_fs_notify (GimpLayer *fs, + const GParamSpec *pspec, + GimpDrawable *drawable); + G_DEFINE_TYPE_WITH_CODE (GimpDrawable, gimp_drawable, GIMP_TYPE_ITEM, G_IMPLEMENT_INTERFACE (GIMP_TYPE_PICKABLE, @@ -250,6 +256,9 @@ gimp_drawable_finalize (GObject *object) { GimpDrawable *drawable = GIMP_DRAWABLE (object); + if (drawable->fs_opacity_node) + gimp_drawable_sync_source_node (drawable, TRUE); + if (drawable->tiles) { tile_manager_unref (drawable->tiles); @@ -672,12 +681,12 @@ gimp_drawable_real_update (GimpDrawable *drawable, gint width, gint height) { - if (drawable->source_node) + if (drawable->tile_source_node) { GObject *operation; GeglRectangle rect; - g_object_get (drawable->source_node, + g_object_get (drawable->tile_source_node, "gegl-operation", &operation, NULL); @@ -757,8 +766,8 @@ gimp_drawable_real_set_tiles (GimpDrawable *drawable, if (old_has_alpha != gimp_drawable_has_alpha (drawable)) gimp_drawable_alpha_changed (drawable); - if (drawable->source_node) - gegl_node_set (drawable->source_node, + if (drawable->tile_source_node) + gegl_node_set (drawable->tile_source_node, "tile-manager", drawable->tiles, NULL); } @@ -888,6 +897,133 @@ gimp_drawable_real_swap_pixels (GimpDrawable *drawable, gimp_drawable_update (drawable, x, y, width, height); } +static void +gimp_drawable_sync_source_node (GimpDrawable *drawable, + gboolean detach_fs) +{ + GimpImage *image = gimp_item_get_image (GIMP_ITEM (drawable)); + GimpLayer *fs = gimp_image_get_floating_selection (image); + GeglNode *output; + + if (! drawable->source_node) + return; + + output = gegl_node_get_output_proxy (drawable->source_node, "output"); + + if (gimp_drawable_has_floating_sel (drawable) && ! detach_fs) + { + gint off_x, off_y; + gint fs_off_x, fs_off_y; + + if (! drawable->fs_opacity_node) + { + GeglNode *fs_source; + + fs_source = gimp_drawable_get_source_node (GIMP_DRAWABLE (fs)); + gegl_node_add_child (drawable->source_node, fs_source); + + drawable->fs_opacity_node = + gegl_node_new_child (drawable->source_node, + "operation", "gegl:opacity", + NULL); + + gegl_node_connect_to (fs_source, "output", + drawable->fs_opacity_node, "input"); + + drawable->fs_offset_node = + gegl_node_new_child (drawable->source_node, + "operation", "gegl:translate", + NULL); + + gegl_node_connect_to (drawable->fs_opacity_node, "output", + drawable->fs_offset_node, "input"); + + drawable->fs_mode_node = + gegl_node_new_child (drawable->source_node, + "operation", "gimp:point-layer-mode", + NULL); + + gegl_node_connect_to (drawable->tile_source_node, "output", + drawable->fs_mode_node, "input"); + gegl_node_connect_to (drawable->fs_offset_node, "output", + drawable->fs_mode_node, "aux"); + + gegl_node_connect_to (drawable->fs_mode_node, "output", + output, "input"); + + g_signal_connect (fs, "notify", + G_CALLBACK (gimp_drawable_fs_notify), + drawable); + } + + gegl_node_set (drawable->fs_opacity_node, + "value", gimp_layer_get_opacity (fs), + NULL); + + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + gimp_item_get_offset (GIMP_ITEM (fs), &fs_off_x, &fs_off_y); + + gegl_node_set (drawable->fs_offset_node, + "x", (gdouble) (fs_off_x - off_x), + "y", (gdouble) (fs_off_y - off_y), + NULL); + + gegl_node_set (drawable->fs_mode_node, + "blend-mode", gimp_layer_get_mode (fs), + NULL); + } + else + { + if (drawable->fs_opacity_node) + { + GeglNode *fs_source; + + gegl_node_disconnect (drawable->fs_opacity_node, "input"); + gegl_node_disconnect (drawable->fs_offset_node, "input"); + gegl_node_disconnect (drawable->fs_mode_node, "input"); + gegl_node_disconnect (drawable->fs_mode_node, "aux"); + + fs_source = gimp_drawable_get_source_node (GIMP_DRAWABLE (fs)); + gegl_node_remove_child (drawable->source_node, + fs_source); + + gegl_node_remove_child (drawable->source_node, + drawable->fs_opacity_node); + drawable->fs_opacity_node = NULL; + + gegl_node_remove_child (drawable->source_node, + drawable->fs_offset_node); + drawable->fs_offset_node = NULL; + + gegl_node_remove_child (drawable->source_node, + drawable->fs_mode_node); + drawable->fs_mode_node = NULL; + + g_signal_handlers_disconnect_by_func (fs, + gimp_drawable_fs_notify, + drawable); + } + + gegl_node_connect_to (drawable->tile_source_node, "output", + output, "input"); + } +} + +static void +gimp_drawable_fs_notify (GimpLayer *fs, + const GParamSpec *pspec, + GimpDrawable *drawable) +{ + if (! strcmp (pspec->name, "offset-x") || + ! strcmp (pspec->name, "offset-y") || + ! strcmp (pspec->name, "visible") || + ! strcmp (pspec->name, "mode") || + ! strcmp (pspec->name, "opacity")) + { + gimp_drawable_sync_source_node (drawable, FALSE); + } +} + /* public functions */ @@ -932,8 +1068,8 @@ gimp_drawable_configure (GimpDrawable *drawable, drawable->preview_cache = NULL; drawable->preview_valid = FALSE; - if (drawable->source_node) - gegl_node_set (drawable->source_node, + if (drawable->tile_source_node) + gegl_node_set (drawable->tile_source_node, "tile-manager", drawable->tiles, NULL); } @@ -1239,13 +1375,16 @@ gimp_drawable_get_source_node (GimpDrawable *drawable) if (drawable->source_node) return drawable->source_node; - drawable->source_node = g_object_new (GEGL_TYPE_NODE, - "operation", "gimp:tilemanager-source", - NULL); - gegl_node_set (drawable->source_node, - "tile-manager", drawable->tiles, - "linear", TRUE, - NULL); + drawable->source_node = gegl_node_new (); + + drawable->tile_source_node = + gegl_node_new_child (drawable->source_node, + "operation", "gimp:tilemanager-source", + "tile-manager", drawable->tiles, + "linear", TRUE, + NULL); + + gimp_drawable_sync_source_node (drawable, FALSE); return drawable->source_node; } @@ -1654,3 +1793,44 @@ gimp_drawable_get_colormap (const GimpDrawable *drawable) return image ? gimp_image_get_colormap (image) : NULL; } + +void +gimp_drawable_attach_floating_sel (GimpDrawable *drawable, + GimpLayer *floating_sel) +{ + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (GIMP_IS_LAYER (floating_sel)); + + g_printerr ("%s\n", G_STRFUNC); + + gimp_drawable_sync_source_node (drawable, FALSE); + +#ifdef __GNUC__ +#warning FIXME: remove this hack when the floating sel is no layer any longer +#endif + g_signal_emit_by_name (floating_sel, "visibility-changed"); +} + +void +gimp_drawable_detach_floating_sel (GimpDrawable *drawable, + GimpLayer *floating_sel) +{ + g_return_if_fail (GIMP_IS_DRAWABLE (drawable)); + g_return_if_fail (GIMP_IS_LAYER (floating_sel)); + + g_printerr ("%s\n", G_STRFUNC); + + gimp_drawable_sync_source_node (drawable, TRUE); + +#ifdef __GNUC__ +#warning FIXME: remove this hack when the floating sel is no layer any longer +#endif + g_signal_emit_by_name (floating_sel, "visibility-changed"); + + /* Invalidate the preview of the obscured drawable. We do this here + * because it will not be done until the floating selection is removed, + * at which point the obscured drawable's preview will not be declared + * invalid. + */ + gimp_viewable_invalidate_preview (GIMP_VIEWABLE (floating_sel)); +} diff --git a/app/core/gimpdrawable.h b/app/core/gimpdrawable.h index 38d64eda59..6589c6bc88 100644 --- a/app/core/gimpdrawable.h +++ b/app/core/gimpdrawable.h @@ -40,6 +40,10 @@ struct _GimpDrawable TileManager *shadow; /* shadow buffer tiles */ GeglNode *source_node; + GeglNode *tile_source_node; + GeglNode *fs_opacity_node; + GeglNode *fs_offset_node; + GeglNode *fs_mode_node; GeglNode *mode_node; @@ -248,5 +252,10 @@ gboolean gimp_drawable_has_floating_sel (const GimpDrawable *drawable); const guchar * gimp_drawable_get_colormap (const GimpDrawable *drawable); +void gimp_drawable_attach_floating_sel (GimpDrawable *drawable, + GimpLayer *floating_sel); +void gimp_drawable_detach_floating_sel (GimpDrawable *drawable, + GimpLayer *floating_sel); + #endif /* __GIMP_DRAWABLE_H__ */ diff --git a/app/core/gimpfloatingselundo.c b/app/core/gimpfloatingselundo.c index 8cb83f2bf6..0083b4231f 100644 --- a/app/core/gimpfloatingselundo.c +++ b/app/core/gimpfloatingselundo.c @@ -114,11 +114,14 @@ gimp_floating_sel_undo_pop (GimpUndo *undo, /* clear the selection */ gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (floating_layer)); + + gimp_drawable_attach_floating_sel (floating_layer->fs.drawable, + floating_layer); } else { - /* Update the preview for the underlying drawable */ - gimp_viewable_invalidate_preview (GIMP_VIEWABLE (floating_layer)); + gimp_drawable_detach_floating_sel (floating_layer->fs.drawable, + floating_layer); /* clear the selection */ gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (floating_layer)); diff --git a/app/core/gimpimage.c b/app/core/gimpimage.c index f3fa3cd497..6e6b717802 100644 --- a/app/core/gimpimage.c +++ b/app/core/gimpimage.c @@ -2969,7 +2969,11 @@ gimp_image_add_layer (GimpImage *image, /* If the layer is a floating selection, set the fs pointer */ if (gimp_layer_is_floating_sel (layer)) - gimp_image_set_floating_selection (image, layer); + { + gimp_image_set_floating_selection (image, layer); + + gimp_drawable_attach_floating_sel (layer->fs.drawable, layer); + } if (old_has_alpha != gimp_image_has_alpha (image)) image->flush_accum.alpha_changed = TRUE; @@ -3019,16 +3023,11 @@ gimp_image_remove_layer (GimpImage *image, old_has_alpha = gimp_image_has_alpha (image); - if (gimp_image_get_floating_selection (image) == layer) + if (gimp_layer_is_floating_sel (layer)) { undo_desc = _("Remove Floating Selection"); - /* Invalidate the preview of the obscured drawable. We do this here - * because it will not be done until the floating selection is removed, - * at which point the obscured drawable's preview will not be declared - * invalid. - */ - gimp_viewable_invalidate_preview (GIMP_VIEWABLE (layer)); + gimp_drawable_detach_floating_sel (layer->fs.drawable, layer); } else { diff --git a/app/core/gimplayer-floating-sel.c b/app/core/gimplayer-floating-sel.c index 69fad956fd..41f5702ecd 100644 --- a/app/core/gimplayer-floating-sel.c +++ b/app/core/gimplayer-floating-sel.c @@ -139,8 +139,7 @@ floating_sel_to_layer (GimpLayer *layer, gimp_image_undo_push_fs_to_layer (image, NULL, layer); - /* clear the selection */ - gimp_drawable_invalidate_boundary (GIMP_DRAWABLE (layer)); + gimp_drawable_detach_floating_sel (layer->fs.drawable, layer); /* Set pointers */ gimp_layer_set_floating_sel_drawable (layer, NULL);