app: streamline GimpCurve
In GimpCurve, replace the use of a fixed-length control-point array with a dynamically-sized array. Adapt GimpCurve's interface, and the rest of the code. In addition to simplifying the code, this fixes a bug where the curve object could be broken by moving the mouse too fast (yep...), and allows more accurate point placement, both in the GUI editor, and through canvas interaction in the Curves tool (see issue #814).
This commit is contained in:
@ -37,6 +37,9 @@
|
||||
#include "gimp-intl.h"
|
||||
|
||||
|
||||
#define EPSILON 1e-6
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
@ -155,7 +158,9 @@ gimp_curve_class_init (GimpCurveClass *klass)
|
||||
"n-points",
|
||||
"Number of Points",
|
||||
"The number of points",
|
||||
17, 17, 17, 0);
|
||||
0, G_MAXINT, 0,
|
||||
/* for backward compatibility */
|
||||
GIMP_CONFIG_PARAM_IGNORE);
|
||||
|
||||
array_spec = g_param_spec_double ("point", NULL, NULL,
|
||||
-1.0, 1.0, 0.0, GIMP_PARAM_READWRITE);
|
||||
@ -237,7 +242,7 @@ gimp_curve_set_property (GObject *object,
|
||||
break;
|
||||
|
||||
case PROP_N_POINTS:
|
||||
gimp_curve_set_n_points (curve, g_value_get_int (value));
|
||||
/* ignored */
|
||||
break;
|
||||
|
||||
case PROP_POINTS:
|
||||
@ -246,19 +251,41 @@ gimp_curve_set_property (GObject *object,
|
||||
gint length;
|
||||
gint i;
|
||||
|
||||
curve->n_points = 0;
|
||||
g_clear_pointer (&curve->points, g_free);
|
||||
|
||||
if (! array)
|
||||
break;
|
||||
|
||||
length = gimp_value_array_length (array);
|
||||
length = gimp_value_array_length (array) / 2;
|
||||
|
||||
for (i = 0; i < curve->n_points && i * 2 < length; i++)
|
||||
curve->points = g_new (GimpVector2, 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);
|
||||
|
||||
curve->points[i].x = g_value_get_double (x);
|
||||
curve->points[i].y = g_value_get_double (y);
|
||||
/* for backward compatibility */
|
||||
if (g_value_get_double (x) < 0.0)
|
||||
continue;
|
||||
|
||||
curve->points[curve->n_points].x = CLAMP (g_value_get_double (x),
|
||||
0.0, 1.0);
|
||||
curve->points[curve->n_points].y = CLAMP (g_value_get_double (y),
|
||||
0.0, 1.0);
|
||||
|
||||
if (curve->n_points > 0)
|
||||
{
|
||||
curve->points[curve->n_points].x = MAX (
|
||||
curve->points[curve->n_points].x,
|
||||
curve->points[curve->n_points - 1].x);
|
||||
}
|
||||
|
||||
curve->n_points++;
|
||||
}
|
||||
|
||||
g_object_notify (object, "n-points");
|
||||
}
|
||||
break;
|
||||
|
||||
@ -281,7 +308,7 @@ gimp_curve_set_property (GObject *object,
|
||||
{
|
||||
GValue *v = gimp_value_array_index (array, i);
|
||||
|
||||
curve->samples[i] = g_value_get_double (v);
|
||||
curve->samples[i] = CLAMP (g_value_get_double (v), 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -484,11 +511,19 @@ gimp_curve_equal (GimpConfig *a,
|
||||
if (a_curve->curve_type != b_curve->curve_type)
|
||||
return FALSE;
|
||||
|
||||
if (memcmp (a_curve->points, b_curve->points,
|
||||
sizeof (GimpVector2) * b_curve->n_points) ||
|
||||
memcmp (a_curve->samples, b_curve->samples,
|
||||
sizeof (gdouble) * b_curve->n_samples))
|
||||
if (a_curve->n_points != b_curve->n_points ||
|
||||
memcmp (a_curve->points, b_curve->points,
|
||||
sizeof (GimpVector2) * a_curve->n_points))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (a_curve->n_samples != b_curve->n_samples ||
|
||||
memcmp (a_curve->samples, b_curve->samples,
|
||||
sizeof (gdouble) * a_curve->n_samples))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -564,18 +599,18 @@ gimp_curve_reset (GimpCurve *curve,
|
||||
|
||||
g_object_notify (G_OBJECT (curve), "samples");
|
||||
|
||||
g_free (curve->points);
|
||||
|
||||
curve->n_points = 2;
|
||||
curve->points = g_new (GimpVector2, 2);
|
||||
|
||||
curve->points[0].x = 0.0;
|
||||
curve->points[0].y = 0.0;
|
||||
|
||||
for (i = 1; i < curve->n_points - 1; i++)
|
||||
{
|
||||
curve->points[i].x = -1.0;
|
||||
curve->points[i].y = -1.0;
|
||||
}
|
||||
|
||||
curve->points[curve->n_points - 1].x = 1.0;
|
||||
curve->points[curve->n_points - 1].y = 1.0;
|
||||
curve->points[1].x = 1.0;
|
||||
curve->points[1].y = 1.0;
|
||||
|
||||
g_object_notify (G_OBJECT (curve), "n-points");
|
||||
g_object_notify (G_OBJECT (curve), "points");
|
||||
|
||||
if (reset_type)
|
||||
@ -599,44 +634,46 @@ gimp_curve_set_curve_type (GimpCurve *curve,
|
||||
|
||||
if (curve->curve_type != curve_type)
|
||||
{
|
||||
gimp_data_freeze (GIMP_DATA (curve));
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (curve));
|
||||
|
||||
curve->curve_type = curve_type;
|
||||
|
||||
if (curve_type == GIMP_CURVE_SMOOTH)
|
||||
{
|
||||
gint n_points;
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < curve->n_points; i++)
|
||||
{
|
||||
curve->points[i].x = -1;
|
||||
curve->points[i].y = -1;
|
||||
}
|
||||
g_free (curve->points);
|
||||
|
||||
/* pick some points from the curve and make them control
|
||||
* points
|
||||
*/
|
||||
n_points = CLAMP (9, curve->n_points / 2, curve->n_points);
|
||||
curve->n_points = 9;
|
||||
curve->points = g_new (GimpVector2, 9);
|
||||
|
||||
for (i = 0; i < n_points; i++)
|
||||
for (i = 0; i < curve->n_points; i++)
|
||||
{
|
||||
gint sample = i * (curve->n_samples - 1) / (n_points - 1);
|
||||
gint point = i * (curve->n_points - 1) / (n_points - 1);
|
||||
gint sample = i * (curve->n_samples - 1) / (curve->n_points - 1);
|
||||
|
||||
curve->points[point].x = ((gdouble) sample /
|
||||
(gdouble) (curve->n_samples - 1));
|
||||
curve->points[point].y = curve->samples[sample];
|
||||
curve->points[i].x = (gdouble) sample /
|
||||
(gdouble) (curve->n_samples - 1);
|
||||
curve->points[i].y = curve->samples[sample];
|
||||
}
|
||||
|
||||
g_object_notify (G_OBJECT (curve), "n-points");
|
||||
g_object_notify (G_OBJECT (curve), "points");
|
||||
}
|
||||
else
|
||||
{
|
||||
gimp_curve_clear_points (curve);
|
||||
}
|
||||
|
||||
g_object_notify (G_OBJECT (curve), "curve-type");
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (curve));
|
||||
|
||||
gimp_data_dirty (GIMP_DATA (curve));
|
||||
gimp_data_thaw (GIMP_DATA (curve));
|
||||
}
|
||||
}
|
||||
|
||||
@ -648,46 +685,6 @@ gimp_curve_get_curve_type (GimpCurve *curve)
|
||||
return curve->curve_type;
|
||||
}
|
||||
|
||||
void
|
||||
gimp_curve_set_n_points (GimpCurve *curve,
|
||||
gint n_points)
|
||||
{
|
||||
g_return_if_fail (GIMP_IS_CURVE (curve));
|
||||
g_return_if_fail (n_points >= 2);
|
||||
g_return_if_fail (n_points <= 1024);
|
||||
|
||||
if (n_points != curve->n_points)
|
||||
{
|
||||
gint i;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (curve));
|
||||
|
||||
curve->n_points = n_points;
|
||||
g_object_notify (G_OBJECT (curve), "n-points");
|
||||
|
||||
curve->points = g_renew (GimpVector2, curve->points, curve->n_points);
|
||||
|
||||
curve->points[0].x = 0.0;
|
||||
curve->points[0].y = 0.0;
|
||||
|
||||
for (i = 1; i < curve->n_points - 1; i++)
|
||||
{
|
||||
curve->points[i].x = -1.0;
|
||||
curve->points[i].y = -1.0;
|
||||
}
|
||||
|
||||
curve->points[curve->n_points - 1].x = 1.0;
|
||||
curve->points[curve->n_points - 1].y = 1.0;
|
||||
|
||||
g_object_notify (G_OBJECT (curve), "points");
|
||||
|
||||
if (curve->curve_type == GIMP_CURVE_SMOOTH)
|
||||
curve->identity = TRUE;
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (curve));
|
||||
}
|
||||
}
|
||||
|
||||
gint
|
||||
gimp_curve_get_n_points (GimpCurve *curve)
|
||||
{
|
||||
@ -736,31 +733,135 @@ gimp_curve_get_n_samples (GimpCurve *curve)
|
||||
}
|
||||
|
||||
gint
|
||||
gimp_curve_get_closest_point (GimpCurve *curve,
|
||||
gimp_curve_get_point_at (GimpCurve *curve,
|
||||
gdouble x)
|
||||
{
|
||||
gint closest_point = 0;
|
||||
gdouble distance = G_MAXDOUBLE;
|
||||
gint closest_point = -1;
|
||||
gdouble distance = EPSILON;
|
||||
gint i;
|
||||
|
||||
g_return_val_if_fail (GIMP_IS_CURVE (curve), 0);
|
||||
g_return_val_if_fail (GIMP_IS_CURVE (curve), -1);
|
||||
|
||||
for (i = 0; i < curve->n_points; i++)
|
||||
{
|
||||
if (curve->points[i].x >= 0.0 &&
|
||||
fabs (x - curve->points[i].x) < distance)
|
||||
{
|
||||
distance = fabs (x - curve->points[i].x);
|
||||
closest_point = i;
|
||||
}
|
||||
}
|
||||
gdouble point_distance;
|
||||
|
||||
if (distance > (1.0 / (curve->n_points * 2.0)))
|
||||
closest_point = ROUND (x * (gdouble) (curve->n_points - 1));
|
||||
point_distance = fabs (x - curve->points[i].x);
|
||||
|
||||
if (point_distance <= distance)
|
||||
{
|
||||
closest_point = i;
|
||||
distance = point_distance;
|
||||
}
|
||||
}
|
||||
|
||||
return closest_point;
|
||||
}
|
||||
|
||||
gint
|
||||
gimp_curve_get_closest_point (GimpCurve *curve,
|
||||
gdouble x,
|
||||
gdouble y,
|
||||
gdouble max_distance)
|
||||
{
|
||||
gint closest_point = -1;
|
||||
gdouble distance2 = G_MAXDOUBLE;
|
||||
gint i;
|
||||
|
||||
g_return_val_if_fail (GIMP_IS_CURVE (curve), -1);
|
||||
|
||||
if (max_distance >= 0.0)
|
||||
distance2 = SQR (max_distance);
|
||||
|
||||
for (i = curve->n_points - 1; i >= 0; i--)
|
||||
{
|
||||
gdouble point_distance2;
|
||||
|
||||
point_distance2 = SQR (x - curve->points[i].x) +
|
||||
SQR (y - curve->points[i].y);
|
||||
|
||||
if (point_distance2 <= distance2)
|
||||
{
|
||||
closest_point = i;
|
||||
distance2 = point_distance2;
|
||||
}
|
||||
}
|
||||
|
||||
return closest_point;
|
||||
}
|
||||
|
||||
gint
|
||||
gimp_curve_add_point (GimpCurve *curve,
|
||||
gdouble x,
|
||||
gdouble y)
|
||||
{
|
||||
GimpVector2 *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_new (GimpVector2, curve->n_points + 1);
|
||||
|
||||
memcpy (points, curve->points,
|
||||
point * sizeof (GimpVector2));
|
||||
memcpy (points + point + 1, curve->points + point,
|
||||
(curve->n_points - point) * sizeof (GimpVector2));
|
||||
|
||||
points[point].x = x;
|
||||
points[point].y = y;
|
||||
|
||||
g_free (curve->points);
|
||||
|
||||
curve->n_points++;
|
||||
curve->points = points;
|
||||
|
||||
g_object_notify (G_OBJECT (curve), "n-points");
|
||||
g_object_notify (G_OBJECT (curve), "points");
|
||||
|
||||
gimp_data_dirty (GIMP_DATA (curve));
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
void
|
||||
gimp_curve_delete_point (GimpCurve *curve,
|
||||
gint point)
|
||||
{
|
||||
GimpVector2 *points;
|
||||
|
||||
g_return_if_fail (GIMP_IS_CURVE (curve));
|
||||
g_return_if_fail (point >= 0 && point < curve->n_points);
|
||||
|
||||
points = g_new (GimpVector2, curve->n_points - 1);
|
||||
|
||||
memcpy (points, curve->points,
|
||||
point * sizeof (GimpVector2));
|
||||
memcpy (points + point, curve->points + point + 1,
|
||||
(curve->n_points - point - 1) * sizeof (GimpVector2));
|
||||
|
||||
g_free (curve->points);
|
||||
|
||||
curve->n_points--;
|
||||
curve->points = points;
|
||||
|
||||
g_object_notify (G_OBJECT (curve), "n-points");
|
||||
g_object_notify (G_OBJECT (curve), "points");
|
||||
|
||||
gimp_data_dirty (GIMP_DATA (curve));
|
||||
}
|
||||
|
||||
void
|
||||
gimp_curve_set_point (GimpCurve *curve,
|
||||
gint point,
|
||||
@ -769,14 +870,15 @@ gimp_curve_set_point (GimpCurve *curve,
|
||||
{
|
||||
g_return_if_fail (GIMP_IS_CURVE (curve));
|
||||
g_return_if_fail (point >= 0 && point < curve->n_points);
|
||||
g_return_if_fail (x == -1.0 || (x >= 0 && x <= 1.0));
|
||||
g_return_if_fail (y == -1.0 || (y >= 0 && y <= 1.0));
|
||||
|
||||
if (curve->curve_type == GIMP_CURVE_FREE)
|
||||
return;
|
||||
curve->points[point].x = CLAMP (x, 0.0, 1.0);
|
||||
curve->points[point].y = CLAMP (y, 0.0, 1.0);
|
||||
|
||||
curve->points[point].x = x;
|
||||
curve->points[point].y = y;
|
||||
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 (G_OBJECT (curve), "points");
|
||||
|
||||
@ -790,40 +892,8 @@ gimp_curve_move_point (GimpCurve *curve,
|
||||
{
|
||||
g_return_if_fail (GIMP_IS_CURVE (curve));
|
||||
g_return_if_fail (point >= 0 && point < curve->n_points);
|
||||
g_return_if_fail (y >= 0 && y <= 1.0);
|
||||
|
||||
if (curve->curve_type == GIMP_CURVE_FREE)
|
||||
return;
|
||||
|
||||
curve->points[point].y = y;
|
||||
|
||||
g_object_notify (G_OBJECT (curve), "points");
|
||||
|
||||
gimp_data_dirty (GIMP_DATA (curve));
|
||||
}
|
||||
|
||||
void
|
||||
gimp_curve_delete_point (GimpCurve *curve,
|
||||
gint point)
|
||||
{
|
||||
g_return_if_fail (GIMP_IS_CURVE (curve));
|
||||
g_return_if_fail (point >= 0 && point < curve->n_points);
|
||||
|
||||
if (point == 0)
|
||||
{
|
||||
curve->points[0].x = 0.0;
|
||||
curve->points[0].y = 0.0;
|
||||
}
|
||||
else if (point == curve->n_points - 1)
|
||||
{
|
||||
curve->points[curve->n_points - 1].x = 1.0;
|
||||
curve->points[curve->n_points - 1].y = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
curve->points[point].x = -1.0;
|
||||
curve->points[point].y = -1.0;
|
||||
}
|
||||
curve->points[point].y = CLAMP (y, 0.0, 1.0);
|
||||
|
||||
g_object_notify (G_OBJECT (curve), "points");
|
||||
|
||||
@ -839,18 +909,27 @@ gimp_curve_get_point (GimpCurve *curve,
|
||||
g_return_if_fail (GIMP_IS_CURVE (curve));
|
||||
g_return_if_fail (point >= 0 && point < curve->n_points);
|
||||
|
||||
if (curve->curve_type == GIMP_CURVE_FREE)
|
||||
{
|
||||
if (x) *x = -1.0;
|
||||
if (y) *y = -1.0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (x) *x = curve->points[point].x;
|
||||
if (y) *y = curve->points[point].y;
|
||||
}
|
||||
|
||||
void
|
||||
gimp_curve_clear_points (GimpCurve *curve)
|
||||
{
|
||||
g_return_if_fail (GIMP_IS_CURVE (curve));
|
||||
|
||||
if (curve->points)
|
||||
{
|
||||
curve->n_points = 0;
|
||||
g_clear_pointer (&curve->points, g_free);
|
||||
|
||||
g_object_notify (G_OBJECT (curve), "n-points");
|
||||
g_object_notify (G_OBJECT (curve), "points");
|
||||
|
||||
gimp_data_dirty (GIMP_DATA (curve));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gimp_curve_set_curve (GimpCurve *curve,
|
||||
gdouble x,
|
||||
@ -909,59 +988,49 @@ gimp_curve_get_uchar (GimpCurve *curve,
|
||||
static void
|
||||
gimp_curve_calculate (GimpCurve *curve)
|
||||
{
|
||||
gint *points;
|
||||
gint i;
|
||||
gint num_pts;
|
||||
gint p1, p2, p3, p4;
|
||||
|
||||
if (gimp_data_is_frozen (GIMP_DATA (curve)))
|
||||
return;
|
||||
|
||||
points = g_newa (gint, curve->n_points);
|
||||
|
||||
switch (curve->curve_type)
|
||||
{
|
||||
case GIMP_CURVE_SMOOTH:
|
||||
/* cycle through the curves */
|
||||
num_pts = 0;
|
||||
for (i = 0; i < curve->n_points; i++)
|
||||
if (curve->points[i].x >= 0.0)
|
||||
points[num_pts++] = i;
|
||||
|
||||
/* Initialize boundary curve points */
|
||||
if (num_pts != 0)
|
||||
if (curve->n_points > 0)
|
||||
{
|
||||
GimpVector2 point;
|
||||
gint boundary;
|
||||
|
||||
point = curve->points[points[0]];
|
||||
point = curve->points[0];
|
||||
boundary = ROUND (point.x * (gdouble) (curve->n_samples - 1));
|
||||
|
||||
for (i = 0; i < boundary; i++)
|
||||
curve->samples[i] = point.y;
|
||||
|
||||
point = curve->points[points[num_pts - 1]];
|
||||
point = curve->points[curve->n_points - 1];
|
||||
boundary = ROUND (point.x * (gdouble) (curve->n_samples - 1));
|
||||
|
||||
for (i = boundary; i < curve->n_samples; i++)
|
||||
curve->samples[i] = point.y;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_pts - 1; i++)
|
||||
for (i = 0; i < curve->n_points - 1; i++)
|
||||
{
|
||||
p1 = points[MAX (i - 1, 0)];
|
||||
p2 = points[i];
|
||||
p3 = points[i + 1];
|
||||
p4 = points[MIN (i + 2, num_pts - 1)];
|
||||
p1 = MAX (i - 1, 0);
|
||||
p2 = i;
|
||||
p3 = i + 1;
|
||||
p4 = MIN (i + 2, curve->n_points - 1);
|
||||
|
||||
gimp_curve_plot (curve, p1, p2, p3, p4);
|
||||
}
|
||||
|
||||
/* ensure that the control points are used exactly */
|
||||
for (i = 0; i < num_pts; i++)
|
||||
for (i = 0; i < curve->n_points; i++)
|
||||
{
|
||||
gdouble x = curve->points[points[i]].x;
|
||||
gdouble y = curve->points[points[i]].y;
|
||||
gdouble x = curve->points[i].x;
|
||||
gdouble y = curve->points[i].y;
|
||||
|
||||
curve->samples[ROUND (x * (gdouble) (curve->n_samples - 1))] = y;
|
||||
}
|
||||
@ -1013,7 +1082,16 @@ gimp_curve_plot (GimpCurve *curve,
|
||||
dx = x3 - x0;
|
||||
dy = y3 - y0;
|
||||
|
||||
g_return_if_fail (dx > 0);
|
||||
if (dx <= EPSILON)
|
||||
{
|
||||
gint index;
|
||||
|
||||
index = ROUND (x0 * (gdouble) (curve->n_samples - 1));
|
||||
|
||||
curve->samples[index] = y3;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (p1 == p2 && p3 == p4)
|
||||
{
|
||||
|
@ -65,17 +65,24 @@ void gimp_curve_set_curve_type (GimpCurve *curve,
|
||||
GimpCurveType curve_type);
|
||||
GimpCurveType gimp_curve_get_curve_type (GimpCurve *curve);
|
||||
|
||||
void gimp_curve_set_n_points (GimpCurve *curve,
|
||||
gint n_points);
|
||||
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_get_closest_point (GimpCurve *curve,
|
||||
gint gimp_curve_get_point_at (GimpCurve *curve,
|
||||
gdouble x);
|
||||
gint gimp_curve_get_closest_point (GimpCurve *curve,
|
||||
gdouble x,
|
||||
gdouble y,
|
||||
gdouble max_distance);
|
||||
|
||||
gint gimp_curve_add_point (GimpCurve *curve,
|
||||
gdouble x,
|
||||
gdouble y);
|
||||
void gimp_curve_delete_point (GimpCurve *curve,
|
||||
gint point);
|
||||
void gimp_curve_set_point (GimpCurve *curve,
|
||||
gint point,
|
||||
gdouble x,
|
||||
@ -83,12 +90,11 @@ void gimp_curve_set_point (GimpCurve *curve,
|
||||
void gimp_curve_move_point (GimpCurve *curve,
|
||||
gint point,
|
||||
gdouble y);
|
||||
void gimp_curve_delete_point (GimpCurve *curve,
|
||||
gint point);
|
||||
void gimp_curve_get_point (GimpCurve *curve,
|
||||
gint point,
|
||||
gdouble *x,
|
||||
gdouble *y);
|
||||
void gimp_curve_clear_points (GimpCurve *curve);
|
||||
|
||||
void gimp_curve_set_curve (GimpCurve *curve,
|
||||
gdouble x,
|
||||
|
@ -421,13 +421,10 @@ gimp_curves_config_new_spline (gint32 channel,
|
||||
gimp_data_freeze (GIMP_DATA (curve));
|
||||
|
||||
gimp_curve_set_curve_type (curve, GIMP_CURVE_SMOOTH);
|
||||
gimp_curve_set_n_points (curve, n_points);
|
||||
|
||||
/* unset the last point */
|
||||
gimp_curve_set_point (curve, curve->n_points - 1, -1.0, -1.0);
|
||||
gimp_curve_clear_points (curve);
|
||||
|
||||
for (i = 0; i < n_points; i++)
|
||||
gimp_curve_set_point (curve, i,
|
||||
gimp_curve_add_point (curve,
|
||||
(gdouble) points[i * 2],
|
||||
(gdouble) points[i * 2 + 1]);
|
||||
|
||||
@ -617,18 +614,18 @@ gimp_curves_config_load_cruft (GimpCurvesConfig *config,
|
||||
gimp_data_freeze (GIMP_DATA (curve));
|
||||
|
||||
gimp_curve_set_curve_type (curve, GIMP_CURVE_SMOOTH);
|
||||
gimp_curve_set_n_points (curve, GIMP_CURVE_N_CRUFT_POINTS);
|
||||
|
||||
gimp_curve_reset (curve, FALSE);
|
||||
gimp_curve_clear_points (curve);
|
||||
|
||||
for (j = 0; j < GIMP_CURVE_N_CRUFT_POINTS; j++)
|
||||
{
|
||||
if (index[i][j] < 0 || value[i][j] < 0)
|
||||
gimp_curve_set_point (curve, j, -1.0, -1.0);
|
||||
else
|
||||
gimp_curve_set_point (curve, j,
|
||||
(gdouble) index[i][j] / 255.0,
|
||||
(gdouble) value[i][j] / 255.0);
|
||||
gdouble x;
|
||||
gdouble y;
|
||||
|
||||
x = (gdouble) index[i][j] / 255.0;
|
||||
y = (gdouble) value[i][j] / 255.0;
|
||||
|
||||
if (x >= 0.0)
|
||||
gimp_curve_add_point (curve, x, y);
|
||||
}
|
||||
|
||||
gimp_data_thaw (GIMP_DATA (curve));
|
||||
@ -662,50 +659,34 @@ gimp_curves_config_save_cruft (GimpCurvesConfig *config,
|
||||
GimpCurve *curve = config->curve[i];
|
||||
gint j;
|
||||
|
||||
if (curve->curve_type == GIMP_CURVE_FREE)
|
||||
if (curve->curve_type == GIMP_CURVE_SMOOTH)
|
||||
{
|
||||
gint n_points;
|
||||
|
||||
for (j = 0; j < curve->n_points; j++)
|
||||
{
|
||||
curve->points[j].x = -1;
|
||||
curve->points[j].y = -1;
|
||||
}
|
||||
|
||||
/* pick some points from the curve and make them control
|
||||
* points
|
||||
*/
|
||||
n_points = CLAMP (9, curve->n_points / 2, curve->n_points);
|
||||
|
||||
for (j = 0; j < n_points; j++)
|
||||
{
|
||||
gint sample = j * (curve->n_samples - 1) / (n_points - 1);
|
||||
gint point = j * (curve->n_points - 1) / (n_points - 1);
|
||||
|
||||
curve->points[point].x = ((gdouble) sample /
|
||||
(gdouble) (curve->n_samples - 1));
|
||||
curve->points[point].y = curve->samples[sample];
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < curve->n_points; j++)
|
||||
{
|
||||
/* don't use gimp_curve_get_point() because that doesn't
|
||||
* work when the curve type is GIMP_CURVE_FREE
|
||||
*/
|
||||
gdouble x = curve->points[j].x;
|
||||
gdouble y = curve->points[j].y;
|
||||
|
||||
if (x < 0.0 || y < 0.0)
|
||||
{
|
||||
g_string_append_printf (string, "%d %d ", -1, -1);
|
||||
g_object_ref (curve);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_string_append_printf (string, "%d %d ",
|
||||
(gint) (x * 255.999),
|
||||
(gint) (y * 255.999));
|
||||
curve = GIMP_CURVE (gimp_data_duplicate (GIMP_DATA (curve)));
|
||||
|
||||
gimp_curve_set_curve_type (curve, GIMP_CURVE_SMOOTH);
|
||||
}
|
||||
|
||||
for (j = 0; j < GIMP_CURVE_N_CRUFT_POINTS; j++)
|
||||
{
|
||||
gint x = -1;
|
||||
gint y = -1;
|
||||
|
||||
if (j < gimp_curve_get_n_points (curve))
|
||||
{
|
||||
gdouble point_x;
|
||||
gdouble point_y;
|
||||
|
||||
gimp_curve_get_point (curve, j, &point_x, &point_y);
|
||||
|
||||
x = floor (point_x * 255.999);
|
||||
y = floor (point_y * 255.999);
|
||||
}
|
||||
|
||||
g_string_append_printf (string, "%d %d ", x, y);
|
||||
}
|
||||
|
||||
g_string_append_printf (string, "\n");
|
||||
|
@ -715,17 +715,14 @@ gimp_levels_config_to_curves_config (GimpLevelsConfig *config)
|
||||
channel++)
|
||||
{
|
||||
GimpCurve *curve = curves->curve[channel];
|
||||
const gint n_points = gimp_curve_get_n_points (curve);
|
||||
static const gint n = 8;
|
||||
gint point = -1;
|
||||
gdouble gamma = config->gamma[channel];
|
||||
gdouble delta_in;
|
||||
gdouble delta_out;
|
||||
gdouble x, y;
|
||||
|
||||
/* clear the points set by default */
|
||||
gimp_curve_set_point (curve, 0, -1, -1);
|
||||
gimp_curve_set_point (curve, n_points - 1, -1, -1);
|
||||
gimp_curve_clear_points (curve);
|
||||
|
||||
delta_in = config->high_input[channel] - config->low_input[channel];
|
||||
delta_out = config->high_output[channel] - config->low_output[channel];
|
||||
@ -733,8 +730,7 @@ gimp_levels_config_to_curves_config (GimpLevelsConfig *config)
|
||||
x = config->low_input[channel];
|
||||
y = config->low_output[channel];
|
||||
|
||||
point = CLAMP (n_points * x, point + 1, n_points - 1 - n);
|
||||
gimp_curve_set_point (curve, point, x, y);
|
||||
gimp_curve_add_point (curve, x, y);
|
||||
|
||||
if (delta_out != 0 && gamma != 1.0)
|
||||
{
|
||||
@ -774,8 +770,7 @@ gimp_levels_config_to_curves_config (GimpLevelsConfig *config)
|
||||
x = config->low_input[channel] + dx;
|
||||
y = config->low_output[channel] + delta_out *
|
||||
gimp_operation_levels_map_input (config, channel, x);
|
||||
point = CLAMP (n_points * x, point + 1, n_points - 1 - n + i);
|
||||
gimp_curve_set_point (curve, point, x, y);
|
||||
gimp_curve_add_point (curve, x, y);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -811,8 +806,7 @@ gimp_levels_config_to_curves_config (GimpLevelsConfig *config)
|
||||
y = config->low_output[channel] + dy;
|
||||
x = config->low_input[channel] + delta_in *
|
||||
gimp_operation_levels_map_input (config_inv, channel, y);
|
||||
point = CLAMP (n_points * x, point + 1, n_points - 1 - n + i);
|
||||
gimp_curve_set_point (curve, point, x, y);
|
||||
gimp_curve_add_point (curve, x, y);
|
||||
}
|
||||
|
||||
g_object_unref (config_inv);
|
||||
@ -822,8 +816,7 @@ gimp_levels_config_to_curves_config (GimpLevelsConfig *config)
|
||||
x = config->high_input[channel];
|
||||
y = config->high_output[channel];
|
||||
|
||||
point = CLAMP (n_points * x, point + 1, n_points - 1);
|
||||
gimp_curve_set_point (curve, point, x, y);
|
||||
gimp_curve_add_point (curve, x, y);
|
||||
}
|
||||
|
||||
return curves;
|
||||
|
@ -242,16 +242,19 @@ gimp_curves_tool_button_release (GimpTool *tool,
|
||||
{
|
||||
GimpCurve *curve = config->curve[config->channel];
|
||||
gdouble value = c_tool->picked_color[config->channel];
|
||||
gint closest;
|
||||
gint point;
|
||||
|
||||
closest = gimp_curve_get_closest_point (curve, value);
|
||||
point = gimp_curve_get_point_at (curve, value);
|
||||
|
||||
gimp_curve_view_set_selected (GIMP_CURVE_VIEW (c_tool->graph),
|
||||
closest);
|
||||
|
||||
gimp_curve_set_point (curve, closest,
|
||||
if (point < 0)
|
||||
{
|
||||
point = gimp_curve_add_point (
|
||||
curve,
|
||||
value, gimp_curve_map_value (curve, value));
|
||||
}
|
||||
|
||||
gimp_curve_view_set_selected (GIMP_CURVE_VIEW (c_tool->graph), point);
|
||||
}
|
||||
else if (state & gimp_get_toggle_behavior_mask ())
|
||||
{
|
||||
GimpHistogramChannel channel;
|
||||
@ -262,18 +265,26 @@ gimp_curves_tool_button_release (GimpTool *tool,
|
||||
{
|
||||
GimpCurve *curve = config->curve[channel];
|
||||
gdouble value = c_tool->picked_color[channel];
|
||||
gint closest;
|
||||
|
||||
if (value != -1)
|
||||
{
|
||||
closest = gimp_curve_get_closest_point (curve, value);
|
||||
gint point;
|
||||
|
||||
gimp_curve_view_set_selected (GIMP_CURVE_VIEW (c_tool->graph),
|
||||
closest);
|
||||
point = gimp_curve_get_point_at (curve, value);
|
||||
|
||||
gimp_curve_set_point (curve, closest,
|
||||
if (point < 0)
|
||||
{
|
||||
point = gimp_curve_add_point (
|
||||
curve,
|
||||
value, gimp_curve_map_value (curve, value));
|
||||
}
|
||||
|
||||
if (channel == config->channel)
|
||||
{
|
||||
gimp_curve_view_set_selected (GIMP_CURVE_VIEW (c_tool->graph),
|
||||
point);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,9 @@
|
||||
#include "gimpwidgets-utils.h"
|
||||
|
||||
|
||||
#define POINT_MAX_DISTANCE 16.0
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
@ -97,6 +100,12 @@ static void gimp_curve_view_cut_clipboard (GimpCurveView *view);
|
||||
static void gimp_curve_view_copy_clipboard (GimpCurveView *view);
|
||||
static void gimp_curve_view_paste_clipboard (GimpCurveView *view);
|
||||
|
||||
static void gimp_curve_view_curve_dirty (GimpCurve *curve,
|
||||
GimpCurveView *view);
|
||||
static void gimp_curve_view_curve_notify_n_points (GimpCurve *curve,
|
||||
GParamSpec *pspec,
|
||||
GimpCurveView *view);
|
||||
|
||||
static void gimp_curve_view_set_cursor (GimpCurveView *view,
|
||||
gdouble x,
|
||||
gdouble y);
|
||||
@ -211,7 +220,7 @@ static void
|
||||
gimp_curve_view_init (GimpCurveView *view)
|
||||
{
|
||||
view->curve = NULL;
|
||||
view->selected = 0;
|
||||
view->selected = -1;
|
||||
view->offset_x = 0.0;
|
||||
view->offset_y = 0.0;
|
||||
view->last_x = 0.0;
|
||||
@ -418,9 +427,6 @@ gimp_curve_view_draw_point (GimpCurveView *view,
|
||||
|
||||
gimp_curve_get_point (view->curve, i, &x, &y);
|
||||
|
||||
if (x < 0.0)
|
||||
return;
|
||||
|
||||
y = 1.0 - y;
|
||||
|
||||
#define RADIUS 3
|
||||
@ -789,10 +795,9 @@ gimp_curve_view_button_press (GtkWidget *widget,
|
||||
gint width, height;
|
||||
gdouble x;
|
||||
gdouble y;
|
||||
gint point;
|
||||
gdouble point_x;
|
||||
gdouble point_y;
|
||||
gint closest_point;
|
||||
gint i;
|
||||
|
||||
if (! curve || bevent->button != 1)
|
||||
return TRUE;
|
||||
@ -809,8 +814,6 @@ gimp_curve_view_button_press (GtkWidget *widget,
|
||||
x = CLAMP (x, 0.0, 1.0);
|
||||
y = CLAMP (y, 0.0, 1.0);
|
||||
|
||||
closest_point = gimp_curve_get_closest_point (curve, x);
|
||||
|
||||
view->grabbed = TRUE;
|
||||
|
||||
view->orig_curve = GIMP_CURVE (gimp_data_duplicate (GIMP_DATA (curve)));
|
||||
@ -820,47 +823,34 @@ gimp_curve_view_button_press (GtkWidget *widget,
|
||||
switch (gimp_curve_get_curve_type (curve))
|
||||
{
|
||||
case GIMP_CURVE_SMOOTH:
|
||||
/* determine the leftmost and rightmost points */
|
||||
view->leftmost = -1.0;
|
||||
for (i = closest_point - 1; i >= 0; i--)
|
||||
{
|
||||
gimp_curve_get_point (curve, i, &point_x, NULL);
|
||||
point = gimp_curve_get_closest_point (curve, x, 1.0 - y,
|
||||
POINT_MAX_DISTANCE /
|
||||
MAX (width, height));
|
||||
|
||||
if (point_x >= 0.0)
|
||||
{
|
||||
view->leftmost = point_x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
view->rightmost = 2.0;
|
||||
for (i = closest_point + 1; i < curve->n_points; i++)
|
||||
{
|
||||
gimp_curve_get_point (curve, i, &point_x, NULL);
|
||||
|
||||
if (point_x >= 0.0)
|
||||
{
|
||||
view->rightmost = point_x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gimp_curve_view_set_selected (view, closest_point);
|
||||
|
||||
gimp_curve_get_point (curve, view->selected, &point_x, &point_y);
|
||||
|
||||
if (point_x >= 0.0)
|
||||
{
|
||||
view->offset_x = point_x - x;
|
||||
view->offset_y = (1.0 - point_y) - y;
|
||||
}
|
||||
else
|
||||
if (point < 0)
|
||||
{
|
||||
if (bevent->state & gimp_get_constrain_behavior_mask ())
|
||||
y = 1.0 - gimp_curve_map_value (view->orig_curve, x);
|
||||
|
||||
gimp_curve_set_point (curve, view->selected, x, 1.0 - y);
|
||||
point = gimp_curve_add_point (curve, x, 1.0 - y);
|
||||
}
|
||||
|
||||
if (point > 0)
|
||||
gimp_curve_get_point (curve, point - 1, &view->leftmost, NULL);
|
||||
else
|
||||
view->leftmost = -1.0;
|
||||
|
||||
if (point < gimp_curve_get_n_points (curve) - 1)
|
||||
gimp_curve_get_point (curve, point + 1, &view->rightmost, NULL);
|
||||
else
|
||||
view->rightmost = 2.0;
|
||||
|
||||
gimp_curve_view_set_selected (view, point);
|
||||
|
||||
gimp_curve_get_point (curve, point, &point_x, &point_y);
|
||||
|
||||
view->offset_x = point_x - x;
|
||||
view->offset_y = (1.0 - point_y) - y;
|
||||
break;
|
||||
|
||||
case GIMP_CURVE_FREE:
|
||||
@ -912,7 +902,7 @@ gimp_curve_view_motion_notify (GtkWidget *widget,
|
||||
gdouble y;
|
||||
gdouble point_x;
|
||||
gdouble point_y;
|
||||
gint closest_point;
|
||||
gint point;
|
||||
|
||||
if (! curve)
|
||||
return TRUE;
|
||||
@ -932,17 +922,19 @@ gimp_curve_view_motion_notify (GtkWidget *widget,
|
||||
x = CLAMP (x, 0.0, 1.0);
|
||||
y = CLAMP (y, 0.0, 1.0);
|
||||
|
||||
closest_point = gimp_curve_get_closest_point (curve, x);
|
||||
|
||||
switch (gimp_curve_get_curve_type (curve))
|
||||
{
|
||||
case GIMP_CURVE_SMOOTH:
|
||||
if (! view->grabbed) /* If no point is grabbed... */
|
||||
{
|
||||
gimp_curve_get_point (curve, closest_point, &point_x, &point_y);
|
||||
point = gimp_curve_get_closest_point (curve, x, 1.0 - y,
|
||||
POINT_MAX_DISTANCE /
|
||||
MAX (width, height));
|
||||
|
||||
if (point_x >= 0.0)
|
||||
if (point >= 0)
|
||||
{
|
||||
gimp_curve_get_point (curve, point, &point_x, &point_y);
|
||||
|
||||
new_cursor = GDK_FLEUR;
|
||||
|
||||
x = point_x;
|
||||
@ -965,21 +957,28 @@ gimp_curve_view_motion_notify (GtkWidget *widget,
|
||||
|
||||
gimp_data_freeze (GIMP_DATA (curve));
|
||||
|
||||
gimp_curve_set_point (curve, view->selected, -1.0, -1.0);
|
||||
|
||||
if (x > view->leftmost && x < view->rightmost)
|
||||
{
|
||||
gint n_points = gimp_curve_get_n_points (curve);
|
||||
|
||||
closest_point = ROUND (x * (gdouble) (n_points - 1));
|
||||
|
||||
gimp_curve_get_point (curve, closest_point, &point_x, NULL);
|
||||
|
||||
if (point_x < 0.0)
|
||||
gimp_curve_view_set_selected (view, closest_point);
|
||||
|
||||
if (view->selected < 0)
|
||||
{
|
||||
gimp_curve_view_set_selected (
|
||||
view,
|
||||
gimp_curve_add_point (curve, x, 1.0 - y));
|
||||
}
|
||||
else
|
||||
{
|
||||
gimp_curve_set_point (curve, view->selected, x, 1.0 - y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (view->selected >= 0)
|
||||
{
|
||||
gimp_curve_delete_point (curve, view->selected);
|
||||
|
||||
gimp_curve_view_set_selected (view, -1);
|
||||
}
|
||||
}
|
||||
|
||||
gimp_data_thaw (GIMP_DATA (curve));
|
||||
}
|
||||
@ -1071,8 +1070,10 @@ gimp_curve_view_key_press (GtkWidget *widget,
|
||||
GimpCurve *curve = view->curve;
|
||||
gboolean handled = FALSE;
|
||||
|
||||
if (! view->grabbed && curve &&
|
||||
gimp_curve_get_curve_type (curve) == GIMP_CURVE_SMOOTH)
|
||||
if (! view->grabbed &&
|
||||
curve &&
|
||||
gimp_curve_get_curve_type (curve) == GIMP_CURVE_SMOOTH &&
|
||||
view->selected >= 0)
|
||||
{
|
||||
gint i = view->selected;
|
||||
gdouble x, y;
|
||||
@ -1209,6 +1210,21 @@ gimp_curve_view_paste_clipboard (GimpCurveView *view)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_curve_view_curve_dirty (GimpCurve *curve,
|
||||
GimpCurveView *view)
|
||||
{
|
||||
gtk_widget_queue_draw (GTK_WIDGET (view));
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_curve_view_curve_notify_n_points (GimpCurve *curve,
|
||||
GParamSpec *pspec,
|
||||
GimpCurveView *view)
|
||||
{
|
||||
gimp_curve_view_set_selected (view, -1);
|
||||
}
|
||||
|
||||
|
||||
/* public functions */
|
||||
|
||||
@ -1218,13 +1234,6 @@ gimp_curve_view_new (void)
|
||||
return g_object_new (GIMP_TYPE_CURVE_VIEW, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_curve_view_curve_dirty (GimpCurve *curve,
|
||||
GimpCurveView *view)
|
||||
{
|
||||
gtk_widget_queue_draw (GTK_WIDGET (view));
|
||||
}
|
||||
|
||||
void
|
||||
gimp_curve_view_set_curve (GimpCurveView *view,
|
||||
GimpCurve *curve,
|
||||
@ -1241,6 +1250,9 @@ gimp_curve_view_set_curve (GimpCurveView *view,
|
||||
g_signal_handlers_disconnect_by_func (view->curve,
|
||||
gimp_curve_view_curve_dirty,
|
||||
view);
|
||||
g_signal_handlers_disconnect_by_func (view->curve,
|
||||
gimp_curve_view_curve_notify_n_points,
|
||||
view);
|
||||
g_object_unref (view->curve);
|
||||
}
|
||||
|
||||
@ -1252,6 +1264,9 @@ gimp_curve_view_set_curve (GimpCurveView *view,
|
||||
g_signal_connect (view->curve, "dirty",
|
||||
G_CALLBACK (gimp_curve_view_curve_dirty),
|
||||
view);
|
||||
g_signal_connect (view->curve, "notify::n-points",
|
||||
G_CALLBACK (gimp_curve_view_curve_notify_n_points),
|
||||
view);
|
||||
}
|
||||
|
||||
if (view->curve_color)
|
||||
@ -1262,6 +1277,8 @@ gimp_curve_view_set_curve (GimpCurveView *view,
|
||||
else
|
||||
view->curve_color = NULL;
|
||||
|
||||
gimp_curve_view_set_selected (view, -1);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (view));
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user