app, libgimp*: add GimpCurve sample API in libgimp and PDB.

This commit is contained in:
Jehan 2026-03-01 21:30:44 +01:00
parent d6f5205a89
commit b87bab9e8c
11 changed files with 285 additions and 121 deletions

View file

@ -1058,10 +1058,21 @@ gimp_curve_clear_points (GimpCurve *curve)
}
}
gdouble
gimp_curve_get_sample (GimpCurve *curve,
gdouble x)
{
g_return_val_if_fail (GIMP_IS_CURVE (curve), 0);
g_return_val_if_fail (curve->curve_type == GIMP_CURVE_FREE, 0);
g_return_val_if_fail (x >= 0 && x <= 1.0, 0);
return curve->samples[ROUND (x * (gdouble) (curve->n_samples - 1))];
}
void
gimp_curve_set_curve (GimpCurve *curve,
gdouble x,
gdouble y)
gimp_curve_set_sample (GimpCurve *curve,
gdouble x,
gdouble y)
{
g_return_if_fail (GIMP_IS_CURVE (curve));
g_return_if_fail (x >= 0 && x <= 1.0);

View file

@ -108,7 +108,9 @@ GimpCurvePointType gimp_curve_get_point_type (GimpCurve *curve,
gint point);
void gimp_curve_clear_points (GimpCurve *curve);
void gimp_curve_set_curve (GimpCurve *curve,
gdouble gimp_curve_get_sample (GimpCurve *curve,
gdouble x);
void gimp_curve_set_sample (GimpCurve *curve,
gdouble x,
gdouble y);

View file

@ -891,7 +891,7 @@ gimp_curve_reset
gimp_curve_set_point
gimp_curve_get_uchar
gimp_curve_move_point
gimp_curve_set_curve
gimp_curve_set_sample
gimp_curve_set_curve_type
gimp_curve_get_closest_point
gimp_gegl_init

View file

@ -466,9 +466,9 @@ gimp_curves_config_new_explicit (gint32 channel,
gimp_curve_set_n_samples (curve, n_samples);
for (i = 0; i < n_samples; i++)
gimp_curve_set_curve (curve,
(gdouble) i / (gdouble) (n_samples - 1),
(gdouble) samples[i]);
gimp_curve_set_sample (curve,
(gdouble) i / (gdouble) (n_samples - 1),
(gdouble) samples[i]);
gimp_data_thaw (GIMP_DATA (curve));

View file

@ -902,7 +902,7 @@ gimp_curve_view_button_press (GtkWidget *widget,
view->last_x = x;
view->last_y = y;
gimp_curve_set_curve (curve, x, 1.0 - y);
gimp_curve_set_sample (curve, x, 1.0 - y);
break;
}
@ -1070,14 +1070,14 @@ gimp_curve_view_motion_notify (GtkWidget *widget,
xpos = CLAMP (xpos, 0.0, 1.0);
ypos = CLAMP (ypos, 0.0, 1.0);
gimp_curve_set_curve (curve, xpos, 1.0 - ypos);
gimp_curve_set_sample (curve, xpos, 1.0 - ypos);
}
gimp_data_thaw (GIMP_DATA (curve));
}
else
{
gimp_curve_set_curve (curve, x, 1.0 - y);
gimp_curve_set_sample (curve, x, 1.0 - y);
}
view->last_x = x;

View file

@ -185,10 +185,12 @@ EXPORTS
gimp_curve_add_point
gimp_curve_clear_points
gimp_curve_delete_point
gimp_curve_get_curve_type
gimp_curve_get_n_points
gimp_curve_get_n_samples
gimp_curve_get_point
gimp_curve_get_point_type
gimp_curve_get_sample
gimp_curve_get_type
gimp_curve_is_identity
gimp_curve_new
@ -197,6 +199,7 @@ EXPORTS
gimp_curve_set_n_samples
gimp_curve_set_point
gimp_curve_set_point_type
gimp_curve_set_sample
gimp_curve_type_get_type
gimp_debug_timer_end
gimp_debug_timer_start

View file

@ -76,6 +76,8 @@ static void gimp_curve_get_property (GObject *object,
GValue *value,
GParamSpec *pspec);
static void gimp_curve_build_samples (GimpCurve *curve);
G_DEFINE_TYPE (GimpCurve, gimp_curve, G_TYPE_OBJECT);
@ -160,7 +162,7 @@ gimp_curve_init (GimpCurve *curve)
{
curve->n_points = 0;
curve->points = NULL;
curve->n_samples = 0;
curve->n_samples = 256;
curve->samples = NULL;
curve->identity = FALSE;
}
@ -192,7 +194,10 @@ gimp_curve_set_property (GObject *object,
break;
case PROP_N_SAMPLES:
gimp_curve_set_n_samples (curve, g_value_get_int (value));
if (curve->curve_type == GIMP_CURVE_FREE)
gimp_curve_set_n_samples (curve, g_value_get_int (value));
else
curve->n_samples = g_value_get_int (value);
break;
default:
@ -230,6 +235,9 @@ gimp_curve_get_property (GObject *object,
/**
* gimp_curve_new:
*
* Creates a new #GimpCurve object, of type
* [enum@Gimp.CurveType.SMOOTH], with 0 points initially.
*
* Returns: (transfer full): a new curve.
*
* Since: 3.2
@ -240,6 +248,22 @@ gimp_curve_new (void)
return g_object_new (GIMP_TYPE_CURVE, NULL);
}
/**
* gimp_curve_get_curve_type:
* @curve: the #GimpCurve.
*
* Returns: the @curve type.
*
* Since: 3.2
*/
GimpCurveType
gimp_curve_get_curve_type (GimpCurve *curve)
{
g_return_val_if_fail (GIMP_IS_CURVE (curve), GIMP_CURVE_SMOOTH);
return curve->curve_type;
}
/**
* gimp_curve_set_curve_type:
* @curve: the #GimpCurve.
@ -267,12 +291,12 @@ gimp_curve_set_curve_type (GimpCurve *curve,
{
g_object_freeze_notify (G_OBJECT (curve));
curve->curve_type = curve_type;
if (curve_type == GIMP_CURVE_SMOOTH)
{
gint i;
curve->curve_type = curve_type;
g_free (curve->points);
/* pick some points from the curve and make them control
@ -296,6 +320,8 @@ gimp_curve_set_curve_type (GimpCurve *curve,
else
{
gimp_curve_clear_points (curve);
curve->curve_type = curve_type;
gimp_curve_build_samples (curve);
}
g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_CURVE_TYPE]);
@ -326,74 +352,11 @@ gint
gimp_curve_get_n_points (GimpCurve *curve)
{
g_return_val_if_fail (GIMP_IS_CURVE (curve), 0);
g_return_val_if_fail (curve->curve_type == GIMP_CURVE_SMOOTH, 0);
return curve->n_points;
}
/**
* gimp_curve_set_n_samples:
* @curve: the #GimpCurve.
* @n_samples: the number of samples.
*
* Sets the number of sample in a [enum@Gimp.CurveType.FREE] @curve.
*
* Samples will be positioned on the curve abscissa at regular interval.
* The more samples, the more your curve will have details. Currently,
* the value of @n_samples is limited and must be between `2^8` and `2^12`.
*
* Note that changing the number of samples will reset the curve to an
* identity curve.
*
* Since: 3.2
*/
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;
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);
if (curve->curve_type == GIMP_CURVE_FREE)
curve->identity = TRUE;
g_object_thaw_notify (G_OBJECT (curve));
g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_N_SAMPLES]);
g_signal_emit (G_OBJECT (curve), gimp_curve_signals[SAMPLES_CHANGED], 0);
}
}
/**
* gimp_curve_get_n_samples:
* @curve: the #GimpCurve.
*
* Gets the number of samples in a [enum@Gimp.CurveType.FREE] curve.
*
* Returns: the number of samples in a freehand curve.
*
* Since: 3.2
*/
gint
gimp_curve_get_n_samples (GimpCurve *curve)
{
g_return_val_if_fail (GIMP_IS_CURVE (curve), 0);
return curve->n_samples;
}
/**
* gimp_curve_add_point:
* @curve: the #GimpCurve.
@ -412,8 +375,7 @@ gimp_curve_get_n_samples (GimpCurve *curve)
* number of points in this @curve. Any such information you currently
* hold should be considered invalid once the curve is changed.
*
* Returns: a point identifier to be used in other functions, or -1 on
* error.
* Returns: a point identifier to be used in other functions.
*
* Since: 3.2
*/
@ -426,9 +388,7 @@ gimp_curve_add_point (GimpCurve *curve,
gint point;
g_return_val_if_fail (GIMP_IS_CURVE (curve), -1);
if (curve->curve_type == GIMP_CURVE_FREE)
return -1;
g_return_val_if_fail (curve->curve_type == GIMP_CURVE_SMOOTH, -1);
x = CLAMP (x, 0.0, 1.0);
y = CLAMP (y, 0.0, 1.0);
@ -486,6 +446,7 @@ gimp_curve_get_point (GimpCurve *curve,
gdouble *y)
{
g_return_if_fail (GIMP_IS_CURVE (curve));
g_return_if_fail (curve->curve_type == GIMP_CURVE_SMOOTH);
g_return_if_fail (point >= 0 && point < curve->n_points);
if (x) *x = curve->points[point].x;
@ -508,6 +469,7 @@ gimp_curve_set_point_type (GimpCurve *curve,
GimpCurvePointType type)
{
g_return_if_fail (GIMP_IS_CURVE (curve));
g_return_if_fail (curve->curve_type == GIMP_CURVE_SMOOTH);
g_return_if_fail (point >= 0 && point < curve->n_points);
curve->points[point].type = type;
@ -529,6 +491,7 @@ 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 (curve->curve_type == GIMP_CURVE_SMOOTH, GIMP_CURVE_POINT_SMOOTH);
g_return_val_if_fail (point >= 0 && point < curve->n_points, GIMP_CURVE_POINT_SMOOTH);
return curve->points[point].type;
@ -558,6 +521,7 @@ gimp_curve_delete_point (GimpCurve *curve,
GimpCurvePoint *points;
g_return_if_fail (GIMP_IS_CURVE (curve));
g_return_if_fail (curve->curve_type == GIMP_CURVE_SMOOTH);
g_return_if_fail (point >= 0 && point < curve->n_points);
points = g_new0 (GimpCurvePoint, curve->n_points - 1);
@ -594,6 +558,7 @@ gimp_curve_set_point (GimpCurve *curve,
gdouble y)
{
g_return_if_fail (GIMP_IS_CURVE (curve));
g_return_if_fail (curve->curve_type == GIMP_CURVE_SMOOTH);
g_return_if_fail (point >= 0 && point < curve->n_points);
curve->points[point].x = CLAMP (x, 0.0, 1.0);
@ -622,6 +587,7 @@ void
gimp_curve_clear_points (GimpCurve *curve)
{
g_return_if_fail (GIMP_IS_CURVE (curve));
g_return_if_fail (curve->curve_type == GIMP_CURVE_SMOOTH);
if (curve->points)
{
@ -632,6 +598,121 @@ gimp_curve_clear_points (GimpCurve *curve)
}
}
/**
* gimp_curve_get_n_samples:
* @curve: the #GimpCurve.
*
* Gets the number of samples in a [enum@Gimp.CurveType.FREE] curve.
*
* Returns: the number of samples in a freehand curve.
*
* Since: 3.2
*/
gint
gimp_curve_get_n_samples (GimpCurve *curve)
{
g_return_val_if_fail (GIMP_IS_CURVE (curve), 0);
g_return_val_if_fail (curve->curve_type == GIMP_CURVE_FREE, 0);
return curve->n_samples;
}
/**
* gimp_curve_set_n_samples:
* @curve: the #GimpCurve.
* @n_samples: the number of samples.
*
* Sets the number of sample in a [enum@Gimp.CurveType.FREE] @curve.
*
* Samples will be positioned on the curve abscissa at regular interval.
* The more samples, the more your curve will have details. Currently,
* the value of @n_samples is limited and must be between `2^8` and `2^12`.
*
* Note that changing the number of samples will reset the curve to an
* identity curve.
*
* Since: 3.2
*/
void
gimp_curve_set_n_samples (GimpCurve *curve,
gint n_samples)
{
g_return_if_fail (GIMP_IS_CURVE (curve));
g_return_if_fail (curve->curve_type == GIMP_CURVE_FREE);
g_return_if_fail (n_samples >= 256);
g_return_if_fail (n_samples <= 4096);
if (n_samples != curve->n_samples)
{
g_object_freeze_notify (G_OBJECT (curve));
curve->n_samples = n_samples;
gimp_curve_build_samples (curve);
g_object_thaw_notify (G_OBJECT (curve));
g_object_notify_by_pspec (G_OBJECT (curve), obj_props[PROP_N_SAMPLES]);
g_signal_emit (G_OBJECT (curve), gimp_curve_signals[SAMPLES_CHANGED], 0);
}
}
/**
* gimp_curve_get_sample:
* @curve: the #GimpCurve.
* @x: an abscissa on a `[0.0, 1.0]` range.
*
* Gets the ordinate @y value corresponding to the passed @x abscissa
* value, in a [enum@Gimp.CurveType.FREE] @curve.
*
* Note that while the @y coordinate will be stored exactly, the @x
* coordinate will be rounded to the closest curve sample on the
* abscissa. The more sample was set with
* [method@Gimp.Curve.set_n_samples], the more precise the rounding will
* be.
*
* Since: 3.2
*/
gdouble
gimp_curve_get_sample (GimpCurve *curve,
gdouble x)
{
g_return_val_if_fail (GIMP_IS_CURVE (curve), 0);
g_return_val_if_fail (curve->curve_type == GIMP_CURVE_FREE, 0);
g_return_val_if_fail (x >= 0 && x <= 1.0, 0);
return curve->samples[ROUND (x * (gdouble) (curve->n_samples - 1))];
}
/**
* gimp_curve_set_sample:
* @curve: the #GimpCurve.
* @x: the point abscissa on a `[0.0, 1.0]` range.
* @y: the point ordinate on a `[0.0, 1.0]` range.
*
* Sets a sample in a [enum@Gimp.CurveType.FREE] @curve, with
* coordinates `(x, y)`.
*
* Note that while the @y coordinate will be stored exactly, the @x
* coordinate will be rounded to the closest curve sample on the
* abscissa. The more sample was set with
* [method@Gimp.Curve.set_n_samples], the more precise the rounding will
* be.
*
* Since: 3.2
*/
void
gimp_curve_set_sample (GimpCurve *curve,
gdouble x,
gdouble y)
{
g_return_if_fail (GIMP_IS_CURVE (curve));
g_return_if_fail (curve->curve_type == GIMP_CURVE_FREE);
g_return_if_fail (x >= 0 && x <= 1.0);
g_return_if_fail (y >= 0 && y <= 1.0);
curve->samples[ROUND (x * (gdouble) (curve->n_samples - 1))] = y;
g_signal_emit (G_OBJECT (curve), gimp_curve_signals[SAMPLES_CHANGED], 0);
}
/**
* gimp_curve_is_identity:
* @curve: a #GimpCurve object
@ -650,3 +731,18 @@ gimp_curve_is_identity (GimpCurve *curve)
return curve->identity;
}
/* Private functions */
static void
gimp_curve_build_samples (GimpCurve *curve)
{
curve->samples = g_renew (gdouble, curve->samples, curve->n_samples);
for (gint i = 0; i < curve->n_samples; i++)
curve->samples[i] = (gdouble) i / (gdouble) (curve->n_samples - 1);
if (curve->curve_type == GIMP_CURVE_FREE)
curve->identity = TRUE;
}

View file

@ -37,15 +37,11 @@ G_DECLARE_FINAL_TYPE (GimpCurve, gimp_curve, GIMP, CURVE, GObject)
GimpCurve * gimp_curve_new (void);
GimpCurveType gimp_curve_get_curve_type (GimpCurve *curve);
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);
@ -53,14 +49,11 @@ 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,
@ -69,8 +62,18 @@ void gimp_curve_set_point (GimpCurve *curve,
gdouble y);
void gimp_curve_clear_points (GimpCurve *curve);
gint gimp_curve_get_n_samples (GimpCurve *curve);
void gimp_curve_set_n_samples (GimpCurve *curve,
gint n_samples);
gdouble gimp_curve_get_sample (GimpCurve *curve,
gdouble x);
void gimp_curve_set_sample (GimpCurve *curve,
gdouble x,
gdouble y);
gboolean gimp_curve_is_identity (GimpCurve *curve);
G_END_DECLS
#endif /* __GIMP_CURVE_H__ */

View file

@ -1211,19 +1211,27 @@ gimp_gp_param_to_value (gpointer gimp,
"curve-type", param->data.d_curve.curve_type,
NULL);
for (gint j = 0; j < param->data.d_curve.n_points; j++)
if (param->data.d_curve.curve_type == GIMP_CURVE_SMOOTH)
{
gimp_curve_add_point (curve, param->data.d_curve.points[j * 2],
param->data.d_curve.points[(j * 2) + 1]);
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]);
gimp_curve_set_point_type (curve, j,
param->data.d_curve.point_types[j]);
}
}
else /* if (param->data.d_curve.curve_type == GIMP_CURVE_FREE) */
{
gint n_samples = param->data.d_curve.n_samples;
if (param->data.d_curve.n_samples > 0)
g_object_set (curve,
"n-samples", param->data.d_curve.n_samples,
NULL);
gimp_curve_set_n_samples (curve, n_samples);
for (gint j = 0; j < n_samples; j++)
gimp_curve_set_sample (curve,
(gdouble) j / (gdouble) (n_samples - 1),
param->data.d_curve.samples[j]);
}
g_value_set_object (value, curve);
@ -1790,29 +1798,46 @@ gimp_value_to_gp_param (const GValue *value,
param->param_type = GP_PARAM_TYPE_CURVE;
param->data.d_curve.n_points = gimp_curve_get_n_points (curve);
g_object_get (curve,
"curve-type", &param->data.d_curve.curve_type,
"n-samples", &param->data.d_curve.n_samples,
NULL);
param->data.d_curve.curve_type = gimp_curve_get_curve_type (curve);
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++)
if (param->data.d_curve.curve_type == GIMP_CURVE_SMOOTH)
{
gdouble x;
gdouble y;
param->data.d_curve.n_points = gimp_curve_get_n_points (curve);
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);
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;
for (gint j = 0; j < param->data.d_curve.n_points; j++)
{
gdouble x;
gdouble y;
param->data.d_curve.point_types[j] =
gimp_curve_get_point_type (curve, j);
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);
}
param->data.d_curve.n_samples = 0;
param->data.d_curve.samples = NULL;
}
else /* if (param->data.d_curve.curve_type == GIMP_CURVE_FREE) */
{
gint n_samples = gimp_curve_get_n_samples (curve);
param->data.d_curve.n_samples = n_samples;
param->data.d_curve.samples = g_new0 (gdouble, n_samples);
for (gint j = 0; j < n_samples; j++)
param->data.d_curve.samples[j] = gimp_curve_get_sample (curve,
(gdouble) j / (gdouble) (n_samples - 1));
param->data.d_curve.n_points = 0;
param->data.d_curve.points = NULL;
param->data.d_curve.point_types = NULL;
}
}
else if (G_VALUE_HOLDS_PARAM (value))
@ -1948,6 +1973,7 @@ _gimp_gp_params_free (GPParam *params,
{
g_free (params[i].data.d_curve.points);
g_free (params[i].data.d_curve.point_types);
g_free (params[i].data.d_curve.samples);
}
break;
}

View file

@ -2250,6 +2250,19 @@ _gp_params_read (GIOChannel *channel,
(*params)[i].data.d_curve.point_types = NULL;
goto cleanup;
}
(*params)[i].data.d_curve.samples = g_new0 (gdouble,
(*params)[i].data.d_curve.n_samples);
if (! _gimp_wire_read_double (channel,
(*params)[i].data.d_curve.samples,
(*params)[i].data.d_curve.n_samples,
user_data))
{
g_free ((*params)[i].data.d_curve.samples);
(*params)[i].data.d_curve.samples = NULL;
goto cleanup;
}
}
break;
}
@ -2510,6 +2523,13 @@ _gp_params_write (GIOChannel *channel,
params[i].data.d_curve.n_points,
user_data))
return;
if (! _gimp_wire_write_double (channel,
params[i].data.d_curve.samples,
params[i].data.d_curve.n_samples,
user_data))
return;
break;
}
}
@ -2595,6 +2615,8 @@ _gp_params_destroy (GPParam *params,
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);
if (params[i].data.d_curve.samples)
g_free (params[i].data.d_curve.samples);
break;
}
}

View file

@ -26,7 +26,7 @@ G_BEGIN_DECLS
/* Increment every time the protocol changes
*/
#define GIMP_PROTOCOL_VERSION 0x0116
#define GIMP_PROTOCOL_VERSION 0x0117
enum
@ -345,6 +345,7 @@ struct _GPParamCurve
guint32 *point_types;
guint32 n_samples;
gdouble *samples;
};
struct _GPParam