path: Add conic curves
So far this just adds the API, if you use it, you'll get lots of g_warnings(). This will be fixed in future commits.
This commit is contained in:
@@ -103,6 +103,7 @@ static gboolean
|
||||
gtk_path_transform_op (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer data)
|
||||
{
|
||||
GtkPathTransform *transform = data;
|
||||
@@ -135,6 +136,15 @@ gtk_path_transform_op (GskPathOperation op,
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
graphene_point_t res[2];
|
||||
gtk_path_transform_point (transform->measure, &pts[1], transform->scale, &res[0]);
|
||||
gtk_path_transform_point (transform->measure, &pts[2], transform->scale, &res[1]);
|
||||
gsk_path_builder_conic_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y, weight);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CLOSE:
|
||||
gsk_path_builder_close (transform->builder);
|
||||
break;
|
||||
@@ -160,7 +170,7 @@ gtk_path_transform (GskPathMeasure *measure,
|
||||
else
|
||||
transform.scale = 1.0f;
|
||||
|
||||
gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_CURVES, gtk_path_transform_op, &transform);
|
||||
gsk_path_foreach (path, GSK_PATH_FOREACH_ALLOW_CURVE, gtk_path_transform_op, &transform);
|
||||
|
||||
return gsk_path_builder_free_to_path (transform.builder);
|
||||
}
|
||||
|
||||
@@ -312,6 +312,8 @@ gsk_path_builder_line_to
|
||||
gsk_path_builder_rel_line_to
|
||||
gsk_path_builder_curve_to
|
||||
gsk_path_builder_rel_curve_to
|
||||
gsk_path_builder_conic_to
|
||||
gsk_path_builder_rel_conic_to
|
||||
gsk_path_builder_close
|
||||
<SUBSECTION Private>
|
||||
GSK_TYPE_PATH_BUILDER
|
||||
|
||||
@@ -217,11 +217,11 @@ gsk_rect_contour_foreach (const GskContour *contour,
|
||||
GRAPHENE_POINT_INIT (self->x, self->y)
|
||||
};
|
||||
|
||||
return func (GSK_PATH_MOVE, &pts[0], 1, user_data)
|
||||
&& func (GSK_PATH_LINE, &pts[0], 2, user_data)
|
||||
&& func (GSK_PATH_LINE, &pts[1], 2, user_data)
|
||||
&& func (GSK_PATH_LINE, &pts[2], 2, user_data)
|
||||
&& func (GSK_PATH_CLOSE, &pts[3], 2, user_data);
|
||||
return func (GSK_PATH_MOVE, &pts[0], 1, 0, user_data)
|
||||
&& func (GSK_PATH_LINE, &pts[0], 2, 0, user_data)
|
||||
&& func (GSK_PATH_LINE, &pts[1], 2, 0, user_data)
|
||||
&& func (GSK_PATH_LINE, &pts[2], 2, 0, user_data)
|
||||
&& func (GSK_PATH_CLOSE, &pts[3], 2, 0, user_data);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
@@ -601,7 +601,7 @@ gsk_circle_contour_curve (const graphene_point_t curve[4],
|
||||
{
|
||||
ForeachWrapper *wrapper = data;
|
||||
|
||||
return wrapper->func (GSK_PATH_CURVE, curve, 4, wrapper->user_data);
|
||||
return wrapper->func (GSK_PATH_CURVE, curve, 4, 0, wrapper->user_data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -613,7 +613,7 @@ gsk_circle_contour_foreach (const GskContour *contour,
|
||||
const GskCircleContour *self = (const GskCircleContour *) contour;
|
||||
graphene_point_t start = GSK_CIRCLE_POINT_INIT (self, self->start_angle);
|
||||
|
||||
if (!func (GSK_PATH_MOVE, &start, 1, user_data))
|
||||
if (!func (GSK_PATH_MOVE, &start, 1, 0, user_data))
|
||||
return FALSE;
|
||||
|
||||
if (!gsk_spline_decompose_arc (&self->center,
|
||||
@@ -627,7 +627,7 @@ gsk_circle_contour_foreach (const GskContour *contour,
|
||||
|
||||
if (fabs (self->start_angle - self->end_angle) >= 360)
|
||||
{
|
||||
if (!func (GSK_PATH_CLOSE, (graphene_point_t[2]) { start, start }, 2, user_data))
|
||||
if (!func (GSK_PATH_CLOSE, (graphene_point_t[2]) { start, start }, 2, 0, user_data))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -908,8 +908,21 @@ gsk_standard_contour_foreach (const GskContour *contour,
|
||||
|
||||
for (i = 0; i < self->n_ops; i ++)
|
||||
{
|
||||
if (!func (self->ops[i].op, &self->points[self->ops[i].point], n_points[self->ops[i].op], user_data))
|
||||
return FALSE;
|
||||
if (self->ops[i].op == GSK_PATH_CONIC)
|
||||
{
|
||||
graphene_point_t pts[3] = { self->points[self->ops[i].point],
|
||||
self->points[self->ops[i].point + 1],
|
||||
self->points[self->ops[i].point + 3] };
|
||||
float weight = self->points[self->ops[i].point + 2].x;
|
||||
|
||||
if (!func (GSK_PATH_CONIC, pts, 3, weight, user_data))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!func (self->ops[i].op, &self->points[self->ops[i].point], n_points[self->ops[i].op], 0, user_data))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
@@ -959,6 +972,16 @@ gsk_standard_contour_print (const GskContour *contour,
|
||||
_g_string_append_point (string, &pt[3]);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
/* This is not valid SVG */
|
||||
g_string_append (string, " O ");
|
||||
_g_string_append_point (string, &pt[1]);
|
||||
g_string_append (string, ", ");
|
||||
_g_string_append_point (string, &pt[3]);
|
||||
g_string_append (string, ", ");
|
||||
_g_string_append_double (string, pt[2].x);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return;
|
||||
@@ -1111,6 +1134,14 @@ gsk_standard_contour_init_measure (const GskContour *contour,
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
LengthDecompose decomp = { array, { length, length, 0, 0, pt[0], i } };
|
||||
gsk_spline_decompose_conic (pt, tolerance, gsk_standard_contour_measure_add_point, &decomp);
|
||||
length = decomp.measure.start;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return NULL;
|
||||
@@ -1171,6 +1202,10 @@ gsk_standard_contour_measure_get_point (GskStandardContour *self,
|
||||
gsk_spline_get_point_cubic (pts, progress, pos, tangent);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
gsk_spline_get_point_conic (pts, progress, pos, tangent);
|
||||
break;
|
||||
|
||||
case GSK_PATH_MOVE:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
@@ -1404,6 +1439,25 @@ gsk_standard_contour_add_segment (const GskContour *contour,
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
graphene_point_t *pts = &self->points[self->ops[start_measure->op].point];
|
||||
graphene_point_t curve[4], discard[4];
|
||||
|
||||
gsk_spline_split_conic (pts, discard, curve, start_progress);
|
||||
if (end_measure && end_measure->op == start_measure->op)
|
||||
{
|
||||
graphene_point_t tiny[4];
|
||||
gsk_spline_split_conic (curve, tiny, discard, (end_progress - start_progress) / (1 - start_progress));
|
||||
gsk_path_builder_move_to (builder, tiny[0].x, tiny[0].y);
|
||||
gsk_path_builder_conic_to (builder, tiny[1].x, tiny[1].y, tiny[3].x, tiny[3].y, tiny[2].x);
|
||||
return;
|
||||
}
|
||||
gsk_path_builder_move_to (builder, curve[0].x, curve[0].y);
|
||||
gsk_path_builder_conic_to (builder, curve[1].x, curve[1].y, curve[3].x, curve[3].y, curve[2].x);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_MOVE:
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
@@ -1433,6 +1487,10 @@ gsk_standard_contour_add_segment (const GskContour *contour,
|
||||
gsk_path_builder_curve_to (builder, pt[1].x, pt[1].y, pt[2].x, pt[2].y, pt[3].x, pt[3].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
gsk_path_builder_conic_to (builder, pt[1].x, pt[1].y, pt[3].x, pt[3].y, pt[2].x);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return;
|
||||
@@ -1465,6 +1523,16 @@ gsk_standard_contour_add_segment (const GskContour *contour,
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
{
|
||||
graphene_point_t *pts = &self->points[self->ops[end_measure->op].point];
|
||||
graphene_point_t curve[4], discard[4];
|
||||
|
||||
gsk_spline_split_conic (pts, curve, discard, end_progress);
|
||||
gsk_path_builder_conic_to (builder, curve[1].x, curve[1].y, curve[3].x, curve[3].y, curve[2].x);
|
||||
}
|
||||
break;
|
||||
|
||||
case GSK_PATH_MOVE:
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
|
||||
@@ -251,6 +251,9 @@ typedef enum {
|
||||
* @GSK_PATH_CURVE: A curve-to operation describing a cubic Bézier curve
|
||||
* with 4 points describing the start point, the two control points
|
||||
* and the end point of the curve.
|
||||
* @GSK_PATH_CONIC: A weighted quadratic bezier curve with 3 points
|
||||
* describing the start point, control point and end point of the
|
||||
* curve. A weight for the curve will be passed, too.
|
||||
*
|
||||
* Path operations can be used to approximate a #GskPath.
|
||||
*
|
||||
@@ -261,6 +264,7 @@ typedef enum {
|
||||
GSK_PATH_CLOSE,
|
||||
GSK_PATH_LINE,
|
||||
GSK_PATH_CURVE,
|
||||
GSK_PATH_CONIC,
|
||||
} GskPathOperation;
|
||||
|
||||
/**
|
||||
|
||||
@@ -262,6 +262,7 @@ static gboolean
|
||||
gsk_path_to_cairo_add_op (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer cr)
|
||||
{
|
||||
switch (op)
|
||||
@@ -282,6 +283,7 @@ gsk_path_to_cairo_add_op (GskPathOperation op,
|
||||
cairo_curve_to (cr, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
@@ -312,7 +314,7 @@ gsk_path_to_cairo (GskPath *self,
|
||||
g_return_if_fail (cr != NULL);
|
||||
|
||||
gsk_path_foreach_with_tolerance (self,
|
||||
GSK_PATH_FOREACH_ALLOW_CURVES,
|
||||
GSK_PATH_FOREACH_ALLOW_CURVE,
|
||||
cairo_get_tolerance (cr),
|
||||
gsk_path_to_cairo_add_op,
|
||||
cr);
|
||||
@@ -458,6 +460,7 @@ gsk_path_foreach_trampoline_add_point (const graphene_point_t *from,
|
||||
trampoline->retval = trampoline->func (GSK_PATH_LINE,
|
||||
(graphene_point_t[2]) { *from, *to },
|
||||
2,
|
||||
0.0f,
|
||||
trampoline->user_data);
|
||||
}
|
||||
|
||||
@@ -465,6 +468,7 @@ static gboolean
|
||||
gsk_path_foreach_trampoline (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer data)
|
||||
{
|
||||
GskPathForeachTrampoline *trampoline = data;
|
||||
@@ -474,11 +478,11 @@ gsk_path_foreach_trampoline (GskPathOperation op,
|
||||
case GSK_PATH_MOVE:
|
||||
case GSK_PATH_CLOSE:
|
||||
case GSK_PATH_LINE:
|
||||
return trampoline->func (op, pts, n_pts, trampoline->user_data);
|
||||
return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);
|
||||
|
||||
case GSK_PATH_CURVE:
|
||||
if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_CURVES)
|
||||
return trampoline->func (op, pts, n_pts, trampoline->user_data);
|
||||
if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_CURVE)
|
||||
return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);
|
||||
|
||||
gsk_spline_decompose_cubic (pts,
|
||||
trampoline->tolerance,
|
||||
@@ -486,6 +490,17 @@ gsk_path_foreach_trampoline (GskPathOperation op,
|
||||
trampoline);
|
||||
return trampoline->retval;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
if (trampoline->flags & GSK_PATH_FOREACH_ALLOW_CONIC)
|
||||
return trampoline->func (op, pts, n_pts, weight, trampoline->user_data);
|
||||
|
||||
/* XXX: decompose into curves if allowed */
|
||||
gsk_spline_decompose_conic ((graphene_point_t[4]) { pts[0], pts[1], { weight, }, pts[2] },
|
||||
trampoline->tolerance,
|
||||
gsk_path_foreach_trampoline_add_point,
|
||||
trampoline);
|
||||
return trampoline->retval;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
@@ -503,7 +518,7 @@ gsk_path_foreach_with_tolerance (GskPath *self,
|
||||
gsize i;
|
||||
|
||||
/* If we need to massage the data, set up a trampoline here */
|
||||
if (flags != GSK_PATH_FOREACH_ALLOW_CURVES)
|
||||
if (flags != (GSK_PATH_FOREACH_ALLOW_CURVE | GSK_PATH_FOREACH_ALLOW_CONIC))
|
||||
{
|
||||
trampoline = (GskPathForeachTrampoline) { flags, tolerance, func, user_data, TRUE };
|
||||
func = gsk_path_foreach_trampoline;
|
||||
@@ -628,7 +643,7 @@ parse_command (const char **p,
|
||||
if (*cmd == 'X')
|
||||
allowed = "mM";
|
||||
else
|
||||
allowed = "mMhHvVzZlLcCsStTqQaA";
|
||||
allowed = "mMhHvVzZlLcCsStTqQoOaA";
|
||||
|
||||
skip_whitespace (p);
|
||||
s = strchr (allowed, **p);
|
||||
@@ -1057,6 +1072,37 @@ gsk_path_parse (const char *string)
|
||||
}
|
||||
break;
|
||||
|
||||
case 'O':
|
||||
case 'o':
|
||||
{
|
||||
double x1, y1, x2, y2, weight;
|
||||
|
||||
if (parse_coordinate_pair (&p, &x1, &y1) &&
|
||||
parse_coordinate_pair (&p, &x2, &y2) &&
|
||||
parse_nonnegative_number (&p, &weight))
|
||||
{
|
||||
if (cmd == 'c')
|
||||
{
|
||||
x1 += x;
|
||||
y1 += y;
|
||||
x2 += x;
|
||||
y2 += y;
|
||||
}
|
||||
if (strchr ("zZ", prev_cmd))
|
||||
{
|
||||
gsk_path_builder_move_to (builder, x, y);
|
||||
path_x = x;
|
||||
path_y = y;
|
||||
}
|
||||
gsk_path_builder_conic_to (builder, x1, y1, x2, y2, weight);
|
||||
x = x2;
|
||||
y = y2;
|
||||
}
|
||||
else
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
case 'a':
|
||||
{
|
||||
|
||||
@@ -31,7 +31,9 @@ G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GskPathForeachFlags:
|
||||
* @GSK_PATH_FOREACH_ALLOW_CURVES: Allow emission of %GSK_PATH_CURVE
|
||||
* @GSK_PATH_FOREACH_ALLOW_CURVE: Allow emission of %GSK_PATH_CURVE
|
||||
* operations.
|
||||
* @GSK_PATH_FOREACH_ALLOW_CONIC: Allow emission of %GSK_PATH_CONIC
|
||||
* operations.
|
||||
*
|
||||
* Flags that can be passed to gsk_path_foreach() to enable additional
|
||||
@@ -43,7 +45,8 @@ G_BEGIN_DECLS
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
GSK_PATH_FOREACH_ALLOW_CURVES = (1 << 0)
|
||||
GSK_PATH_FOREACH_ALLOW_CURVE = (1 << 0),
|
||||
GSK_PATH_FOREACH_ALLOW_CONIC = (1 << 1)
|
||||
} GskPathForeachFlags;
|
||||
|
||||
/**
|
||||
@@ -51,6 +54,7 @@ typedef enum
|
||||
* @op: The operation to perform
|
||||
* @pts: The points of the operation
|
||||
* @n_pts: The number of points
|
||||
* @weight: The weight for conic curves, or unused if not a conic curve.
|
||||
* @user_data: The user data provided with the function
|
||||
*
|
||||
* Prototype of the callback to iterate throught the operations of
|
||||
@@ -62,6 +66,7 @@ typedef enum
|
||||
typedef gboolean (* GskPathForeachFunc) (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data);
|
||||
|
||||
#define GSK_TYPE_PATH (gsk_path_get_type ())
|
||||
|
||||
@@ -549,6 +549,78 @@ gsk_path_builder_rel_curve_to (GskPathBuilder *builder,
|
||||
builder->current_point.y + y3);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_conic_to:
|
||||
* @builder: a #GskPathBuilder
|
||||
* @x1: x coordinate of control point
|
||||
* @y1: y coordinate of control point
|
||||
* @x2: x coordinate of the end of the curve
|
||||
* @y2: y coordinate of the end of the curve
|
||||
* @weight: weight of the curve
|
||||
*
|
||||
* Adds a [conic curve](https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline)
|
||||
* from the current point to @x2, @y2 with the given
|
||||
* @weight and @x1, @y1 as the single control point.
|
||||
*
|
||||
* Conic curves can be used to draw ellipses and circles.
|
||||
*
|
||||
* After this, @x2, @y2 will be the new current point.
|
||||
**/
|
||||
void
|
||||
gsk_path_builder_conic_to (GskPathBuilder *builder,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float weight)
|
||||
{
|
||||
g_return_if_fail (builder != NULL);
|
||||
g_return_if_fail (weight >= 0);
|
||||
|
||||
builder->flags &= ~GSK_PATH_FLAT;
|
||||
gsk_path_builder_append_current (builder,
|
||||
GSK_PATH_CONIC,
|
||||
3, (graphene_point_t[3]) {
|
||||
GRAPHENE_POINT_INIT (x1, y1),
|
||||
GRAPHENE_POINT_INIT (weight, 0),
|
||||
GRAPHENE_POINT_INIT (x2, y2)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_rel_conic_to:
|
||||
* @builder: a #GskPathBuilder
|
||||
* @x1: x offset of control point
|
||||
* @y1: y offset of control point
|
||||
* @x2: x offset of the end of the curve
|
||||
* @y2: y offset of the end of the curve
|
||||
* @weight: weight of the curve
|
||||
*
|
||||
* Adds a [conic curve](https://en.wikipedia.org/wiki/Non-uniform_rational_B-spline)
|
||||
* from the current point to @x2, @y2 with the given
|
||||
* @weight and @x1, @y1 as the single control point.
|
||||
*
|
||||
* This is the relative version of gsk_path_builder_conic_to().
|
||||
**/
|
||||
void
|
||||
gsk_path_builder_rel_conic_to (GskPathBuilder *builder,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float weight)
|
||||
{
|
||||
g_return_if_fail (builder != NULL);
|
||||
g_return_if_fail (weight >= 0);
|
||||
|
||||
gsk_path_builder_conic_to (builder,
|
||||
builder->current_point.x + x1,
|
||||
builder->current_point.y + y1,
|
||||
builder->current_point.x + x2,
|
||||
builder->current_point.y + y2,
|
||||
weight);
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_builder_close:
|
||||
* @builder: a #GskPathBuilder
|
||||
|
||||
@@ -99,6 +99,20 @@ void gsk_path_builder_rel_curve_to (GskPathBuilder
|
||||
float x3,
|
||||
float y3);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_conic_to (GskPathBuilder *builder,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float weight);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_rel_conic_to (GskPathBuilder *builder,
|
||||
float x1,
|
||||
float y1,
|
||||
float x2,
|
||||
float y2,
|
||||
float weight);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gsk_path_builder_close (GskPathBuilder *builder);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
197
gsk/gskspline.c
197
gsk/gskspline.c
@@ -25,17 +25,18 @@
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#define MIN_PROGRESS (1/1024.f)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
graphene_point_t last_point;
|
||||
float last_progress;
|
||||
float tolerance;
|
||||
GskSplineAddPointFunc func;
|
||||
gpointer user_data;
|
||||
} GskCubicDecomposition;
|
||||
} GskSplineDecompose;
|
||||
|
||||
static void
|
||||
gsk_spline_decompose_add_point (GskCubicDecomposition *decomp,
|
||||
gsk_spline_decompose_add_point (GskSplineDecompose *decomp,
|
||||
const graphene_point_t *pt,
|
||||
float progress)
|
||||
{
|
||||
@@ -47,6 +48,20 @@ gsk_spline_decompose_add_point (GskCubicDecomposition *decomp,
|
||||
decomp->last_progress += progress;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_spline_decompose_finish (GskSplineDecompose *decomp,
|
||||
const graphene_point_t *end_point)
|
||||
{
|
||||
g_assert (graphene_point_equal (&decomp->last_point, end_point));
|
||||
g_assert (decomp->last_progress == 1.0f || decomp->last_progress == 0.0f);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GskSplineDecompose decomp;
|
||||
float tolerance;
|
||||
} GskCubicDecomposition;
|
||||
|
||||
static void
|
||||
gsk_spline_cubic_get_coefficients (graphene_point_t coeffs[4],
|
||||
const graphene_point_t pts[4])
|
||||
@@ -202,22 +217,22 @@ gsk_spline_cubic_too_curvy (const graphene_point_t pts[4],
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_spline_decompose_into (GskCubicDecomposition *decomp,
|
||||
const graphene_point_t pts[4],
|
||||
float progress)
|
||||
gsk_spline_cubic_decompose (GskCubicDecomposition *d,
|
||||
const graphene_point_t pts[4],
|
||||
float progress)
|
||||
{
|
||||
graphene_point_t left[4], right[4];
|
||||
|
||||
if (!gsk_spline_cubic_too_curvy (pts, decomp->tolerance) || progress < 1 / 1024.f)
|
||||
if (!gsk_spline_cubic_too_curvy (pts, d->tolerance) || progress < MIN_PROGRESS)
|
||||
{
|
||||
gsk_spline_decompose_add_point (decomp, &pts[3], progress);
|
||||
gsk_spline_decompose_add_point (&d->decomp, &pts[3], progress);
|
||||
return;
|
||||
}
|
||||
|
||||
gsk_spline_split_cubic (pts, left, right, 0.5);
|
||||
|
||||
gsk_spline_decompose_into (decomp, left, progress / 2);
|
||||
gsk_spline_decompose_into (decomp, right, progress / 2);
|
||||
gsk_spline_cubic_decompose (d, left, progress / 2);
|
||||
gsk_spline_cubic_decompose (d, right, progress / 2);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -226,12 +241,166 @@ gsk_spline_decompose_cubic (const graphene_point_t pts[4],
|
||||
GskSplineAddPointFunc add_point_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskCubicDecomposition decomp = { pts[0], 0.0f, tolerance, add_point_func, user_data };
|
||||
GskCubicDecomposition decomp = { { pts[0], 0.0f, add_point_func, user_data }, tolerance };
|
||||
|
||||
gsk_spline_decompose_into (&decomp, pts, 1.0f);
|
||||
gsk_spline_cubic_decompose (&decomp, pts, 1.0f);
|
||||
|
||||
g_assert (graphene_point_equal (&decomp.last_point, &pts[3]));
|
||||
g_assert (decomp.last_progress == 1.0f || decomp.last_progress == 0.0f);
|
||||
gsk_spline_decompose_finish (&decomp.decomp, &pts[3]);
|
||||
}
|
||||
|
||||
/* CONIC */
|
||||
|
||||
typedef struct {
|
||||
graphene_point_t num[3];
|
||||
graphene_point_t denom[3];
|
||||
} ConicCoefficients;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GskSplineDecompose decomp;
|
||||
float tolerance;
|
||||
ConicCoefficients c;
|
||||
} GskConicDecomposition;
|
||||
|
||||
|
||||
static void
|
||||
gsk_spline_conic_get_coefficents (ConicCoefficients *c,
|
||||
const graphene_point_t pts[4])
|
||||
{
|
||||
float w = pts[2].x;
|
||||
graphene_point_t pw = GRAPHENE_POINT_INIT (w * pts[1].x, w * pts[1].y);
|
||||
|
||||
c->num[2] = pts[0];
|
||||
c->num[1] = GRAPHENE_POINT_INIT (2 * (pw.x - pts[0].x),
|
||||
2 * (pw.y - pts[0].y));
|
||||
c->num[0] = GRAPHENE_POINT_INIT (pts[3].x - 2 * pw.x + pts[0].x,
|
||||
pts[3].y - 2 * pw.y + pts[0].y);
|
||||
|
||||
c->denom[2] = GRAPHENE_POINT_INIT (1, 1);
|
||||
c->denom[1] = GRAPHENE_POINT_INIT (2 * (w - 1), 2 * (w - 1));
|
||||
c->denom[0] = GRAPHENE_POINT_INIT (-c->denom[1].x, -c->denom[1].y);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_spline_eval_quad (const graphene_point_t quad[3],
|
||||
float progress,
|
||||
graphene_point_t *result)
|
||||
{
|
||||
*result = GRAPHENE_POINT_INIT ((quad[0].x * progress + quad[1].x) * progress + quad[2].x,
|
||||
(quad[0].y * progress + quad[1].y) * progress + quad[2].y);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gsk_spline_eval_conic (const ConicCoefficients *c,
|
||||
float progress,
|
||||
graphene_point_t *result)
|
||||
{
|
||||
graphene_point_t num, denom;
|
||||
|
||||
gsk_spline_eval_quad (c->num, progress, &num);
|
||||
gsk_spline_eval_quad (c->denom, progress, &denom);
|
||||
*result = GRAPHENE_POINT_INIT (num.x / denom.x, num.y / denom.y);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_spline_get_point_conic (const graphene_point_t pts[4],
|
||||
float progress,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent)
|
||||
{
|
||||
ConicCoefficients c;
|
||||
|
||||
gsk_spline_conic_get_coefficents (&c, pts);
|
||||
|
||||
if (pos)
|
||||
gsk_spline_eval_conic (&c, progress, pos);
|
||||
|
||||
if (tangent)
|
||||
{
|
||||
graphene_point_t tmp;
|
||||
float w = pts[2].x;
|
||||
|
||||
/* The tangent will be 0 in these corner cases, just
|
||||
* treat it like a line here. */
|
||||
if ((progress <= 0.f && graphene_point_equal (&pts[0], &pts[1])) ||
|
||||
(progress >= 1.f && graphene_point_equal (&pts[1], &pts[3])))
|
||||
{
|
||||
graphene_vec2_init (tangent, pts[3].x - pts[0].x, pts[3].y - pts[0].y);
|
||||
return;
|
||||
}
|
||||
|
||||
gsk_spline_eval_quad ((graphene_point_t[3]) {
|
||||
GRAPHENE_POINT_INIT ((w - 1) * (pts[3].x - pts[0].x),
|
||||
(w - 1) * (pts[3].y - pts[0].y)),
|
||||
GRAPHENE_POINT_INIT (pts[3].x - pts[0].x - 2 * w * (pts[1].x - pts[0].x),
|
||||
pts[3].y - pts[0].y - 2 * w * (pts[1].y - pts[0].y)),
|
||||
GRAPHENE_POINT_INIT (w * (pts[1].x - pts[0].x),
|
||||
w * (pts[1].y - pts[0].y))
|
||||
},
|
||||
progress,
|
||||
&tmp);
|
||||
graphene_vec2_init (tangent, tmp.x, tmp.y);
|
||||
graphene_vec2_normalize (tangent, tangent);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gsk_spline_split_conic (const graphene_point_t pts[4],
|
||||
graphene_point_t result1[4],
|
||||
graphene_point_t result2[4],
|
||||
float progress)
|
||||
{
|
||||
g_warning ("FIXME: Stop treating conics as lines");
|
||||
}
|
||||
|
||||
/* taken from Skia, including the very descriptive name */
|
||||
static gboolean
|
||||
gsk_spline_conic_too_curvy (const graphene_point_t *start,
|
||||
const graphene_point_t *mid,
|
||||
const graphene_point_t *end,
|
||||
float tolerance)
|
||||
{
|
||||
return fabs ((start->x + end->x) * 0.5 - mid->x) > tolerance
|
||||
|| fabs ((start->y + end->y) * 0.5 - mid->y) > tolerance;
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_spline_decompose_conic_subdivide (GskConicDecomposition *d,
|
||||
const graphene_point_t *start,
|
||||
float start_progress,
|
||||
const graphene_point_t *end,
|
||||
float end_progress)
|
||||
{
|
||||
graphene_point_t mid;
|
||||
float mid_progress;
|
||||
|
||||
mid_progress = (start_progress + end_progress) / 2;
|
||||
gsk_spline_eval_conic (&d->c, mid_progress, &mid);
|
||||
|
||||
if (end_progress - start_progress < MIN_PROGRESS ||
|
||||
!gsk_spline_conic_too_curvy (start, &mid, end, d->tolerance))
|
||||
{
|
||||
gsk_spline_decompose_add_point (&d->decomp, end, end_progress - start_progress);
|
||||
return;
|
||||
}
|
||||
|
||||
gsk_spline_decompose_conic_subdivide (d, start, start_progress, &mid, mid_progress);
|
||||
gsk_spline_decompose_conic_subdivide (d, &mid, mid_progress, end, end_progress);
|
||||
}
|
||||
|
||||
void
|
||||
gsk_spline_decompose_conic (const graphene_point_t pts[4],
|
||||
float tolerance,
|
||||
GskSplineAddPointFunc add_point_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskConicDecomposition d = { { pts[0], 0.0f, add_point_func, user_data }, tolerance, };
|
||||
|
||||
gsk_spline_conic_get_coefficents (&d.c, pts);
|
||||
|
||||
gsk_spline_decompose_conic_subdivide (&d, &pts[0], 0.0f, &pts[3], 1.0f);
|
||||
|
||||
gsk_spline_decompose_finish (&d.decomp, &pts[3]);
|
||||
}
|
||||
|
||||
/* Spline deviation from the circle in radius would be given by:
|
||||
|
||||
@@ -44,6 +44,19 @@ void gsk_spline_decompose_cubic (const graphene_
|
||||
GskSplineAddPointFunc add_point_func,
|
||||
gpointer user_data);
|
||||
|
||||
void gsk_spline_get_point_conic (const graphene_point_t pts[4],
|
||||
float progress,
|
||||
graphene_point_t *pos,
|
||||
graphene_vec2_t *tangent);
|
||||
void gsk_spline_split_conic (const graphene_point_t pts[4],
|
||||
graphene_point_t result1[4],
|
||||
graphene_point_t result2[4],
|
||||
float progress);
|
||||
void gsk_spline_decompose_conic (const graphene_point_t pts[4],
|
||||
float tolerance,
|
||||
GskSplineAddPointFunc add_point_func,
|
||||
gpointer user_data);
|
||||
|
||||
typedef gboolean (* GskSplineAddCurveFunc) (const graphene_point_t curve[4],
|
||||
gpointer user_data);
|
||||
gboolean gsk_spline_decompose_arc (const graphene_point_t *center,
|
||||
|
||||
@@ -324,6 +324,16 @@ path_operation_print (const PathOperation *p,
|
||||
_g_string_append_point (string, &p->pts[3]);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
/* This is not valid SVG */
|
||||
g_string_append (string, " O ");
|
||||
_g_string_append_point (string, &p->pts[1]);
|
||||
g_string_append (string, ", ");
|
||||
_g_string_append_point (string, &p->pts[2]);
|
||||
g_string_append (string, ", ");
|
||||
_g_string_append_double (string, p->weight);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
return;
|
||||
@@ -354,6 +364,11 @@ path_operation_equal (const PathOperation *p1,
|
||||
&& graphene_point_near (&p1->pts[2], &p2->pts[2], epsilon)
|
||||
&& graphene_point_near (&p1->pts[3], &p2->pts[3], epsilon);
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
return graphene_point_near (&p1->pts[1], &p2->pts[1], epsilon)
|
||||
&& graphene_point_near (&p1->pts[2], &p2->pts[2], epsilon)
|
||||
&& G_APPROX_VALUE (p1->weight, p2->weight, epsilon);
|
||||
|
||||
default:
|
||||
g_return_val_if_reached (FALSE);
|
||||
}
|
||||
@@ -363,6 +378,7 @@ static gboolean
|
||||
collect_path_operation_cb (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_array_append_vals (user_data,
|
||||
@@ -377,6 +393,7 @@ collect_path_operation_cb (GskPathOperation op,
|
||||
GRAPHENE_POINT_INIT(n_pts > 3 ? pts[3].x : 0,
|
||||
n_pts > 3 ? pts[3].y : 0)
|
||||
},
|
||||
weight
|
||||
},
|
||||
1);
|
||||
return TRUE;
|
||||
@@ -943,6 +960,7 @@ static gboolean
|
||||
rotate_path_cb (GskPathOperation op,
|
||||
const graphene_point_t *pts,
|
||||
gsize n_pts,
|
||||
float weight,
|
||||
gpointer user_data)
|
||||
{
|
||||
GskPathBuilder **builders = user_data;
|
||||
@@ -969,6 +987,11 @@ rotate_path_cb (GskPathOperation op,
|
||||
gsk_path_builder_curve_to (builders[1], pts[1].y, -pts[1].x, pts[2].y, -pts[2].x, pts[3].y, -pts[3].x);
|
||||
break;
|
||||
|
||||
case GSK_PATH_CONIC:
|
||||
gsk_path_builder_conic_to (builders[0], pts[2].x, pts[2].y, pts[3].x, pts[3].y, weight);
|
||||
gsk_path_builder_conic_to (builders[1], pts[2].y, -pts[2].x, pts[3].y, -pts[3].x, weight);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return FALSE;
|
||||
|
||||
Reference in New Issue
Block a user