Issue #12568: support GIMP's GEGL operations with a custom config argument…

… in new filter API.
This commit is contained in:
Jehan
2025-01-27 13:17:49 +01:00
parent 79b200335c
commit 679220d8f5
6 changed files with 291 additions and 65 deletions

View File

@ -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);

View File

@ -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",

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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
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");