diff --git a/app/core/core-types.h b/app/core/core-types.h index b53ffea899..93cb8958c5 100644 --- a/app/core/core-types.h +++ b/app/core/core-types.h @@ -163,6 +163,7 @@ typedef struct _GimpTagCache GimpTagCache; /* drawable objects */ typedef struct _GimpDrawable GimpDrawable; +typedef struct _GimpDrawableFilterMask GimpDrawableFilterMask; typedef struct _GimpChannel GimpChannel; typedef struct _GimpLayerMask GimpLayerMask; typedef struct _GimpSelection GimpSelection; diff --git a/app/core/gimpdrawable.c b/app/core/gimpdrawable.c index f38a4ddc07..52d5469f52 100644 --- a/app/core/gimpdrawable.c +++ b/app/core/gimpdrawable.c @@ -580,7 +580,7 @@ gimp_drawable_scale (GimpItem *item, if (GIMP_IS_DRAWABLE_FILTER (list->data)) { GimpDrawableFilter *filter = list->data; - GimpChannel *mask = gimp_drawable_filter_get_mask (filter); + GimpChannel *mask = GIMP_CHANNEL (gimp_drawable_filter_get_mask (filter)); GeglRectangle *rect = GEGL_RECTANGLE (0, 0, new_width, new_height); @@ -689,7 +689,7 @@ gimp_drawable_resize (GimpItem *item, if (GIMP_IS_DRAWABLE_FILTER (list->data)) { GimpDrawableFilter *filter = list->data; - GimpChannel *mask = gimp_drawable_filter_get_mask (filter); + GimpChannel *mask = GIMP_CHANNEL (gimp_drawable_filter_get_mask (filter)); GeglRectangle rect = {0, 0, new_width, new_height}; /* Don't resize partial layer effects */ @@ -1399,8 +1399,8 @@ gimp_drawable_convert_type (GimpDrawable *drawable, { if (GIMP_IS_DRAWABLE_FILTER (filter_list->data)) { - GimpDrawableFilter *filter = filter_list->data; - GimpChannel *mask; + GimpDrawableFilter *filter = filter_list->data; + GimpDrawableFilterMask *mask; mask = gimp_drawable_filter_get_mask (filter); diff --git a/app/core/gimpdrawablefilter.c b/app/core/gimpdrawablefilter.c index c02b81ba8e..b26f107136 100644 --- a/app/core/gimpdrawablefilter.c +++ b/app/core/gimpdrawablefilter.c @@ -46,6 +46,7 @@ #include "gimpchannel.h" #include "gimpdrawable-filters.h" #include "gimpdrawablefilter.h" +#include "gimpdrawablefiltermask.h" #include "gimperror.h" #include "gimpidtable.h" #include "gimpimage.h" @@ -63,6 +64,7 @@ enum { PROP_0, PROP_ID, + PROP_DRAWABLE, PROP_MASK, N_PROPS }; @@ -75,7 +77,7 @@ struct _GimpDrawableFilter gint ID; GimpDrawable *drawable; - GimpChannel *mask; + GimpDrawableFilterMask *mask; GeglNode *operation; gboolean has_input; @@ -189,14 +191,22 @@ gimp_drawable_filter_class_init (GimpDrawableFilterClass *klass) object_class->dispose = gimp_drawable_filter_dispose; object_class->finalize = gimp_drawable_filter_finalize; - drawable_filter_props[PROP_ID] = g_param_spec_int ("id", NULL, NULL, - 0, G_MAXINT, 0, - GIMP_PARAM_READABLE); + drawable_filter_props[PROP_ID] = g_param_spec_int ("id", NULL, NULL, + 0, G_MAXINT, 0, + GIMP_PARAM_READABLE); - drawable_filter_props[PROP_MASK] = g_param_spec_object ("mask", - NULL, NULL, - GIMP_TYPE_CHANNEL, - GIMP_PARAM_READWRITE); + drawable_filter_props[PROP_DRAWABLE] = g_param_spec_object ("drawable", NULL, NULL, + GIMP_TYPE_DRAWABLE, + GIMP_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY); + /* The mask is in fact a GimpDrawableFilterMask but the property is a + * GimpDrawable to allow setting any drawable. The set_property() code + * will take care of creating a new object of the proper type. + */ + drawable_filter_props[PROP_MASK] = g_param_spec_object ("mask", + NULL, NULL, + GIMP_TYPE_DRAWABLE, + GIMP_PARAM_READWRITE); g_object_class_install_properties (object_class, N_PROPS, drawable_filter_props); } @@ -223,15 +233,30 @@ gimp_drawable_filter_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { - GimpDrawableFilter *filter = GIMP_DRAWABLE_FILTER (object); + GimpDrawableFilter *filter = GIMP_DRAWABLE_FILTER (object); + GimpDrawableFilterMask *mask = NULL; + GimpImage *image; switch (property_id) { + case PROP_DRAWABLE: + g_set_object (&filter->drawable, g_value_get_object (value)); + break; + case PROP_MASK: - g_set_object (&filter->mask, g_value_get_object (value)); + image = gimp_item_get_image (GIMP_ITEM (filter->drawable)); + + if (g_value_get_object (value) != NULL) + mask = GIMP_DRAWABLE_FILTER_MASK (gimp_item_convert (GIMP_ITEM (g_value_get_object (value)), + image, GIMP_TYPE_DRAWABLE_FILTER_MASK)); + g_set_object (&filter->mask, mask); + g_clear_object (&mask); if (filter->mask) - gimp_drawable_filter_sync_mask (filter); + { + gimp_drawable_filter_mask_set_filter (filter->mask, filter); + gimp_drawable_filter_sync_mask (filter); + } break; default: @@ -321,10 +346,10 @@ gimp_drawable_filter_new (GimpDrawable *drawable, filter = g_object_new (GIMP_TYPE_DRAWABLE_FILTER, "name", undo_desc, "icon-name", icon_name, + "drawable", drawable, "mask", NULL, NULL); - filter->drawable = g_object_ref (drawable); filter->operation = g_object_ref (operation); image = gimp_item_get_image (GIMP_ITEM (drawable)); @@ -397,7 +422,6 @@ gimp_drawable_filter_duplicate (GimpDrawable *drawable, { GimpImage *image; GimpDrawableFilter *filter; - GimpChannel *mask; GeglNode *prior_node; GeglNode *node = gegl_node_new (); gchar *operation; @@ -466,15 +490,9 @@ gimp_drawable_filter_duplicate (GimpDrawable *drawable, image = gimp_item_get_image (GIMP_ITEM (drawable)); if (image != NULL) - { - mask = GIMP_CHANNEL (gimp_item_convert (GIMP_ITEM (prior_filter->mask), - image, GIMP_TYPE_CHANNEL)); - - g_object_set (filter, - "mask", mask, - NULL); - g_object_unref (mask); - } + g_object_set (filter, + "mask", prior_filter->mask, + NULL); g_free (operation); @@ -517,7 +535,7 @@ gimp_drawable_filter_get_operation (GimpDrawableFilter *filter) return filter->operation; } -GimpChannel * +GimpDrawableFilterMask * gimp_drawable_filter_get_mask (GimpDrawableFilter *filter) { g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER (filter), NULL); @@ -1183,17 +1201,12 @@ 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; + GimpImage *image = gimp_item_get_image (GIMP_ITEM (filter->drawable)); 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_object_set (filter, + "mask", GIMP_DRAWABLE (gimp_image_get_mask (image)), + NULL); g_signal_handlers_disconnect_by_func (image, gimp_drawable_filter_mask_changed, diff --git a/app/core/gimpdrawablefilter.h b/app/core/gimpdrawablefilter.h index 921561a604..844b5eefe5 100644 --- a/app/core/gimpdrawablefilter.h +++ b/app/core/gimpdrawablefilter.h @@ -66,7 +66,7 @@ GimpDrawableFilter * GimpDrawable * gimp_drawable_filter_get_drawable (GimpDrawableFilter *filter); GeglNode * gimp_drawable_filter_get_operation (GimpDrawableFilter *filter); -GimpChannel * +GimpDrawableFilterMask * gimp_drawable_filter_get_mask (GimpDrawableFilter *filter); gdouble gimp_drawable_filter_get_opacity (GimpDrawableFilter *filter); GimpLayerMode diff --git a/app/core/gimpdrawablefiltermask.c b/app/core/gimpdrawablefiltermask.c new file mode 100644 index 0000000000..b79306141c --- /dev/null +++ b/app/core/gimpdrawablefiltermask.c @@ -0,0 +1,208 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdrawablefiltermask.c + * Copyright (C) 2024 Jehan + * + * 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 . + */ + +#include "config.h" + +#include +#include +#include + +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gegl/gimp-babl.h" + +#include "gimpdrawablefilter.h" +#include "gimpdrawablefiltermask.h" +#include "gimperror.h" +#include "gimpimage.h" + +#include "gimp-intl.h" + + +static gboolean gimp_drawable_filter_mask_is_attached (GimpItem *item); +static GimpItemTree * gimp_drawable_filter_mask_get_tree (GimpItem *item); +static gboolean gimp_drawable_filter_mask_rename (GimpItem *item, + const gchar *new_name, + const gchar *undo_desc, + GError **error); + +static void gimp_drawable_filter_mask_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); + + +G_DEFINE_TYPE (GimpDrawableFilterMask, gimp_drawable_filter_mask, GIMP_TYPE_CHANNEL) + +#define parent_class gimp_drawable_filter_mask_parent_class + + +static void +gimp_drawable_filter_mask_class_init (GimpDrawableFilterMaskClass *klass) +{ + GimpItemClass *item_class = GIMP_ITEM_CLASS (klass); + GimpDrawableClass *drawable_class = GIMP_DRAWABLE_CLASS (klass); + + item_class->is_attached = gimp_drawable_filter_mask_is_attached; + item_class->get_tree = gimp_drawable_filter_mask_get_tree; + item_class->rename = gimp_drawable_filter_mask_rename; + + drawable_class->convert_type = gimp_drawable_filter_mask_convert_type; +} + +static void +gimp_drawable_filter_mask_init (GimpDrawableFilterMask *drawable_filter_mask) +{ +} + +static gboolean +gimp_drawable_filter_mask_is_attached (GimpItem *item) +{ + GimpDrawableFilterMask *mask = GIMP_DRAWABLE_FILTER_MASK (item); + GimpDrawable *drawable; + + if (! mask->filter) + return FALSE; + + drawable = gimp_drawable_filter_get_drawable (mask->filter); + + return (drawable && + gimp_item_is_attached (GIMP_ITEM (drawable)) && + gimp_drawable_filter_get_mask (mask->filter) == mask); +} + +static GimpItemTree * +gimp_drawable_filter_mask_get_tree (GimpItem *item) +{ + return NULL; +} + +static gboolean +gimp_drawable_filter_mask_rename (GimpItem *item, + const gchar *new_name, + const gchar *undo_desc, + GError **error) +{ + g_set_error (error, GIMP_ERROR, GIMP_FAILED, + /* TODO: localized after string freeze. */ + "Cannot rename effect masks."); + + return FALSE; +} + +static void +gimp_drawable_filter_mask_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) +{ + new_format = + gimp_babl_mask_format (gimp_babl_format_get_precision (new_format)); + + GIMP_DRAWABLE_CLASS (parent_class)->convert_type (drawable, dest_image, + new_format, + src_profile, + dest_profile, + layer_dither_type, + mask_dither_type, + push_undo, + progress); +} + + +/* public functions */ + +GimpDrawableFilterMask * +gimp_drawable_filter_mask_new (GimpImage *image, + gint width, + gint height) +{ + GeglColor *black = gegl_color_new ("black"); + GimpChannel *channel; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (width > 0 && height > 0, NULL); + + gimp_color_set_alpha (black, 0.5); + channel = GIMP_CHANNEL (gimp_drawable_new (GIMP_TYPE_DRAWABLE_FILTER_MASK, + image, NULL, + 0, 0, width, height, + gimp_image_get_mask_format (image))); + + gimp_channel_set_color (channel, black, FALSE); + gimp_channel_set_show_masked (channel, TRUE); + + channel->x2 = width; + channel->y2 = height; + + g_object_unref (black); + + return GIMP_DRAWABLE_FILTER_MASK (channel); +} + +void +gimp_drawable_filter_mask_set_filter (GimpDrawableFilterMask *mask, + GimpDrawableFilter *filter) +{ + g_return_if_fail (GIMP_IS_DRAWABLE_FILTER_MASK (mask)); + g_return_if_fail (filter == NULL || GIMP_IS_DRAWABLE_FILTER (filter)); + + mask->filter = filter; + + if (filter) + { + GimpDrawable *drawable; + gchar *mask_name; + gint offset_x; + gint offset_y; + + drawable = gimp_drawable_filter_get_drawable (mask->filter); + + if (drawable) + { + gimp_item_get_offset (GIMP_ITEM (drawable), &offset_x, &offset_y); + gimp_item_set_offset (GIMP_ITEM (mask), offset_x, offset_y); + } + + mask_name = g_strdup_printf (_("%s mask"), gimp_object_get_name (filter)); + + gimp_object_take_name (GIMP_OBJECT (mask), mask_name); + } +} + +GimpDrawableFilter * +gimp_drawable_filter_mask_get_filter (GimpDrawableFilterMask *mask) +{ + g_return_val_if_fail (GIMP_IS_DRAWABLE_FILTER_MASK (mask), NULL); + + return mask->filter; +} diff --git a/app/core/gimpdrawablefiltermask.h b/app/core/gimpdrawablefiltermask.h new file mode 100644 index 0000000000..af12b2bca0 --- /dev/null +++ b/app/core/gimpdrawablefiltermask.h @@ -0,0 +1,61 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * gimpdrawablefiltermask.h + * Copyright (C) 2024 Jehan + * + * 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 . + */ + +#ifndef __GIMP_DRAWABLE_FILTER_MASK_H__ +#define __GIMP_DRAWABLE_FILTER_MASK_H__ + + +#include "gimpchannel.h" + + +#define GIMP_TYPE_DRAWABLE_FILTER_MASK (gimp_drawable_filter_mask_get_type ()) +#define GIMP_DRAWABLE_FILTER_MASK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GIMP_TYPE_DRAWABLE_FILTER_MASK, GimpDrawableFilterMask)) +#define GIMP_DRAWABLE_FILTER_MASK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GIMP_TYPE_DRAWABLE_FILTER_MASK, GimpDrawableFilterMaskClass)) +#define GIMP_IS_DRAWABLE_FILTER_MASK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GIMP_TYPE_DRAWABLE_FILTER_MASK)) +#define GIMP_IS_DRAWABLE_FILTER_MASK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GIMP_TYPE_DRAWABLE_FILTER_MASK)) +#define GIMP_DRAWABLE_FILTER_MASK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GIMP_TYPE_DRAWABLE_FILTER_MASK, GimpDrawableFilterMaskClass)) + + +typedef struct _GimpDrawableFilterMaskClass GimpDrawableFilterMaskClass; + +struct _GimpDrawableFilterMask +{ + GimpChannel parent_instance; + + GimpDrawableFilter *filter; +}; + +struct _GimpDrawableFilterMaskClass +{ + GimpChannelClass parent_class; +}; + + +GType gimp_drawable_filter_mask_get_type (void) G_GNUC_CONST; + +GimpDrawableFilterMask * gimp_drawable_filter_mask_new (GimpImage *image, + gint width, + gint height); +void gimp_drawable_filter_mask_set_filter (GimpDrawableFilterMask *mask, + GimpDrawableFilter *filter); +GimpDrawableFilter * gimp_drawable_filter_mask_get_filter (GimpDrawableFilterMask *mask); + + +#endif /* __GIMP_DRAWABLE_FILTER_MASK_H__ */ diff --git a/app/core/gimpgrouplayer.c b/app/core/gimpgrouplayer.c index ce2ef0e6f5..0cc347c70a 100644 --- a/app/core/gimpgrouplayer.c +++ b/app/core/gimpgrouplayer.c @@ -2136,7 +2136,7 @@ gimp_group_layer_update_size (GimpGroupLayer *group) GimpDrawableFilter *filter = list->data; GimpChannel *filter_mask; - filter_mask = gimp_drawable_filter_get_mask (filter); + filter_mask = GIMP_CHANNEL (gimp_drawable_filter_get_mask (filter)); /* Don't resize partial layer effects */ if (gimp_channel_is_empty (filter_mask)) diff --git a/app/core/gimpitem.c b/app/core/gimpitem.c index 8c5fbee2fc..920ede0c6f 100644 --- a/app/core/gimpitem.c +++ b/app/core/gimpitem.c @@ -1807,7 +1807,7 @@ gimp_item_transform (GimpItem *item, GimpDrawableFilter *filter = filter_list->data; GimpChannel *mask; - mask = gimp_drawable_filter_get_mask (filter); + mask = GIMP_CHANNEL (gimp_drawable_filter_get_mask (filter)); /* Don't resize partial layer effects */ if (! mask || gimp_channel_is_empty (mask)) diff --git a/app/core/meson.build b/app/core/meson.build index f5e7f647cd..36fead6b32 100644 --- a/app/core/meson.build +++ b/app/core/meson.build @@ -114,6 +114,7 @@ libappcore_sources = [ 'gimpdrawable-transform.c', 'gimpdrawable.c', 'gimpdrawablefilter.c', + 'gimpdrawablefiltermask.c', 'gimpdrawablefilterundo.c', 'gimpdrawablemodundo.c', 'gimpdrawablepropundo.c', diff --git a/app/tools/gimpfiltertool.c b/app/tools/gimpfiltertool.c index deadfa3c70..5572c430ee 100644 --- a/app/tools/gimpfiltertool.c +++ b/app/tools/gimpfiltertool.c @@ -1553,7 +1553,7 @@ gimp_filter_tool_create_filter (GimpFilterTool *filter_tool) { GimpChannel *mask = NULL; - mask = gimp_drawable_filter_get_mask (filter_tool->existing_filter); + mask = GIMP_CHANNEL (gimp_drawable_filter_get_mask (filter_tool->existing_filter)); gimp_drawable_filter_apply_with_mask (filter_tool->filter, mask, NULL); } @@ -2189,11 +2189,11 @@ gimp_filter_tool_set_config (GimpFilterTool *filter_tool, if (filter_tool->existing_filter && gimp_drawable_filter_get_mask (filter_tool->filter) == NULL) { - GimpContainer *filters; - GimpChannel *mask; - GimpDrawable *existing_drawable; - gint index; - const gchar *name = _("Editing filter..."); + GimpContainer *filters; + GimpDrawableFilterMask *mask; + GimpDrawable *existing_drawable; + gint index; + const gchar *name = _("Editing filter..."); /* Get drawable from existing filter, as we might have a different * drawable selected in the layer tree */ diff --git a/app/xcf/xcf-save.c b/app/xcf/xcf-save.c index 4d59f465d1..b42c7565d8 100644 --- a/app/xcf/xcf-save.c +++ b/app/xcf/xcf-save.c @@ -1966,9 +1966,9 @@ xcf_save_layer (XcfInfo *info, { if (GIMP_IS_DRAWABLE_FILTER (filter_list->data)) { - GimpDrawableFilter *filter = filter_list->data; - GimpChannel *mask = NULL; - GeglNode *op_node = NULL; + GimpDrawableFilter *filter = filter_list->data; + GimpDrawableFilterMask *mask = NULL; + GeglNode *op_node = NULL; mask = gimp_drawable_filter_get_mask (filter); op_node = gimp_drawable_filter_get_operation (filter); @@ -2040,9 +2040,9 @@ xcf_save_layer (XcfInfo *info, { if (GIMP_IS_DRAWABLE_FILTER (list->data)) { - GimpDrawableFilter *filter = list->data; - GimpChannel *mask = NULL; - GeglNode *op_node = NULL; + GimpDrawableFilter *filter = list->data; + GimpDrawableFilterMask *mask = NULL; + GeglNode *op_node = NULL; mask = gimp_drawable_filter_get_mask (filter); op_node = gimp_drawable_filter_get_operation (filter); @@ -2174,7 +2174,7 @@ xcf_save_effect (XcfInfo *info, offset = info->cp + info->bytes_per_offset; xcf_write_offset_check_error (info, &offset, 1, ;); - effect_mask = gimp_drawable_filter_get_mask (filter_drawable); + effect_mask = GIMP_CHANNEL (gimp_drawable_filter_get_mask (filter_drawable)); xcf_check_error (xcf_save_channel (info, image, effect_mask, error), ;); diff --git a/po/POTFILES.in b/po/POTFILES.in index d6eb2ff62e..80a1b1a4df 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -148,6 +148,7 @@ app/core/gimpdrawable-levels.c app/core/gimpdrawable-offset.c app/core/gimpdrawable-stroke.c app/core/gimpdrawable-transform.c +app/core/gimpdrawablefiltermask.c app/core/gimpdynamicsoutput.c app/core/gimpfilloptions.c app/core/gimpgradient-load.c