diff --git a/app/core/core-enums.h b/app/core/core-enums.h index d2ba601d00..062634c504 100644 --- a/app/core/core-enums.h +++ b/app/core/core-enums.h @@ -194,7 +194,7 @@ typedef enum /*< pdb-skip >*/ GType gimp_curve_point_type_get_type (void) G_GNUC_CONST; -typedef enum /*< pdb-skip >*/ +typedef enum { GIMP_CURVE_POINT_SMOOTH, /*< desc="Smooth" >*/ GIMP_CURVE_POINT_CORNER /*< desc="Corner" >*/ @@ -205,7 +205,7 @@ typedef enum /*< pdb-skip >*/ GType gimp_curve_type_get_type (void) G_GNUC_CONST; -typedef enum /*< pdb-skip >*/ +typedef enum { GIMP_CURVE_SMOOTH, /*< desc="Smooth" >*/ GIMP_CURVE_FREE /*< desc="Freehand" >*/ diff --git a/app/core/gimpparamspecs.c b/app/core/gimpparamspecs.c index 237bfab74a..df48f0c9ad 100644 --- a/app/core/gimpparamspecs.c +++ b/app/core/gimpparamspecs.c @@ -29,6 +29,7 @@ #include "gimp.h" #include "gimpbrush.h" #include "gimpcontext.h" +#include "gimpcurve.h" #include "gimpdisplay.h" #include "gimpdrawablefilter.h" #include "gimpgradient.h" diff --git a/app/plug-in/gimpgpparams.c b/app/plug-in/gimpgpparams.c index d4b9e0adad..1cf29cae97 100644 --- a/app/plug-in/gimpgpparams.c +++ b/app/plug-in/gimpgpparams.c @@ -33,6 +33,7 @@ #include "core/gimp.h" #include "core/gimpbrush.h" +#include "core/gimpcurve.h" #include "core/gimpdisplay.h" #include "core/gimpdrawablefilter.h" #include "core/gimpgradient.h" diff --git a/app/plug-in/plug-in-rc.c b/app/plug-in/plug-in-rc.c index 8459c67011..2b82393384 100644 --- a/app/plug-in/plug-in-rc.c +++ b/app/plug-in/plug-in-rc.c @@ -1048,6 +1048,15 @@ plug_in_proc_arg_deserialize (GScanner *scanner, goto error; } break; + + case GP_PARAM_DEF_TYPE_CURVE: + if (! gimp_scanner_parse_int (scanner, + ¶m_def.meta.m_curve.none_ok)) + { + token = G_TOKEN_INT; + goto error; + } + break; } if (! gimp_scanner_parse_token (scanner, G_TOKEN_RIGHT_PAREN)) @@ -1119,6 +1128,9 @@ plug_in_proc_arg_deserialize (GScanner *scanner, case GP_PARAM_DEF_TYPE_FILE: g_free (param_def.meta.m_file.default_uri); break; + + case GP_PARAM_DEF_TYPE_CURVE: + break; } return token; @@ -1318,6 +1330,11 @@ plug_in_rc_write_proc_arg (GimpConfigWriter *writer, gimp_config_writer_string (writer, param_def.meta.m_file.default_uri); break; + + case GP_PARAM_DEF_TYPE_CURVE: + gimp_config_writer_printf (writer, "%d", + param_def.meta.m_curve.none_ok); + break; } gimp_config_writer_close (writer); diff --git a/libgimp/gimp.def b/libgimp/gimp.def index 4078b8ef5a..089aed2e40 100644 --- a/libgimp/gimp.def +++ b/libgimp/gimp.def @@ -182,6 +182,22 @@ EXPORTS gimp_convert_dither_type_get_type gimp_convolve gimp_convolve_default + gimp_curve_add_point + gimp_curve_clear_points + gimp_curve_delete_point + gimp_curve_get_n_points + gimp_curve_get_n_samples + gimp_curve_get_point + gimp_curve_get_point_type + gimp_curve_get_type + gimp_curve_is_identity + gimp_curve_new + gimp_curve_point_type_get_type + gimp_curve_set_curve_type + gimp_curve_set_n_samples + gimp_curve_set_point + gimp_curve_set_point_type + gimp_curve_type_get_type gimp_debug_timer_end gimp_debug_timer_start gimp_default_display @@ -731,6 +747,7 @@ EXPORTS gimp_palettes_set_popup gimp_param_brush_get_type gimp_param_channel_get_type + gimp_param_curve_get_type gimp_param_display_get_type gimp_param_drawable_filter_get_type gimp_param_drawable_get_type @@ -750,6 +767,7 @@ EXPORTS gimp_param_selection_get_type gimp_param_spec_brush gimp_param_spec_channel + gimp_param_spec_curve gimp_param_spec_display gimp_param_spec_display_none_allowed gimp_param_spec_drawable diff --git a/libgimp/gimp.h b/libgimp/gimp.h index 76eecf5ff7..e9f15246a7 100644 --- a/libgimp/gimp.h +++ b/libgimp/gimp.h @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include diff --git a/libgimp/gimpcurve.c b/libgimp/gimpcurve.c new file mode 100644 index 0000000000..a49d233699 --- /dev/null +++ b/libgimp/gimpcurve.c @@ -0,0 +1,667 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * gimpcurve.c + * Copyright (C) 2026 Alx Sa + * + * This library is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#include "config.h" + +#include "gimp.h" + +#include "gimpcurve.h" + + +enum +{ + PROP_0, + PROP_CURVE_TYPE, + PROP_N_POINTS, + PROP_POINTS, + PROP_POINT_TYPES, + PROP_N_SAMPLES, + PROP_SAMPLES, + N_PROPS +}; +static GParamSpec *obj_props[N_PROPS] = { NULL, }; + +typedef struct +{ + gdouble x; + gdouble y; + + GimpCurvePointType type; +} GimpCurvePoint; + +struct _GimpCurve +{ + GObject parent_instance; + + GimpCurveType curve_type; + + gint n_points; + GimpCurvePoint *points; + + gint n_samples; + gdouble *samples; + + gboolean identity; /* whether the curve is an identity mapping */ +}; + +static void gimp_curve_finalize (GObject *object); +static void gimp_curve_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_curve_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE (GimpCurve, gimp_curve, G_TYPE_OBJECT); + +static void gimp_curve_class_init (GimpCurveClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *array_spec; + + object_class->finalize = gimp_curve_finalize; + object_class->set_property = gimp_curve_set_property; + object_class->get_property = gimp_curve_get_property; + + obj_props[PROP_CURVE_TYPE] = + g_param_spec_enum ("curve-type", + "Curve Type", + "The curve type", + GIMP_TYPE_CURVE_TYPE, + GIMP_CURVE_SMOOTH, + GIMP_CONFIG_PARAM_FLAGS); + + obj_props[PROP_N_POINTS] = + g_param_spec_int ("n-points", + "Number of Points", + "The number of points", + 0, G_MAXINT, 0, + /* for backward compatibility */ + GIMP_CONFIG_PARAM_IGNORE | GIMP_CONFIG_PARAM_FLAGS); + + array_spec = g_param_spec_double ("point", NULL, NULL, + -1.0, 1.0, 0.0, GIMP_PARAM_READWRITE); + obj_props[PROP_POINTS] = + gimp_param_spec_value_array ("points", + NULL, NULL, + array_spec, + GIMP_CONFIG_PARAM_FLAGS); + + array_spec = g_param_spec_enum ("point-type", NULL, NULL, + GIMP_TYPE_CURVE_POINT_TYPE, + GIMP_CURVE_POINT_SMOOTH, + GIMP_PARAM_READWRITE); + obj_props[PROP_POINT_TYPES] = + gimp_param_spec_value_array ("point-types", + NULL, NULL, + array_spec, + GIMP_CONFIG_PARAM_FLAGS); + + obj_props[PROP_N_SAMPLES] = + g_param_spec_int ("n-samples", + "Number of Samples", + "The number of samples", + 256, 256, 256, + GIMP_CONFIG_PARAM_FLAGS); + + array_spec = g_param_spec_double ("sample", NULL, NULL, + 0.0, 1.0, 0.0, GIMP_PARAM_READWRITE); + obj_props[PROP_SAMPLES] = + gimp_param_spec_value_array ("samples", + NULL, NULL, + array_spec, + GIMP_CONFIG_PARAM_FLAGS); + + g_object_class_install_properties (object_class, N_PROPS, obj_props); +} + +static void +gimp_curve_init (GimpCurve *curve) +{ + curve->n_points = 0; + curve->points = NULL; + curve->n_samples = 0; + curve->samples = NULL; + curve->identity = FALSE; +} + +static void +gimp_curve_finalize (GObject *object) +{ + GimpCurve *curve = GIMP_CURVE (object); + + g_clear_pointer (&curve->points, g_free); + curve->n_points = 0; + + g_clear_pointer (&curve->samples, g_free); + curve->n_samples = 0; +} + +static void +gimp_curve_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpCurve *curve = GIMP_CURVE (object); + + switch (property_id) + { + case PROP_CURVE_TYPE: + gimp_curve_set_curve_type (curve, g_value_get_enum (value)); + break; + + case PROP_N_POINTS: + /* ignored */ + break; + + case PROP_POINTS: + { + GimpValueArray *array = g_value_get_boxed (value); + GimpCurvePoint *points; + gint length; + gint n_points; + gint i; + + if (! array) + { + gimp_curve_clear_points (curve); + + break; + } + + length = gimp_value_array_length (array) / 2; + + n_points = 0; + points = g_new0 (GimpCurvePoint, length); + + for (i = 0; i < length; i++) + { + GValue *x = gimp_value_array_index (array, i * 2); + GValue *y = gimp_value_array_index (array, i * 2 + 1); + + /* for backward compatibility */ + if (g_value_get_double (x) < 0.0) + continue; + + points[n_points].x = CLAMP (g_value_get_double (x), 0.0, 1.0); + points[n_points].y = CLAMP (g_value_get_double (y), 0.0, 1.0); + + if (n_points > 0) + { + points[n_points].x = MAX (points[n_points].x, + points[n_points - 1].x); + } + + if (n_points < curve->n_points) + points[n_points].type = curve->points[n_points].type; + else + points[n_points].type = GIMP_CURVE_POINT_SMOOTH; + + n_points++; + } + + g_free (curve->points); + + curve->n_points = n_points; + curve->points = points; + + g_object_notify_by_pspec (object, obj_props[PROP_N_POINTS]); + g_object_notify_by_pspec (object, obj_props[PROP_POINT_TYPES]); + } + break; + + case PROP_POINT_TYPES: + { + GimpValueArray *array = g_value_get_boxed (value); + GimpCurvePoint *points; + gint length; + gdouble x = 0.0; + gdouble y = 0.0; + gint i; + + if (! array) + { + gimp_curve_clear_points (curve); + + break; + } + + length = gimp_value_array_length (array); + + points = g_new0 (GimpCurvePoint, length); + + for (i = 0; i < length; i++) + { + GValue *type = gimp_value_array_index (array, i); + + points[i].type = g_value_get_uint (type); + + if (i < curve->n_points) + { + x = curve->points[i].x; + y = curve->points[i].y; + } + + points[i].x = x; + points[i].y = y; + } + + g_free (curve->points); + + curve->n_points = length; + curve->points = points; + + g_object_notify_by_pspec (object, obj_props[PROP_N_POINTS]); + g_object_notify_by_pspec (object, obj_props[PROP_POINTS]); + } + break; + + case PROP_N_SAMPLES: + gimp_curve_set_n_samples (curve, g_value_get_int (value)); + break; + + case PROP_SAMPLES: + { + GimpValueArray *array = g_value_get_boxed (value); + gint length; + gint i; + + if (! array) + break; + + length = gimp_value_array_length (array); + + for (i = 0; i < curve->n_samples && i < length; i++) + { + GValue *v = gimp_value_array_index (array, i); + + curve->samples[i] = CLAMP (g_value_get_double (v), 0.0, 1.0); + } + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_curve_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpCurve *curve = GIMP_CURVE (object); + + switch (property_id) + { + case PROP_CURVE_TYPE: + g_value_set_enum (value, curve->curve_type); + break; + + case PROP_N_POINTS: + g_value_set_int (value, curve->n_points); + break; + + case PROP_POINTS: + { + GimpValueArray *array = gimp_value_array_new (curve->n_points * 2); + GValue v = G_VALUE_INIT; + gint i; + + g_value_init (&v, G_TYPE_DOUBLE); + + for (i = 0; i < curve->n_points; i++) + { + g_value_set_double (&v, curve->points[i].x); + gimp_value_array_append (array, &v); + + g_value_set_double (&v, curve->points[i].y); + gimp_value_array_append (array, &v); + } + + g_value_unset (&v); + + g_value_take_boxed (value, array); + } + break; + + case PROP_POINT_TYPES: + { + GimpValueArray *array = gimp_value_array_new (curve->n_points); + GValue v = G_VALUE_INIT; + gint i; + + g_value_init (&v, GIMP_TYPE_CURVE_POINT_TYPE); + + for (i = 0; i < curve->n_points; i++) + { + g_value_set_enum (&v, curve->points[i].type); + gimp_value_array_append (array, &v); + } + + g_value_unset (&v); + + g_value_take_boxed (value, array); + } + break; + + case PROP_N_SAMPLES: + g_value_set_int (value, curve->n_samples); + break; + + case PROP_SAMPLES: + { + GimpValueArray *array = gimp_value_array_new (curve->n_samples); + GValue v = G_VALUE_INIT; + gint i; + + g_value_init (&v, G_TYPE_DOUBLE); + + for (i = 0; i < curve->n_samples; i++) + { + g_value_set_double (&v, curve->samples[i]); + gimp_value_array_append (array, &v); + } + + g_value_unset (&v); + + g_value_take_boxed (value, array); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +/* Public Functions */ + +GimpCurve * +gimp_curve_new (void) +{ + return g_object_new (GIMP_TYPE_CURVE, NULL); +} + +void +gimp_curve_set_curve_type (GimpCurve *curve, + GimpCurveType curve_type) +{ + g_return_if_fail (GIMP_IS_CURVE (curve)); + + if (curve->curve_type != curve_type) + { + g_object_freeze_notify (G_OBJECT (curve)); + + curve->curve_type = curve_type; + + if (curve_type == GIMP_CURVE_SMOOTH) + { + gint i; + + g_free (curve->points); + + /* pick some points from the curve and make them control + * points + */ + curve->n_points = 9; + curve->points = g_new0 (GimpCurvePoint, 9); + + for (i = 0; i < curve->n_points; i++) + { + gint sample = i * (curve->n_samples - 1) / (curve->n_points - 1); + + curve->points[i].x = (gdouble) sample / + (gdouble) (curve->n_samples - 1); + curve->points[i].y = curve->samples[sample]; + curve->points[i].type = GIMP_CURVE_POINT_SMOOTH; + } + + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_N_POINTS]); + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_POINTS]); + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_POINT_TYPES]); + } + else + { + gimp_curve_clear_points (curve); + } + + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_CURVE_TYPE]); + + g_object_thaw_notify (G_OBJECT (curve)); + } +} + +gint +gimp_curve_get_n_points (GimpCurve *curve) +{ + g_return_val_if_fail (GIMP_IS_CURVE (curve), 0); + + return curve->n_points; +} + +void +gimp_curve_set_n_samples (GimpCurve *curve, + gint n_samples) +{ + g_return_if_fail (GIMP_IS_CURVE (curve)); + g_return_if_fail (n_samples >= 256); + g_return_if_fail (n_samples <= 4096); + + if (n_samples != curve->n_samples) + { + gint i; + + g_object_freeze_notify (G_OBJECT (curve)); + + curve->n_samples = n_samples; + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_N_SAMPLES]); + + curve->samples = g_renew (gdouble, curve->samples, curve->n_samples); + + for (i = 0; i < curve->n_samples; i++) + curve->samples[i] = (gdouble) i / (gdouble) (curve->n_samples - 1); + + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_SAMPLES]); + + if (curve->curve_type == GIMP_CURVE_FREE) + curve->identity = TRUE; + + g_object_thaw_notify (G_OBJECT (curve)); + } +} + +gint +gimp_curve_get_n_samples (GimpCurve *curve) +{ + g_return_val_if_fail (GIMP_IS_CURVE (curve), 0); + + return curve->n_samples; +} + +gint +gimp_curve_add_point (GimpCurve *curve, + gdouble x, + gdouble y) +{ + GimpCurvePoint *points; + gint point; + + g_return_val_if_fail (GIMP_IS_CURVE (curve), -1); + + if (curve->curve_type == GIMP_CURVE_FREE) + return -1; + + x = CLAMP (x, 0.0, 1.0); + y = CLAMP (y, 0.0, 1.0); + + for (point = 0; point < curve->n_points; point++) + { + if (curve->points[point].x > x) + break; + } + + points = g_new0 (GimpCurvePoint, curve->n_points + 1); + + memcpy (points, curve->points, + point * sizeof (GimpCurvePoint)); + memcpy (points + point + 1, curve->points + point, + (curve->n_points - point) * sizeof (GimpCurvePoint)); + + points[point].x = x; + points[point].y = y; + points[point].type = GIMP_CURVE_POINT_SMOOTH; + + g_free (curve->points); + + curve->n_points++; + curve->points = points; + + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_N_POINTS]); + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_POINTS]); + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_POINT_TYPES]); + + return point; +} + +void +gimp_curve_get_point (GimpCurve *curve, + gint point, + gdouble *x, + gdouble *y) +{ + g_return_if_fail (GIMP_IS_CURVE (curve)); + g_return_if_fail (point >= 0 && point < curve->n_points); + + if (x) *x = curve->points[point].x; + if (y) *y = curve->points[point].y; +} + +void +gimp_curve_set_point_type (GimpCurve *curve, + gint point, + GimpCurvePointType type) +{ + g_return_if_fail (GIMP_IS_CURVE (curve)); + g_return_if_fail (point >= 0 && point < curve->n_points); + + curve->points[point].type = type; + + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_POINT_TYPES]); +} + +GimpCurvePointType +gimp_curve_get_point_type (GimpCurve *curve, + gint point) +{ + g_return_val_if_fail (GIMP_IS_CURVE (curve), GIMP_CURVE_POINT_SMOOTH); + g_return_val_if_fail (point >= 0 && point < curve->n_points, GIMP_CURVE_POINT_SMOOTH); + + return curve->points[point].type; +} + +void +gimp_curve_delete_point (GimpCurve *curve, + gint point) +{ + GimpCurvePoint *points; + + g_return_if_fail (GIMP_IS_CURVE (curve)); + g_return_if_fail (point >= 0 && point < curve->n_points); + + points = g_new0 (GimpCurvePoint, curve->n_points - 1); + + memcpy (points, curve->points, + point * sizeof (GimpCurvePoint)); + memcpy (points + point, curve->points + point + 1, + (curve->n_points - point - 1) * sizeof (GimpCurvePoint)); + + g_free (curve->points); + + curve->n_points--; + curve->points = points; + + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_N_POINTS]); + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_POINTS]); + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_POINT_TYPES]); +} + +void +gimp_curve_set_point (GimpCurve *curve, + gint point, + gdouble x, + gdouble y) +{ + g_return_if_fail (GIMP_IS_CURVE (curve)); + g_return_if_fail (point >= 0 && point < curve->n_points); + + curve->points[point].x = CLAMP (x, 0.0, 1.0); + curve->points[point].y = CLAMP (y, 0.0, 1.0); + + if (point > 0) + curve->points[point].x = MAX (x, curve->points[point - 1].x); + + if (point < curve->n_points - 1) + curve->points[point].x = MIN (x, curve->points[point + 1].x); + + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_POINTS]); +} + + +void +gimp_curve_clear_points (GimpCurve *curve) +{ + g_return_if_fail (GIMP_IS_CURVE (curve)); + + if (curve->points) + { + g_clear_pointer (&curve->points, g_free); + curve->n_points = 0; + + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_N_POINTS]); + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_POINTS]); + g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_POINT_TYPES]); + } +} + + +/** + * gimp_curve_is_identity: + * @curve: a #GimpCurve object + * + * If this function returns %TRUE, then the curve maps each value to + * itself. If it returns %FALSE, then this assumption can not be made. + * + * Returns: %TRUE if the curve is an identity mapping, %FALSE otherwise. + **/ +gboolean +gimp_curve_is_identity (GimpCurve *curve) +{ + g_return_val_if_fail (GIMP_IS_CURVE (curve), FALSE); + + return curve->identity; +} diff --git a/libgimp/gimpcurve.h b/libgimp/gimpcurve.h new file mode 100644 index 0000000000..8083844efe --- /dev/null +++ b/libgimp/gimpcurve.h @@ -0,0 +1,76 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * gimpcurve.h + * Copyright (C) 2026 Alx Sa + * + * This library is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#if !defined (__GIMP_H_INSIDE__) && !defined (GIMP_COMPILATION) +#error "Only can be included directly." +#endif + +#ifndef __GIMP_CURVE_H__ +#define __GIMP_CURVE_H__ + +G_BEGIN_DECLS + +/* For information look into the C source or the html documentation */ + + +#define GIMP_TYPE_CURVE (gimp_curve_get_type ()) +G_DECLARE_FINAL_TYPE (GimpCurve, gimp_curve, GIMP, CURVE, GObject) + + +GimpCurve * gimp_curve_new (void); + +void gimp_curve_set_curve_type (GimpCurve *curve, + GimpCurveType curve_type); + +gint gimp_curve_get_n_points (GimpCurve *curve); + +void gimp_curve_set_n_samples (GimpCurve *curve, + gint n_samples); +gint gimp_curve_get_n_samples (GimpCurve *curve); + +gint gimp_curve_add_point (GimpCurve *curve, + gdouble x, + gdouble y); +void gimp_curve_get_point (GimpCurve *curve, + gint point, + gdouble *x, + gdouble *y); + +GimpCurvePointType gimp_curve_get_point_type (GimpCurve *curve, + gint point); + +void gimp_curve_set_point_type (GimpCurve *curve, + gint point, + GimpCurvePointType type); + +void gimp_curve_delete_point (GimpCurve *curve, + gint point); +void gimp_curve_set_point (GimpCurve *curve, + gint point, + gdouble x, + gdouble y); +void gimp_curve_clear_points (GimpCurve *curve); + +gboolean gimp_curve_is_identity (GimpCurve *curve); + +G_END_DECLS + +#endif /* __GIMP_CURVE_H__ */ diff --git a/libgimp/gimpenums.c.tail b/libgimp/gimpenums.c.tail index 39cf0a3185..d920bf9ea2 100644 --- a/libgimp/gimpenums.c.tail +++ b/libgimp/gimpenums.c.tail @@ -18,6 +18,8 @@ static const GimpGetTypeFunc get_type_funcs[] = gimp_convert_dither_type_get_type, gimp_convert_palette_type_get_type, gimp_convolve_type_get_type, + gimp_curve_point_type_get_type, + gimp_curve_type_get_type, gimp_desaturate_mode_get_type, gimp_dodge_burn_type_get_type, gimp_export_capabilities_get_type, @@ -87,6 +89,8 @@ static const gchar * const type_names[] = "GimpConvertDitherType", "GimpConvertPaletteType", "GimpConvolveType", + "GimpCurvePointType", + "GimpCurveType", "GimpDesaturateMode", "GimpDodgeBurnType", "GimpExportCapabilities", diff --git a/libgimp/gimpenums.h b/libgimp/gimpenums.h index 1ffb415476..7c2d7637a7 100644 --- a/libgimp/gimpenums.h +++ b/libgimp/gimpenums.h @@ -64,6 +64,42 @@ typedef enum } GimpConvertDitherType; +#define GIMP_TYPE_CURVE_POINT_TYPE (gimp_curve_point_type_get_type ()) + +GType gimp_curve_point_type_get_type (void) G_GNUC_CONST; + +/** + * GimpCurvePointType: + * @GIMP_CURVE_POINT_SMOOTH: GIMP_CURVE_POINT_SMOOTH + * @GIMP_CURVE_POINT_CORNER: GIMP_CURVE_POINT_CORNER + * + * Extracted from app/core/core-enums.h + **/ +typedef enum +{ + GIMP_CURVE_POINT_SMOOTH, + GIMP_CURVE_POINT_CORNER +} GimpCurvePointType; + + +#define GIMP_TYPE_CURVE_TYPE (gimp_curve_type_get_type ()) + +GType gimp_curve_type_get_type (void) G_GNUC_CONST; + +/** + * GimpCurveType: + * @GIMP_CURVE_SMOOTH: GIMP_CURVE_SMOOTH + * @GIMP_CURVE_FREE: GIMP_CURVE_FREE + * + * Extracted from app/core/core-enums.h + **/ +typedef enum +{ + GIMP_CURVE_SMOOTH, + GIMP_CURVE_FREE +} GimpCurveType; + + #define GIMP_TYPE_HISTOGRAM_CHANNEL (gimp_histogram_channel_get_type ()) GType gimp_histogram_channel_get_type (void) G_GNUC_CONST; diff --git a/libgimp/gimpgpparams-body.c b/libgimp/gimpgpparams-body.c index 446652a39d..ae9c02bcb2 100644 --- a/libgimp/gimpgpparams-body.c +++ b/libgimp/gimpgpparams-body.c @@ -374,6 +374,12 @@ _gimp_gp_param_def_to_param_spec (const GPParamDef *param_def) return pspec; } break; + + case GP_PARAM_DEF_TYPE_CURVE: + if (! strcmp (param_def->type_name, "GimpParamCurve")) + return gimp_param_spec_curve (name, nick, blurb, + param_def->meta.m_id.none_ok, + flags); } g_warning ("%s: GParamSpec type unsupported '%s'", G_STRFUNC, @@ -700,6 +706,13 @@ _gimp_param_spec_to_gp_param_def (GParamSpec *pspec, { param_def->param_def_type = GP_PARAM_DEF_TYPE_EXPORT_OPTIONS; } + else if (GIMP_IS_PARAM_SPEC_CURVE (pspec) || + (pspec_type == G_TYPE_PARAM_OBJECT && value_type == GIMP_TYPE_CURVE)) + { + param_def->param_def_type = GP_PARAM_DEF_TYPE_CURVE; + param_def->type_name = "GimpParamCurve"; + param_def->meta.m_curve.none_ok = TRUE; + } else if (pspec_type == G_TYPE_PARAM_OBJECT && value_type != G_TYPE_FILE && value_type != GEGL_TYPE_COLOR) @@ -1179,7 +1192,8 @@ gimp_gp_param_to_value (gpointer gimp, { g_value_set_object (value, get_display_by_id (gimp, param->data.d_int)); } - else if (GIMP_VALUE_HOLDS_RESOURCE (value)) + else if (GIMP_VALUE_HOLDS_RESOURCE (value) && + ! GIMP_VALUE_HOLDS_CURVE (value)) { g_value_set_object (value, get_resource_by_id (param->data.d_int)); } @@ -1187,6 +1201,34 @@ gimp_gp_param_to_value (gpointer gimp, { g_value_set_object (value, get_unit_by_id (gimp, param->data.d_int)); } + else if (GIMP_VALUE_HOLDS_CURVE (value)) + { + GimpCurve *curve; + + curve = g_object_new (GIMP_TYPE_CURVE, NULL); + + g_object_set (curve, + "curve-type", param->data.d_curve.curve_type, + NULL); + + for (gint j = 0; j < param->data.d_curve.n_points; j++) + { + gimp_curve_add_point (curve, param->data.d_curve.points[j * 2], + param->data.d_curve.points[(j * 2) + 1]); + + gimp_curve_set_point_type (curve, j, + param->data.d_curve.point_types[j]); + } + + if (param->data.d_curve.n_samples > 0) + g_object_set (curve, + "n-samples", param->data.d_curve.n_samples, + NULL); + + g_value_set_object (value, curve); + + g_object_unref (curve); + } else if (g_type_is_a (G_VALUE_TYPE (value), GIMP_TYPE_EXPORT_OPTIONS)) { GimpExportOptions *options; @@ -1721,7 +1763,8 @@ gimp_value_to_gp_param (const GValue *value, param->data.d_int = display ? gimp_display_get_id (display) : -1; } - else if (GIMP_VALUE_HOLDS_RESOURCE (value)) + else if (GIMP_VALUE_HOLDS_RESOURCE (value) && + ! GIMP_VALUE_HOLDS_CURVE (value)) { GObject *resource = g_value_get_object (value); @@ -1741,6 +1784,37 @@ gimp_value_to_gp_param (const GValue *value, { param->param_type = GP_PARAM_TYPE_EXPORT_OPTIONS; } + else if (g_type_is_a (G_VALUE_TYPE (value), GIMP_TYPE_CURVE)) + { + GimpCurve *curve = g_value_get_object (value); + + param->param_type = GP_PARAM_TYPE_CURVE; + + g_object_get (curve, + "curve-type", ¶m->data.d_curve.curve_type, + "n-points", ¶m->data.d_curve.n_points, + "n-samples", ¶m->data.d_curve.n_samples, + NULL); + + param->data.d_curve.points = g_new0 (gdouble, + 2 * param->data.d_curve.n_points); + + param->data.d_curve.point_types = g_new0 (GimpCurvePointType, + param->data.d_curve.n_points); + + for (gint j = 0; j < param->data.d_curve.n_points; j++) + { + gdouble x; + gdouble y; + + gimp_curve_get_point (curve, j, &x, &y); + param->data.d_curve.points[j * 2] = x; + param->data.d_curve.points[(j * 2) + 1] = y; + + param->data.d_curve.point_types[j] = + gimp_curve_get_point_type (curve, j); + } + } else if (G_VALUE_HOLDS_PARAM (value)) { param->param_type = GP_PARAM_TYPE_PARAM_DEF; @@ -1868,6 +1942,14 @@ _gimp_gp_params_free (GPParam *params, params[i].data.d_value_array.n_values, full_copy); break; + + case GP_PARAM_TYPE_CURVE: + if (full_copy) + { + g_free (params[i].data.d_curve.points); + g_free (params[i].data.d_curve.point_types); + } + break; } } diff --git a/libgimp/gimpparamspecs-body.c b/libgimp/gimpparamspecs-body.c index 81cd9d76b8..95c3cb430d 100644 --- a/libgimp/gimpparamspecs-body.c +++ b/libgimp/gimpparamspecs-body.c @@ -2045,3 +2045,93 @@ gimp_param_spec_font (const gchar *name, none_ok, GIMP_RESOURCE (default_value), default_to_context, flags); } + +/* + * GIMP_TYPE_PARAM_CURVE + */ + +#define GIMP_PARAM_SPEC_CURVE(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_CURVE, GimpParamSpecCurve)) + +typedef struct _GimpParamSpecCurve GimpParamSpecCurve; + +struct _GimpParamSpecCurve +{ + GParamSpecObject parent_instance; + + gboolean none_ok; +}; + +static void gimp_param_curve_class_init (GParamSpecClass *klass); +static void gimp_param_curve_init (GParamSpec *pspec); + +GType +gimp_param_curve_get_type (void) +{ + static GType type = 0; + + if (! type) + { + const GTypeInfo info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_curve_class_init, + NULL, NULL, + sizeof (GimpParamSpecCurve), + 0, + (GInstanceInitFunc) gimp_param_curve_init + }; + + type = g_type_register_static (G_TYPE_PARAM_OBJECT, + "GimpParamCurve", &info, 0); + } + + return type; +} + +static void +gimp_param_curve_class_init (GParamSpecClass *klass) +{ + klass->value_type = GIMP_TYPE_CURVE; +} + +static void +gimp_param_curve_init (GParamSpec *pspec) +{ +} + +/** + * gimp_param_spec_curve: + * @name: Canonical name of the property specified. + * @nick: Nick name of the property specified. + * @blurb: Description of the property specified. + * @none_ok: Whether no is a valid value. + * @flags: Flags for the property specified. + * + * Creates a new #GimpParamSpecCurve specifying a + * [type@Curve] property. + * + * See g_param_spec_internal() for details on property names. + * + * Returns: (transfer floating): The newly created #GimpParamSpecCurve. + * + * Since: 3.2 + **/ +GParamSpec * +gimp_param_spec_curve (const gchar *name, + const gchar *nick, + const gchar *blurb, + gboolean none_ok, + GParamFlags flags) +{ + GimpParamSpecCurve *cspec; + + cspec = g_param_spec_internal (GIMP_TYPE_PARAM_CURVE, + name, nick, blurb, flags); + + g_return_val_if_fail (cspec, NULL); + + cspec->none_ok = none_ok ? TRUE : FALSE; + + return G_PARAM_SPEC (cspec); +} diff --git a/libgimp/gimpparamspecs.h b/libgimp/gimpparamspecs.h index e90ba9ce10..80dd436843 100644 --- a/libgimp/gimpparamspecs.h +++ b/libgimp/gimpparamspecs.h @@ -448,6 +448,25 @@ GParamSpec * gimp_param_spec_font (const gchar *name, GParamFlags flags); +/* + * GIMP_TYPE_PARAM_CURVE + */ + +#define GIMP_VALUE_HOLDS_CURVE(value) (G_TYPE_CHECK_VALUE_TYPE ((value), \ + GIMP_TYPE_CURVE)) + +#define GIMP_TYPE_PARAM_CURVE (gimp_param_curve_get_type ()) +#define GIMP_IS_PARAM_SPEC_CURVE(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), GIMP_TYPE_PARAM_CURVE)) + +GType gimp_param_curve_get_type (void) G_GNUC_CONST; + +GParamSpec * gimp_param_spec_curve (const gchar *name, + const gchar *nick, + const gchar *blurb, + gboolean none_ok, + GParamFlags flags); + + G_END_DECLS #endif /* __LIBGIMP_GIMP_PARAM_SPECS_H__ */ diff --git a/libgimp/gimpprocedure-params.h b/libgimp/gimpprocedure-params.h index 1fceff073a..94fe96885f 100644 --- a/libgimp/gimpprocedure-params.h +++ b/libgimp/gimpprocedure-params.h @@ -469,6 +469,20 @@ G_BEGIN_DECLS g_value_set_object (gimp_value_array_index (args, n), value) +/* Curve */ + +#define GIMP_VALUES_GET_CURVE(args, n) \ + g_value_get_object (gimp_value_array_index (args, n)) + +#define GIMP_VALUES_DUP_CURVE(args, n) \ + g_value_dup_object (gimp_value_array_index (args, n)) + +#define GIMP_VALUES_SET_CURVE(args, n, value) \ + g_value_set_object (gimp_value_array_index (args, n), value) + +#define GIMP_VALUES_TAKE_CURVE(args, n, value) \ + g_value_take_object (gimp_value_array_index (args, n), value) + /* Unit */ #define GIMP_VALUES_GET_UNIT(args, n) \ diff --git a/libgimp/gimptypes.h b/libgimp/gimptypes.h index 57121b58a6..82d8843ad8 100644 --- a/libgimp/gimptypes.h +++ b/libgimp/gimptypes.h @@ -59,6 +59,7 @@ typedef struct _GimpDisplay GimpDisplay; typedef struct _GimpResource GimpResource; typedef struct _GimpBrush GimpBrush; +typedef struct _GimpCurve GimpCurve; typedef struct _GimpFont GimpFont; typedef struct _GimpGradient GimpGradient; typedef struct _GimpPattern GimpPattern; diff --git a/libgimp/meson.build b/libgimp/meson.build index a7d84415b2..097af6a828 100644 --- a/libgimp/meson.build +++ b/libgimp/meson.build @@ -186,6 +186,7 @@ libgimp_sources_introspectable = [ 'gimpbatchprocedure.c', 'gimpbrush.c', 'gimpchannel.c', + 'gimpcurve.c', 'gimpdisplay.c', 'gimpdrawable.c', 'gimpdrawablefilter.c', @@ -252,6 +253,7 @@ libgimp_headers_introspectable = [ 'gimpbatchprocedure.h', 'gimpbrush.h', 'gimpchannel.h', + 'gimpcurve.h', 'gimpdisplay.h', 'gimpdrawable.h', 'gimpdrawablefilter.h', diff --git a/libgimpbase/gimpprotocol.c b/libgimpbase/gimpprotocol.c index 58821a6dad..f9c758adee 100644 --- a/libgimpbase/gimpprotocol.c +++ b/libgimpbase/gimpprotocol.c @@ -1317,6 +1317,13 @@ _gp_param_def_read (GIOChannel *channel, user_data)) return FALSE; break; + + case GP_PARAM_DEF_TYPE_CURVE: + if (! _gimp_wire_read_int32 (channel, + (guint32 *) ¶m_def->meta.m_curve.none_ok, 1, + user_data)) + return FALSE; + break; } return TRUE; @@ -1377,6 +1384,9 @@ _gp_param_def_destroy (GPParamDef *param_def) case GP_PARAM_DEF_TYPE_FILE: g_free (param_def->meta.m_file.default_uri); break; + + case GP_PARAM_DEF_TYPE_CURVE: + break; } } @@ -1684,6 +1694,13 @@ _gp_param_def_write (GIOChannel *channel, user_data)) return FALSE; break; + + case GP_PARAM_DEF_TYPE_CURVE: + if (! _gimp_wire_write_int32 (channel, + (guint32 *) ¶m_def->meta.m_curve.none_ok, 1, + user_data)) + return FALSE; + break; } return TRUE; @@ -2190,6 +2207,51 @@ _gp_params_read (GIOChannel *channel, (*params)[i].data.d_value_array.n_values = (guint32) n_values; break; } + + case GP_PARAM_TYPE_CURVE: + { + if (! _gimp_wire_read_int32 (channel, + &(*params)[i].data.d_curve.curve_type, 1, + user_data)) + goto cleanup; + + if (! _gimp_wire_read_int32 (channel, + &(*params)[i].data.d_curve.n_points, 1, + user_data)) + goto cleanup; + + if (! _gimp_wire_read_int32 (channel, + &(*params)[i].data.d_curve.n_samples, 1, + user_data)) + goto cleanup; + + (*params)[i].data.d_curve.points = g_new0 (gdouble, + 2 * (*params)[i].data.d_curve.n_points); + + if (! _gimp_wire_read_double (channel, + (*params)[i].data.d_curve.points, + 2 * (*params)[i].data.d_curve.n_points, + user_data)) + { + g_free ((*params)[i].data.d_curve.points); + (*params)[i].data.d_curve.points = NULL; + goto cleanup; + } + + (*params)[i].data.d_curve.point_types = g_new0 (guint32, + (*params)[i].data.d_curve.n_points); + + if (! _gimp_wire_read_int32 (channel, + (*params)[i].data.d_curve.point_types, + (*params)[i].data.d_curve.n_points, + user_data)) + { + g_free ((*params)[i].data.d_curve.point_types); + (*params)[i].data.d_curve.point_types = NULL; + goto cleanup; + } + } + break; } } @@ -2421,6 +2483,34 @@ _gp_params_write (GIOChannel *channel, user_data); break; + case GP_PARAM_TYPE_CURVE: + if (! _gimp_wire_write_int32 (channel, + (const guint32 *) ¶ms[i].data.d_curve.curve_type, 1, + user_data)) + return; + + if (! _gimp_wire_write_int32 (channel, + (const guint32 *) ¶ms[i].data.d_curve.n_points, 1, + user_data)) + return; + + if (! _gimp_wire_write_int32 (channel, + (const guint32 *) ¶ms[i].data.d_curve.n_samples, 1, + user_data)) + return; + + if (! _gimp_wire_write_double (channel, + params[i].data.d_curve.points, + 2 * params[i].data.d_curve.n_points, + user_data)) + return; + + if (! _gimp_wire_write_int32 (channel, + (const guint32 *) params[i].data.d_curve.point_types, + params[i].data.d_curve.n_points, + user_data)) + return; + break; } } } @@ -2499,6 +2589,13 @@ _gp_params_destroy (GPParam *params, _gp_params_destroy (params[i].data.d_value_array.values, params[i].data.d_value_array.n_values); break; + + case GP_PARAM_TYPE_CURVE: + if (params[i].data.d_curve.points) + g_free (params[i].data.d_curve.points); + if (params[i].data.d_curve.point_types) + g_free (params[i].data.d_curve.point_types); + break; } } diff --git a/libgimpbase/gimpprotocol.h b/libgimpbase/gimpprotocol.h index c9efb35294..bdf7551821 100644 --- a/libgimpbase/gimpprotocol.h +++ b/libgimpbase/gimpprotocol.h @@ -26,7 +26,7 @@ G_BEGIN_DECLS /* Increment every time the protocol changes */ -#define GIMP_PROTOCOL_VERSION 0x0115 +#define GIMP_PROTOCOL_VERSION 0x0116 enum @@ -61,7 +61,8 @@ typedef enum GP_PARAM_DEF_TYPE_ID_ARRAY, GP_PARAM_DEF_TYPE_EXPORT_OPTIONS, GP_PARAM_DEF_TYPE_RESOURCE, - GP_PARAM_DEF_TYPE_FILE + GP_PARAM_DEF_TYPE_FILE, + GP_PARAM_DEF_TYPE_CURVE } GPParamDefType; typedef enum @@ -81,6 +82,7 @@ typedef enum GP_PARAM_TYPE_EXPORT_OPTIONS, GP_PARAM_TYPE_PARAM_DEF, GP_PARAM_TYPE_VALUE_ARRAY, + GP_PARAM_TYPE_CURVE } GPParamType; @@ -102,6 +104,7 @@ typedef struct _GPParamDefID GPParamDefID; typedef struct _GPParamDefIDArray GPParamDefIDArray; typedef struct _GPParamDefResource GPParamDefResource; typedef struct _GPParamDefFile GPParamDefFile; +typedef struct _GPParamDefCurve GPParamDefCurve; typedef struct _GPParam GPParam; typedef struct _GPParamArray GPParamArray; typedef struct _GPParamIDArray GPParamIDArray; @@ -110,6 +113,7 @@ typedef struct _GPParamColor GPParamColor; typedef struct _GPParamColorArray GPParamColorArray; typedef struct _GPParamExportOptions GPParamExportOptions; typedef struct _GPParamValueArray GPParamValueArray; +typedef struct _GPParamCurve GPParamCurve; typedef struct _GPProcRun GPProcRun; typedef struct _GPProcReturn GPProcReturn; typedef struct _GPProcInstall GPProcInstall; @@ -250,6 +254,11 @@ struct _GPParamDefFile gchar *default_uri; }; +struct _GPParamDefCurve +{ + gint32 none_ok; +}; + struct _GPParamDef { GPParamDefType param_def_type; @@ -274,6 +283,7 @@ struct _GPParamDef GPParamDefChoice m_choice; GPParamDefResource m_resource; GPParamDefFile m_file; + GPParamDefCurve m_curve; } meta; }; @@ -326,6 +336,17 @@ struct _GPParamValueArray GPParam *values; }; +struct _GPParamCurve +{ + guint32 curve_type; + + guint32 n_points; + gdouble *points; + guint32 *point_types; + + guint32 n_samples; +}; + struct _GPParam { GPParamType param_type; @@ -347,6 +368,7 @@ struct _GPParam GPParamExportOptions d_export_options; GPParamDef d_param_def; GPParamValueArray d_value_array; + GPParamCurve d_curve; } data; }; diff --git a/libgimpconfig/gimpconfig-params.c b/libgimpconfig/gimpconfig-params.c index 68ad9b744f..c12b276e18 100644 --- a/libgimpconfig/gimpconfig-params.c +++ b/libgimpconfig/gimpconfig-params.c @@ -322,7 +322,8 @@ gimp_config_param_spec_duplicate (GParamSpec *pspec) g_strcmp0 (type_name, "GimpLayerMask") == 0 || g_strcmp0 (type_name, "GimpSelection") == 0 || g_strcmp0 (type_name, "GimpPath") == 0 || - g_strcmp0 (type_name, "GimpDrawableFilter") == 0) + g_strcmp0 (type_name, "GimpDrawableFilter") == 0 || + g_strcmp0 (type_name, "GimpCurve") == 0) { copy = g_param_spec_object (name, nick, blurb, value_type, diff --git a/pdb/app.pl b/pdb/app.pl index fcf0031a56..d87452f918 100644 --- a/pdb/app.pl +++ b/pdb/app.pl @@ -341,6 +341,17 @@ gimp_param_spec_pattern ("$name", $default, $default_to_context, $flags) +CODE + } + elsif ($pdbtype eq 'curve') { + $none_ok = exists $arg->{none_ok} ? 'TRUE' : 'FALSE'; + $default = exists $arg->{default} ? $arg->{default} : NULL; + $pspec = < '2', GIMP_CONVERT_DITHER_FIXED => '3' } }, + GimpCurvePointType => + { contig => 1, + header => 'core/core-enums.h', + symbols => [ qw(GIMP_CURVE_POINT_SMOOTH GIMP_CURVE_POINT_CORNER) ], + mapping => { GIMP_CURVE_POINT_SMOOTH => '0', + GIMP_CURVE_POINT_CORNER => '1' } + }, + GimpCurveType => + { contig => 1, + header => 'core/core-enums.h', + symbols => [ qw(GIMP_CURVE_SMOOTH GIMP_CURVE_FREE) ], + mapping => { GIMP_CURVE_SMOOTH => '0', + GIMP_CURVE_FREE => '1' } + }, GimpHistogramChannel => { contig => 1, header => 'core/core-enums.h', diff --git a/pdb/pdb.pl b/pdb/pdb.pl index 6e136473bb..7b4fcafe58 100644 --- a/pdb/pdb.pl +++ b/pdb/pdb.pl @@ -674,6 +674,18 @@ package Gimp::CodeGen::pdb; take_value_func => 'g_value_set_object ($value, $var)', headers => [ qw("text/gimpfont.h") ] }, + curve => { name => 'CURVE', + gtype => 'GIMP_TYPE_CURVE', + type => 'GimpCurve *', + const_type => 'GimpCurve *', + init_value => 'NULL', + out_annotate => '(transfer full)', + get_value_func => '$var = g_value_get_object ($value)', + dup_value_func => '$var = GIMP_VALUES_DUP_CURVE ($value)', + set_value_func => 'g_value_set_object ($value, $var)', + take_value_func => 'g_value_take_object ($value, $var)', + headers => [ qw("core/gimpcurve.h") ] }, + valuearray => { name => 'VALUEARRAY', gtype => 'GIMP_TYPE_VALUE_ARRAY', type => 'GimpValueArray *',