From 679220d8f5773c757dcfd5353239a898e6dba92f Mon Sep 17 00:00:00 2001 From: Jehan Date: Mon, 27 Jan 2025 13:17:49 +0100 Subject: [PATCH] =?UTF-8?q?Issue=20#12568:=20support=20GIMP's=20GEGL=20ope?= =?UTF-8?q?rations=20with=20a=20custom=20config=20argument=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … in new filter API. --- app/core/gimpdrawablefilter.c | 78 +++++++++++++++-- app/pdb/drawable-filter-cmds.c | 138 ++++++++++++++++++++++++------- libgimp/gimpdrawablefilter.c | 4 +- libgimp/gimpdrawablefilter_pdb.c | 14 ++-- libgimp/gimpdrawablefilter_pdb.h | 4 +- pdb/groups/drawable_filter.pdb | 118 ++++++++++++++++++++++---- 6 files changed, 291 insertions(+), 65 deletions(-) diff --git a/app/core/gimpdrawablefilter.c b/app/core/gimpdrawablefilter.c index 4414985894..425375bcca 100644 --- a/app/core/gimpdrawablefilter.c +++ b/app/core/gimpdrawablefilter.c @@ -38,6 +38,8 @@ #include "core-types.h" +#include "operations/gimp-operation-config.h" + #include "gegl/gimp-babl.h" #include "gegl/gimpapplicator.h" #include "gegl/gimp-gegl-utils.h" @@ -769,9 +771,12 @@ gimp_drawable_filter_set_preview_split (GimpDrawableFilter *filter, } } -/* This function is mostly for usage by libgimp API. The idea is to have +/* This function is **ONLY** for usage by libgimp API. The idea is to have * a single function which updates a bunch of settings in a single call * and in particular a single rendering update. + * + * Also it does some funky config object switch for custom operations + * which is only needed libgimp-side. */ gboolean gimp_drawable_filter_update (GimpDrawableFilter *filter, @@ -786,8 +791,12 @@ gimp_drawable_filter_update (GimpDrawableFilter *filter, const GimpDrawable **auxinputs, GError **error) { + GimpImage *image; + GimpObject *settings = NULL; + GeglNode *node = NULL; GParamSpec **pspecs; gchar *opname; + guint n_parent_pspecs = 0; guint n_pspecs; gint n_values; gint n_auxinputs; @@ -822,14 +831,55 @@ gimp_drawable_filter_update (GimpDrawableFilter *filter, gegl_node_get (filter->operation, "operation", &opname, NULL); - pspecs = gegl_operation_list_properties (opname, &n_pspecs); - for (gint i = 0; i < n_pspecs; i++) + image = gimp_item_get_image (GIMP_ITEM (filter->drawable)); + node = gimp_drawable_filter_get_operation (filter); + if (gimp_operation_config_is_custom (image->gimp, opname)) { + GObjectClass *klass; + GObjectClass *parent_klass; + + gegl_node_get (node, + "config", &settings, + NULL); + klass = G_OBJECT_GET_CLASS (settings); + parent_klass = G_OBJECT_CLASS (g_type_class_peek_parent (klass)); + g_free (g_object_class_list_properties (parent_klass, &n_parent_pspecs)); + pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (settings), &n_pspecs); + } + else + { + pspecs = gegl_operation_list_properties (opname, &n_pspecs); + } + + for (gint i = n_parent_pspecs; i < n_pspecs; i++) + { + GParamSpec *target_pspec; GParamSpec *pspec = pspecs[i]; GValue old_value = G_VALUE_INIT; gint j; - gegl_node_get_property (filter->operation, pspec->name, &old_value); + if (settings) + target_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (settings), pspec->name); + else + target_pspec = gegl_node_find_property (node, pspec->name); + if (! target_pspec) + { + /* If this ever happens, this is more likely a bug in our + * PDB code, unless someone tried to call the PDB procedure + * directly with bad data. + */ + g_set_error (error, GIMP_ERROR, GIMP_FAILED, + /* TODO: localize after string freeze. */ + "GEGL operation '%s' has been called with a " + "non-existent argument name '%s' (#%d).", + opname, pspec->name, i); + break; + } + + if (settings) + g_object_get_property (G_OBJECT (settings), pspec->name, &old_value); + else + gegl_node_get_property (node, pspec->name, &old_value); for (j = 0; j < n_values; j++) if (g_strcmp0 (pspec->name, propnames[j]) == 0) @@ -897,7 +947,10 @@ gimp_drawable_filter_update (GimpDrawableFilter *filter, if (g_param_values_cmp (pspec, new_value, &old_value) != 0) { - gegl_node_set_property (filter->operation, pspec->name, new_value); + if (settings) + g_object_set_property (G_OBJECT (settings), pspec->name, new_value); + else + gegl_node_set_property (node, pspec->name, new_value); changed = TRUE; } @@ -913,7 +966,10 @@ gimp_drawable_filter_update (GimpDrawableFilter *filter, g_value_init (&default_value, pspec->value_type); g_param_value_set_default (pspec, &default_value); - gegl_node_set_property (filter->operation, pspec->name, &default_value); + if (settings) + g_object_set_property (G_OBJECT (settings), pspec->name, &default_value); + else + gegl_node_set_property (node, pspec->name, &default_value); changed = TRUE; g_value_unset (&default_value); @@ -951,7 +1007,7 @@ gimp_drawable_filter_update (GimpDrawableFilter *filter, GeglNode *src_node; GeglBuffer *buffer; - if (! gegl_node_has_pad (filter->operation, auxinputnames[i])) + if (! gegl_node_has_pad (node, auxinputnames[i])) { g_set_error (error, GIMP_ERROR, GIMP_FAILED, /* TODO: localize after string freeze. */ @@ -964,18 +1020,22 @@ gimp_drawable_filter_update (GimpDrawableFilter *filter, buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (auxinputs[i])); g_object_ref (buffer); - src_node = gegl_node_new_child (gegl_node_get_parent (filter->operation), + src_node = gegl_node_new_child (gegl_node_get_parent (node), "operation", "gegl:buffer-source", "buffer", buffer, NULL); g_object_unref (buffer); - gegl_node_connect (src_node, "output", filter->operation, auxinputnames[i]); + gegl_node_connect (src_node, "output", node, auxinputnames[i]); } } + if (settings) + gegl_node_set (node, "config", settings, NULL); + g_object_thaw_notify (G_OBJECT (filter)); + g_clear_object (&settings); g_free (pspecs); g_free (opname); diff --git a/app/pdb/drawable-filter-cmds.c b/app/pdb/drawable-filter-cmds.c index be4824cdda..39b9676b93 100644 --- a/app/pdb/drawable-filter-cmds.c +++ b/app/pdb/drawable-filter-cmds.c @@ -39,6 +39,8 @@ #include "core/gimpimage-undo-push.h" #include "core/gimpitem.h" #include "core/gimpparamspecs.h" +#include "operations/gimp-operation-config.h" +#include "operations/gimpoperationsettings.h" #include "gimppdb.h" #include "gimppdberror.h" @@ -387,18 +389,46 @@ drawable_filter_get_number_arguments_invoker (GimpProcedure *procedure, { gboolean success = TRUE; GimpValueArray *return_vals; - const gchar *operation_name; + GimpDrawableFilter *filter; gint num_args = 0; - operation_name = g_value_get_string (gimp_value_array_index (args, 0)); + filter = g_value_get_object (gimp_value_array_index (args, 0)); if (success) { - if (gegl_has_operation (operation_name)) + GeglNode *node; + const gchar *opname; + + node = gimp_drawable_filter_get_operation (filter); + opname = gegl_node_get_operation (node); + + if (gegl_has_operation (opname)) { guint n_properties; - g_free (gegl_operation_list_properties (operation_name, &n_properties)); + if (gimp_operation_config_is_custom (gimp, opname)) + { + GimpObject *settings = NULL; + GObjectClass *klass; + GObjectClass *parent_klass; + guint n_parent_properties; + + gegl_node_get (node, + "config", &settings, + NULL); + klass = G_OBJECT_GET_CLASS (settings); + parent_klass = G_OBJECT_CLASS (g_type_class_peek_parent (klass)); + + g_free (g_object_class_list_properties (parent_klass, &n_parent_properties)); + g_free (g_object_class_list_properties (klass, &n_properties)); + g_clear_object (&settings); + n_properties -= n_parent_properties; + } + else + { + g_free (gegl_operation_list_properties (opname, &n_properties)); + } + num_args = (gint) n_properties; } else @@ -426,25 +456,49 @@ drawable_filter_get_pspec_invoker (GimpProcedure *procedure, { gboolean success = TRUE; GimpValueArray *return_vals; - const gchar *operation_name; + GimpDrawableFilter *filter; gint arg_num; GParamSpec *param_spec = NULL; - operation_name = g_value_get_string (gimp_value_array_index (args, 0)); + filter = g_value_get_object (gimp_value_array_index (args, 0)); arg_num = g_value_get_int (gimp_value_array_index (args, 1)); if (success) { - if (gegl_has_operation (operation_name)) + GimpObject *settings = NULL; + GeglNode *node; + const gchar *opname; + + node = gimp_drawable_filter_get_operation (filter); + opname = gegl_node_get_operation (node); + + if (gegl_has_operation (opname)) { - GParamSpec **specs; - guint n_properties; + GParamSpec **specs; + guint n_properties; + guint n_parent_properties = 0; - specs = gegl_operation_list_properties (operation_name, &n_properties); - - if (arg_num >= 0 && arg_num < n_properties) + if (gimp_operation_config_is_custom (gimp, opname)) { - param_spec = g_param_spec_ref (specs[arg_num]); + GObjectClass *klass; + GObjectClass *parent_klass; + + gegl_node_get (node, + "config", &settings, + NULL); + klass = G_OBJECT_GET_CLASS (settings); + parent_klass = G_OBJECT_CLASS (g_type_class_peek_parent (klass)); + g_free (g_object_class_list_properties (parent_klass, &n_parent_properties)); + } + + if (settings != NULL) + specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (settings), &n_properties); + else + specs = gegl_operation_list_properties (opname, &n_properties); + + if (arg_num >= 0 && n_parent_properties + arg_num < n_properties) + { + param_spec = g_param_spec_ref (specs[n_parent_properties + arg_num]); } else { @@ -457,6 +511,8 @@ drawable_filter_get_pspec_invoker (GimpProcedure *procedure, { success = FALSE; } + + g_clear_object (&settings); } return_vals = gimp_procedure_get_return_values (procedure, success, @@ -490,23 +546,50 @@ drawable_filter_get_arguments_invoker (GimpProcedure *procedure, const gchar *opname; GParamSpec **specs; guint n_specs; + guint n_parent_properties = 0; GStrvBuilder *names_builder; + GimpObject *settings = NULL; node = gimp_drawable_filter_get_operation (filter); opname = gegl_node_get_operation (node); - specs = gegl_operation_list_properties (opname, &n_specs); + if (gegl_has_operation (opname) && + gimp_operation_config_is_custom (gimp, opname)) + { + GObjectClass *klass; + GObjectClass *parent_klass; + + gegl_node_get (node, + "config", &settings, + NULL); + klass = G_OBJECT_GET_CLASS (settings); + parent_klass = G_OBJECT_CLASS (g_type_class_peek_parent (klass)); + g_free (g_object_class_list_properties (parent_klass, &n_parent_properties)); + } + + if (settings != NULL) + { + specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (settings), &n_specs); + n_specs -= n_parent_properties; + } + else + { + specs = gegl_operation_list_properties (opname, &n_specs); + } names_builder = g_strv_builder_new (); values = gimp_value_array_new (n_specs); for (gint i = 0; i < n_specs; i++) { - GParamSpec *pspec = specs[i]; + GParamSpec *pspec = specs[n_parent_properties + i]; GValue value = G_VALUE_INIT; g_value_init (&value, pspec->value_type); - gegl_node_get_property (node, pspec->name, &value); + if (settings != NULL) + g_object_get_property (G_OBJECT (settings), pspec->name, &value); + else + gegl_node_get_property (node, pspec->name, &value); if (GEGL_IS_PARAM_SPEC_ENUM (pspec)) { @@ -553,6 +636,7 @@ drawable_filter_get_arguments_invoker (GimpProcedure *procedure, g_strv_builder_unref (names_builder); g_free (specs); + g_clear_object (&settings); } return_vals = gimp_procedure_get_return_values (procedure, success, @@ -958,12 +1042,11 @@ register_drawable_filter_procs (GimpPDB *pdb) "Jehan", "2024"); gimp_procedure_add_argument (procedure, - gimp_param_spec_string ("operation-name", - "operation name", - "The procedure name", - FALSE, FALSE, TRUE, - NULL, - GIMP_PARAM_READWRITE)); + gimp_param_spec_drawable_filter ("filter", + "filter", + "The filter", + FALSE, + GIMP_PARAM_READWRITE)); gimp_procedure_add_return_value (procedure, g_param_spec_int ("num-args", "num args", @@ -988,12 +1071,11 @@ register_drawable_filter_procs (GimpPDB *pdb) "Jehan", "2024"); gimp_procedure_add_argument (procedure, - gimp_param_spec_string ("operation-name", - "operation name", - "The procedure name", - FALSE, FALSE, TRUE, - NULL, - GIMP_PARAM_READWRITE)); + gimp_param_spec_drawable_filter ("filter", + "filter", + "The filter", + FALSE, + GIMP_PARAM_READWRITE)); gimp_procedure_add_argument (procedure, g_param_spec_int ("arg-num", "arg num", diff --git a/libgimp/gimpdrawablefilter.c b/libgimp/gimpdrawablefilter.c index c87ad26f03..98ebc29f48 100644 --- a/libgimp/gimpdrawablefilter.c +++ b/libgimp/gimpdrawablefilter.c @@ -323,7 +323,7 @@ gimp_drawable_filter_get_config (GimpDrawableFilter *filter) canonical_name = gimp_canonicalize_identifier (op_name); config_type_name = g_strdup_printf ("GimpDrawableFilterConfig-%s", canonical_name); config_type = g_type_from_name (config_type_name); - n_args = _gimp_drawable_filter_get_number_arguments (op_name); + n_args = _gimp_drawable_filter_get_number_arguments (filter); if (! config_type) { @@ -335,7 +335,7 @@ gimp_drawable_filter_get_config (GimpDrawableFilter *filter) { GParamSpec *pspec; - pspec = _gimp_drawable_filter_get_pspec (op_name, i); + pspec = _gimp_drawable_filter_get_pspec (filter, i); config_args[i] = pspec; } diff --git a/libgimp/gimpdrawablefilter_pdb.c b/libgimp/gimpdrawablefilter_pdb.c index 3641ecf401..8cc4b7367f 100644 --- a/libgimp/gimpdrawablefilter_pdb.c +++ b/libgimp/gimpdrawablefilter_pdb.c @@ -416,7 +416,7 @@ _gimp_drawable_filter_update (GimpDrawableFilter *filter, /** * _gimp_drawable_filter_get_number_arguments: - * @operation_name: The procedure name. + * @filter: The filter. * * Queries for the number of arguments on the specified filter. * @@ -430,14 +430,14 @@ _gimp_drawable_filter_update (GimpDrawableFilter *filter, * Since: 3.0 **/ gint -_gimp_drawable_filter_get_number_arguments (const gchar *operation_name) +_gimp_drawable_filter_get_number_arguments (GimpDrawableFilter *filter) { GimpValueArray *args; GimpValueArray *return_vals; gint num_args = 0; args = gimp_value_array_new_from_types (NULL, - G_TYPE_STRING, operation_name, + GIMP_TYPE_DRAWABLE_FILTER, filter, G_TYPE_NONE); return_vals = _gimp_pdb_run_procedure_array (gimp_get_pdb (), @@ -455,7 +455,7 @@ _gimp_drawable_filter_get_number_arguments (const gchar *operation_name) /** * _gimp_drawable_filter_get_pspec: - * @operation_name: The procedure name. + * @filter: The filter. * @arg_num: The argument number. * * Queries for information on the specified filter's argument. @@ -468,15 +468,15 @@ _gimp_drawable_filter_get_number_arguments (const gchar *operation_name) * Since: 3.0 **/ GParamSpec * -_gimp_drawable_filter_get_pspec (const gchar *operation_name, - gint arg_num) +_gimp_drawable_filter_get_pspec (GimpDrawableFilter *filter, + gint arg_num) { GimpValueArray *args; GimpValueArray *return_vals; GParamSpec *param_spec = NULL; args = gimp_value_array_new_from_types (NULL, - G_TYPE_STRING, operation_name, + GIMP_TYPE_DRAWABLE_FILTER, filter, G_TYPE_INT, arg_num, G_TYPE_NONE); diff --git a/libgimp/gimpdrawablefilter_pdb.h b/libgimp/gimpdrawablefilter_pdb.h index 07a79f2b7f..844c256927 100644 --- a/libgimp/gimpdrawablefilter_pdb.h +++ b/libgimp/gimpdrawablefilter_pdb.h @@ -53,8 +53,8 @@ G_GNUC_INTERNAL gboolean _gimp_drawable_filter_update (GimpDraw GimpLayerColorSpace composite_space, const gchar **auxinputnames, const GimpDrawable **auxinputs); -G_GNUC_INTERNAL gint _gimp_drawable_filter_get_number_arguments (const gchar *operation_name); -G_GNUC_INTERNAL GParamSpec* _gimp_drawable_filter_get_pspec (const gchar *operation_name, +G_GNUC_INTERNAL gint _gimp_drawable_filter_get_number_arguments (GimpDrawableFilter *filter); +G_GNUC_INTERNAL GParamSpec* _gimp_drawable_filter_get_pspec (GimpDrawableFilter *filter, gint arg_num); G_GNUC_INTERNAL gchar** _gimp_drawable_filter_get_arguments (GimpDrawableFilter *filter, GimpValueArray **values); diff --git a/pdb/groups/drawable_filter.pdb b/pdb/groups/drawable_filter.pdb index 1d4da5bfa0..1b07f7b08f 100644 --- a/pdb/groups/drawable_filter.pdb +++ b/pdb/groups/drawable_filter.pdb @@ -372,8 +372,8 @@ HELP $lib_private = 1; @inargs = ( - { name => 'operation_name', type => 'string', non_empty => 1, - desc => 'The procedure name' } + { name => 'filter', type => 'filter', + desc => 'The filter' } ); @outargs = ( @@ -384,11 +384,39 @@ HELP %invoke = ( code => <<'CODE' { - if (gegl_has_operation (operation_name)) + GeglNode *node; + const gchar *opname; + + node = gimp_drawable_filter_get_operation (filter); + opname = gegl_node_get_operation (node); + + if (gegl_has_operation (opname)) { guint n_properties; - g_free (gegl_operation_list_properties (operation_name, &n_properties)); + if (gimp_operation_config_is_custom (gimp, opname)) + { + GimpObject *settings = NULL; + GObjectClass *klass; + GObjectClass *parent_klass; + guint n_parent_properties; + + gegl_node_get (node, + "config", &settings, + NULL); + klass = G_OBJECT_GET_CLASS (settings); + parent_klass = G_OBJECT_CLASS (g_type_class_peek_parent (klass)); + + g_free (g_object_class_list_properties (parent_klass, &n_parent_properties)); + g_free (g_object_class_list_properties (klass, &n_properties)); + g_clear_object (&settings); + n_properties -= n_parent_properties; + } + else + { + g_free (gegl_operation_list_properties (opname, &n_properties)); + } + num_args = (gint) n_properties; } else @@ -414,8 +442,8 @@ HELP $lib_private = 1; @inargs = ( - { name => 'operation_name', type => 'string', non_empty => 1, - desc => 'The procedure name' }, + { name => 'filter', type => 'filter', + desc => 'The filter' }, { name => 'arg_num', type => 'int32', desc => 'The argument number' } ); @@ -426,18 +454,42 @@ HELP ); %invoke = ( - code => < <<'CODE' { - if (gegl_has_operation (operation_name)) + GimpObject *settings = NULL; + GeglNode *node; + const gchar *opname; + + node = gimp_drawable_filter_get_operation (filter); + opname = gegl_node_get_operation (node); + + if (gegl_has_operation (opname)) { - GParamSpec **specs; - guint n_properties; + GParamSpec **specs; + guint n_properties; + guint n_parent_properties = 0; - specs = gegl_operation_list_properties (operation_name, &n_properties); - - if (arg_num >= 0 && arg_num < n_properties) + if (gimp_operation_config_is_custom (gimp, opname)) { - param_spec = g_param_spec_ref (specs[arg_num]); + GObjectClass *klass; + GObjectClass *parent_klass; + + gegl_node_get (node, + "config", &settings, + NULL); + klass = G_OBJECT_GET_CLASS (settings); + parent_klass = G_OBJECT_CLASS (g_type_class_peek_parent (klass)); + g_free (g_object_class_list_properties (parent_klass, &n_parent_properties)); + } + + if (settings != NULL) + specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (settings), &n_properties); + else + specs = gegl_operation_list_properties (opname, &n_properties); + + if (arg_num >= 0 && n_parent_properties + arg_num < n_properties) + { + param_spec = g_param_spec_ref (specs[n_parent_properties + arg_num]); } else { @@ -450,6 +502,8 @@ HELP { success = FALSE; } + + g_clear_object (&settings); } CODE ); @@ -486,23 +540,50 @@ HELP const gchar *opname; GParamSpec **specs; guint n_specs; + guint n_parent_properties = 0; GStrvBuilder *names_builder; + GimpObject *settings = NULL; node = gimp_drawable_filter_get_operation (filter); opname = gegl_node_get_operation (node); - specs = gegl_operation_list_properties (opname, &n_specs); + if (gegl_has_operation (opname) && + gimp_operation_config_is_custom (gimp, opname)) + { + GObjectClass *klass; + GObjectClass *parent_klass; + + gegl_node_get (node, + "config", &settings, + NULL); + klass = G_OBJECT_GET_CLASS (settings); + parent_klass = G_OBJECT_CLASS (g_type_class_peek_parent (klass)); + g_free (g_object_class_list_properties (parent_klass, &n_parent_properties)); + } + + if (settings != NULL) + { + specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (settings), &n_specs); + n_specs -= n_parent_properties; + } + else + { + specs = gegl_operation_list_properties (opname, &n_specs); + } names_builder = g_strv_builder_new (); values = gimp_value_array_new (n_specs); for (gint i = 0; i < n_specs; i++) { - GParamSpec *pspec = specs[i]; + GParamSpec *pspec = specs[n_parent_properties + i]; GValue value = G_VALUE_INIT; g_value_init (&value, pspec->value_type); - gegl_node_get_property (node, pspec->name, &value); + if (settings != NULL) + g_object_get_property (G_OBJECT (settings), pspec->name, &value); + else + gegl_node_get_property (node, pspec->name, &value); if (GEGL_IS_PARAM_SPEC_ENUM (pspec)) { @@ -549,6 +630,7 @@ HELP g_strv_builder_unref (names_builder); g_free (specs); + g_clear_object (&settings); } CODE ); @@ -600,6 +682,8 @@ CODE "core/gimpdrawable-filters.h" "core/gimpimage-undo-push.h" "core/gimpitem.h" + "operations/gimp-operation-config.h" + "operations/gimpoperationsettings.h" "gimppdberror.h" "gimp-intl.h");