core: Initial non-destructive editing implementation

This patch implements an initial form of
non-destructive editing. Filters now stay active
instead of being immediately merged down.
A new column is added to the layer tree view, which
can be clicked to show a pop-over menu.
Filters can currently be hidden/shown, edited, reordered,
deleted, and merged down from this pop-over menu.

Currently, this works on layers and layer selections only.
Plenty of room for improvement!
This commit is contained in:
Alx Sa 2023-06-19 14:54:21 +00:00 committed by Jehan
parent ec90cd1d9a
commit e678a20951
50 changed files with 3071 additions and 335 deletions

View file

@ -51,12 +51,6 @@ static gchar * filters_parse_operation (Gimp *gimp,
const gchar *icon_name,
GimpObject **settings);
static void filters_run_procedure (Gimp *gimp,
GimpDisplay *display,
GimpProcedure *procedure,
GimpRunMode run_mode);
/* public functions */
void
@ -86,6 +80,7 @@ filters_apply_cmd_callback (GimpAction *action,
&settings);
procedure = gimp_gegl_procedure_new (image->gimp,
NULL,
GIMP_RUN_NONINTERACTIVE, settings,
operation,
gimp_action_get_name (action),
@ -130,6 +125,7 @@ filters_apply_interactive_cmd_callback (GimpAction *action,
}
procedure = gimp_gegl_procedure_new (image->gimp,
NULL,
GIMP_RUN_INTERACTIVE, NULL,
g_variant_get_string (value, NULL),
gimp_action_get_name (action),
@ -240,7 +236,7 @@ filters_parse_operation (Gimp *gimp,
return g_strdup (operation_str);
}
static void
void
filters_run_procedure (Gimp *gimp,
GimpDisplay *display,
GimpProcedure *procedure,

View file

@ -33,5 +33,10 @@ void filters_history_cmd_callback (GimpAction *action,
GVariant *value,
gpointer data);
void filters_run_procedure (Gimp *gimp,
GimpDisplay *display,
GimpProcedure *procedure,
GimpRunMode run_mode);
#endif /* __FILTERS_COMMANDS_H__ */

View file

@ -41,6 +41,7 @@
#include "core/gimpcontext.h"
#include "core/gimpdisplay.h"
#include "core/gimpdrawable-operation.h"
#include "core/gimpdrawablefilter.h"
#include "core/gimpimage.h"
#include "core/gimplayermask.h"
#include "core/gimpparamspecs.h"
@ -366,6 +367,7 @@ gimp_gegl_procedure_execute_async (GimpProcedure *procedure,
if (! strcmp (tool_name, "gimp-operation-tool"))
{
gimp_operation_tool_set_operation (GIMP_OPERATION_TOOL (active_tool),
gegl_procedure->filter,
gegl_procedure->operation,
gimp_procedure_get_label (procedure),
gimp_procedure_get_label (procedure),
@ -376,6 +378,25 @@ gimp_gegl_procedure_execute_async (GimpProcedure *procedure,
tool_manager_initialize_active (gimp, display);
/* For GIMP-specific GEGL operations, we need to copy over the
* config object stored in the GeglNode */
if (gegl_procedure->filter &&
(! strcmp (gegl_procedure->operation, "gimp:brightness-contrast") ||
! strcmp (gegl_procedure->operation, "gimp:curves") ||
! strcmp (gegl_procedure->operation, "gimp:levels") ||
! strcmp (gegl_procedure->operation, "gimp:threshold")))
{
GeglNode *node;
GIMP_FILTER_TOOL (active_tool)->existing_filter = gegl_procedure->filter;
gimp_filter_set_active (GIMP_FILTER (gegl_procedure->filter), FALSE);
node = gimp_drawable_filter_get_operation (gegl_procedure->filter);
gegl_node_get (node,
"config", &settings,
NULL);
}
if (settings)
gimp_filter_tool_set_config (GIMP_FILTER_TOOL (active_tool),
GIMP_CONFIG (settings));
@ -386,15 +407,16 @@ gimp_gegl_procedure_execute_async (GimpProcedure *procedure,
/* public functions */
GimpProcedure *
gimp_gegl_procedure_new (Gimp *gimp,
GimpRunMode default_run_mode,
GimpObject *default_settings,
const gchar *operation,
const gchar *name,
const gchar *menu_label,
const gchar *tooltip,
const gchar *icon_name,
const gchar *help_id)
gimp_gegl_procedure_new (Gimp *gimp,
GimpDrawableFilter *filter,
GimpRunMode default_run_mode,
GimpObject *default_settings,
const gchar *operation,
const gchar *name,
const gchar *menu_label,
const gchar *tooltip,
const gchar *icon_name,
const gchar *help_id)
{
GimpProcedure *procedure;
GimpGeglProcedure *gegl_procedure;
@ -412,6 +434,7 @@ gimp_gegl_procedure_new (Gimp *gimp,
gegl_procedure = GIMP_GEGL_PROCEDURE (procedure);
gegl_procedure->filter = filter;
gegl_procedure->operation = g_strdup (operation);
gegl_procedure->default_run_mode = default_run_mode;
gegl_procedure->menu_label = g_strdup (menu_label);

View file

@ -40,12 +40,13 @@ struct _GimpGeglProcedure
{
GimpProcedure parent_instance;
gchar *operation;
GimpDrawableFilter *filter;
gchar *operation;
GimpRunMode default_run_mode;
GimpObject *default_settings;
GimpRunMode default_run_mode;
GimpObject *default_settings;
gchar *menu_label;
gchar *menu_label;
};
struct _GimpGeglProcedureClass
@ -56,15 +57,15 @@ struct _GimpGeglProcedureClass
GType gimp_gegl_procedure_get_type (void) G_GNUC_CONST;
GimpProcedure * gimp_gegl_procedure_new (Gimp *gimp,
GimpRunMode default_run_mode,
GimpObject *default_settings,
const gchar *operation,
const gchar *name,
const gchar *menu_label,
const gchar *tooltip,
const gchar *icon_name,
const gchar *help_id);
GimpProcedure * gimp_gegl_procedure_new (Gimp *gimp,
GimpDrawableFilter *filter,
GimpRunMode default_run_mode,
GimpObject *default_settings,
const gchar *operation,
const gchar *name,
const gchar *menu_label,
const gchar *tooltip,
const gchar *icon_name,
const gchar *help_id);
#endif /* __GIMP_GEGL_PROCEDURE_H__ */

View file

@ -39,6 +39,8 @@
#include "core/gimpcontainer.h"
#include "core/gimpcontext.h"
#include "core/gimpdrawable-fill.h"
#include "core/gimpdrawable-filters.h"
#include "core/gimpdrawablefilter.h"
#include "core/gimpgrouplayer.h"
#include "core/gimpimage.h"
#include "core/gimpimage-merge.h"
@ -48,6 +50,7 @@
#include "core/gimplayerpropundo.h"
#include "core/gimplayer-floating-selection.h"
#include "core/gimplayer-new.h"
#include "core/gimplist.h"
#include "core/gimppickable.h"
#include "core/gimppickable-auto-shrink.h"
#include "core/gimptoolinfo.h"
@ -811,6 +814,35 @@ layers_duplicate_cmd_callback (GimpAction *action,
gimp_item_get_index (iter->data),
TRUE);
new_layers = g_list_prepend (new_layers, new_layer);
/* Import any attached layer effects */
if (gimp_drawable_has_filters (GIMP_DRAWABLE (iter->data)))
{
GList *filter_list;
GimpContainer *filters;
filters = gimp_drawable_get_filters (GIMP_DRAWABLE (iter->data));
for (filter_list = GIMP_LIST (filters)->queue->tail; filter_list;
filter_list = g_list_previous (filter_list))
{
if (GIMP_IS_DRAWABLE_FILTER (filter_list->data))
{
GimpDrawableFilter *old_filter = filter_list->data;
GimpDrawableFilter *filter;
filter =
gimp_drawable_filter_duplicate (GIMP_DRAWABLE (new_layer),
old_filter);
gimp_drawable_filter_apply (filter, NULL);
gimp_drawable_filter_commit (filter, TRUE, NULL, FALSE);
gimp_drawable_filter_layer_mask_freeze (filter);
g_object_unref (filter);
}
}
}
}
gimp_image_set_selected_layers (image, new_layers);

View file

@ -1284,6 +1284,9 @@ gimp_undo_type_get_type (void)
{ GIMP_UNDO_FOREGROUND_SELECT, "GIMP_UNDO_FOREGROUND_SELECT", "foreground-select" },
{ GIMP_UNDO_PARASITE_ATTACH, "GIMP_UNDO_PARASITE_ATTACH", "parasite-attach" },
{ GIMP_UNDO_PARASITE_REMOVE, "GIMP_UNDO_PARASITE_REMOVE", "parasite-remove" },
{ GIMP_UNDO_FILTER_ADD, "GIMP_UNDO_FILTER_ADD", "filter-add" },
{ GIMP_UNDO_FILTER_REMOVE, "GIMP_UNDO_FILTER_REMOVE", "filter-remove" },
{ GIMP_UNDO_FILTER_REORDER, "GIMP_UNDO_FILTER_REORDER", "filter-reorder" },
{ GIMP_UNDO_CANT, "GIMP_UNDO_CANT", "cant" },
{ 0, NULL, NULL }
};
@ -1393,6 +1396,9 @@ gimp_undo_type_get_type (void)
{ GIMP_UNDO_FOREGROUND_SELECT, NC_("undo-type", "Select foreground"), NULL },
{ GIMP_UNDO_PARASITE_ATTACH, NC_("undo-type", "Attach parasite"), NULL },
{ GIMP_UNDO_PARASITE_REMOVE, NC_("undo-type", "Remove parasite"), NULL },
{ GIMP_UNDO_FILTER_ADD, NC_("undo-type", "Add effect"), NULL },
{ GIMP_UNDO_FILTER_REMOVE, NC_("undo-type", "Remove effect"), NULL },
{ GIMP_UNDO_FILTER_REORDER, NC_("undo-type", "Reorder effect"), NULL },
{ GIMP_UNDO_CANT, NC_("undo-type", "Not undoable"), NULL },
{ 0, NULL, NULL }
};

View file

@ -605,7 +605,7 @@ typedef enum /*< pdb-skip >*/
GIMP_UNDO_ITEM_COLOR_TAG, /*< desc="Item color tag" >*/
GIMP_UNDO_ITEM_LOCK_CONTENT, /*< desc="Lock/Unlock content" >*/
GIMP_UNDO_ITEM_LOCK_POSITION, /*< desc="Lock/Unlock position" >*/
GIMP_UNDO_ITEM_LOCK_VISIBILITY, /*< desc="Lock/Unlock visibility" >*/
GIMP_UNDO_ITEM_LOCK_VISIBILITY, /*< desc="Lock/Unlock visibility" >*/
GIMP_UNDO_LAYER_ADD, /*< desc="New layer" >*/
GIMP_UNDO_LAYER_REMOVE, /*< desc="Delete layer" >*/
GIMP_UNDO_LAYER_MODE, /*< desc="Set layer mode" >*/
@ -638,6 +638,9 @@ typedef enum /*< pdb-skip >*/
GIMP_UNDO_FOREGROUND_SELECT, /*< desc="Select foreground" >*/
GIMP_UNDO_PARASITE_ATTACH, /*< desc="Attach parasite" >*/
GIMP_UNDO_PARASITE_REMOVE, /*< desc="Remove parasite" >*/
GIMP_UNDO_FILTER_ADD, /*< desc="Add effect" >*/
GIMP_UNDO_FILTER_REMOVE, /*< desc="Remove effect" >*/
GIMP_UNDO_FILTER_REORDER, /*< desc="Reorder effect" >*/
GIMP_UNDO_CANT /*< desc="Not undoable" >*/
} GimpUndoType;

View file

@ -224,7 +224,7 @@ gimp_drawable_edit_fill (GimpDrawable *drawable,
composite_mode);
gimp_drawable_filter_apply (filter, NULL);
gimp_drawable_filter_commit (filter, NULL, FALSE);
gimp_drawable_filter_commit (filter, FALSE, NULL, FALSE);
g_object_unref (filter);
g_object_unref (operation);

View file

@ -34,14 +34,18 @@
#include "gimpdrawable.h"
#include "gimpdrawable-filters.h"
#include "gimpdrawable-private.h"
#include "gimpdrawablefilter.h"
#include "gimpdrawablefilterundo.h"
#include "gimpfilter.h"
#include "gimpfilterstack.h"
#include "gimpimage.h"
#include "gimpimage-undo.h"
#include "core/gimpimage-undo-push.h"
#include "gimplayer.h"
#include "gimpprogress.h"
#include "gimpprojection.h"
#include "gimp-intl.h"
GimpContainer *
gimp_drawable_get_filters (GimpDrawable *drawable)
@ -81,6 +85,8 @@ gimp_drawable_add_filter (GimpDrawable *drawable,
gimp_container_add (drawable->private->filter_stack,
GIMP_OBJECT (filter));
gimp_drawable_filters_changed (drawable);
}
void
@ -93,17 +99,120 @@ gimp_drawable_remove_filter (GimpDrawable *drawable,
gimp_container_remove (drawable->private->filter_stack,
GIMP_OBJECT (filter));
gimp_drawable_filters_changed (drawable);
}
void
gimp_drawable_clear_filters (GimpDrawable *drawable)
{
g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
gimp_container_clear (drawable->private->filter_stack);
gimp_drawable_filters_changed (drawable);
}
void
gimp_drawable_remove_last_filter (GimpDrawable *drawable)
{
GimpDrawableFilter *filter = NULL;
if (! GIMP_IS_DRAWABLE (drawable))
return;
if (gimp_drawable_has_filters (drawable))
{
filter = GIMP_LIST (drawable->private->filter_stack)->queue->head->data;
if (GIMP_IS_DRAWABLE_FILTER (filter))
{
gimp_drawable_remove_filter (drawable,
GIMP_FILTER (filter));
gimp_drawable_filters_changed (drawable);
}
}
}
void
gimp_drawable_merge_filters (GimpDrawable *drawable)
{
GList *list;
GeglBuffer *buffer = NULL;
GeglBuffer *new_buffer = NULL;
if (! GIMP_IS_DRAWABLE (drawable))
return;
gimp_image_undo_group_start (gimp_item_get_image (GIMP_ITEM (drawable)),
GIMP_UNDO_GROUP_DRAWABLE,
_("Rasterize filters"));
/* Save buffer with effects for later use */
buffer = gimp_drawable_get_buffer_with_effects (drawable);
if (buffer)
{
gint64 width = (gint64) gegl_buffer_get_width (buffer);
gint64 height = (gint64) gegl_buffer_get_height (buffer);
new_buffer = gegl_buffer_new (GEGL_RECTANGLE (0, 0, width, height),
gegl_buffer_get_format (buffer));
gimp_gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE,
new_buffer, NULL);
g_clear_object (&buffer);
}
for (list = GIMP_LIST (drawable->private->filter_stack)->queue->tail;
list;
list = g_list_previous (list))
{
if (GIMP_IS_DRAWABLE_FILTER (list->data))
{
GimpDrawableFilter *filter = list->data;
const Babl *format;
format = gimp_drawable_get_format (drawable);
gimp_image_undo_push_filter_remove (gimp_item_get_image (GIMP_ITEM (drawable)),
_("Merge filter"),
drawable, filter);
gimp_drawable_merge_filter (drawable,
GIMP_FILTER (filter),
NULL,
_("Rasterize filters"),
format,
TRUE, TRUE, FALSE);
}
}
/* Update with correct buffer */
if (new_buffer)
{
gimp_drawable_set_buffer (drawable, TRUE, NULL, new_buffer);
g_clear_object (&new_buffer);
}
gimp_image_undo_group_end (gimp_item_get_image (GIMP_ITEM (drawable)));
gimp_drawable_filters_changed (drawable);
}
gboolean
gimp_drawable_has_filter (GimpDrawable *drawable,
GimpFilter *filter)
{
gboolean filter_exists = FALSE;
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), FALSE);
g_return_val_if_fail (GIMP_IS_FILTER (filter), FALSE);
return gimp_container_have (drawable->private->filter_stack,
GIMP_OBJECT (filter));
filter_exists = gimp_container_have (drawable->private->filter_stack,
GIMP_OBJECT (filter));
return filter_exists;
}
gboolean
@ -339,5 +448,8 @@ gimp_drawable_merge_filter (GimpDrawable *drawable,
rect.width, rect.height);
}
if (success)
gimp_drawable_filters_changed (drawable);
return success;
}

View file

@ -29,6 +29,10 @@ void gimp_drawable_add_filter (GimpDrawable *drawable,
GimpFilter *filter);
void gimp_drawable_remove_filter (GimpDrawable *drawable,
GimpFilter *filter);
void gimp_drawable_clear_filters (GimpDrawable *drawable);
void gimp_drawable_remove_last_filter
(GimpDrawable *drawable);
void gimp_drawable_merge_filters (GimpDrawable *drawable);
gboolean gimp_drawable_has_filter (GimpDrawable *drawable,
GimpFilter *filter);

View file

@ -89,7 +89,7 @@ gimp_drawable_apply_operation_with_config (GimpDrawable *drawable,
}
gimp_drawable_filter_apply (filter, NULL);
gimp_drawable_filter_commit (filter, progress, TRUE);
gimp_drawable_filter_commit (filter, FALSE, progress, TRUE);
g_object_unref (filter);

View file

@ -31,6 +31,7 @@
#include "gegl/gimp-gegl-apply-operation.h"
#include "gegl/gimp-gegl-loops.h"
#include "gegl/gimp-gegl-utils.h"
#include "gegl/gimptilehandlervalidate.h"
#include "gimp-memsize.h"
#include "gimp-utils.h"
@ -38,15 +39,18 @@
#include "gimpcontext.h"
#include "gimpdrawable-combine.h"
#include "gimpdrawable-fill.h"
#include "gimpdrawable-filters.h"
#include "gimpdrawable-floating-selection.h"
#include "gimpdrawable-preview.h"
#include "gimpdrawable-private.h"
#include "gimpdrawable-shadow.h"
#include "gimpdrawable-transform.h"
#include "gimpdrawablefilter.h"
#include "gimpfilterstack.h"
#include "gimpimage.h"
#include "gimpimage-colormap.h"
#include "gimpimage-undo-push.h"
#include "gimplayer.h"
#include "gimpmarshal.h"
#include "gimppickable.h"
#include "gimpprogress.h"
@ -66,6 +70,7 @@ enum
FORMAT_CHANGED,
ALPHA_CHANGED,
BOUNDING_BOX_CHANGED,
FILTERS_CHANGED,
LAST_SIGNAL
};
@ -270,6 +275,14 @@ gimp_drawable_class_init (GimpDrawableClass *klass)
NULL, NULL, NULL,
G_TYPE_NONE, 0);
gimp_drawable_signals[FILTERS_CHANGED] =
g_signal_new ("filters-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpDrawableClass, filters_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->dispose = gimp_drawable_dispose;
object_class->finalize = gimp_drawable_finalize;
object_class->set_property = gimp_drawable_set_property;
@ -297,6 +310,7 @@ gimp_drawable_class_init (GimpDrawableClass *klass)
klass->format_changed = NULL;
klass->alpha_changed = NULL;
klass->bounding_box_changed = NULL;
klass->filters_changed = NULL;
klass->estimate_memsize = gimp_drawable_real_estimate_memsize;
klass->update_all = gimp_drawable_real_update_all;
klass->invalidate_boundary = NULL;
@ -336,12 +350,13 @@ gimp_color_managed_iface_init (GimpColorManagedInterface *iface)
static void
gimp_pickable_iface_init (GimpPickableInterface *iface)
{
iface->get_image = (GimpImage * (*) (GimpPickable *pickable)) gimp_item_get_image;
iface->get_format = (const Babl * (*) (GimpPickable *pickable)) gimp_drawable_get_format;
iface->get_format_with_alpha = (const Babl * (*) (GimpPickable *pickable)) gimp_drawable_get_format_with_alpha;
iface->get_buffer = (GeglBuffer * (*) (GimpPickable *pickable)) gimp_drawable_get_buffer;
iface->get_pixel_at = gimp_drawable_get_pixel_at;
iface->get_pixel_average = gimp_drawable_get_pixel_average;
iface->get_image = (GimpImage * (*) (GimpPickable *pickable)) gimp_item_get_image;
iface->get_format = (const Babl * (*) (GimpPickable *pickable)) gimp_drawable_get_format;
iface->get_format_with_alpha = (const Babl * (*) (GimpPickable *pickable)) gimp_drawable_get_format_with_alpha;
iface->get_buffer = (GeglBuffer * (*) (GimpPickable *pickable)) gimp_drawable_get_buffer;
iface->get_buffer_with_effects = (GeglBuffer * (*) (GimpPickable *pickable)) gimp_drawable_get_buffer_with_effects;
iface->get_pixel_at = gimp_drawable_get_pixel_at;
iface->get_pixel_average = gimp_drawable_get_pixel_average;
}
static void
@ -554,6 +569,30 @@ gimp_drawable_scale (GimpItem *item,
0, 0),
TRUE);
g_object_unref (new_buffer);
if (GIMP_IS_LAYER (drawable))
{
GList *list;
for (list = GIMP_LIST (drawable->private->filter_stack)->queue->tail;
list; list = g_list_previous (list))
{
if (GIMP_IS_DRAWABLE_FILTER (list->data))
{
GimpDrawableFilter *filter = list->data;
GimpChannel *mask = gimp_drawable_filter_get_mask (filter);
GeglRectangle *rect = GEGL_RECTANGLE (0, 0,
new_width,
new_height);
/* Don't resize partial layer effects */
if (gimp_channel_is_empty (mask))
gimp_drawable_filter_refresh_crop (filter, rect);
}
}
if (list)
g_list_free (list);
}
}
static void
@ -637,6 +676,28 @@ gimp_drawable_resize (GimpItem *item,
0, 0),
TRUE);
g_object_unref (new_buffer);
if (GIMP_IS_LAYER (drawable))
{
GList *list;
for (list = GIMP_LIST (drawable->private->filter_stack)->queue->tail;
list; list = g_list_previous (list))
{
if (GIMP_IS_DRAWABLE_FILTER (list->data))
{
GimpDrawableFilter *filter = list->data;
GimpChannel *mask = gimp_drawable_filter_get_mask (filter);
GeglRectangle rect = {0, 0, new_width, new_height};
/* Don't resize partial layer effects */
if (gimp_channel_is_empty (mask))
gimp_drawable_filter_refresh_crop (filter, &rect);
}
}
if (list)
g_list_free (list);
}
}
static void
@ -1439,6 +1500,55 @@ gimp_drawable_set_buffer_full (GimpDrawable *drawable,
gimp_drawable_update (drawable, 0, 0, -1, -1);
}
GeglBuffer *
gimp_drawable_get_buffer_with_effects (GimpDrawable *drawable)
{
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
if (drawable->private->paint_count == 0)
{
if (gimp_drawable_has_filters (drawable))
{
GeglNode *source = NULL;
GeglBuffer *buffer;
GimpTileHandlerValidate *validate;
source = gimp_drawable_get_source_node (drawable);
buffer = GIMP_DRAWABLE_GET_CLASS (drawable)->get_buffer (drawable);
if (source)
{
buffer = gegl_buffer_new (gegl_buffer_get_extent (buffer),
gegl_buffer_get_format (buffer));
validate =
GIMP_TILE_HANDLER_VALIDATE (gimp_tile_handler_validate_new (source));
gimp_tile_handler_validate_assign (validate, buffer);
g_object_unref (validate);
gimp_tile_handler_validate_invalidate (validate,
gegl_buffer_get_extent (buffer));
return buffer;
}
else
{
return buffer;
}
}
else
{
return GIMP_DRAWABLE_GET_CLASS (drawable)->get_buffer (drawable);
}
}
else
{
return drawable->private->paint_buffer;
}
}
void
gimp_drawable_steal_buffer (GimpDrawable *drawable,
GimpDrawable *src_drawable)
@ -1936,6 +2046,16 @@ gimp_drawable_end_paint (GimpDrawable *drawable)
drawable->private->paint_count--;
/* Refresh filters after painting */
if (gimp_drawable_has_filters (drawable) &&
drawable->private->paint_count == 0)
{
gimp_item_set_visible (GIMP_ITEM (drawable), FALSE, FALSE);
gimp_image_flush (gimp_item_get_image (GIMP_ITEM (drawable)));
gimp_item_set_visible (GIMP_ITEM (drawable),TRUE, FALSE);
gimp_image_flush (gimp_item_get_image (GIMP_ITEM (drawable)));
}
return result;
}
@ -2004,3 +2124,9 @@ gimp_drawable_is_painting (GimpDrawable *drawable)
return drawable->private->paint_count > 0;
}
void
gimp_drawable_filters_changed (GimpDrawable *drawable)
{
g_signal_emit (drawable, gimp_drawable_signals[FILTERS_CHANGED], 0);
}

View file

@ -53,160 +53,163 @@ struct _GimpDrawableClass
void (* format_changed) (GimpDrawable *drawable);
void (* alpha_changed) (GimpDrawable *drawable);
void (* bounding_box_changed) (GimpDrawable *drawable);
void (* filters_changed) (GimpDrawable *drawable);
/* virtual functions */
gint64 (* estimate_memsize) (GimpDrawable *drawable,
GimpComponentType component_type,
gint width,
gint height);
void (* update_all) (GimpDrawable *drawable);
void (* invalidate_boundary) (GimpDrawable *drawable);
void (* get_active_components) (GimpDrawable *drawable,
gboolean *active);
GimpComponentMask (* get_active_mask) (GimpDrawable *drawable);
gboolean (* supports_alpha) (GimpDrawable *drawable);
void (* convert_type) (GimpDrawable *drawable,
GimpImage *dest_image,
const Babl *new_format,
GimpColorProfile *src_profile,
GimpColorProfile *dest_profile,
GeglDitherMethod layer_dither_type,
GeglDitherMethod mask_dither_type,
gboolean push_undo,
GimpProgress *progress);
void (* apply_buffer) (GimpDrawable *drawable,
GeglBuffer *buffer,
const GeglRectangle *buffer_region,
gboolean push_undo,
const gchar *undo_desc,
gdouble opacity,
GimpLayerMode mode,
GimpLayerColorSpace blend_space,
GimpLayerColorSpace composite_space,
GimpLayerCompositeMode composite_mode,
GeglBuffer *base_buffer,
gint base_x,
gint base_y);
GeglBuffer * (* get_buffer) (GimpDrawable *drawable);
void (* set_buffer) (GimpDrawable *drawable,
gboolean push_undo,
const gchar *undo_desc,
GeglBuffer *buffer,
const GeglRectangle *bounds);
GeglRectangle (* get_bounding_box) (GimpDrawable *drawable);
void (* push_undo) (GimpDrawable *drawable,
const gchar *undo_desc,
GeglBuffer *buffer,
gint x,
gint y,
gint width,
gint height);
void (* swap_pixels) (GimpDrawable *drawable,
GeglBuffer *buffer,
gint x,
gint y);
GeglNode * (* get_source_node) (GimpDrawable *drawable);
gint64 (* estimate_memsize) (GimpDrawable *drawable,
GimpComponentType component_type,
gint width,
gint height);
void (* update_all) (GimpDrawable *drawable);
void (* invalidate_boundary) (GimpDrawable *drawable);
void (* get_active_components) (GimpDrawable *drawable,
gboolean *active);
GimpComponentMask (* get_active_mask) (GimpDrawable *drawable);
gboolean (* supports_alpha) (GimpDrawable *drawable);
void (* convert_type) (GimpDrawable *drawable,
GimpImage *dest_image,
const Babl *new_format,
GimpColorProfile *src_profile,
GimpColorProfile *dest_profile,
GeglDitherMethod layer_dither_type,
GeglDitherMethod mask_dither_type,
gboolean push_undo,
GimpProgress *progress);
void (* apply_buffer) (GimpDrawable *drawable,
GeglBuffer *buffer,
const GeglRectangle *buffer_region,
gboolean push_undo,
const gchar *undo_desc,
gdouble opacity,
GimpLayerMode mode,
GimpLayerColorSpace blend_space,
GimpLayerColorSpace composite_space,
GimpLayerCompositeMode composite_mode,
GeglBuffer *base_buffer,
gint base_x,
gint base_y);
GeglBuffer * (* get_buffer) (GimpDrawable *drawable);
void (* set_buffer) (GimpDrawable *drawable,
gboolean push_undo,
const gchar *undo_desc,
GeglBuffer *buffer,
const GeglRectangle *bounds);
GeglBuffer * (* get_buffer_with_effects) (GimpDrawable *drawable);
GeglRectangle (* get_bounding_box) (GimpDrawable *drawable);
void (* push_undo) (GimpDrawable *drawable,
const gchar *undo_desc,
GeglBuffer *buffer,
gint x,
gint y,
gint width,
gint height);
void (* swap_pixels) (GimpDrawable *drawable,
GeglBuffer *buffer,
gint x,
gint y);
GeglNode * (* get_source_node) (GimpDrawable *drawable);
};
GType gimp_drawable_get_type (void) G_GNUC_CONST;
GType gimp_drawable_get_type (void) G_GNUC_CONST;
GimpDrawable * gimp_drawable_new (GType type,
GimpImage *image,
const gchar *name,
gint offset_x,
gint offset_y,
gint width,
gint height,
const Babl *format);
GimpDrawable * gimp_drawable_new (GType type,
GimpImage *image,
const gchar *name,
gint offset_x,
gint offset_y,
gint width,
gint height,
const Babl *format);
gint64 gimp_drawable_estimate_memsize (GimpDrawable *drawable,
GimpComponentType component_type,
gint width,
gint height);
gint64 gimp_drawable_estimate_memsize (GimpDrawable *drawable,
GimpComponentType component_type,
gint width,
gint height);
void gimp_drawable_update (GimpDrawable *drawable,
gint x,
gint y,
gint width,
gint height);
void gimp_drawable_update_all (GimpDrawable *drawable);
void gimp_drawable_update (GimpDrawable *drawable,
gint x,
gint y,
gint width,
gint height);
void gimp_drawable_update_all (GimpDrawable *drawable);
void gimp_drawable_invalidate_boundary (GimpDrawable *drawable);
void gimp_drawable_get_active_components (GimpDrawable *drawable,
gboolean *active);
GimpComponentMask gimp_drawable_get_active_mask (GimpDrawable *drawable);
void gimp_drawable_invalidate_boundary (GimpDrawable *drawable);
void gimp_drawable_get_active_components (GimpDrawable *drawable,
gboolean *active);
GimpComponentMask gimp_drawable_get_active_mask (GimpDrawable *drawable);
gboolean gimp_drawable_supports_alpha (GimpDrawable *drawable);
gboolean gimp_drawable_supports_alpha (GimpDrawable *drawable);
void gimp_drawable_convert_type (GimpDrawable *drawable,
GimpImage *dest_image,
GimpImageBaseType new_base_type,
GimpPrecision new_precision,
gboolean new_has_alpha,
GimpColorProfile *src_profile,
GimpColorProfile *dest_profile,
GeglDitherMethod layer_dither_type,
GeglDitherMethod mask_dither_type,
gboolean push_undo,
GimpProgress *progress);
void gimp_drawable_convert_type (GimpDrawable *drawable,
GimpImage *dest_image,
GimpImageBaseType new_base_type,
GimpPrecision new_precision,
gboolean new_has_alpha,
GimpColorProfile *src_profile,
GimpColorProfile *dest_profile,
GeglDitherMethod layer_dither_type,
GeglDitherMethod mask_dither_type,
gboolean push_undo,
GimpProgress *progress);
void gimp_drawable_apply_buffer (GimpDrawable *drawable,
GeglBuffer *buffer,
const GeglRectangle *buffer_rect,
gboolean push_undo,
const gchar *undo_desc,
gdouble opacity,
GimpLayerMode mode,
GimpLayerColorSpace blend_space,
GimpLayerColorSpace composite_space,
GimpLayerCompositeMode composite_mode,
GeglBuffer *base_buffer,
gint base_x,
gint base_y);
void gimp_drawable_apply_buffer (GimpDrawable *drawable,
GeglBuffer *buffer,
const GeglRectangle *buffer_rect,
gboolean push_undo,
const gchar *undo_desc,
gdouble opacity,
GimpLayerMode mode,
GimpLayerColorSpace blend_space,
GimpLayerColorSpace composite_space,
GimpLayerCompositeMode composite_mode,
GeglBuffer *base_buffer,
gint base_x,
gint base_y);
GeglBuffer * gimp_drawable_get_buffer (GimpDrawable *drawable);
void gimp_drawable_set_buffer (GimpDrawable *drawable,
gboolean push_undo,
const gchar *undo_desc,
GeglBuffer *buffer);
void gimp_drawable_set_buffer_full (GimpDrawable *drawable,
gboolean push_undo,
const gchar *undo_desc,
GeglBuffer *buffer,
const GeglRectangle *bounds,
gboolean update);
GeglBuffer * gimp_drawable_get_buffer (GimpDrawable *drawable);
void gimp_drawable_set_buffer (GimpDrawable *drawable,
gboolean push_undo,
const gchar *undo_desc,
GeglBuffer *buffer);
void gimp_drawable_set_buffer_full (GimpDrawable *drawable,
gboolean push_undo,
const gchar *undo_desc,
GeglBuffer *buffer,
const GeglRectangle *bounds,
gboolean update);
GeglBuffer * gimp_drawable_get_buffer_with_effects (GimpDrawable *drawable);
void gimp_drawable_steal_buffer (GimpDrawable *drawable,
GimpDrawable *src_drawable);
void gimp_drawable_steal_buffer (GimpDrawable *drawable,
GimpDrawable *src_drawable);
void gimp_drawable_set_format (GimpDrawable *drawable,
const Babl *format,
gboolean copy_buffer,
gboolean push_undo);
void gimp_drawable_set_format (GimpDrawable *drawable,
const Babl *format,
gboolean copy_buffer,
gboolean push_undo);
GeglNode * gimp_drawable_get_source_node (GimpDrawable *drawable);
GeglNode * gimp_drawable_get_mode_node (GimpDrawable *drawable);
GeglNode * gimp_drawable_get_source_node (GimpDrawable *drawable);
GeglNode * gimp_drawable_get_mode_node (GimpDrawable *drawable);
GeglRectangle gimp_drawable_get_bounding_box (GimpDrawable *drawable);
GeglRectangle gimp_drawable_get_bounding_box (GimpDrawable *drawable);
gboolean gimp_drawable_update_bounding_box
(GimpDrawable *drawable);
(GimpDrawable *drawable);
void gimp_drawable_swap_pixels (GimpDrawable *drawable,
GeglBuffer *buffer,
gint x,
gint y);
void gimp_drawable_swap_pixels (GimpDrawable *drawable,
GeglBuffer *buffer,
gint x,
gint y);
void gimp_drawable_push_undo (GimpDrawable *drawable,
const gchar *undo_desc,
GeglBuffer *buffer,
gint x,
gint y,
gint width,
gint height);
void gimp_drawable_push_undo (GimpDrawable *drawable,
const gchar *undo_desc,
GeglBuffer *buffer,
gint x,
gint y,
gint width,
gint height);
void gimp_drawable_disable_resize_undo (GimpDrawable *drawable);
void gimp_drawable_enable_resize_undo (GimpDrawable *drawable);
void gimp_drawable_disable_resize_undo (GimpDrawable *drawable);
void gimp_drawable_enable_resize_undo (GimpDrawable *drawable);
const Babl * gimp_drawable_get_space (GimpDrawable *drawable);
const Babl * gimp_drawable_get_format (GimpDrawable *drawable);
@ -234,5 +237,7 @@ gboolean gimp_drawable_end_paint (GimpDrawable *drawable)
gboolean gimp_drawable_flush_paint (GimpDrawable *drawable);
gboolean gimp_drawable_is_painting (GimpDrawable *drawable);
void gimp_drawable_filters_changed (GimpDrawable *drawable);
#endif /* __GIMP_DRAWABLE_H__ */

View file

@ -54,12 +54,19 @@ enum
LAST_SIGNAL
};
enum
{
PROP_0,
PROP_MASK
};
struct _GimpDrawableFilter
{
GimpFilter parent_instance;
GimpDrawable *drawable;
GimpChannel *mask;
GeglNode *operation;
gboolean has_input;
@ -94,7 +101,15 @@ struct _GimpDrawableFilter
GimpApplicator *applicator;
};
static void gimp_drawable_filter_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_drawable_filter_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static void gimp_drawable_filter_dispose (GObject *object);
static void gimp_drawable_filter_finalize (GObject *object);
@ -159,8 +174,16 @@ gimp_drawable_filter_class_init (GimpDrawableFilterClass *klass)
NULL, NULL, NULL,
G_TYPE_NONE, 0);
object_class->dispose = gimp_drawable_filter_dispose;
object_class->finalize = gimp_drawable_filter_finalize;
object_class->set_property = gimp_drawable_filter_set_property;
object_class->get_property = gimp_drawable_filter_get_property;
object_class->dispose = gimp_drawable_filter_dispose;
object_class->finalize = gimp_drawable_filter_finalize;
g_object_class_install_property (object_class, PROP_MASK,
g_param_spec_object ("mask",
NULL, NULL,
GIMP_TYPE_CHANNEL,
GIMP_PARAM_READWRITE));
}
static void
@ -179,6 +202,49 @@ gimp_drawable_filter_init (GimpDrawableFilter *drawable_filter)
drawable_filter->composite_mode = GIMP_LAYER_COMPOSITE_AUTO;
}
static void
gimp_drawable_filter_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpDrawableFilter *filter = GIMP_DRAWABLE_FILTER (object);
switch (property_id)
{
case PROP_MASK:
g_set_object (&filter->mask, g_value_get_object (value));
if (filter->mask)
gimp_drawable_filter_sync_mask (filter);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_drawable_filter_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpDrawableFilter *filter = GIMP_DRAWABLE_FILTER (object);
switch (property_id)
{
case PROP_MASK:
g_value_set_object (value, gimp_drawable_filter_get_mask (filter));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_drawable_filter_dispose (GObject *object)
{
@ -195,9 +261,10 @@ gimp_drawable_filter_finalize (GObject *object)
{
GimpDrawableFilter *drawable_filter = GIMP_DRAWABLE_FILTER (object);
g_clear_object (&drawable_filter->operation);
g_clear_object (&drawable_filter->applicator);
g_clear_object (&drawable_filter->drawable);
g_clear_object (&drawable_filter->operation);
g_clear_object (&drawable_filter->mask);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@ -219,6 +286,7 @@ gimp_drawable_filter_new (GimpDrawable *drawable,
filter = g_object_new (GIMP_TYPE_DRAWABLE_FILTER,
"name", undo_desc,
"icon-name", icon_name,
"mask", NULL,
NULL);
filter->drawable = g_object_ref (drawable);
@ -226,8 +294,11 @@ gimp_drawable_filter_new (GimpDrawable *drawable,
node = gimp_filter_get_node (GIMP_FILTER (filter));
gegl_node_add_child (node, operation);
gimp_gegl_node_set_underlying_operation (node, operation);
if (! gegl_node_get_parent (operation))
{
gegl_node_add_child (node, operation);
gimp_gegl_node_set_underlying_operation (node, operation);
}
filter->applicator = gimp_applicator_new (node);
@ -281,6 +352,78 @@ gimp_drawable_filter_new (GimpDrawable *drawable,
return filter;
}
GimpDrawableFilter *
gimp_drawable_filter_duplicate (GimpDrawable *drawable,
GimpDrawableFilter *prior_filter)
{
GimpDrawableFilter *filter;
GimpChannel *mask;
GeglNode *prior_node;
GeglNode *node = gegl_node_new ();
const gchar *operation;
const gchar *undo_desc;
const gchar *icon_name;
GParamSpec **pspecs;
guint n_pspecs;
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL);
g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (prior_filter), NULL);
prior_node = gimp_drawable_filter_get_operation (prior_filter);
g_object_get (prior_filter,
"name", &undo_desc,
"icon-name", &icon_name,
NULL);
gegl_node_get (prior_node,
"operation", &operation,
NULL);
gegl_node_set (node,
"operation", operation,
NULL);
pspecs = gegl_operation_list_properties (operation, &n_pspecs);
for (gint i = 0; i < n_pspecs; i++)
{
GParamSpec *pspec = pspecs[i];
GValue value = G_VALUE_INIT;
g_value_init (&value, pspec->value_type);
gegl_node_get_property (prior_node, pspec->name,
&value);
gegl_node_set_property (node, pspec->name,
&value);
g_value_unset (&value);
}
g_free (pspecs);
filter = gimp_drawable_filter_new (drawable, undo_desc, node, icon_name);
g_object_unref (node);
gimp_drawable_filter_set_opacity (filter, prior_filter->opacity);
gimp_drawable_filter_set_mode (filter,
prior_filter->paint_mode,
prior_filter->blend_space,
prior_filter->composite_space,
prior_filter->composite_mode);
gimp_drawable_filter_set_region (filter,
prior_filter->region);
mask = GIMP_CHANNEL (gimp_item_duplicate (GIMP_ITEM (prior_filter->mask),
GIMP_TYPE_CHANNEL));
g_object_set (filter,
"mask", mask,
NULL);
g_object_unref (mask);
return filter;
}
GimpDrawable *
gimp_drawable_filter_get_drawable (GimpDrawableFilter *filter)
{
@ -297,6 +440,62 @@ gimp_drawable_filter_get_operation (GimpDrawableFilter *filter)
return filter->operation;
}
GimpChannel *
gimp_drawable_filter_get_mask (GimpDrawableFilter *filter)
{
g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), NULL);
return filter->mask;
}
gdouble
gimp_drawable_filter_get_opacity (GimpDrawableFilter *filter)
{
g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), 0.0f);
return filter->opacity;
}
GimpLayerMode
gimp_drawable_filter_get_paint_mode (GimpDrawableFilter *filter)
{
g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), 0);
return filter->paint_mode;
}
GimpLayerColorSpace
gimp_drawable_filter_get_blend_space (GimpDrawableFilter *filter)
{
g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), 0);
return filter->blend_space;
}
GimpLayerColorSpace
gimp_drawable_filter_get_composite_space (GimpDrawableFilter *filter)
{
g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), 0);
return filter->composite_space;
}
GimpLayerCompositeMode
gimp_drawable_filter_get_composite_mode (GimpDrawableFilter *filter)
{
g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), 0);
return filter->composite_mode;
}
GimpFilterRegion
gimp_drawable_filter_get_region (GimpDrawableFilter *filter)
{
g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), 0);
return filter->region;
}
void
gimp_drawable_filter_set_clip (GimpDrawableFilter *filter,
gboolean clip)
@ -307,6 +506,7 @@ gimp_drawable_filter_set_clip (GimpDrawableFilter *filter,
{
filter->clip = clip;
gimp_drawable_filter_sync_region (filter);
gimp_drawable_filter_sync_clip (filter, TRUE);
}
}
@ -568,6 +768,7 @@ gimp_drawable_filter_apply (GimpDrawableFilter *filter,
gboolean
gimp_drawable_filter_commit (GimpDrawableFilter *filter,
gboolean non_destructive,
GimpProgress *progress,
gboolean cancellable)
{
@ -589,16 +790,25 @@ gimp_drawable_filter_commit (GimpDrawableFilter *filter,
filter->preview_split_position);
gimp_drawable_filter_set_preview (filter, TRUE);
success = gimp_drawable_merge_filter (filter->drawable,
GIMP_FILTER (filter),
progress,
gimp_object_get_name (filter),
format,
filter->filter_clip,
cancellable,
FALSE);
/* Only commit if filter is applied destructively */
if (! non_destructive)
{
success = gimp_drawable_merge_filter (filter->drawable,
GIMP_FILTER (filter),
progress,
gimp_object_get_name (filter),
format,
filter->filter_clip,
cancellable,
FALSE);
gimp_drawable_filter_remove_filter (filter);
gimp_drawable_filter_remove_filter (filter);
}
else
{
if (gimp_viewable_preview_is_frozen (GIMP_VIEWABLE (filter->drawable)))
gimp_viewable_preview_thaw (GIMP_VIEWABLE (filter->drawable));
}
if (! success)
gimp_drawable_filter_update_drawable (filter, NULL);
@ -620,6 +830,41 @@ gimp_drawable_filter_abort (GimpDrawableFilter *filter)
}
}
void
gimp_drawable_filter_layer_mask_freeze (GimpDrawableFilter *filter)
{
GimpImage *image = gimp_item_get_image (GIMP_ITEM (filter->drawable));
GimpChannel *mask;
if (! filter->mask)
{
mask = GIMP_CHANNEL (gimp_item_duplicate (GIMP_ITEM (gimp_image_get_mask (image)),
GIMP_TYPE_CHANNEL));
g_set_object (&filter->mask, mask);
g_object_unref (mask);
}
g_signal_handlers_disconnect_by_func (image,
gimp_drawable_filter_mask_changed,
filter);
}
void gimp_drawable_filter_refresh_crop (GimpDrawableFilter *filter,
GeglRectangle *rect)
{
g_return_if_fail (GIMP_IS_DRAWABLE_FILTER (filter));
if (rect)
{
gimp_drawable_filter_set_clip (filter, TRUE);
gimp_drawable_filter_set_clip (filter, FALSE);
gimp_drawable_filter_set_region (filter, GIMP_FILTER_REGION_SELECTION);
gimp_drawable_filter_set_region (filter, GIMP_FILTER_REGION_DRAWABLE);
gimp_drawable_filter_set_crop (filter, NULL, FALSE);
gimp_drawable_filter_set_crop (filter, rect, FALSE);
}
}
/* private functions */
@ -642,10 +887,9 @@ gimp_drawable_filter_sync_clip (GimpDrawableFilter *filter,
if (! clip)
{
GimpImage *image = gimp_item_get_image (GIMP_ITEM (filter->drawable));
GimpChannel *mask = gimp_image_get_mask (image);
GimpChannel *mask = GIMP_CHANNEL (filter->mask);
if (! gimp_channel_is_empty (mask))
if (mask && ! gimp_channel_is_empty (mask))
clip = TRUE;
}
@ -838,7 +1082,7 @@ gimp_drawable_filter_sync_crop (GimpDrawableFilter *filter,
gimp_applicator_set_crop (filter->applicator, enabled ? &new_rect : NULL);
if (update &&
if (update &&
gimp_drawable_filter_is_active (filter) &&
! gegl_rectangle_equal (&old_rect, &new_rect))
{
@ -921,11 +1165,22 @@ static void
gimp_drawable_filter_sync_mask (GimpDrawableFilter *filter)
{
GimpImage *image = gimp_item_get_image (GIMP_ITEM (filter->drawable));
GimpChannel *mask = gimp_image_get_mask (image);
GimpChannel *mask = NULL;
if (gimp_channel_is_empty (mask))
if (! filter->mask)
mask = gimp_image_get_mask (image);
else
mask = GIMP_CHANNEL (filter->mask);
if (! mask || gimp_channel_is_empty (mask))
{
gimp_applicator_set_mask_buffer (filter->applicator, NULL);
gimp_item_mask_intersect (GIMP_ITEM (filter->drawable),
&filter->filter_area.x,
&filter->filter_area.y,
&filter->filter_area.width,
&filter->filter_area.height);
}
else
{
@ -939,13 +1194,15 @@ gimp_drawable_filter_sync_mask (GimpDrawableFilter *filter)
gimp_applicator_set_mask_buffer (filter->applicator, mask_buffer);
gimp_applicator_set_mask_offset (filter->applicator,
-offset_x, -offset_y);
}
gimp_item_mask_intersect (GIMP_ITEM (filter->drawable),
&filter->filter_area.x,
&filter->filter_area.y,
&filter->filter_area.width,
&filter->filter_area.height);
/* Update filter crop */
filter->filter_area.x = mask->x1;
filter->filter_area.y = mask->y1;
filter->filter_area.width = mask->x2 - mask->x1;
filter->filter_area.height = mask->y2 - mask->y1;
gimp_drawable_filter_sync_region (filter);
}
}
static void
@ -1052,24 +1309,25 @@ gimp_drawable_filter_add_filter (GimpDrawableFilter *filter)
g_signal_connect (image, "component-active-changed",
G_CALLBACK (gimp_drawable_filter_affect_changed),
filter);
g_signal_connect (image, "mask-changed",
G_CALLBACK (gimp_drawable_filter_mask_changed),
filter);
g_signal_connect (filter->drawable, "lock-position-changed",
G_CALLBACK (gimp_drawable_filter_lock_position_changed),
filter);
g_signal_connect (filter->drawable, "format-changed",
G_CALLBACK (gimp_drawable_filter_format_changed),
filter);
g_signal_connect (filter->drawable, "removed",
G_CALLBACK (gimp_drawable_filter_drawable_removed),
filter);
if (! filter->mask)
g_signal_connect_object (image, "mask-changed",
G_CALLBACK (gimp_drawable_filter_mask_changed),
filter, 0);
g_signal_connect_object (filter->drawable, "lock-position-changed",
G_CALLBACK (gimp_drawable_filter_lock_position_changed),
filter, 0);
g_signal_connect_object (filter->drawable, "format-changed",
G_CALLBACK (gimp_drawable_filter_format_changed),
filter, 0);
g_signal_connect_object (filter->drawable, "removed",
G_CALLBACK (gimp_drawable_filter_drawable_removed),
filter, 0);
if (GIMP_IS_LAYER (filter->drawable))
{
g_signal_connect (filter->drawable, "lock-alpha-changed",
G_CALLBACK (gimp_drawable_filter_lock_alpha_changed),
filter);
g_signal_connect_object (filter->drawable, "lock-alpha-changed",
G_CALLBACK (gimp_drawable_filter_lock_alpha_changed),
filter, 0);
}
return TRUE;
@ -1101,9 +1359,7 @@ gimp_drawable_filter_remove_filter (GimpDrawableFilter *filter)
g_signal_handlers_disconnect_by_func (filter->drawable,
gimp_drawable_filter_lock_position_changed,
filter);
g_signal_handlers_disconnect_by_func (image,
gimp_drawable_filter_mask_changed,
filter);
g_signal_handlers_disconnect_by_func (image,
gimp_drawable_filter_affect_changed,
filter);
@ -1111,9 +1367,12 @@ gimp_drawable_filter_remove_filter (GimpDrawableFilter *filter)
gimp_drawable_remove_filter (filter->drawable,
GIMP_FILTER (filter));
gimp_drawable_update_bounding_box (filter->drawable);
if (filter->drawable &&
GIMP_IS_DRAWABLE (filter->drawable))
gimp_drawable_update_bounding_box (filter->drawable);
gimp_viewable_preview_thaw (GIMP_VIEWABLE (filter->drawable));
if (gimp_viewable_preview_is_frozen (GIMP_VIEWABLE (filter->drawable)))
gimp_viewable_preview_thaw (GIMP_VIEWABLE (filter->drawable));
return TRUE;
}
@ -1181,13 +1440,22 @@ static void
gimp_drawable_filter_mask_changed (GimpImage *image,
GimpDrawableFilter *filter)
{
gimp_drawable_filter_update_drawable (filter, NULL);
if (! filter->mask)
{
gimp_drawable_filter_update_drawable (filter, NULL);
gimp_drawable_filter_sync_mask (filter);
gimp_drawable_filter_sync_clip (filter, FALSE);
gimp_drawable_filter_sync_region (filter);
gimp_drawable_filter_sync_mask (filter);
gimp_drawable_filter_sync_clip (filter, FALSE);
gimp_drawable_filter_sync_region (filter);
gimp_drawable_filter_update_drawable (filter, NULL);
gimp_drawable_filter_update_drawable (filter, NULL);
}
else
{
g_signal_handlers_disconnect_by_func (image,
gimp_drawable_filter_mask_changed,
filter);
}
}
static void
@ -1210,7 +1478,8 @@ static void
gimp_drawable_filter_drawable_removed (GimpDrawable *drawable,
GimpDrawableFilter *filter)
{
gimp_drawable_filter_remove_filter (filter);
if (filter)
gimp_drawable_filter_remove_filter (filter);
}
static void

View file

@ -54,10 +54,29 @@ GimpDrawableFilter *
const gchar *undo_desc,
GeglNode *operation,
const gchar *icon_name);
GimpDrawableFilter *
gimp_drawable_filter_duplicate (GimpDrawable *drawable,
GimpDrawableFilter *prior_filter);
GimpDrawable *
gimp_drawable_filter_get_drawable (GimpDrawableFilter *filter);
GeglNode * gimp_drawable_filter_get_operation (GimpDrawableFilter *filter);
GimpChannel *
gimp_drawable_filter_get_mask (GimpDrawableFilter *filter);
gdouble gimp_drawable_filter_get_opacity (GimpDrawableFilter *filter);
GimpLayerMode
gimp_drawable_filter_get_paint_mode (GimpDrawableFilter *filter);
GimpLayerColorSpace
gimp_drawable_filter_get_blend_space
(GimpDrawableFilter *filter);
GimpLayerColorSpace
gimp_drawable_filter_get_composite_space
(GimpDrawableFilter *filter);
GimpLayerCompositeMode
gimp_drawable_filter_get_composite_mode
(GimpDrawableFilter *filter);
GimpFilterRegion
gimp_drawable_filter_get_region (GimpDrawableFilter *filter);
void gimp_drawable_filter_set_clip (GimpDrawableFilter *filter,
gboolean clip);
@ -97,9 +116,15 @@ void gimp_drawable_filter_apply (GimpDrawableFilter *filter,
const GeglRectangle *area);
gboolean gimp_drawable_filter_commit (GimpDrawableFilter *filter,
gboolean non_destructive,
GimpProgress *progress,
gboolean cancellable);
void gimp_drawable_filter_abort (GimpDrawableFilter *filter);
void gimp_drawable_filter_layer_mask_freeze
(GimpDrawableFilter *filter);
void gimp_drawable_filter_refresh_crop (GimpDrawableFilter *filter,
GeglRectangle *rect);
#endif /* __GIMP_DRAWABLE_FILTER_H__ */

View file

@ -0,0 +1,228 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libgimpbase/gimpbase.h"
#include "core-types.h"
#include "gimpcontainer.h"
#include "gimpdrawable-filters.h"
#include "gimpdrawablefilter.h"
#include "gimpdrawablefilterundo.h"
#include "gimpimage.h"
#include "gimpitem.h"
enum
{
PROP_0,
PROP_FILTER
};
static void gimp_drawable_filter_undo_constructed (GObject *object);
static void gimp_drawable_filter_undo_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec);
static void gimp_drawable_filter_undo_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec);
static gint64 gimp_drawable_filter_undo_get_memsize (GimpObject *object,
gint64 *gui_size);
static void gimp_drawable_filter_undo_pop (GimpUndo *undo,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum);
static void gimp_drawable_filter_undo_free (GimpUndo *undo,
GimpUndoMode undo_mode);
G_DEFINE_TYPE (GimpDrawableFilterUndo, gimp_drawable_filter_undo, GIMP_TYPE_UNDO)
#define parent_class gimp_drawable_filter_undo_parent_class
static void
gimp_drawable_filter_undo_class_init (GimpDrawableFilterUndoClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
GimpUndoClass *undo_class = GIMP_UNDO_CLASS (klass);
object_class->constructed = gimp_drawable_filter_undo_constructed;
object_class->set_property = gimp_drawable_filter_undo_set_property;
object_class->get_property = gimp_drawable_filter_undo_get_property;
gimp_object_class->get_memsize = gimp_drawable_filter_undo_get_memsize;
undo_class->pop = gimp_drawable_filter_undo_pop;
undo_class->free = gimp_drawable_filter_undo_free;
g_object_class_install_property (object_class, PROP_FILTER,
g_param_spec_object ("filter", NULL, NULL,
GIMP_TYPE_DRAWABLE_FILTER,
GIMP_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY));
}
static void
gimp_drawable_filter_undo_init (GimpDrawableFilterUndo *undo)
{
}
static void
gimp_drawable_filter_undo_constructed (GObject *object)
{
GimpDrawableFilterUndo *drawable_filter_undo = GIMP_DRAWABLE_FILTER_UNDO (object);
GimpDrawable *drawable;
GimpContainer *filter_stack;
G_OBJECT_CLASS (parent_class)->constructed (object);
gimp_assert (GIMP_IS_DRAWABLE_FILTER (drawable_filter_undo->filter));
drawable = gimp_drawable_filter_get_drawable (drawable_filter_undo->filter);
if (drawable)
{
filter_stack = gimp_drawable_get_filters (drawable);
drawable_filter_undo->row_index =
gimp_container_get_child_index (filter_stack,
GIMP_OBJECT (drawable_filter_undo->filter));
}
}
static void
gimp_drawable_filter_undo_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
GimpDrawableFilterUndo *drawable_filter_undo = GIMP_DRAWABLE_FILTER_UNDO (object);
switch (property_id)
{
case PROP_FILTER:
drawable_filter_undo->filter = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
gimp_drawable_filter_undo_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
GimpDrawableFilterUndo *drawable_filter_undo = GIMP_DRAWABLE_FILTER_UNDO (object);
switch (property_id)
{
case PROP_FILTER:
g_value_set_object (value, drawable_filter_undo->filter);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static gint64
gimp_drawable_filter_undo_get_memsize (GimpObject *object,
gint64 *gui_size)
{
GimpDrawableFilterUndo *drawable_filter_undo = GIMP_DRAWABLE_FILTER_UNDO (object);
gint64 memsize = 0;
memsize += gimp_object_get_memsize (GIMP_OBJECT (drawable_filter_undo->filter),
NULL);
memsize += sizeof (guint32);
return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
gui_size);
}
static void
gimp_drawable_filter_undo_pop (GimpUndo *undo,
GimpUndoMode undo_mode,
GimpUndoAccumulator *accum)
{
GimpDrawableFilterUndo *drawable_filter_undo = GIMP_DRAWABLE_FILTER_UNDO (undo);
GimpDrawableFilter *filter = GIMP_DRAWABLE_FILTER (drawable_filter_undo->filter);
GimpDrawable *drawable = gimp_drawable_filter_get_drawable (filter);
GimpContainer *filter_stack = gimp_drawable_get_filters (drawable);
GIMP_UNDO_CLASS (parent_class)->pop (undo, undo_mode, accum);
if ((undo_mode == GIMP_UNDO_MODE_UNDO &&
undo->undo_type == GIMP_UNDO_FILTER_ADD) ||
(undo_mode == GIMP_UNDO_MODE_REDO &&
undo->undo_type == GIMP_UNDO_FILTER_REMOVE) )
{
if (drawable)
{
gimp_drawable_remove_filter (drawable, GIMP_FILTER (filter));
gimp_item_set_visible (GIMP_ITEM (drawable), FALSE, FALSE);
gimp_image_flush (undo->image);
gimp_item_set_visible (GIMP_ITEM (drawable), TRUE, FALSE);
gimp_image_flush (undo->image);
}
}
if ((undo_mode == GIMP_UNDO_MODE_UNDO &&
undo->undo_type == GIMP_UNDO_FILTER_REMOVE) ||
(undo_mode == GIMP_UNDO_MODE_REDO &&
undo->undo_type == GIMP_UNDO_FILTER_ADD) )
{
if (drawable)
{
gimp_drawable_filter_apply (filter, NULL);
gimp_container_reorder (filter_stack, GIMP_OBJECT (filter),
drawable_filter_undo->row_index);
}
}
else if (undo->undo_type == GIMP_UNDO_FILTER_REORDER)
{
gimp_container_reorder (filter_stack, GIMP_OBJECT (filter),
drawable_filter_undo->row_index);
gimp_drawable_filter_apply (filter, NULL);
}
}
static void
gimp_drawable_filter_undo_free (GimpUndo *undo,
GimpUndoMode undo_mode)
{
GimpDrawableFilterUndo *drawable_filter_undo = GIMP_DRAWABLE_FILTER_UNDO (undo);
if (drawable_filter_undo->filter)
g_clear_object (&drawable_filter_undo->filter);
GIMP_UNDO_CLASS (parent_class)->free (undo, undo_mode);
}

View file

@ -0,0 +1,53 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __GIMP_DRAWABLE_FILTER_UNDO_H__
#define __GIMP_DRAWABLE_FILTER_UNDO_H__
#include "gimpundo.h"
#define GIMP_TYPE_DRAWABLE_FILTER_UNDO (gimp_drawable_filter_undo_get_type ())
#define GIMP_DRAWABLE_FILTER_UNDO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DRAWABLE_FILTER_UNDO, GimpDrawableFilterUndo))
#define GIMP_DRAWABLE_FILTER_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DRAWABLE_FILTER_UNDO, GimpDrawableFilterUndoClass))
#define GIMP_IS_DRAWABLE_FILTER_UNDO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DRAWABLE_FILTER_UNDO))
#define GIMP_IS_DRAWABLE_FILTER_UNDO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DRAWABLE_FILTER_UNDO))
#define GIMP_DRAWABLE_FILTER_UNDO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DRAWABLE_FILTER_UNDO, GimpDrawableFilterUndoClass))
typedef struct _GimpDrawableFilterUndo GimpDrawableFilterUndo;
typedef struct _GimpDrawableFilterUndoClass GimpDrawableFilterUndoClass;
struct _GimpDrawableFilterUndo
{
GimpUndo parent_instance;
GimpDrawableFilter *filter;
guint32 row_index;
};
struct _GimpDrawableFilterUndoClass
{
GimpUndoClass parent_class;
};
GType gimp_drawable_filter_undo_get_type (void) G_GNUC_CONST;
#endif /* __GIMP_DRAWABLE_FILTER_UNDO_H__ */

View file

@ -30,6 +30,8 @@
#include "gimp.h"
#include "gimpchannel.h"
#include "gimpdrawable-filters.h"
#include "gimpdrawablefilter.h"
#include "gimpguide.h"
#include "gimpimage.h"
#include "gimpimage-color-profile.h"
@ -259,6 +261,36 @@ gimp_image_duplicate_layers (GimpImage *image,
gimp_image_add_layer (new_image, new_layer,
NULL, count++, FALSE);
/* Import any attached layer effects */
if (gimp_drawable_has_filters (GIMP_DRAWABLE (layer)))
{
GList *filter_list;
GimpContainer *filters;
filters = gimp_drawable_get_filters (GIMP_DRAWABLE (layer));
for (filter_list = GIMP_LIST (filters)->queue->tail; filter_list;
filter_list = g_list_previous (filter_list))
{
if (GIMP_IS_DRAWABLE_FILTER (filter_list->data))
{
GimpDrawableFilter *old_filter = filter_list->data;
GimpDrawableFilter *filter;
filter =
gimp_drawable_filter_duplicate (GIMP_DRAWABLE (new_layer),
old_filter);
gimp_drawable_filter_apply (filter, NULL);
gimp_drawable_filter_commit (filter, TRUE, NULL, FALSE);
gimp_drawable_filter_layer_mask_freeze (filter);
g_object_unref (filter);
}
}
}
}
new_item_stack = GIMP_ITEM_STACK (gimp_image_get_layers (new_image));

View file

@ -27,6 +27,8 @@
#include "gimp.h"
#include "gimpchannelpropundo.h"
#include "gimpchannelundo.h"
#include "gimpdrawablefilter.h"
#include "gimpdrawablefilterundo.h"
#include "gimpdrawablemodundo.h"
#include "gimpdrawablepropundo.h"
#include "gimpdrawableundo.h"
@ -306,6 +308,58 @@ gimp_image_undo_push_drawable_format (GimpImage *image,
}
/***************************/
/* Drawable Filter Undos */
/***************************/
GimpUndo *
gimp_image_undo_push_filter_add (GimpImage *image,
const gchar *undo_desc,
GimpDrawable *drawable,
GimpDrawableFilter *filter)
{
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), NULL);
return gimp_image_undo_push (image, GIMP_TYPE_DRAWABLE_FILTER_UNDO,
GIMP_UNDO_FILTER_ADD, undo_desc,
GIMP_DIRTY_DRAWABLE,
"filter", filter,
NULL);
}
GimpUndo *
gimp_image_undo_push_filter_remove (GimpImage *image,
const gchar *undo_desc,
GimpDrawable *drawable,
GimpDrawableFilter *filter)
{
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), NULL);
return gimp_image_undo_push (image, GIMP_TYPE_DRAWABLE_FILTER_UNDO,
GIMP_UNDO_FILTER_REMOVE, undo_desc,
GIMP_DIRTY_DRAWABLE,
"filter", filter,
NULL);
}
GimpUndo *
gimp_image_undo_push_filter_reorder (GimpImage *image,
const gchar *undo_desc,
GimpDrawable *drawable,
GimpDrawableFilter *filter)
{
g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL);
g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), NULL);
return gimp_image_undo_push (image, GIMP_TYPE_DRAWABLE_FILTER_UNDO,
GIMP_UNDO_FILTER_REORDER, undo_desc,
GIMP_DIRTY_DRAWABLE,
"filter", filter,
NULL);
}
/****************/
/* Mask Undos */
/****************/

View file

@ -77,6 +77,26 @@ GimpUndo * gimp_image_undo_push_drawable_format (GimpImage *image,
GimpDrawable *drawable);
/* drawable filter undos */
GimpUndo * gimp_image_undo_push_filter_add (GimpImage *image,
const gchar *undo_desc,
GimpDrawable *drawable,
GimpDrawableFilter
*filter);
GimpUndo * gimp_image_undo_push_filter_remove (GimpImage *image,
const gchar *undo_desc,
GimpDrawable *drawable,
GimpDrawableFilter
*filter);
GimpUndo * gimp_image_undo_push_filter_reorder (GimpImage *image,
const gchar *undo_desc,
GimpDrawable *drawable,
GimpDrawableFilter
*filter);
/* mask undos */
GimpUndo * gimp_image_undo_push_mask (GimpImage *image,

View file

@ -44,6 +44,7 @@
#include "gimp-parasites.h"
#include "gimp-utils.h"
#include "gimpcontext.h"
#include "gimpdrawable-filters.h"
#include "gimpdrawable-floating-selection.h"
#include "gimpdrawablestack.h"
#include "gimpgrid.h"
@ -2974,6 +2975,13 @@ gimp_image_get_xcf_version (GimpImage *image,
"GIMP 3.0"));
version = MAX (19, version);
}
if (gimp_drawable_has_filters (GIMP_DRAWABLE (layer)))
{
ADD_REASON (g_strdup_printf (_("Layer effects were added in %s"),
"GIMP 3.0"));
version = MAX (20, version);
}
}
g_list_free (items);
@ -3141,6 +3149,7 @@ gimp_image_get_xcf_version (GimpImage *image,
case 17:
case 18:
case 19:
case 20:
if (gimp_version) *gimp_version = 300;
if (version_string) *version_string = "GIMP 3.0";
break;

View file

@ -188,6 +188,23 @@ gimp_pickable_get_buffer (GimpPickable *pickable)
return NULL;
}
GeglBuffer *
gimp_pickable_get_buffer_with_effects (GimpPickable *pickable)
{
GimpPickableInterface *pickable_iface;
g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), NULL);
pickable_iface = GIMP_PICKABLE_GET_IFACE (pickable);
if (pickable_iface->get_buffer_with_effects)
return pickable_iface->get_buffer_with_effects (pickable);
else if (pickable_iface->get_buffer)
return pickable_iface->get_buffer (pickable);
return NULL;
}
gboolean
gimp_pickable_get_pixel_at (GimpPickable *pickable,
gint x,

View file

@ -30,74 +30,76 @@ struct _GimpPickableInterface
GTypeInterface base_iface;
/* virtual functions */
void (* flush) (GimpPickable *pickable);
GimpImage * (* get_image) (GimpPickable *pickable);
const Babl * (* get_format) (GimpPickable *pickable);
const Babl * (* get_format_with_alpha) (GimpPickable *pickable);
GeglBuffer * (* get_buffer) (GimpPickable *pickable);
gboolean (* get_pixel_at) (GimpPickable *pickable,
gint x,
gint y,
const Babl *format,
gpointer pixel);
gdouble (* get_opacity_at) (GimpPickable *pickable,
gint x,
gint y);
void (* get_pixel_average) (GimpPickable *pickable,
const GeglRectangle *rect,
const Babl *format,
gpointer pixel);
void (* pixel_to_rgb) (GimpPickable *pickable,
const Babl *format,
gpointer pixel,
GimpRGB *color);
void (* rgb_to_pixel) (GimpPickable *pickable,
const GimpRGB *color,
const Babl *format,
gpointer pixel);
void (* flush) (GimpPickable *pickable);
GimpImage * (* get_image) (GimpPickable *pickable);
const Babl * (* get_format) (GimpPickable *pickable);
const Babl * (* get_format_with_alpha) (GimpPickable *pickable);
GeglBuffer * (* get_buffer) (GimpPickable *pickable);
GeglBuffer * (* get_buffer_with_effects) (GimpPickable *pickable);
gboolean (* get_pixel_at) (GimpPickable *pickable,
gint x,
gint y,
const Babl *format,
gpointer pixel);
gdouble (* get_opacity_at) (GimpPickable *pickable,
gint x,
gint y);
void (* get_pixel_average) (GimpPickable *pickable,
const GeglRectangle *rect,
const Babl *format,
gpointer pixel);
void (* pixel_to_rgb) (GimpPickable *pickable,
const Babl *format,
gpointer pixel,
GimpRGB *color);
void (* rgb_to_pixel) (GimpPickable *pickable,
const GimpRGB *color,
const Babl *format,
gpointer pixel);
};
void gimp_pickable_flush (GimpPickable *pickable);
GimpImage * gimp_pickable_get_image (GimpPickable *pickable);
const Babl * gimp_pickable_get_format (GimpPickable *pickable);
const Babl * gimp_pickable_get_format_with_alpha (GimpPickable *pickable);
GeglBuffer * gimp_pickable_get_buffer (GimpPickable *pickable);
gboolean gimp_pickable_get_pixel_at (GimpPickable *pickable,
gint x,
gint y,
const Babl *format,
gpointer pixel);
gboolean gimp_pickable_get_color_at (GimpPickable *pickable,
gint x,
gint y,
GimpRGB *color);
gdouble gimp_pickable_get_opacity_at (GimpPickable *pickable,
gint x,
gint y);
void gimp_pickable_get_pixel_average (GimpPickable *pickable,
const GeglRectangle *rect,
const Babl *format,
gpointer pixel);
void gimp_pickable_pixel_to_rgb (GimpPickable *pickable,
const Babl *format,
gpointer pixel,
GimpRGB *color);
void gimp_pickable_rgb_to_pixel (GimpPickable *pickable,
const GimpRGB *color,
const Babl *format,
gpointer pixel);
void gimp_pickable_srgb_to_image_color (GimpPickable *pickable,
const GimpRGB *color,
GimpRGB *image_color);
void gimp_pickable_flush (GimpPickable *pickable);
GimpImage * gimp_pickable_get_image (GimpPickable *pickable);
const Babl * gimp_pickable_get_format (GimpPickable *pickable);
const Babl * gimp_pickable_get_format_with_alpha (GimpPickable *pickable);
GeglBuffer * gimp_pickable_get_buffer (GimpPickable *pickable);
GeglBuffer * gimp_pickable_get_buffer_with_effects (GimpPickable *pickable);
gboolean gimp_pickable_get_pixel_at (GimpPickable *pickable,
gint x,
gint y,
const Babl *format,
gpointer pixel);
gboolean gimp_pickable_get_color_at (GimpPickable *pickable,
gint x,
gint y,
GimpRGB *color);
gdouble gimp_pickable_get_opacity_at (GimpPickable *pickable,
gint x,
gint y);
void gimp_pickable_get_pixel_average (GimpPickable *pickable,
const GeglRectangle *rect,
const Babl *format,
gpointer pixel);
void gimp_pickable_pixel_to_rgb (GimpPickable *pickable,
const Babl *format,
gpointer pixel,
GimpRGB *color);
void gimp_pickable_rgb_to_pixel (GimpPickable *pickable,
const GimpRGB *color,
const Babl *format,
gpointer pixel);
void gimp_pickable_srgb_to_image_color (GimpPickable *pickable,
const GimpRGB *color,
GimpRGB *image_color);
gboolean gimp_pickable_pick_color (GimpPickable *pickable,
gint x,
gint y,
gboolean sample_average,
gdouble average_radius,
gpointer pixel,
GimpRGB *color);
gboolean gimp_pickable_pick_color (GimpPickable *pickable,
gint x,
gint y,
gboolean sample_average,
gdouble average_radius,
gpointer pixel,
GimpRGB *color);
#endif /* __GIMP_PICKABLE_H__ */

View file

@ -114,6 +114,7 @@ libappcore_sources = [
'gimpdrawable-transform.c',
'gimpdrawable.c',
'gimpdrawablefilter.c',
'gimpdrawablefilterundo.c',
'gimpdrawablemodundo.c',
'gimpdrawablepropundo.c',
'gimpdrawablestack.c',

View file

@ -141,15 +141,27 @@ gimp_gegl_progress_connect (GeglNode *node,
g_return_if_fail (GIMP_IS_PROGRESS (progress));
g_return_if_fail (text != NULL);
g_signal_connect (node, "progress",
G_CALLBACK (gimp_gegl_progress_callback),
progress);
g_signal_connect_object (node, "progress",
G_CALLBACK (gimp_gegl_progress_callback),
progress, 0);
g_object_set_data_full (G_OBJECT (node),
"gimp-progress-text", g_strdup (text),
(GDestroyNotify) g_free);
}
void
gimp_gegl_progress_disconnect (GeglNode *node,
GimpProgress *progress)
{
g_return_if_fail (GEGL_IS_NODE (node));
g_return_if_fail (GIMP_IS_PROGRESS (progress));
g_signal_handlers_disconnect_by_func (node,
gimp_gegl_progress_callback,
progress);
}
gboolean
gimp_gegl_node_is_source_operation (GeglNode *node)
{

View file

@ -33,6 +33,8 @@ GeglColor * gimp_gegl_color_new (const GimpRGB *rgb,
void gimp_gegl_progress_connect (GeglNode *node,
GimpProgress *progress,
const gchar *text);
void gimp_gegl_progress_disconnect (GeglNode *node,
GimpProgress *progress);
gboolean gimp_gegl_node_is_source_operation (GeglNode *node);
gboolean gimp_gegl_node_is_point_operation (GeglNode *node);

View file

@ -33,6 +33,7 @@
#include "core/gimp.h"
#include "core/gimpchannel-select.h"
#include "core/gimpdrawable-fill.h"
#include "core/gimpdrawable-filters.h"
#include "core/gimpdrawable-foreground-extract.h"
#include "core/gimpdrawable-offset.h"
#include "core/gimpdrawable-preview.h"
@ -502,6 +503,35 @@ drawable_mask_intersect_invoker (GimpProcedure *procedure,
return return_vals;
}
static GimpValueArray *
drawable_merge_filters_invoker (GimpProcedure *procedure,
Gimp *gimp,
GimpContext *context,
GimpProgress *progress,
const GimpValueArray *args,
GError **error)
{
gboolean success = TRUE;
GimpDrawable *drawable;
drawable = g_value_get_object (gimp_value_array_index (args, 0));
if (success)
{
if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
GIMP_PDB_ITEM_CONTENT, error) &&
gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
{
gimp_drawable_merge_filters (drawable);
}
else
success = FALSE;
}
return gimp_procedure_get_return_values (procedure, success,
error ? *error : NULL);
}
static GimpValueArray *
drawable_merge_shadow_invoker (GimpProcedure *procedure,
Gimp *gimp,
@ -1348,6 +1378,29 @@ register_drawable_procs (GimpPDB *pdb)
gimp_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
/*
* gimp-drawable-merge-filters
*/
procedure = gimp_procedure_new (drawable_merge_filters_invoker);
gimp_object_set_static_name (GIMP_OBJECT (procedure),
"gimp-drawable-merge-filters");
gimp_procedure_set_static_help (procedure,
"Merge the layer effect filters to the specified drawable.",
"This procedure combines the contents of the drawable's filter stack (for export) with the specified drawable.",
NULL);
gimp_procedure_set_static_attribution (procedure,
"Spencer Kimball & Peter Mattis",
"Spencer Kimball & Peter Mattis",
"1995-1996");
gimp_procedure_add_argument (procedure,
gimp_param_spec_drawable ("drawable",
"drawable",
"The drawable",
FALSE,
GIMP_PARAM_READWRITE));
gimp_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
/*
* gimp-drawable-merge-shadow
*/

View file

@ -30,7 +30,7 @@
#include "internal-procs.h"
/* 781 procedures registered total */
/* 782 procedures registered total */
void
internal_procs_init (GimpPDB *pdb)

View file

@ -497,7 +497,7 @@ gimp_bucket_fill_tool_commit (GimpBucketFillTool *tool)
{
if (tool->priv->filter)
{
gimp_drawable_filter_commit (tool->priv->filter,
gimp_drawable_filter_commit (tool->priv->filter, FALSE,
GIMP_PROGRESS (tool), FALSE);
gimp_image_flush (gimp_display_get_image (GIMP_TOOL (tool)->display));
}

View file

@ -1029,7 +1029,8 @@ gimp_cage_tool_commit (GimpCageTool *ct)
gimp_tool_control_push_preserve (tool->control, TRUE);
gimp_drawable_filter_commit (ct->filter, GIMP_PROGRESS (tool), FALSE);
gimp_drawable_filter_commit (ct->filter, FALSE,
GIMP_PROGRESS (tool), FALSE);
g_clear_object (&ct->filter);
gimp_tool_control_pop_preserve (tool->control);

View file

@ -46,12 +46,14 @@
#include "core/gimp.h"
#include "core/gimpchannel.h"
#include "core/gimpdrawable.h"
#include "core/gimpdrawable-filters.h"
#include "core/gimpdrawablefilter.h"
#include "core/gimperror.h"
#include "core/gimpguide.h"
#include "core/gimpimage.h"
#include "core/gimpimage-guides.h"
#include "core/gimpimage-pick-color.h"
#include "core/gimpimage-undo-push.h"
#include "core/gimplayer.h"
#include "core/gimplist.h"
#include "core/gimppickable.h"
@ -153,7 +155,8 @@ static void gimp_filter_tool_real_config_notify
const GParamSpec *pspec);
static void gimp_filter_tool_halt (GimpFilterTool *filter_tool);
static void gimp_filter_tool_commit (GimpFilterTool *filter_tool);
static void gimp_filter_tool_commit (GimpFilterTool *filter_tool,
gboolean non_destructive);
static void gimp_filter_tool_dialog (GimpFilterTool *filter_tool);
static void gimp_filter_tool_reset (GimpFilterTool *filter_tool);
@ -331,7 +334,7 @@ gimp_filter_tool_initialize (GimpTool *tool,
return FALSE;
}
gimp_filter_tool_get_operation (filter_tool);
gimp_filter_tool_get_operation (filter_tool, NULL);
gimp_filter_tool_disable_color_picking (filter_tool);
@ -339,7 +342,7 @@ gimp_filter_tool_initialize (GimpTool *tool,
g_list_free (tool->drawables);
tool->drawables = drawables;
if (filter_tool->config)
if (filter_tool->config && ! filter_tool->existing_filter)
gimp_config_reset (GIMP_CONFIG (filter_tool->config));
if (! filter_tool->gui)
@ -465,7 +468,8 @@ gimp_filter_tool_control (GimpTool *tool,
GimpToolAction action,
GimpDisplay *display)
{
GimpFilterTool *filter_tool = GIMP_FILTER_TOOL (tool);
GimpFilterTool *filter_tool = GIMP_FILTER_TOOL (tool);
gboolean non_destructive = TRUE;
switch (action)
{
@ -478,7 +482,12 @@ gimp_filter_tool_control (GimpTool *tool,
break;
case GIMP_TOOL_ACTION_COMMIT:
gimp_filter_tool_commit (filter_tool);
/* TODO: Expand non-destructive editing to other drawables
* besides layers */
if (! GIMP_IS_LAYER (tool->drawables->data))
non_destructive = FALSE;
gimp_filter_tool_commit (filter_tool, non_destructive);
break;
}
@ -994,10 +1003,17 @@ gimp_filter_tool_halt (GimpFilterTool *filter_tool)
if (filter_tool->filter)
{
gimp_drawable_filter_abort (filter_tool->filter);
g_signal_handlers_disconnect_by_func (filter_tool->filter,
gimp_filter_tool_flush,
filter_tool);
g_clear_object (&filter_tool->filter);
gimp_filter_tool_remove_guide (filter_tool);
}
if (filter_tool->operation)
gimp_gegl_progress_disconnect (filter_tool->operation,
GIMP_PROGRESS (filter_tool));
g_clear_object (&filter_tool->operation);
if (filter_tool->config)
@ -1022,16 +1038,80 @@ gimp_filter_tool_halt (GimpFilterTool *filter_tool)
tool->display = NULL;
g_list_free (tool->drawables);
tool->drawables = NULL;
if (filter_tool->existing_filter)
{
gimp_filter_set_active (GIMP_FILTER (filter_tool->existing_filter), TRUE);
/* Restore buttons in layer tree view */
gimp_drawable_filters_changed (gimp_drawable_filter_get_drawable (filter_tool->existing_filter));
}
filter_tool->existing_filter = NULL;
}
/* Add code to prevent creating new filter when editing */
static void
gimp_filter_tool_commit (GimpFilterTool *filter_tool)
gimp_filter_tool_commit (GimpFilterTool *filter_tool,
gboolean non_destructive)
{
GimpTool *tool = GIMP_TOOL (filter_tool);
if (filter_tool->gui)
gimp_tool_gui_hide (filter_tool->gui);
/* Copy over filter info back to existing filter */
if (filter_tool->existing_filter)
{
GeglNode *existing_node;
gdouble opacity;
GimpLayerMode paint_mode;
GimpLayerColorSpace blend_space;
GimpLayerColorSpace composite_space;
GimpLayerCompositeMode composite_mode;
GimpFilterRegion region;
GParamSpec **pspecs;
guint n_pspecs;
gchar *name = NULL;
opacity = gimp_drawable_filter_get_opacity (filter_tool->filter);
paint_mode = gimp_drawable_filter_get_paint_mode (filter_tool->filter);
blend_space = gimp_drawable_filter_get_blend_space (filter_tool->filter);
composite_space = gimp_drawable_filter_get_composite_space (filter_tool->filter);
composite_mode = gimp_drawable_filter_get_composite_mode (filter_tool->filter);
region = gimp_drawable_filter_get_region (filter_tool->filter);
existing_node = gimp_drawable_filter_get_operation (filter_tool->existing_filter);
gegl_node_get (existing_node,
"operation", &name,
NULL);
pspecs = gegl_operation_list_properties (name, &n_pspecs);
g_free (name);
for (gint i = 0; i < n_pspecs; i++)
{
GParamSpec *pspec = pspecs[i];
GValue value = G_VALUE_INIT;
g_value_init (&value, pspec->value_type);
gegl_node_get_property (filter_tool->operation, pspec->name,
&value);
gegl_node_set_property (existing_node, pspec->name,
&value);
}
gimp_drawable_filter_set_opacity (filter_tool->existing_filter, opacity);
gimp_drawable_filter_set_mode (filter_tool->existing_filter,
paint_mode, blend_space, composite_space,
composite_mode);
gimp_drawable_filter_set_region (filter_tool->existing_filter, region);
/* Restore buttons in layer tree view */
gimp_drawable_filters_changed (gimp_drawable_filter_get_drawable (filter_tool->existing_filter));
}
if (filter_tool->filter)
{
GimpFilterOptions *options = GIMP_FILTER_TOOL_GET_OPTIONS (tool);
@ -1039,10 +1119,36 @@ gimp_filter_tool_commit (GimpFilterTool *filter_tool)
if (! options->preview)
gimp_drawable_filter_apply (filter_tool->filter, NULL);
gimp_drawable_filter_layer_mask_freeze (filter_tool->filter);
if (non_destructive)
{
gimp_drawable_filter_abort (filter_tool->filter);
if (! filter_tool->existing_filter)
gimp_drawable_filter_apply (filter_tool->filter, NULL);
}
gimp_tool_control_push_preserve (tool->control, TRUE);
gimp_drawable_filter_commit (filter_tool->filter,
GIMP_PROGRESS (tool), TRUE);
if (! filter_tool->existing_filter)
gimp_drawable_filter_commit (filter_tool->filter, non_destructive,
GIMP_PROGRESS (tool), non_destructive);
g_signal_handlers_disconnect_by_func (filter_tool->filter,
gimp_filter_tool_flush,
filter_tool);
if (non_destructive && ! filter_tool->existing_filter)
{
GimpDrawable *drawable =
gimp_drawable_filter_get_drawable (filter_tool->filter);
gimp_image_undo_push_filter_add (gimp_display_get_image (tool->display),
_("Add filter"),
drawable, filter_tool->filter);
}
g_clear_object (&filter_tool->filter);
gimp_tool_control_pop_preserve (tool->control);
@ -1059,6 +1165,11 @@ gimp_filter_tool_commit (GimpFilterTool *filter_tool)
config->filter_tool_max_recent);
}
}
if (filter_tool->existing_filter)
gimp_filter_set_active (GIMP_FILTER (filter_tool->existing_filter), TRUE);
filter_tool->existing_filter = NULL;
}
static void
@ -1227,6 +1338,33 @@ gimp_filter_tool_create_filter (GimpFilterTool *filter_tool)
if (options->preview)
gimp_drawable_filter_apply (filter_tool->filter, NULL);
/* If editing existing filter, shift into the right location
* in the filter stack. */
if (filter_tool->existing_filter)
{
GimpContainer *filters;
GimpChannel *mask;
gint index;
const gchar *name = _("Editing filter...");
filters = gimp_drawable_get_filters (GIMP_DRAWABLE (tool->drawables->data));
if (filters)
{
index = gimp_container_get_child_index (filters,
GIMP_OBJECT (filter_tool->existing_filter));
gimp_container_reorder (filters, GIMP_OBJECT (filter_tool->filter),
index);
}
mask = gimp_drawable_filter_get_mask (filter_tool->existing_filter);
g_object_set (filter_tool->filter,
"name", name,
"mask", mask,
NULL);
}
}
static void
@ -1594,7 +1732,8 @@ gimp_filter_tool_set_has_settings (GimpFilterTool *filter_tool,
/* public functions */
void
gimp_filter_tool_get_operation (GimpFilterTool *filter_tool)
gimp_filter_tool_get_operation (GimpFilterTool *filter_tool,
GimpDrawableFilter *existing_filter)
{
GimpTool *tool;
GimpFilterToolClass *klass;
@ -1643,6 +1782,11 @@ gimp_filter_tool_get_operation (GimpFilterTool *filter_tool)
filter_tool->operation = gegl_node_new_child (NULL,
"operation", operation_name,
NULL);
if (existing_filter)
{
filter_tool->existing_filter = existing_filter;
gimp_filter_set_active (GIMP_FILTER (filter_tool->existing_filter), FALSE);
}
filter_tool->config =
g_object_new (gimp_operation_config_get_type (tool->tool_info->gimp,
@ -1651,6 +1795,55 @@ gimp_filter_tool_get_operation (GimpFilterTool *filter_tool)
GIMP_TYPE_OPERATION_SETTINGS),
NULL);
/* Update layer effect if we're editing it */
if (filter_tool->existing_filter)
{
GeglNode *existing_node;
gdouble opacity;
GimpLayerMode paint_mode;
GimpFilterRegion region;
GParamSpec **pspecs;
guint n_pspecs;
const gchar *name;
opacity = gimp_drawable_filter_get_opacity (filter_tool->existing_filter);
paint_mode = gimp_drawable_filter_get_paint_mode (filter_tool->existing_filter);
region = gimp_drawable_filter_get_region (filter_tool->existing_filter);
existing_node = gimp_drawable_filter_get_operation (filter_tool->existing_filter);
gegl_node_get (existing_node,
"operation", &name,
NULL);
if (! strcmp (gimp_object_get_name (tool->tool_info), "gimp-operation-tool"))
{
pspecs = gegl_operation_list_properties (operation_name, &n_pspecs);
for (gint i = 0; i < n_pspecs; i++)
{
GValue value = G_VALUE_INIT;
GParamSpec *pspec = pspecs[i];
GParamSpec *gimp_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (filter_tool->config),
pspec->name);
g_value_init (&value, pspec->value_type);
gegl_node_get_property (existing_node, pspec->name,
&value);
g_object_set_property (G_OBJECT (filter_tool->config), gimp_pspec->name,
&value);
g_value_unset (&value);
}
g_free (pspecs);
}
g_object_set (filter_tool->config,
"gimp-opacity", opacity,
"gimp-mode", paint_mode,
"gimp-region", region,
NULL);
}
gimp_operation_config_sync_node (filter_tool->config,
filter_tool->operation);
gimp_operation_config_connect_node (filter_tool->config,

View file

@ -39,6 +39,7 @@ struct _GimpFilterTool
GimpColorTool parent_instance;
GeglNode *operation;
GimpDrawableFilter *existing_filter;
GObject *config;
GObject *default_config;
GimpContainer *settings;
@ -103,7 +104,8 @@ struct _GimpFilterToolClass
GType gimp_filter_tool_get_type (void) G_GNUC_CONST;
void gimp_filter_tool_get_operation (GimpFilterTool *filter_tool);
void gimp_filter_tool_get_operation (GimpFilterTool *filter_tool,
GimpDrawableFilter *existing_filter);
void gimp_filter_tool_set_config (GimpFilterTool *filter_tool,
GimpConfig *config);

View file

@ -249,7 +249,7 @@ gimp_gegl_tool_halt (GimpGeglTool *gegl_tool)
{
GimpOperationTool *op_tool = GIMP_OPERATION_TOOL (gegl_tool);
gimp_operation_tool_set_operation (op_tool, NULL,
gimp_operation_tool_set_operation (op_tool, NULL, NULL,
NULL, NULL, NULL, NULL, NULL);
}
@ -272,10 +272,15 @@ gimp_gegl_tool_operation_changed (GtkWidget *widget,
if (operation)
{
const gchar *title;
const gchar *description;
title = gegl_operation_get_key (operation, "title");
description = gegl_operation_get_key (operation, "description");
if (! title)
title = gegl_operation_get_key (operation, "name");
if (description)
{
gtk_label_set_text (GTK_LABEL (tool->description_label), description);
@ -287,9 +292,11 @@ gimp_gegl_tool_operation_changed (GtkWidget *widget,
}
gimp_operation_tool_set_operation (GIMP_OPERATION_TOOL (tool),
NULL,
operation,
_("GEGL Operation"),
_("GEGL Operation"),
title ?
title : _("GEGL Operation"),
NULL,
GIMP_ICON_GEGL,
GIMP_HELP_TOOL_GEGL);

View file

@ -769,7 +769,7 @@ gimp_gradient_tool_commit (GimpGradientTool *gradient_tool)
gimp_tool_control_push_preserve (tool->control, TRUE);
gimp_drawable_filter_commit (gradient_tool->filter,
gimp_drawable_filter_commit (gradient_tool->filter, FALSE,
GIMP_PROGRESS (tool), FALSE);
g_clear_object (&gradient_tool->filter);

View file

@ -810,13 +810,14 @@ gimp_operation_tool_relink_chains (GimpOperationTool *op_tool)
/* public functions */
void
gimp_operation_tool_set_operation (GimpOperationTool *op_tool,
const gchar *operation,
const gchar *title,
const gchar *description,
const gchar *undo_desc,
const gchar *icon_name,
const gchar *help_id)
gimp_operation_tool_set_operation (GimpOperationTool *op_tool,
GimpDrawableFilter *filter,
const gchar *operation,
const gchar *title,
const gchar *description,
const gchar *undo_desc,
const gchar *icon_name,
const gchar *help_id)
{
GimpTool *tool;
GimpFilterTool *filter_tool;
@ -855,7 +856,13 @@ gimp_operation_tool_set_operation (GimpOperationTool *op_tool,
if (! operation)
return;
gimp_filter_tool_get_operation (filter_tool);
gimp_filter_tool_get_operation (filter_tool, filter);
/* Update filter name for GeglOperation tool presets */
if (filter_tool->filter && description)
g_object_set (filter_tool->filter,
"name", description,
NULL);
if (tool->drawables)
gimp_operation_tool_sync_op (op_tool, TRUE);

View file

@ -60,6 +60,7 @@ void gimp_operation_tool_register (GimpToolRegisterCallback callback,
GType gimp_operation_tool_get_type (void) G_GNUC_CONST;
void gimp_operation_tool_set_operation (GimpOperationTool *op_tool,
GimpDrawableFilter *filter,
const gchar *operation,
const gchar *title,
const gchar *description,

View file

@ -379,7 +379,8 @@ gimp_seamless_clone_tool_commit (GimpSeamlessCloneTool *sc)
{
gimp_tool_control_push_preserve (tool->control, TRUE);
gimp_drawable_filter_commit (sc->filter, GIMP_PROGRESS (tool), FALSE);
gimp_drawable_filter_commit (sc->filter, FALSE,
GIMP_PROGRESS (tool), FALSE);
g_clear_object (&sc->filter);
gimp_tool_control_pop_preserve (tool->control);
@ -528,7 +529,8 @@ gimp_seamless_clone_tool_key_press (GimpTool *tool,
* rectangle each time (in the update function) or by
* invalidating and re-rendering all now (expensive and
* perhaps useless */
gimp_drawable_filter_commit (sct->filter, GIMP_PROGRESS (tool), FALSE);
gimp_drawable_filter_commit (sct->filter, FALSE,
GIMP_PROGRESS (tool), FALSE);
g_clear_object (&sct->filter);
gimp_tool_control_set_preserve (tool->control, FALSE);

View file

@ -35,6 +35,8 @@
#include "core/gimpasyncset.h"
#include "core/gimpcontext.h"
#include "core/gimpdatafactory.h"
#include "core/gimpdrawable-filters.h"
#include "core/gimpdrawablefilter.h"
#include "core/gimperror.h"
#include "core/gimpimage.h"
#include "core/gimp-palettes.h"
@ -42,6 +44,7 @@
#include "core/gimpimage-undo.h"
#include "core/gimpimage-undo-push.h"
#include "core/gimplayer-floating-selection.h"
#include "core/gimplist.h"
#include "core/gimptoolinfo.h"
#include "core/gimpundostack.h"
@ -1089,6 +1092,26 @@ gimp_text_tool_frame_item (GimpTextTool *text_tool)
gimp_tool_rectangle_frame_item (GIMP_TOOL_RECTANGLE (text_tool->widget),
GIMP_ITEM (text_tool->layer));
/* Update crop of any filters applied to text */
if (text_tool->layer)
{
GList *list;
GimpDrawable *drawable = GIMP_DRAWABLE (text_tool->layer);
GimpContainer *filters = gimp_drawable_get_filters (drawable);
for (list = GIMP_LIST (filters)->queue->tail;
list; list = g_list_previous (list))
{
GimpDrawableFilter *filter = list->data;
/* TODO: Handle partial layer effect */
gimp_drawable_filter_set_region (filter, GIMP_FILTER_REGION_SELECTION);
gimp_drawable_filter_set_region (filter, GIMP_FILTER_REGION_DRAWABLE);
}
if (list)
g_list_free (list);
}
text_tool->handle_rectangle_change_complete = TRUE;
}

View file

@ -982,7 +982,8 @@ gimp_warp_tool_commit (GimpWarpTool *wt)
gimp_warp_tool_set_sampler (wt, /* commit = */ TRUE);
gimp_drawable_filter_commit (wt->filter, GIMP_PROGRESS (tool), FALSE);
gimp_drawable_filter_commit (wt->filter, FALSE,
GIMP_PROGRESS (tool), FALSE);
g_clear_object (&wt->filter);
gimp_tool_control_pop_preserve (tool->control);

View file

@ -30,15 +30,23 @@
#include "widgets-types.h"
#include "actions/gimpgeglprocedure.h"
#include "actions/filters-commands.h"
#include "core/gimp.h"
#include "core/gimp-filter-history.h"
#include "core/gimpchannel.h"
#include "core/gimpcontainer.h"
#include "core/gimpcontext.h"
#include "core/gimpdrawable-filters.h"
#include "core/gimpdrawablefilter.h"
#include "core/gimpdrawablefilterundo.h"
#include "core/gimpimage.h"
#include "core/gimpimage-undo.h"
#include "core/gimpimage-undo-push.h"
#include "core/gimpitem-exclusive.h"
#include "core/gimpitemundo.h"
#include "core/gimplist.h"
#include "core/gimptreehandler.h"
#include "core/gimpundostack.h"
@ -50,6 +58,7 @@
#include "gimpdnd.h"
#include "gimpdocked.h"
#include "gimpitemtreeview.h"
#include "gimplayertreeview.h"
#include "gimpmenufactory.h"
#include "gimpviewrenderer.h"
#include "gimpuimanager.h"
@ -80,6 +89,21 @@ struct _GimpItemTreeViewPrivate
GtkWidget *lock_popover;
GtkWidget *lock_box;
GtkWidget *effects_popover;
GtkWidget *effects_box;
GtkWidget *effects_filters;
GtkWidget *effects_options;
GtkWidget *effects_visible_button;
GtkWidget *effects_edit_button;
GtkWidget *effects_raise_button;
GtkWidget *effects_lower_button;
GtkWidget *effects_merge_button;
GtkWidget *effects_remove_button;
GimpDrawable *effects_drawable;
GimpDrawableFilter
*effects_filter;
GList *locks;
GtkWidget *new_button;
@ -93,11 +117,14 @@ struct _GimpItemTreeViewPrivate
gint model_column_locked;
gint model_column_lock_icon;
gint model_column_color_tag;
gint model_column_effects;
GtkCellRenderer *eye_cell;
GtkCellRenderer *lock_cell;
GtkCellRenderer *effects_cell;
GimpTreeHandler *visible_changed_handler;
GimpTreeHandler *color_tag_changed_handler;
GimpTreeHandler *filters_changed_handler;
};
typedef struct
@ -200,6 +227,8 @@ static void gimp_item_tree_view_color_tag_changed (GimpItem *item,
GimpItemTreeView *view);
static void gimp_item_tree_view_lock_changed (GimpItem *item,
GimpItemTreeView *view);
static void gimp_item_tree_view_filters_changed (GimpItem *item,
GimpItemTreeView *view);
static void gimp_item_tree_view_eye_clicked (GtkCellRendererToggle *toggle,
gchar *path,
@ -209,9 +238,43 @@ static void gimp_item_tree_view_lock_clicked (GtkCellRendererToggle *togg
gchar *path,
GdkModifierType state,
GimpItemTreeView *view);
static void gimp_item_tree_view_effects_clicked (GtkCellRendererToggle *toggle,
gchar *path,
GdkModifierType state,
GimpItemTreeView *view);
static gboolean
gimp_item_tree_view_effects_filters_selected
(GimpContainerView *view,
GList *filters,
GList *paths,
GimpItemTreeView *item_view);
static void gimp_item_tree_view_effects_activate_filter
(GtkWidget *widget,
GimpViewable *viewable,
gpointer insert_data,
GimpItemTreeView *view);
static void gimp_item_tree_view_effects_visible_toggled
(GtkWidget *widget,
GimpItemTreeView *view);
static void gimp_item_tree_view_effects_edited_clicked
(GtkWidget *widget,
GimpItemTreeView *view);
static void gimp_item_tree_view_effects_raised_clicked
(GtkWidget *widget,
GimpItemTreeView *view);
static void gimp_item_tree_view_effects_lowered_clicked
(GtkWidget *widget,
GimpItemTreeView *view);
static void gimp_item_tree_view_effects_merged_clicked
(GtkWidget *widget,
GimpItemTreeView *view);
static void gimp_item_tree_view_effects_removed_clicked
(GtkWidget *widget,
GimpItemTreeView *view);
static gboolean gimp_item_tree_view_lock_button_release (GtkWidget *widget,
GdkEvent *event,
GimpItemTreeView *view);
GdkEvent *event,
GimpItemTreeView *view);
static void gimp_item_tree_view_lock_toggled (GtkWidget *widget,
GimpItemTreeView *view);
static void gimp_item_tree_view_update_lock_box (GimpItemTreeView *view,
@ -363,9 +426,16 @@ gimp_item_tree_view_init (GimpItemTreeView *view)
&tree_view->n_model_columns,
GDK_TYPE_RGBA);
view->priv->model_column_effects =
gimp_container_tree_store_columns_add (tree_view->model_columns,
&tree_view->n_model_columns,
G_TYPE_BOOLEAN);
gimp_container_tree_view_set_dnd_drop_to_empty (tree_view, TRUE);
view->priv->image = NULL;
view->priv->image = NULL;
view->priv->effects_drawable = NULL;
view->priv->effects_filter = NULL;
}
static void
@ -377,6 +447,8 @@ gimp_item_tree_view_constructed (GObject *object)
GimpItemTreeView *item_view = GIMP_ITEM_TREE_VIEW (object);
GtkTreeViewColumn *column;
GtkWidget *image;
GtkWidget *label;
gchar *text;
GtkIconSize button_icon_size = GTK_ICON_SIZE_SMALL_TOOLBAR;
gint pixel_icon_size = 16;
gint button_spacing;
@ -462,6 +534,32 @@ gimp_item_tree_view_constructed (GObject *object)
G_CALLBACK (gimp_item_tree_view_lock_clicked),
item_view);
/* TODO: Expand layer effects to other drawable types */
if (GIMP_IS_LAYER_TREE_VIEW (object))
{
column = gtk_tree_view_column_new ();
gtk_tree_view_insert_column (tree_view->view, column, 2);
item_view->priv->effects_cell = gimp_cell_renderer_toggle_new (GIMP_ICON_DISPLAY_FILTER);
g_object_set (item_view->priv->effects_cell,
"xpad", 0,
"ypad", 0,
"icon-size", pixel_icon_size,
NULL);
gtk_tree_view_column_pack_start (column, item_view->priv->effects_cell, FALSE);
gtk_tree_view_column_set_attributes (column, item_view->priv->effects_cell,
"active",
item_view->priv->model_column_effects,
NULL);
gimp_container_tree_view_add_toggle_cell (tree_view,
item_view->priv->effects_cell);
g_signal_connect (item_view->priv->effects_cell, "clicked",
G_CALLBACK (gimp_item_tree_view_effects_clicked),
item_view);
}
/* disable the default GimpContainerView drop handler */
gimp_container_view_set_dnd_widget (GIMP_CONTAINER_VIEW (item_view), NULL);
@ -577,6 +675,120 @@ gimp_item_tree_view_constructed (GObject *object)
item_view);
gtk_container_add (GTK_CONTAINER (item_view->priv->lock_popover), item_view->priv->lock_box);
gtk_widget_show (item_view->priv->lock_box);
/* Effects box. */
if (GIMP_IS_LAYER_TREE_VIEW (object))
{
item_view->priv->effects_filters = gtk_box_new (GTK_ORIENTATION_VERTICAL, button_spacing);
item_view->priv->effects_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, button_spacing);
item_view->priv->effects_options = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, button_spacing);
/* Effects Buttons */
item_view->priv->effects_visible_button = gtk_toggle_button_new ();
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (item_view->priv->effects_visible_button),
TRUE);
image = gtk_image_new_from_icon_name (GIMP_ICON_VISIBLE,
GTK_ICON_SIZE_SMALL_TOOLBAR);
gtk_container_add (GTK_CONTAINER (item_view->priv->effects_visible_button), image);
gimp_help_set_help_data (item_view->priv->effects_visible_button,
_("Toggle the visibility of all filters."),
NULL);
g_signal_connect (item_view->priv->effects_visible_button, "toggled",
G_CALLBACK (gimp_item_tree_view_effects_visible_toggled),
item_view);
gtk_box_pack_start (GTK_BOX (item_view->priv->effects_options),
item_view->priv->effects_visible_button, TRUE, TRUE, 0);
gtk_widget_set_visible (image, TRUE);
gtk_widget_set_visible (item_view->priv->effects_visible_button, TRUE);
item_view->priv->effects_edit_button =
gtk_button_new_from_icon_name (GIMP_ICON_EDIT,
GTK_ICON_SIZE_SMALL_TOOLBAR);
gimp_help_set_help_data (item_view->priv->effects_edit_button,
_("Edit the selected filter."),
NULL);
g_signal_connect (item_view->priv->effects_edit_button, "clicked",
G_CALLBACK (gimp_item_tree_view_effects_edited_clicked),
item_view);
gtk_box_pack_start (GTK_BOX (item_view->priv->effects_options),
item_view->priv->effects_edit_button, TRUE, TRUE, 0);
gtk_widget_set_visible (item_view->priv->effects_edit_button, TRUE);
item_view->priv->effects_raise_button =
gtk_button_new_from_icon_name (GIMP_ICON_GO_UP,
GTK_ICON_SIZE_SMALL_TOOLBAR);
gimp_help_set_help_data (item_view->priv->effects_raise_button,
_("Raise filter one step up in the stack."),
NULL);
g_signal_connect (item_view->priv->effects_raise_button, "clicked",
G_CALLBACK (gimp_item_tree_view_effects_raised_clicked),
item_view);
gtk_box_pack_start (GTK_BOX (item_view->priv->effects_options),
item_view->priv->effects_raise_button, TRUE, TRUE, 0);
gtk_widget_set_visible (item_view->priv->effects_raise_button, TRUE);
item_view->priv->effects_lower_button =
gtk_button_new_from_icon_name (GIMP_ICON_GO_DOWN,
GTK_ICON_SIZE_SMALL_TOOLBAR);
gimp_help_set_help_data (item_view->priv->effects_lower_button,
_("Lower filter one step down in the stack."),
NULL);
g_signal_connect (item_view->priv->effects_lower_button, "clicked",
G_CALLBACK (gimp_item_tree_view_effects_lowered_clicked),
item_view);
gtk_box_pack_start (GTK_BOX (item_view->priv->effects_options),
item_view->priv->effects_lower_button, TRUE, TRUE, 0);
gtk_widget_set_visible (item_view->priv->effects_lower_button, TRUE);
item_view->priv->effects_merge_button =
gtk_button_new_from_icon_name (GIMP_ICON_LAYER_MERGE_DOWN,
GTK_ICON_SIZE_SMALL_TOOLBAR);
gimp_help_set_help_data (item_view->priv->effects_merge_button,
_("Merge all active filters down."),
NULL);
g_signal_connect (item_view->priv->effects_merge_button, "clicked",
G_CALLBACK (gimp_item_tree_view_effects_merged_clicked),
item_view);
gtk_box_pack_start (GTK_BOX (item_view->priv->effects_options),
item_view->priv->effects_merge_button, TRUE, TRUE, 0);
gtk_widget_set_visible (item_view->priv->effects_merge_button, TRUE);
item_view->priv->effects_remove_button =
gtk_button_new_from_icon_name (GIMP_ICON_EDIT_DELETE,
GTK_ICON_SIZE_SMALL_TOOLBAR);
gimp_help_set_help_data (item_view->priv->effects_remove_button,
_("Remove the selected filter."),
NULL);
g_signal_connect (item_view->priv->effects_remove_button, "clicked",
G_CALLBACK (gimp_item_tree_view_effects_removed_clicked),
item_view);
gtk_box_pack_start (GTK_BOX (item_view->priv->effects_options),
item_view->priv->effects_remove_button, TRUE, TRUE, 0);
gtk_widget_set_visible (item_view->priv->effects_remove_button, TRUE);
label = gtk_label_new (NULL);
text = g_strdup_printf ("<b>%s</b>",
_("Layer Effects"));
gtk_label_set_markup (GTK_LABEL (label), text);
gtk_widget_set_visible (label, TRUE);
g_free (text);
/* Effects popover. */
item_view->priv->effects_popover = gtk_popover_new (GTK_WIDGET (tree_view->view));
gtk_popover_set_modal (GTK_POPOVER (item_view->priv->effects_popover), TRUE);
gtk_container_add (GTK_CONTAINER (item_view->priv->effects_popover),
item_view->priv->effects_filters);
gtk_box_pack_start (GTK_BOX (item_view->priv->effects_filters), label,
FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (item_view->priv->effects_filters),
item_view->priv->effects_box, FALSE, FALSE, 0);
gtk_box_pack_start (GTK_BOX (item_view->priv->effects_filters),
item_view->priv->effects_options, FALSE, FALSE, 0);
gtk_widget_set_visible (item_view->priv->effects_box, TRUE);
gtk_widget_set_visible (item_view->priv->effects_options, TRUE);
gtk_widget_set_visible (item_view->priv->effects_filters, TRUE);
}
}
static void
@ -606,6 +818,14 @@ gimp_item_tree_view_dispose (GObject *object)
view->priv->locks = NULL;
}
if (view->priv->effects_popover)
{
gtk_widget_destroy (view->priv->effects_popover);
view->priv->effects_popover = NULL;
}
view->priv->effects_drawable = NULL;
view->priv->effects_filter = NULL;
G_OBJECT_CLASS (parent_class)->dispose (object);
}
@ -722,6 +942,12 @@ gimp_item_tree_view_style_updated (GtkWidget *widget)
"icon-size", pixel_icon_size,
NULL);
if (GIMP_IS_LAYER_TREE_VIEW (view))
g_object_set (view->priv->effects_cell,
"icon-name", GIMP_ICON_DISPLAY_FILTER,
"icon-size", pixel_icon_size,
NULL);
GTK_WIDGET_CLASS (parent_class)->style_updated (widget);
}
@ -1033,7 +1259,9 @@ gimp_item_tree_view_real_set_image (GimpItemTreeView *view,
view);
}
view->priv->image = image;
view->priv->image = image;
view->priv->effects_drawable = NULL;
view->priv->effects_filter = NULL;
if (view->priv->image)
{
@ -1106,6 +1334,12 @@ gimp_item_tree_view_set_container (GimpContainerView *view,
gimp_tree_handler_disconnect (item_view->priv->color_tag_changed_handler);
item_view->priv->color_tag_changed_handler = NULL;
if (GIMP_IS_LAYER_TREE_VIEW (item_view))
{
gimp_tree_handler_disconnect (item_view->priv->filters_changed_handler);
item_view->priv->filters_changed_handler = NULL;
}
for (list = item_view->priv->locks; list; list = list->next)
{
LockToggle *data = list->data;
@ -1138,6 +1372,12 @@ gimp_item_tree_view_set_container (GimpContainerView *view,
G_CALLBACK (gimp_item_tree_view_lock_changed),
view);
}
if (GIMP_IS_LAYER_TREE_VIEW (item_view))
item_view->priv->filters_changed_handler =
gimp_tree_handler_connect (container, "filters-changed",
G_CALLBACK (gimp_item_tree_view_filters_changed),
view);
}
}
@ -1236,6 +1476,20 @@ gimp_item_tree_view_insert_item (GimpContainerView *view,
has_color ? (GdkRGBA *) &color : NULL,
-1);
if (GIMP_IS_LAYER_TREE_VIEW (item_view))
{
GimpContainer *filters;
gint n_filters;
filters = gimp_drawable_get_filters (GIMP_DRAWABLE (item));
n_filters = gimp_container_get_n_children (filters);
gtk_tree_store_set (GTK_TREE_STORE (tree_view->model), iter,
item_view->priv->model_column_effects,
n_filters > 0,
-1);
}
return iter;
}
@ -1764,6 +2018,407 @@ gimp_item_tree_view_lock_clicked (GtkCellRendererToggle *toggle,
}
}
/* "Effects" callbacks */
static void
gimp_item_tree_view_effects_clicked (GtkCellRendererToggle *toggle,
gchar *path_str,
GdkModifierType state,
GimpItemTreeView *view)
{
GtkTreePath *path;
GtkTreeIter iter;
path = gtk_tree_path_new_from_string (path_str);
if (gtk_tree_model_get_iter (GIMP_CONTAINER_TREE_VIEW (view)->model,
&iter, path))
{
GimpViewRenderer *renderer;
GimpContainerTreeStore *store;
GimpItem *item;
GimpContainer *filters;
GdkRectangle rect;
GtkWidget *filter_view;
GList *filter_list;
GList *children;
gint n_children = 0;
gboolean visible = TRUE;
children = gtk_container_get_children (GTK_CONTAINER (view->priv->effects_box));
/* Update the filter state. */
store = GIMP_CONTAINER_TREE_STORE (GIMP_CONTAINER_TREE_VIEW (view)->model);
renderer = gimp_container_tree_store_get_renderer (store, &iter);
item = GIMP_ITEM (renderer->viewable);
g_object_unref (renderer);
/* Get filters */
if (children)
{
g_signal_handlers_disconnect_by_func (children->data,
gimp_item_tree_view_effects_filters_selected,
view);
g_signal_handlers_disconnect_by_func (children->data,
gimp_item_tree_view_effects_activate_filter,
view);
gtk_widget_destroy (children->data);
g_list_free (children);
}
view->priv->effects_drawable = GIMP_DRAWABLE (item);
filters = gimp_drawable_get_filters (GIMP_DRAWABLE (item));
for (filter_list = GIMP_LIST (filters)->queue->tail; filter_list;
filter_list = g_list_previous (filter_list))
{
if (GIMP_IS_DRAWABLE_FILTER (filter_list->data))
{
if (! gimp_filter_get_active (GIMP_FILTER (filter_list->data)))
visible = FALSE;
n_children++;
}
}
/* TODO: Revisit when we can set individual filter visiblity */
g_signal_handlers_block_by_func (view->priv->effects_visible_button,
gimp_item_tree_view_effects_visible_toggled,
view);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (view->priv->effects_visible_button),
visible);
g_signal_handlers_unblock_by_func (view->priv->effects_visible_button,
gimp_item_tree_view_effects_visible_toggled,
view);
/* Only show if we have at least one active filter */
if (n_children > 0)
{
filter_view = gimp_container_tree_view_new (filters,
gimp_container_view_get_context (GIMP_CONTAINER_VIEW (view)),
GIMP_VIEW_SIZE_SMALL, 0);
g_signal_connect (GIMP_CONTAINER_TREE_VIEW (filter_view),
"select-items",
G_CALLBACK (gimp_item_tree_view_effects_filters_selected),
view);
g_signal_connect_object (GIMP_CONTAINER_TREE_VIEW (filter_view),
"activate-item",
G_CALLBACK (gimp_item_tree_view_effects_activate_filter),
view, 0);
gtk_box_pack_start (GTK_BOX (view->priv->effects_box), filter_view, TRUE, TRUE, 0);
gtk_widget_set_visible (filter_view, TRUE);
gtk_widget_set_size_request (view->priv->effects_box, 200, 24 * (n_children + 1));
/* Change popover position. */
gtk_tree_view_get_cell_area (GIMP_CONTAINER_TREE_VIEW (view)->view, path,
gtk_tree_view_get_column (GIMP_CONTAINER_TREE_VIEW (view)->view, 2),
&rect);
gtk_tree_view_convert_bin_window_to_widget_coords (GIMP_CONTAINER_TREE_VIEW (view)->view,
rect.x, rect.y, &rect.x, &rect.y);
gtk_popover_set_pointing_to (GTK_POPOVER (view->priv->effects_popover), &rect);
gimp_item_tree_view_filters_changed (item, view);
gtk_widget_show (view->priv->effects_popover);
}
}
}
static gboolean
gimp_item_tree_view_effects_filters_selected (GimpContainerView *view,
GList *filters,
GList *paths,
GimpItemTreeView *item_view)
{
g_return_val_if_fail (g_list_length (filters) <= 1, FALSE);
if (filters &&
item_view->priv->effects_drawable &&
GIMP_IS_DRAWABLE (item_view->priv->effects_drawable))
{
GimpDrawableFilter *filter = filters->data;
GimpContainer *container;
gint index;
gint n_children;
item_view->priv->effects_filter = filter;
container =
gimp_drawable_get_filters (GIMP_DRAWABLE (item_view->priv->effects_drawable));
index = gimp_container_get_child_index (container,
GIMP_OBJECT (filter));
n_children = gimp_container_get_n_children (container);
gtk_widget_set_sensitive (item_view->priv->effects_raise_button,
(index != 0));
gtk_widget_set_sensitive (item_view->priv->effects_lower_button,
(index != n_children - 1));
}
return TRUE;
}
static void
gimp_item_tree_view_effects_activate_filter (GtkWidget *widget,
GimpViewable *viewable,
gpointer insert_data,
GimpItemTreeView *view)
{
if (gtk_widget_get_sensitive (view->priv->effects_edit_button))
gimp_item_tree_view_effects_edited_clicked (widget, view);
}
static void
gimp_item_tree_view_effects_visible_toggled (GtkWidget *widget,
GimpItemTreeView *view)
{
if (view->priv->effects_drawable)
{
gboolean visible;
GimpContainer *filter_stack;
GList *list;
GimpImage *image = view->priv->image;
visible = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
filter_stack = gimp_drawable_get_filters (GIMP_DRAWABLE (view->priv->effects_drawable));
for (list = GIMP_LIST (filter_stack)->queue->head; list;
list = g_list_next (list))
{
if (GIMP_IS_DRAWABLE_FILTER (list->data))
{
GimpFilter *filter = list->data;
gimp_filter_set_active (filter, visible);
}
}
/* Hack to make the effects visibly change */
gimp_item_set_visible (GIMP_ITEM (view->priv->effects_drawable), FALSE, FALSE);
gimp_image_flush (image);
gimp_item_set_visible (GIMP_ITEM (view->priv->effects_drawable), TRUE, FALSE);
gimp_image_flush (image);
}
}
static void
gimp_item_tree_view_effects_edited_clicked (GtkWidget *widget,
GimpItemTreeView *view)
{
if (! view->priv->effects_filter ||
! GIMP_IS_DRAWABLE_FILTER (view->priv->effects_filter))
return;
if (view->priv->effects_drawable)
{
GimpImage *image = view->priv->image;
GeglNode *op = gimp_drawable_filter_get_operation (view->priv->effects_filter);
if (op)
{
GimpProcedure *procedure;
GVariant *variant;
gchar *operation;
gchar *name;
g_object_get (view->priv->effects_filter,
"name", &name,
NULL);
g_object_get (op,
"operation", &operation,
NULL);
if (operation)
{
procedure = gimp_gegl_procedure_new (image->gimp,
view->priv->effects_filter,
GIMP_RUN_INTERACTIVE, NULL,
operation,
name,
name,
NULL, NULL, NULL);
gimp_filter_history_add (image->gimp, procedure);
variant = g_variant_new_uint64 (GPOINTER_TO_SIZE (procedure));
g_variant_take_ref (variant);
filters_run_procedure (image->gimp,
gimp_context_get_display (gimp_get_user_context (image->gimp)),
procedure, GIMP_RUN_INTERACTIVE);
g_variant_unref (variant);
g_object_unref (procedure);
/* Disable buttons until we're done editing */
gtk_widget_set_sensitive (view->priv->effects_box, FALSE);
gtk_widget_set_sensitive (view->priv->effects_visible_button, FALSE);
gtk_widget_set_sensitive (view->priv->effects_edit_button, FALSE);
gtk_widget_set_sensitive (view->priv->effects_raise_button, FALSE);
gtk_widget_set_sensitive (view->priv->effects_lower_button, FALSE);
gtk_widget_set_sensitive (view->priv->effects_merge_button, FALSE);
gtk_widget_set_sensitive (view->priv->effects_remove_button, FALSE);
}
g_free (name);
g_free (operation);
}
}
}
static void
gimp_item_tree_view_effects_raised_clicked (GtkWidget *widget,
GimpItemTreeView *view)
{
if (! view->priv->effects_filter ||
! GIMP_IS_DRAWABLE_FILTER (view->priv->effects_filter))
return;
if (view->priv->effects_drawable)
{
GimpImage *image = view->priv->image;
GimpContainer *filters;
gint index;
filters = gimp_drawable_get_filters (GIMP_DRAWABLE (view->priv->effects_drawable));
index = gimp_container_get_child_index (filters,
GIMP_OBJECT (view->priv->effects_filter));
index--;
if (index >= 0)
{
gimp_image_undo_push_filter_reorder (image, _("Reorder filter"),
view->priv->effects_drawable,
view->priv->effects_filter);
gimp_container_reorder (filters, GIMP_OBJECT (view->priv->effects_filter),
index);
if (gtk_widget_get_sensitive (view->priv->effects_edit_button))
{
gtk_widget_set_sensitive (view->priv->effects_lower_button, TRUE);
if (index == 0)
gtk_widget_set_sensitive (view->priv->effects_raise_button, FALSE);
}
/* Hack to make the effects visibly change */
gimp_item_set_visible (GIMP_ITEM (view->priv->effects_drawable), FALSE, FALSE);
gimp_image_flush (image);
gimp_item_set_visible (GIMP_ITEM (view->priv->effects_drawable), TRUE, FALSE);
gimp_image_flush (image);
}
}
}
static void
gimp_item_tree_view_effects_lowered_clicked (GtkWidget *widget,
GimpItemTreeView *view)
{
if (! view->priv->effects_filter ||
! GIMP_IS_DRAWABLE_FILTER (view->priv->effects_filter))
return;
if (view->priv->effects_drawable)
{
GimpImage *image = view->priv->image;
GimpContainer *filters;
gint index;
filters = gimp_drawable_get_filters (GIMP_DRAWABLE (view->priv->effects_drawable));
index = gimp_container_get_child_index (filters,
GIMP_OBJECT (view->priv->effects_filter));
index++;
if (index < gimp_container_get_n_children (filters))
{
gimp_image_undo_push_filter_reorder (image, _("Reorder filter"),
view->priv->effects_drawable,
view->priv->effects_filter);
gimp_container_reorder (filters, GIMP_OBJECT (view->priv->effects_filter),
index);
if (gtk_widget_get_sensitive (view->priv->effects_edit_button))
{
gtk_widget_set_sensitive (view->priv->effects_raise_button, TRUE);
if (index == gimp_container_get_n_children (filters) - 1)
gtk_widget_set_sensitive (view->priv->effects_lower_button, FALSE);
}
/* Hack to make the effects visibly change */
gimp_item_set_visible (GIMP_ITEM (view->priv->effects_drawable), FALSE, FALSE);
gimp_image_flush (image);
gimp_item_set_visible (GIMP_ITEM (view->priv->effects_drawable), TRUE, FALSE);
gimp_image_flush (image);
}
}
}
static void
gimp_item_tree_view_effects_merged_clicked (GtkWidget *widget,
GimpItemTreeView *view)
{
if (! view->priv->effects_filter ||
! GIMP_IS_DRAWABLE_FILTER (view->priv->effects_filter))
return;
if (view->priv->effects_drawable)
{
GimpImage *image = view->priv->image;
GeglNode *op = gimp_drawable_filter_get_operation (view->priv->effects_filter);
if (op)
{
gimp_drawable_merge_filters (GIMP_DRAWABLE (view->priv->effects_drawable));
gimp_drawable_clear_filters (GIMP_DRAWABLE (view->priv->effects_drawable));
/* Hack to make the effects visibly change */
gimp_item_set_visible (GIMP_ITEM (view->priv->effects_drawable), FALSE, FALSE);
gimp_image_flush (image);
gimp_item_set_visible (GIMP_ITEM (view->priv->effects_drawable), TRUE, FALSE);
gimp_image_flush (image);
}
}
}
static void
gimp_item_tree_view_effects_removed_clicked (GtkWidget *widget,
GimpItemTreeView *view)
{
if (view->priv->effects_drawable)
{
GimpImage *image = view->priv->image;
GeglNode *op = NULL;
if (view->priv->effects_filter &&
GIMP_IS_DRAWABLE_FILTER (view->priv->effects_filter))
op = gimp_drawable_filter_get_operation (view->priv->effects_filter);
if (op)
{
gimp_image_undo_push_filter_remove (image, _("Remove filter"),
view->priv->effects_drawable,
view->priv->effects_filter);
gimp_drawable_filter_abort (view->priv->effects_filter);
view->priv->effects_filter = NULL;
}
else
{
gimp_drawable_remove_last_filter (GIMP_DRAWABLE (view->priv->effects_drawable));
}
/* Hack to make the effects visibly change */
gimp_item_set_visible (GIMP_ITEM (view->priv->effects_drawable), FALSE, FALSE);
gimp_image_flush (image);
gimp_item_set_visible (GIMP_ITEM (view->priv->effects_drawable), TRUE, FALSE);
gimp_image_flush (image);
}
}
/* "Color Tag" callbacks */
@ -1836,6 +2491,68 @@ gimp_item_tree_view_lock_changed (GimpItem *item,
gimp_item_tree_view_update_lock_box (view, item, NULL);
}
static void
gimp_item_tree_view_filters_changed (GimpItem *item,
GimpItemTreeView *view)
{
GimpContainerView *container_view = GIMP_CONTAINER_VIEW (view);
GimpContainerTreeView *tree_view = GIMP_CONTAINER_TREE_VIEW (view);
GtkTreeIter *iter;
GimpContainer *filters;
GList *filter_list = NULL;
gint n_filters = 0;
gboolean fs_disabled = FALSE;
iter = gimp_container_view_lookup (container_view,
(GimpViewable *) item);
filters = gimp_drawable_get_filters (GIMP_DRAWABLE (item));
/* Since floating selections are also stored in the filter stack,
* we need to verify what's in there to get the correct count */
for (filter_list = GIMP_LIST (filters)->queue->tail; filter_list;
filter_list = g_list_previous (filter_list))
{
if (GIMP_IS_DRAWABLE_FILTER (filter_list->data))
n_filters++;
else
fs_disabled = TRUE;
}
if (fs_disabled)
view->priv->effects_filter = NULL;
if (iter)
gtk_tree_store_set (GTK_TREE_STORE (tree_view->model), iter,
view->priv->model_column_effects,
n_filters > 0,
-1);
/* Re-enable buttons after editing */
gtk_widget_set_sensitive (view->priv->effects_box, ! fs_disabled);
gtk_widget_set_sensitive (view->priv->effects_visible_button, ! fs_disabled);
gtk_widget_set_sensitive (view->priv->effects_edit_button, ! fs_disabled);
gtk_widget_set_sensitive (view->priv->effects_raise_button, ! fs_disabled);
gtk_widget_set_sensitive (view->priv->effects_lower_button, ! fs_disabled);
gtk_widget_set_sensitive (view->priv->effects_merge_button, ! fs_disabled);
gtk_widget_set_sensitive (view->priv->effects_remove_button, ! fs_disabled);
if (view->priv->effects_popover &&
view->priv->effects_filter &&
! fs_disabled)
{
if (GIMP_IS_DRAWABLE_FILTER (view->priv->effects_filter))
{
gint index = gimp_container_get_child_index (filters,
GIMP_OBJECT (view->priv->effects_filter));
if (index == 0)
gtk_widget_set_sensitive (view->priv->effects_raise_button, FALSE);
else if (index == (n_filters - 1))
gtk_widget_set_sensitive (view->priv->effects_lower_button, FALSE);
}
}
}
static gboolean
gimp_item_tree_view_lock_button_release (GtkWidget *widget,
GdkEvent *event,

View file

@ -36,7 +36,10 @@
#include "core/gimp.h"
#include "core/gimpcontainer.h"
#include "core/gimpdrawable-filters.h"
#include "core/gimpdrawable-private.h" /* eek */
#include "core/gimpdrawablefilter.h"
#include "core/gimpfilterstack.h"
#include "core/gimpgrid.h"
#include "core/gimpgrouplayer.h"
#include "core/gimpimage.h"
@ -85,8 +88,27 @@
/* #define GIMP_XCF_PATH_DEBUG */
/* Filters can not be created until a layer is attached
* to an image, so we use this struct to store relevant
* information until then */
typedef struct
{
GeglNode *operation;
gchar *name;
gchar *icon_name;
GimpChannel *mask;
gboolean is_visible;
gdouble opacity;
GimpLayerMode paint_mode;
GimpLayerColorSpace blend_space;
GimpLayerColorSpace composite_space;
GimpLayerCompositeMode composite_mode;
GimpFilterRegion region;
} FilterData;
static void xcf_load_add_masks (GimpImage *image);
static void xcf_load_add_effects (XcfInfo *info,
GimpImage *image);
static gboolean xcf_load_image_props (XcfInfo *info,
GimpImage *image);
static gboolean xcf_load_layer_props (XcfInfo *info,
@ -105,6 +127,8 @@ static gboolean xcf_check_layer_props (XcfInfo *info,
static gboolean xcf_load_channel_props (XcfInfo *info,
GimpImage *image,
GimpChannel **channel);
static gboolean xcf_load_effect_props (XcfInfo *info,
FilterData *filter);
static gboolean xcf_load_vectors_props (XcfInfo *info,
GimpImage *image,
GimpVectors **vectors);
@ -116,6 +140,10 @@ static GimpLayer * xcf_load_layer (XcfInfo *info,
GList **item_path);
static GimpChannel * xcf_load_channel (XcfInfo *info,
GimpImage *image);
static FilterData * xcf_load_effect (XcfInfo *info,
GimpDrawable *drawable);
static void xcf_load_free_effect (FilterData *data);
static void xcf_load_free_effects (GList *effects);
static GimpVectors * xcf_load_vectors (XcfInfo *info,
GimpImage *image);
static GimpLayerMask * xcf_load_layer_mask (XcfInfo *info,
@ -767,7 +795,10 @@ xcf_load_image (Gimp *gimp,
}
if (n_broken_layers == 0 && n_broken_channels == 0)
xcf_load_add_masks (image);
{
xcf_load_add_masks (image);
xcf_load_add_effects (info, image);
}
if (info->floating_sel && info->floating_sel_drawable)
{
@ -948,6 +979,7 @@ xcf_load_image (Gimp *gimp,
"of it as I can, but it is incomplete."));
xcf_load_add_masks (image);
xcf_load_add_effects (info, image);
gimp_image_undo_enable (image);
@ -1015,6 +1047,86 @@ xcf_load_add_masks (GimpImage *image)
g_list_free (layers);
}
static void
xcf_load_add_effects (XcfInfo *info,
GimpImage *image)
{
GList *layers;
GList *list;
layers = gimp_image_get_layer_list (image);
for (list = layers; list; list = g_list_next (list))
{
GimpLayer *layer = list->data;
GList *effects_nodes;
effects_nodes = g_object_get_data (G_OBJECT (layer), "gimp-layer-effects");
if (effects_nodes)
{
GList *iter;
for (iter = effects_nodes; iter; iter = iter->next)
{
FilterData *data = iter->data;
GSList *children;
if (! data->icon_name)
data->icon_name = g_strdup ("gimp-gegl");
children = gegl_node_get_children (data->operation);
if (g_slist_length (children) == 1)
{
GimpDrawableFilter *filter = NULL;
GeglNode *op;
op = g_object_ref (children->data);
gegl_node_remove_child (data->operation, op);
filter = gimp_drawable_filter_new (GIMP_DRAWABLE (layer),
data->name, op,
data->icon_name);
gimp_drawable_filter_set_opacity (filter, data->opacity);
gimp_drawable_filter_set_mode (filter, data->paint_mode,
data->blend_space,
data->composite_space,
data->composite_mode);
gimp_drawable_filter_set_region (filter, data->region);
gimp_drawable_filter_apply (filter, NULL);
g_object_set (filter,
"mask", data->mask,
NULL);
gimp_drawable_filter_commit (filter, TRUE, NULL, FALSE);
gimp_drawable_filter_layer_mask_freeze (filter);
g_object_unref (op);
g_object_unref (filter);
}
else
{
gimp_message (info->gimp, G_OBJECT (info->progress),
GIMP_MESSAGE_WARNING,
"XCF Warning: failed loading filter \"%s\": "
"node has %d children, 1 expected.",
data->name, g_slist_length (children));
}
g_slist_free (children);
}
g_object_set_data (G_OBJECT (layer), "gimp-layer-effects", NULL);
}
}
g_list_free (layers);
}
static gboolean
xcf_load_image_props (XcfInfo *info,
GimpImage *image)
@ -2271,6 +2383,145 @@ xcf_load_channel_props (XcfInfo *info,
return FALSE;
}
static gboolean
xcf_load_effect_props (XcfInfo *info,
FilterData *filter)
{
PropType prop_type;
guint32 prop_size;
while (TRUE)
{
if (! xcf_load_prop (info, &prop_type, &prop_size))
return FALSE;
switch (prop_type)
{
case PROP_END:
return TRUE;
case PROP_FILTER_NAME:
{
gchar *filter_name;
goffset base = info->cp;
/* Go back so we can get the size of the string */
xcf_seek_pos (info, base - sizeof (guint32), NULL);
xcf_read_string (info, &filter_name, 1);
filter->name = filter_name;
}
break;
case PROP_FILTER_ICON:
{
gchar *filter_icon;
goffset base = info->cp;
/* Go back so we can get the size of the string */
xcf_seek_pos (info, base - sizeof (guint32), NULL);
xcf_read_string (info, &filter_icon, 1);
filter->icon_name = filter_icon;
}
break;
case PROP_VISIBLE:
{
gboolean visible;
xcf_read_int32 (info, (guint32 *) &visible, 1);
filter->is_visible = visible;
}
break;
case PROP_OPACITY:
{
guint32 opacity;
xcf_read_int32 (info, &opacity, 1);
filter->opacity = (gdouble) opacity / 255.0;
}
break;
case PROP_MODE:
{
GimpLayerMode mode;
xcf_read_int32 (info, (guint32 *) &mode, 1);
if (mode == GIMP_LAYER_MODE_OVERLAY_LEGACY)
mode = GIMP_LAYER_MODE_SOFTLIGHT_LEGACY;
filter->paint_mode = mode;
}
break;
case PROP_BLEND_SPACE:
{
gint32 blend_space;
xcf_read_int32 (info, (guint32 *) &blend_space, 1);
/* TODO: Revisit when blend space can be set
* on filter effects */
blend_space = GIMP_LAYER_COLOR_SPACE_AUTO;
filter->blend_space = blend_space;
}
break;
case PROP_COMPOSITE_SPACE:
{
gint32 composite_space;
xcf_read_int32 (info, (guint32 *) &composite_space, 1);
/* TODO: Revisit when composite space can be set
* on filter effects */
composite_space = GIMP_LAYER_COLOR_SPACE_AUTO;
filter->composite_space = composite_space;
}
break;
case PROP_COMPOSITE_MODE:
{
gint32 composite_mode;
xcf_read_int32 (info, (guint32 *) &composite_mode, 1);
/* TODO: Revisit when composite mode can be set
* on filter effects */
composite_mode = GIMP_LAYER_COMPOSITE_AUTO;
filter->composite_mode = composite_mode;
}
break;
case PROP_FILTER_REGION:
{
GimpFilterRegion region;
xcf_read_int32 (info, (guint32 *) &region, 1);
filter->region = region;
}
break;
default:
#ifdef GIMP_UNSTABLE
g_printerr ("unexpected/unknown effect property: %d (skipping)\n",
prop_type);
#endif
if (! xcf_skip_unknown_prop (info, prop_size))
return FALSE;
}
}
return FALSE;
}
static gboolean
xcf_load_vectors_props (XcfInfo *info,
GimpImage *image,
@ -2454,6 +2705,7 @@ xcf_load_layer (XcfInfo *info,
GimpLayer *layer;
GimpLayerMask *layer_mask;
goffset hierarchy_offset;
goffset effects_offset = 0;
goffset layer_mask_offset;
gboolean apply_mask = TRUE;
gboolean edit_mask = FALSE;
@ -2618,6 +2870,8 @@ xcf_load_layer (XcfInfo *info,
cur_offset = info->cp;
xcf_read_offset (info, &hierarchy_offset, 1);
xcf_read_offset (info, &layer_mask_offset, 1);
if (info->file_version >= 20)
xcf_read_offset (info, &effects_offset, 1);
/* read in the hierarchy (ignore it for group layers, both as an
* optimization and because the hierarchy's extents don't match
@ -2650,6 +2904,8 @@ xcf_load_layer (XcfInfo *info,
gimp_viewable_set_expanded (GIMP_VIEWABLE (layer), expanded);
}
cur_offset += info->bytes_per_offset;
/* read in the layer mask */
if (layer_mask_offset != 0)
{
@ -2665,6 +2921,8 @@ xcf_load_layer (XcfInfo *info,
if (! layer_mask)
goto error;
cur_offset += info->bytes_per_offset;
xcf_progress_update (info);
/* don't add the layer mask yet, that won't work for group
@ -2682,6 +2940,89 @@ xcf_load_layer (XcfInfo *info,
GINT_TO_POINTER (show_mask));
}
/* read in any layer effects and effect masks */
if (effects_offset != 0)
{
GList *filter_data_list = NULL;
gint filter_count = 0;
cur_offset += info->bytes_per_offset;
while (TRUE)
{
FilterData *filter_data;
GimpChannel *effect_mask;
/* if the offset is 0 then we are at the end
* of the effect list.
*/
if (effects_offset == 0)
break;
if (effects_offset < cur_offset)
{
GIMP_LOG (XCF, "Invalid effect offset: %" G_GOFFSET_FORMAT
" at offset: %" G_GOFFSET_FORMAT, effects_offset, cur_offset);
goto error;
}
/* seek to the effect offset */
if (! xcf_seek_pos (info, effects_offset, NULL))
goto error;
filter_data = xcf_load_effect (info, GIMP_DRAWABLE (layer));
if (! filter_data)
goto error;
xcf_progress_update (info);
/* restore the saved position so we'll be ready to
* read the next offset.
*/
cur_offset += info->bytes_per_offset;
if (! xcf_seek_pos (info, cur_offset, NULL))
goto error;
/* read in the offset of the effect mask */
xcf_read_offset (info, &effects_offset, 1);
if (effects_offset < cur_offset)
{
GIMP_LOG (XCF, "Invalid effect mask offset: %" G_GOFFSET_FORMAT
" at offset: %" G_GOFFSET_FORMAT, effects_offset, cur_offset);
goto error;
}
/* seek to the effect mask offset */
if (! xcf_seek_pos (info, effects_offset, NULL))
goto error;
effect_mask = xcf_load_channel (info, image);
if (! effect_mask)
goto error;
filter_data->mask = effect_mask;
filter_data_list = g_list_prepend (filter_data_list, filter_data);
filter_count++;
xcf_progress_update (info);
/* restore the saved position so we'll be ready to
* read the next offset.
*/
cur_offset += info->bytes_per_offset;
if (! xcf_seek_pos (info, cur_offset, NULL))
goto error;
/* read in the offset of the next effect */
xcf_read_offset (info, &effects_offset, 1);
}
if (filter_count > 0)
g_object_set_data_full (G_OBJECT (layer), "gimp-layer-effects", filter_data_list,
(GDestroyNotify) xcf_load_free_effects);
}
/* attach the floating selection... */
if (is_fs_drawable)
info->floating_sel_drawable = GIMP_DRAWABLE (layer);
@ -2793,6 +3134,57 @@ xcf_load_channel (XcfInfo *info,
return NULL;
}
static FilterData *
xcf_load_effect (XcfInfo *info,
GimpDrawable *drawable)
{
FilterData *filter;
GeglNode *operation;
gchar *xml;
filter = g_new0 (FilterData, 1);
xcf_read_string (info, &xml, 1);
operation = gegl_node_new_from_xml (xml, NULL);
g_free (xml);
if (! operation)
goto error;
filter->operation = operation;
/* read in the effect properties */
if (! xcf_load_effect_props (info, &(*filter)))
goto error;
xcf_progress_update (info);
return filter;
error:
xcf_load_free_effect (filter);
return NULL;
}
static void
xcf_load_free_effect (FilterData *data)
{
g_free (data->name);
g_free (data->icon_name);
g_clear_object (&data->operation);
g_clear_object (&data->mask);
g_free (data);
}
static void
xcf_load_free_effects (GList *effects)
{
g_list_free_full (effects, (GDestroyNotify) xcf_load_free_effect);
}
/* The new path structure since XCF 18. */
static GimpVectors *
xcf_load_vectors (XcfInfo *info,

View file

@ -70,6 +70,9 @@ typedef enum
PROP_ITEM_SET_ITEM = 41,
PROP_LOCK_VISIBILITY = 42,
PROP_SELECTED_PATH = 43,
PROP_FILTER_NAME = 44,
PROP_FILTER_ICON = 45,
PROP_FILTER_REGION = 46,
} PropType;
typedef enum

View file

@ -38,6 +38,8 @@
#include "core/gimpcontainer.h"
#include "core/gimpchannel.h"
#include "core/gimpdrawable.h"
#include "core/gimpdrawable-filters.h"
#include "core/gimpdrawablefilter.h"
#include "core/gimpgrid.h"
#include "core/gimpguide.h"
#include "core/gimpimage.h"
@ -51,6 +53,7 @@
#include "core/gimpitemlist.h"
#include "core/gimplayer.h"
#include "core/gimplayermask.h"
#include "core/gimplist.h"
#include "core/gimpparasitelist.h"
#include "core/gimpprogress.h"
#include "core/gimpsamplepoint.h"
@ -114,6 +117,10 @@ static gboolean xcf_save_channel_props (XcfInfo *info,
GimpImage *image,
GimpChannel *channel,
GError **error);
static gboolean xcf_save_effect_props (XcfInfo *info,
GimpImage *image,
GimpFilter *filter,
GError **error);
static gboolean xcf_save_path_props (XcfInfo *info,
GimpImage *image,
GimpVectors *vectors,
@ -131,6 +138,10 @@ static gboolean xcf_save_channel (XcfInfo *info,
GimpImage *image,
GimpChannel *channel,
GError **error);
static gboolean xcf_save_effect (XcfInfo *info,
GimpImage *image,
GimpFilter *filter,
GError **error);
static gboolean xcf_save_path (XcfInfo *info,
GimpImage *image,
GimpVectors *vectors,
@ -817,6 +828,44 @@ xcf_save_channel_props (XcfInfo *info,
return TRUE;
}
static gboolean
xcf_save_effect_props (XcfInfo *info,
GimpImage *image,
GimpFilter *filter,
GError **error)
{
const gchar *name;
const gchar *icon;
g_object_get (filter,
"name", &name,
"icon-name", &icon,
NULL);
xcf_check_error (xcf_save_prop (info, image, PROP_FILTER_NAME, error,
name), ;);
xcf_check_error (xcf_save_prop (info, image, PROP_FILTER_ICON, error,
icon), ;);
xcf_check_error (xcf_save_prop (info, image, PROP_VISIBLE, error,
gimp_filter_get_active (filter)), ;);
xcf_check_error (xcf_save_prop (info, image, PROP_OPACITY, error,
gimp_drawable_filter_get_opacity (GIMP_DRAWABLE_FILTER (filter))), ;);
xcf_check_error (xcf_save_prop (info, image, PROP_MODE, error,
gimp_drawable_filter_get_paint_mode (GIMP_DRAWABLE_FILTER (filter))), ;);
xcf_check_error (xcf_save_prop (info, image, PROP_BLEND_SPACE, error,
gimp_drawable_filter_get_blend_space (GIMP_DRAWABLE_FILTER (filter))), ;);
xcf_check_error (xcf_save_prop (info, image, PROP_COMPOSITE_SPACE, error,
gimp_drawable_filter_get_composite_space (GIMP_DRAWABLE_FILTER (filter))), ;);
xcf_check_error (xcf_save_prop (info, image, PROP_COMPOSITE_MODE, error,
gimp_drawable_filter_get_composite_mode (GIMP_DRAWABLE_FILTER (filter))), ;);
xcf_check_error (xcf_save_prop (info, image, PROP_FILTER_REGION, error,
gimp_drawable_filter_get_region (GIMP_DRAWABLE_FILTER (filter))), ;);
xcf_check_error (xcf_save_prop (info, image, PROP_END, error), ;);
return TRUE;
}
static gboolean
xcf_save_path_props (XcfInfo *info,
GimpImage *image,
@ -1620,6 +1669,37 @@ xcf_save_prop (XcfInfo *info,
xcf_write_int32_check_error (info, &set_n, 1, va_end (args));
}
break;
case PROP_FILTER_NAME:
{
const gchar *filter_name = va_arg (args, gchar *);
xcf_write_prop_type_check_error (info, prop_type, va_end (args));
xcf_write_string_check_error (info, (gchar **) &filter_name, 1, va_end (args));
}
break;
case PROP_FILTER_ICON:
{
const gchar *filter_icon = va_arg (args, gchar *);
xcf_write_prop_type_check_error (info, prop_type, va_end (args));
xcf_write_string_check_error (info, (gchar **) &filter_icon, 1, va_end (args));
}
break;
case PROP_FILTER_REGION:
{
guint32 filter_region = va_arg (args, guint32);
size = 4;
xcf_write_prop_type_check_error (info, prop_type, va_end (args));
xcf_write_int32_check_error (info, &size, 1, va_end (args));
xcf_write_int32_check_error (info, &filter_region, 1, va_end (args));
}
break;
}
va_end (args);
@ -1633,11 +1713,15 @@ xcf_save_layer (XcfInfo *info,
GimpLayer *layer,
GError **error)
{
goffset saved_pos;
goffset offset;
guint32 value;
const gchar *string;
GError *tmp_error = NULL;
goffset saved_pos;
goffset offset;
guint32 value;
const gchar *string;
GimpContainer *filters;
GList *filter_list;
guint32 num_effects = 0;
GList *list;
GError *tmp_error = NULL;
/* check and see if this is the drawable that the floating
* selection is attached to.
@ -1650,6 +1734,17 @@ xcf_save_layer (XcfInfo *info,
xcf_check_error (xcf_seek_pos (info, saved_pos, error), ;);
}
/* Get filter information */
filters = gimp_drawable_get_filters (GIMP_DRAWABLE (layer));
/* Since floating selections are also stored in the filter stack,
* we need to verify what's in there to get the correct count */
for (filter_list = GIMP_LIST (filters)->queue->tail; filter_list;
filter_list = g_list_previous (filter_list))
{
if (GIMP_IS_DRAWABLE_FILTER (filter_list->data))
num_effects++;
}
/* write out the width, height and image type information for the layer */
value = gimp_item_get_width (GIMP_ITEM (layer));
xcf_write_int32_check_error (info, &value, 1, ;);
@ -1667,8 +1762,8 @@ xcf_save_layer (XcfInfo *info,
/* write out the layer properties */
xcf_save_layer_props (info, image, layer, error);
/* write out the layer tile hierarchy */
offset = info->cp + 2 * info->bytes_per_offset;
/* write out the layer tile hierarchy and effects */
offset = info->cp + (2 + (num_effects * 2) + 1) * info->bytes_per_offset;
xcf_write_offset_check_error (info, &offset, 1, ;);
saved_pos = info->cp;
@ -1676,6 +1771,10 @@ xcf_save_layer (XcfInfo *info,
/* write a zero layer mask offset */
xcf_write_zero_offset_check_error (info, 1, ;);
/* write out zero effect and effect mask offset(s) */
for (gint i = 0; i <= (num_effects * 2); i++)
xcf_write_zero_offset_check_error (info, 1, ;);
xcf_check_error (xcf_save_buffer (info, image,
gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
error), ;);
@ -1690,11 +1789,55 @@ xcf_save_layer (XcfInfo *info,
xcf_check_error (xcf_seek_pos (info, saved_pos, error), ;);
xcf_write_offset_check_error (info, &offset, 1, ;);
saved_pos = info->cp;
xcf_check_error (xcf_seek_pos (info, offset, error), ;);
xcf_check_error (xcf_save_channel (info, image, GIMP_CHANNEL (mask),
error), ;);
}
/* write out any layer effects and effect masks */
if (num_effects > 0)
{
saved_pos += info->bytes_per_offset;
for (list = GIMP_LIST (filters)->queue->head; list;
list = g_list_next (list))
{
if (GIMP_IS_DRAWABLE_FILTER (list->data))
{
GimpDrawableFilter *filter = list->data;
GimpChannel *effect_mask;
offset = info->cp;
xcf_check_error (xcf_seek_pos (info, saved_pos, error), ;);
xcf_write_offset_check_error (info, &offset, 1, ;);
saved_pos = info->cp;
xcf_check_error (xcf_seek_pos (info, offset, error), ;);
xcf_check_error (xcf_save_effect (info, image, GIMP_FILTER (filter),
error), ;);
/* write out effect mask */
effect_mask = gimp_drawable_filter_get_mask (filter);
offset = info->cp;
xcf_check_error (xcf_seek_pos (info, saved_pos, error), ;);
xcf_write_offset_check_error (info, &offset, 1, ;);
saved_pos = info->cp;
xcf_check_error (xcf_seek_pos (info, offset, error), ;);
xcf_check_error (xcf_save_channel (info, image, effect_mask,
error), ;);
}
}
g_list_free (list);
}
return TRUE;
}
@ -1746,6 +1889,60 @@ xcf_save_channel (XcfInfo *info,
return TRUE;
}
static gboolean
xcf_save_effect (XcfInfo *info,
GimpImage *image,
GimpFilter *filter,
GError **error)
{
const gchar *string;
GimpDrawableFilter *filter_drawable;
GeglNode *node;
GeglNode *save_node = gegl_node_new ();
const gchar *operation;
GParamSpec **pspecs;
guint n_pspecs;
GError *tmp_error = NULL;
filter_drawable = GIMP_DRAWABLE_FILTER (filter);
node = gimp_drawable_filter_get_operation (filter_drawable);
/* Create operation-only node */
gegl_node_get (node,
"operation", &operation,
NULL);
gegl_node_set (save_node,
"operation", operation,
NULL);
pspecs = gegl_operation_list_properties (operation, &n_pspecs);
for (gint i = 0; i < n_pspecs; i++)
{
GParamSpec *pspec = pspecs[i];
GValue value = G_VALUE_INIT;
g_value_init (&value, pspec->value_type);
gegl_node_get_property (node, pspec->name,
&value);
gegl_node_set_property (save_node, pspec->name,
&value);
g_value_unset (&value);
}
g_free (pspecs);
/* Write out GEGL xml */
string = gegl_node_to_xml_full (save_node, save_node, "/");
xcf_write_string_check_error (info, (gchar **) &string, 1, ;);
/* write out the effect properties */
xcf_save_effect_props (info, image, filter, error);
return TRUE;
}
static gint
xcf_calc_levels (gint size,
gint tile_size)

View file

@ -88,6 +88,7 @@ static GimpXcfLoaderFunc * const xcf_loaders[] =
xcf_load_image, /* version 17 */
xcf_load_image, /* version 18 */
xcf_load_image, /* version 19 */
xcf_load_image, /* version 20 */
};

View file

@ -236,6 +236,7 @@ EXPORTS
gimp_drawable_levels_stretch
gimp_drawable_mask_bounds
gimp_drawable_mask_intersect
gimp_drawable_merge_filters
gimp_drawable_merge_shadow
gimp_drawable_offset
gimp_drawable_posterize

View file

@ -592,6 +592,40 @@ gimp_drawable_mask_intersect (GimpDrawable *drawable,
return non_empty;
}
/**
* gimp_drawable_merge_filters:
* @drawable: The drawable.
*
* Merge the layer effect filters to the specified drawable.
*
* This procedure combines the contents of the drawable's filter stack
* (for export) with the specified drawable.
*
* Returns: TRUE on success.
**/
gboolean
gimp_drawable_merge_filters (GimpDrawable *drawable)
{
GimpValueArray *args;
GimpValueArray *return_vals;
gboolean success = TRUE;
args = gimp_value_array_new_from_types (NULL,
GIMP_TYPE_DRAWABLE, drawable,
G_TYPE_NONE);
return_vals = _gimp_pdb_run_procedure_array (gimp_get_pdb (),
"gimp-drawable-merge-filters",
args);
gimp_value_array_unref (args);
success = GIMP_VALUES_GET_ENUM (return_vals, 0) == GIMP_PDB_SUCCESS;
gimp_value_array_unref (return_vals);
return success;
}
/**
* gimp_drawable_merge_shadow:
* @drawable: The drawable.

View file

@ -56,6 +56,7 @@ gboolean gimp_drawable_mask_intersect (GimpDrawable
gint *y,
gint *width,
gint *height);
gboolean gimp_drawable_merge_filters (GimpDrawable *drawable);
gboolean gimp_drawable_merge_shadow (GimpDrawable *drawable,
gboolean undo);
gboolean gimp_drawable_free_shadow (GimpDrawable *drawable);

View file

@ -172,6 +172,21 @@ export_flatten (GimpImage *image,
*drawables = g_list_prepend (NULL, flattened);
}
static void
export_merge_layer_effects (GimpImage *image,
GList **drawables)
{
GList *layers;
GList *iter;
layers = gimp_image_list_layers (image);
for (iter = layers; iter; iter = g_list_next (iter))
gimp_drawable_merge_filters (GIMP_DRAWABLE (iter->data));
g_list_free (layers);
}
static void
export_remove_alpha (GimpImage *image,
GList **drawables)
@ -379,6 +394,15 @@ static ExportAction export_action_flatten =
0
};
static ExportAction export_action_merge_layer_effects =
{
export_merge_layer_effects,
NULL,
N_("%s plug-in can't handle layer effects"),
{ N_("Merge Layer Effects"), NULL },
0
};
static ExportAction export_action_remove_alpha =
{
export_remove_alpha,
@ -870,6 +894,9 @@ gimp_export_image (GimpImage **image,
return GIMP_EXPORT_CANCEL;
}
/* Merge down layer effects for non-project file formats */
if (! (capabilities & GIMP_EXPORT_CAN_HANDLE_LAYER_EFFECTS))
actions = g_slist_prepend (actions, &export_action_merge_layer_effects);
/* check alpha and layer masks */
layers = gimp_image_list_layers (*image);

View file

@ -40,6 +40,7 @@ G_BEGIN_DECLS
* @GIMP_EXPORT_CAN_HANDLE_ALPHA: Handles alpha channels
* @GIMP_EXPORT_CAN_HANDLE_LAYERS: Handles layers
* @GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION: Handles animation of layers
* @GIMP_EXPORT_CAN_HANDLE_LAYER_EFFECTS: Handles layer effects
* @GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS: Handles layer masks
* @GIMP_EXPORT_NEEDS_ALPHA: Needs alpha channels
* @GIMP_EXPORT_NEEDS_CROP: Needs to crop content to image bounds
@ -56,8 +57,9 @@ typedef enum
GIMP_EXPORT_CAN_HANDLE_LAYERS = 1 << 5,
GIMP_EXPORT_CAN_HANDLE_LAYERS_AS_ANIMATION = 1 << 6,
GIMP_EXPORT_CAN_HANDLE_LAYER_MASKS = 1 << 7,
GIMP_EXPORT_NEEDS_ALPHA = 1 << 8,
GIMP_EXPORT_NEEDS_CROP = 1 << 9
GIMP_EXPORT_CAN_HANDLE_LAYER_EFFECTS = 1 << 8,
GIMP_EXPORT_NEEDS_ALPHA = 1 << 9,
GIMP_EXPORT_NEEDS_CROP = 1 << 10
} GimpExportCapabilities;

View file

@ -16,6 +16,38 @@
# "Perlized" from C source by Manish Singh <yosh@gimp.org>
sub drawable_merge_filters {
$blurb = 'Merge the layer effect filters to the specified drawable.';
$help = <<'HELP';
This procedure combines the contents of the drawable's filter stack
(for export) with the specified drawable.
HELP
&std_pdb_misc;
@inargs = (
{ name => 'drawable', type => 'drawable',
desc => 'The drawable' }
);
%invoke = (
headers => [ qw("core/gimpdrawable-filters.h") ],
code => <<'CODE'
{
if (gimp_pdb_item_is_attached (GIMP_ITEM (drawable), NULL,
GIMP_PDB_ITEM_CONTENT, error) &&
gimp_pdb_item_is_not_group (GIMP_ITEM (drawable), error))
{
gimp_drawable_merge_filters (drawable);
}
else
success = FALSE;
}
CODE
);
}
sub drawable_merge_shadow {
$blurb = 'Merge the shadow buffer with the specified drawable.';
@ -899,6 +931,7 @@ CODE
"gegl/gimp-babl-compat.h"
"core/gimp.h"
"core/gimpchannel-select.h"
"core/gimpdrawable-filters.h"
"core/gimpdrawable-offset.h"
"core/gimpimage.h"
"core/gimptempbuf.h"
@ -920,10 +953,11 @@ CODE
drawable_get_offsets
drawable_mask_bounds
drawable_mask_intersect
drawable_merge_filters
drawable_merge_shadow
drawable_free_shadow
drawable_update
drawable_fill
drawable_fill
drawable_offset
drawable_thumbnail
drawable_sub_thumbnail