diff --git a/demos/gtk-demo/path_fill.c b/demos/gtk-demo/path_fill.c index a01631b9da..8ec6aee421 100644 --- a/demos/gtk-demo/path_fill.c +++ b/demos/gtk-demo/path_fill.c @@ -246,6 +246,10 @@ build_path (GskPathOperation op, gsk_path_builder_line_to (builder, pts[1].x, pts[1].y); break; + case GSK_PATH_QUAD: + gsk_path_builder_quad_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y); + break; + case GSK_PATH_CUBIC: gsk_path_builder_cubic_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y); break; diff --git a/demos/gtk-demo/path_text.c b/demos/gtk-demo/path_text.c index 59de2c42b2..f479c5cbf6 100644 --- a/demos/gtk-demo/path_text.c +++ b/demos/gtk-demo/path_text.c @@ -122,6 +122,14 @@ gtk_path_transform_op (GskPathOperation op, } break; + case GSK_PATH_QUAD: + { + graphene_point_t res[2]; + gtk_path_transform_point (transform->measure, &pts[1], &transform->offset, transform->scale, &res[0]); + gtk_path_transform_point (transform->measure, &pts[2], &transform->offset, transform->scale, &res[1]); + gsk_path_builder_quad_to (transform->builder, res[0].x, res[0].y, res[1].x, res[1].y); + } + break; case GSK_PATH_CUBIC: { graphene_point_t res[3]; diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c index 90ce3522d3..90c4f323e2 100644 --- a/gsk/gskcontour.c +++ b/gsk/gskcontour.c @@ -982,6 +982,13 @@ gsk_standard_contour_print (const GskContour *contour, _g_string_append_point (string, &pt[1]); break; + case GSK_PATH_QUAD: + g_string_append (string, " Q "); + _g_string_append_point (string, &pt[1]); + g_string_append (string, ", "); + _g_string_append_point (string, &pt[2]); + break; + case GSK_PATH_CUBIC: g_string_append (string, " C "); _g_string_append_point (string, &pt[1]); diff --git a/gsk/gskcurve.c b/gsk/gskcurve.c index 96bd933f57..4292a8b951 100644 --- a/gsk/gskcurve.c +++ b/gsk/gskcurve.c @@ -218,6 +218,225 @@ static const GskCurveClass GSK_LINE_CURVE_CLASS = { gsk_line_curve_get_start_end_tangent }; +/** QUADRATIC **/ + +static void +gsk_quad_curve_init_from_points (GskQuadCurve *self, + const graphene_point_t pts[3]) +{ + self->op = GSK_PATH_QUAD; + self->has_coefficients = FALSE; + memcpy (self->points, pts, sizeof (graphene_point_t) * 3); +} + +static void +gsk_quad_curve_init (GskCurve *curve, + gskpathop op) +{ + GskQuadCurve *self = &curve->quad; + + gsk_quad_curve_init_from_points (self, gsk_pathop_points (op)); +} + +static void +gsk_quad_curve_init_foreach (GskCurve *curve, + GskPathOperation op, + const graphene_point_t *pts, + gsize n_pts, + float weight) +{ + GskQuadCurve *self = &curve->quad; + + g_assert (n_pts == 3); + + gsk_quad_curve_init_from_points (self, pts); +} + +static void +gsk_quad_curve_ensure_coefficients (const GskQuadCurve *curve) +{ + GskQuadCurve *self = (GskQuadCurve *) curve; + const graphene_point_t *pts = self->points; + + if (self->has_coefficients) + return; + + self->coeffs[2] = pts[0]; + self->coeffs[1] = GRAPHENE_POINT_INIT (2 * (pts[1].x - pts[0].x), + 2 * (pts[1].y - pts[0].y)); + self->coeffs[0] = GRAPHENE_POINT_INIT (pts[2].x - 2 * pts[1].x + pts[0].x, + pts[2].y - 2 * pts[1].y + pts[0].y); + + self->has_coefficients = TRUE; +} + +static void +gsk_quad_curve_get_point (const GskCurve *curve, + float t, + graphene_point_t *pos) +{ + GskQuadCurve *self = (GskQuadCurve *) &curve->quad; + const graphene_point_t *c = self->coeffs; + + gsk_quad_curve_ensure_coefficients (self); + + *pos = GRAPHENE_POINT_INIT ((c[0].x * t + c[1].x) * t + c[2].x, + (c[0].y * t + c[1].y) * t + c[2].y); +} + +static void +gsk_quad_curve_get_tangent (const GskCurve *curve, + float t, + graphene_vec2_t *tangent) +{ + GskQuadCurve *self = (GskQuadCurve *) &curve->quad; + const graphene_point_t *c = self->coeffs; + + gsk_quad_curve_ensure_coefficients (self); + + graphene_vec2_init (tangent, + 2.0f * c[0].x * t + c[1].x, + 2.0f * c[0].y * t + c[1].y); + graphene_vec2_normalize (tangent, tangent); +} + +static void +gsk_quad_curve_split (const GskCurve *curve, + float progress, + GskCurve *start, + GskCurve *end) +{ + GskQuadCurve *self = (GskQuadCurve *) &curve->quad; + const graphene_point_t *pts = self->points; + graphene_point_t ab, bc; + graphene_point_t final; + + graphene_point_interpolate (&pts[0], &pts[1], progress, &ab); + graphene_point_interpolate (&pts[1], &pts[2], progress, &bc); + graphene_point_interpolate (&ab, &bc, progress, &final); + + if (start) + gsk_quad_curve_init_from_points (&start->quad, (graphene_point_t[3]) { pts[0], ab, final }); + if (end) + gsk_quad_curve_init_from_points (&end->quad, (graphene_point_t[3]) { final, bc, pts[2] }); +} + +static void +gsk_quad_curve_segment (const GskCurve *curve, + float start, + float end, + GskCurve *segment) +{ + GskCurve tmp; + + gsk_quad_curve_split (curve, start, NULL, &tmp); + gsk_quad_curve_split (&tmp, (end - start) / (1.0f - start), segment, NULL); +} + +/* taken from Skia, including the very descriptive name */ +static gboolean +gsk_quad_curve_too_curvy (const GskQuadCurve *self, + float tolerance) +{ + const graphene_point_t *pts = self->points; + float dx, dy; + + dx = fabs (pts[1].x / 2 - (pts[0].x + pts[2].x) / 4); + dy = fabs (pts[1].y / 2 - (pts[0].y + pts[2].y) / 4); + + return MAX (dx, dy) > tolerance; +} + +static gboolean +gsk_quad_curve_decompose_step (const GskCurve *curve, + float start_progress, + float end_progress, + float tolerance, + GskCurveAddLineFunc add_line_func, + gpointer user_data) +{ + const GskQuadCurve *self = &curve->quad; + GskCurve left, right; + float mid_progress; + + if (!gsk_quad_curve_too_curvy (self, tolerance)) + return add_line_func (&self->points[0], &self->points[2], start_progress, end_progress, GSK_CURVE_LINE_REASON_STRAIGHT, user_data); + if (end_progress - start_progress <= MIN_PROGRESS) + return add_line_func (&self->points[0], &self->points[2], start_progress, end_progress, GSK_CURVE_LINE_REASON_SHORT, user_data); + + gsk_quad_curve_split ((const GskCurve *) self, 0.5, &left, &right); + mid_progress = (start_progress + end_progress) / 2; + + return gsk_quad_curve_decompose_step (&left, start_progress, mid_progress, tolerance, add_line_func, user_data) + && gsk_quad_curve_decompose_step (&right, mid_progress, end_progress, tolerance, add_line_func, user_data); +} + +static gboolean +gsk_quad_curve_decompose (const GskCurve *curve, + float tolerance, + GskCurveAddLineFunc add_line_func, + gpointer user_data) +{ + return gsk_quad_curve_decompose_step (curve, 0.0, 1.0, tolerance, add_line_func, user_data); +} + +static gskpathop +gsk_quad_curve_pathop (const GskCurve *curve) +{ + const GskQuadCurve *self = &curve->quad; + + return gsk_pathop_encode (self->op, self->points); +} + +static const graphene_point_t * +gsk_quad_curve_get_start_point (const GskCurve *curve) +{ + const GskQuadCurve *self = &curve->quad; + + return &self->points[0]; +} + +static const graphene_point_t * +gsk_quad_curve_get_end_point (const GskCurve *curve) +{ + const GskQuadCurve *self = &curve->quad; + + return &self->points[2]; +} + +static void +gsk_quad_curve_get_start_tangent (const GskCurve *curve, + graphene_vec2_t *tangent) +{ + const GskQuadCurve *self = &curve->quad; + + get_tangent (&self->points[0], &self->points[1], tangent); +} + +static void +gsk_quad_curve_get_end_tangent (const GskCurve *curve, + graphene_vec2_t *tangent) +{ + const GskQuadCurve *self = &curve->quad; + + get_tangent (&self->points[1], &self->points[2], tangent); +} + +static const GskCurveClass GSK_QUAD_CURVE_CLASS = { + gsk_quad_curve_init, + gsk_quad_curve_init_foreach, + gsk_quad_curve_get_point, + gsk_quad_curve_get_tangent, + gsk_quad_curve_split, + gsk_quad_curve_segment, + gsk_quad_curve_decompose, + gsk_quad_curve_pathop, + gsk_quad_curve_get_start_point, + gsk_quad_curve_get_end_point, + gsk_quad_curve_get_start_tangent, + gsk_quad_curve_get_end_tangent +}; + /** CUBIC **/ static void @@ -870,6 +1089,7 @@ get_class (GskPathOperation op) const GskCurveClass *klasses[] = { [GSK_PATH_CLOSE] = &GSK_LINE_CURVE_CLASS, [GSK_PATH_LINE] = &GSK_LINE_CURVE_CLASS, + [GSK_PATH_QUAD] = &GSK_QUAD_CURVE_CLASS, [GSK_PATH_CUBIC] = &GSK_CUBIC_CURVE_CLASS, [GSK_PATH_CONIC] = &GSK_CONIC_CURVE_CLASS, }; diff --git a/gsk/gskcurveprivate.h b/gsk/gskcurveprivate.h index deb43064cf..5810d8c852 100644 --- a/gsk/gskcurveprivate.h +++ b/gsk/gskcurveprivate.h @@ -30,6 +30,7 @@ typedef gpointer gskpathop; typedef union _GskCurve GskCurve; typedef struct _GskLineCurve GskLineCurve; +typedef struct _GskQuadCurve GskQuadCurve; typedef struct _GskCubicCurve GskCubicCurve; typedef struct _GskConicCurve GskConicCurve; @@ -42,6 +43,17 @@ struct _GskLineCurve graphene_point_t points[2]; }; +struct _GskQuadCurve +{ + GskPathOperation op; + + gboolean has_coefficients; + + graphene_point_t points[3]; + + graphene_point_t coeffs[3]; +}; + struct _GskCubicCurve { GskPathOperation op; @@ -72,6 +84,7 @@ union _GskCurve { GskPathOperation op; GskLineCurve line; + GskQuadCurve quad; GskCubicCurve cubic; GskConicCurve conic; }; diff --git a/gsk/gskenums.h b/gsk/gskenums.h index afeee1fbc5..aa82694d39 100644 --- a/gsk/gskenums.h +++ b/gsk/gskenums.h @@ -257,6 +257,9 @@ typedef enum { * @GSK_PATH_CLOSE: A close operation ending the current contour with * a line back to the starting point. Two points describe the start * and end of the line. + * @GSK_PATH_QUAD: A curve-to operation describing a quadratic Bézier curve + * with 3 points describing the start point, the control point + * and the end point of the curve. * @GSK_PATH_CUBIC: 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. @@ -272,6 +275,7 @@ typedef enum { GSK_PATH_MOVE, GSK_PATH_CLOSE, GSK_PATH_LINE, + GSK_PATH_QUAD, GSK_PATH_CUBIC, GSK_PATH_CONIC, } GskPathOperation; diff --git a/gsk/gskpath.c b/gsk/gskpath.c index 2b7ec322fe..8004e9581f 100644 --- a/gsk/gskpath.c +++ b/gsk/gskpath.c @@ -229,6 +229,17 @@ gsk_path_to_cairo_add_op (GskPathOperation op, cairo_line_to (cr, pts[1].x, pts[1].y); break; + case GSK_PATH_QUAD: + { + double x, y; + cairo_get_current_point (cr, &x, &y); + cairo_curve_to (cr, + 1/3.f * x + 2/3.f * pts[1].x, 1/3.f * y + 2/3.f * pts[1].y, + 2/3.f * pts[1].x + 1/3.f * pts[2].x, 2/3.f * pts[1].y + 1/3.f * pts[2].y, + pts[2].x, pts[2].y); + } + break; + case GSK_PATH_CUBIC: cairo_curve_to (cr, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y); break; @@ -426,6 +437,7 @@ gsk_path_foreach_trampoline (GskPathOperation op, case GSK_PATH_LINE: return trampoline->func (op, pts, n_pts, weight, trampoline->user_data); + case GSK_PATH_QUAD: case GSK_PATH_CUBIC: { GskCurve curve; @@ -971,7 +983,7 @@ gsk_path_parse (const char *string) case 'Q': case 'q': { - double x1, y1, x2, y2, xx1, yy1, xx2, yy2; + double x1, y1, x2, y2; if (parse_coordinate_pair (&p, &x1, &y1) && parse_coordinate_pair (&p, &x2, &y2)) @@ -983,17 +995,13 @@ gsk_path_parse (const char *string) x2 += x; y2 += y; } - xx1 = (x + 2.0 * x1) / 3.0; - yy1 = (y + 2.0 * y1) / 3.0; - xx2 = (x2 + 2.0 * x1) / 3.0; - yy2 = (y2 + 2.0 * y1) / 3.0; if (_strchr ("zZ", prev_cmd)) { gsk_path_builder_move_to (builder, x, y); path_x = x; path_y = y; } - gsk_path_builder_cubic_to (builder, xx1, yy1, xx2, yy2, x2, y2); + gsk_path_builder_quad_to (builder, x1, y1, x2, y2); prev_x1 = x1; prev_y1 = y1; x = x2; @@ -1007,7 +1015,7 @@ gsk_path_parse (const char *string) case 'T': case 't': { - double x1, y1, x2, y2, xx1, yy1, xx2, yy2; + double x1, y1, x2, y2; if (parse_coordinate_pair (&p, &x2, &y2)) { @@ -1026,17 +1034,13 @@ gsk_path_parse (const char *string) x1 = x; y1 = y; } - xx1 = (x + 2.0 * x1) / 3.0; - yy1 = (y + 2.0 * y1) / 3.0; - xx2 = (x2 + 2.0 * x1) / 3.0; - yy2 = (y2 + 2.0 * y1) / 3.0; if (_strchr ("zZ", prev_cmd)) { gsk_path_builder_move_to (builder, x, y); path_x = x; path_y = y; } - gsk_path_builder_cubic_to (builder, xx1, yy1, xx2, yy2, x2, y2); + gsk_path_builder_quad_to (builder, x1, y1, x2, y2); prev_x1 = x1; prev_y1 = y1; x = x2; diff --git a/gsk/gskpath.h b/gsk/gskpath.h index 9ca8e479ad..771c721428 100644 --- a/gsk/gskpath.h +++ b/gsk/gskpath.h @@ -31,10 +31,9 @@ G_BEGIN_DECLS /** * GskPathForeachFlags: - * @GSK_PATH_FOREACH_ALLOW_CUBIC: Allow emission of %GSK_PATH_CUBIC - * operations. - * @GSK_PATH_FOREACH_ALLOW_CONIC: Allow emission of %GSK_PATH_CONIC - * operations. + * @GSK_PATH_FOREACH_ALLOW_QUAD: Allow emission of %GSK_PATH_QUAD operations + * @GSK_PATH_FOREACH_ALLOW_CUBIC: Allow emission of %GSK_PATH_CUBIC 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 * features. @@ -45,8 +44,9 @@ G_BEGIN_DECLS */ typedef enum { - GSK_PATH_FOREACH_ALLOW_CUBIC = (1 << 0), - GSK_PATH_FOREACH_ALLOW_CONIC = (1 << 1) + GSK_PATH_FOREACH_ALLOW_QUAD = (1 << 0), + GSK_PATH_FOREACH_ALLOW_CUBIC = (1 << 1), + GSK_PATH_FOREACH_ALLOW_CONIC = (1 << 2) } GskPathForeachFlags; /** diff --git a/gsk/gskpathbuilder.c b/gsk/gskpathbuilder.c index c562258c8a..0f883e950b 100644 --- a/gsk/gskpathbuilder.c +++ b/gsk/gskpathbuilder.c @@ -652,6 +652,67 @@ gsk_path_builder_rel_line_to (GskPathBuilder *self, self->current_point.y + y); } +/** + * gsk_path_builder_quad_to: + * @self: 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 + * + * Adds a [quadratic Bézier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve) + * from the current point to @x2, @y2 with @x1, @y1 as the control point. + * + * After this, @x2, @y2 will be the new current point. + **/ +void +gsk_path_builder_quad_to (GskPathBuilder *self, + float x1, + float y1, + float x2, + float y2) +{ + g_return_if_fail (self != NULL); + + self->flags &= ~GSK_PATH_FLAT; + gsk_path_builder_append_current (self, + GSK_PATH_QUAD, + 2, (graphene_point_t[2]) { + GRAPHENE_POINT_INIT (x1, y1), + GRAPHENE_POINT_INIT (x2, y2) + }); +} + +/** + * gsk_path_builder_rel_quad_to: + * @self: 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 + * + * Adds a [quadratic Bézier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve) + * from the current point to @x2, @y2 with @x1, @y1 as the control point. + * All coordinates are given relative to the current point. + * + * This is the relative version of gsk_path_builder_quad_to(). + **/ +void +gsk_path_builder_rel_quad_to (GskPathBuilder *self, + float x1, + float y1, + float x2, + float y2) +{ + g_return_if_fail (self != NULL); + + gsk_path_builder_quad_to (self, + self->current_point.x + x1, + self->current_point.y + y1, + self->current_point.x + x2, + self->current_point.y + y2); +} + /** * gsk_path_builder_cubic_to: * @self: a #GskPathBuilder diff --git a/gsk/gskpathbuilder.h b/gsk/gskpathbuilder.h index 7439ab5fd6..61bd734fac 100644 --- a/gsk/gskpathbuilder.h +++ b/gsk/gskpathbuilder.h @@ -97,6 +97,18 @@ void gsk_path_builder_rel_line_to (GskPathBuilder float x, float y); GDK_AVAILABLE_IN_ALL +void gsk_path_builder_quad_to (GskPathBuilder *self, + float x1, + float y1, + float x2, + float y2); +GDK_AVAILABLE_IN_ALL +void gsk_path_builder_rel_quad_to (GskPathBuilder *self, + float x1, + float y1, + float x2, + float y2); +GDK_AVAILABLE_IN_ALL void gsk_path_builder_cubic_to (GskPathBuilder *self, float x1, float y1, diff --git a/gsk/gskpathdash.c b/gsk/gskpathdash.c index b4819f9360..27a81a3b71 100644 --- a/gsk/gskpathdash.c +++ b/gsk/gskpathdash.c @@ -215,6 +215,7 @@ gsk_path_dash_foreach (GskPathOperation op, G_GNUC_FALLTHROUGH; case GSK_PATH_LINE: + case GSK_PATH_QUAD: case GSK_PATH_CUBIC: case GSK_PATH_CONIC: gsk_curve_init_foreach (&self->curve, op, pts, n_pts, weight); diff --git a/gsk/gskpathopprivate.h b/gsk/gskpathopprivate.h index 40cd203b45..794053b828 100644 --- a/gsk/gskpathopprivate.h +++ b/gsk/gskpathopprivate.h @@ -89,6 +89,9 @@ gsk_pathop_foreach (gskpathop pop, case GSK_PATH_LINE: return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 2, 0, user_data); + case GSK_PATH_QUAD: + return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 3, 0, user_data); + case GSK_PATH_CUBIC: return func (gsk_pathop_op (pop), gsk_pathop_points (pop), 4, 0, user_data); @@ -124,6 +127,10 @@ gsk_path_builder_pathop_to (GskPathBuilder *builder, gsk_path_builder_line_to (builder, pts[1].x, pts[1].y); break; + case GSK_PATH_QUAD: + gsk_path_builder_quad_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y); + break; + case GSK_PATH_CUBIC: gsk_path_builder_cubic_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y); break; @@ -158,6 +165,10 @@ gsk_path_builder_pathop_reverse_to (GskPathBuilder *builder, gsk_path_builder_line_to (builder, pts[1].x, pts[1].y); break; + case GSK_PATH_QUAD: + gsk_path_builder_quad_to (builder, pts[1].x, pts[1].y, pts[0].x, pts[0].y); + break; + case GSK_PATH_CUBIC: gsk_path_builder_cubic_to (builder, pts[2].x, pts[2].y, pts[1].x, pts[1].y, pts[0].x, pts[0].y); break; diff --git a/testsuite/gsk/curve.c b/testsuite/gsk/curve.c index 03b9b956c7..b8d474324b 100644 --- a/testsuite/gsk/curve.c +++ b/testsuite/gsk/curve.c @@ -32,6 +32,17 @@ init_random_curve (GskCurve *curve) } break; + case GSK_PATH_QUAD: + { + graphene_point_t p[3]; + + init_random_point (&p[0]); + init_random_point (&p[1]); + init_random_point (&p[2]); + gsk_curve_init (curve, gsk_pathop_encode (GSK_PATH_QUAD, p)); + } + break; + case GSK_PATH_CUBIC: { graphene_point_t p[4]; diff --git a/testsuite/gsk/dash.c b/testsuite/gsk/dash.c index 59a524198d..5791e57034 100644 --- a/testsuite/gsk/dash.c +++ b/testsuite/gsk/dash.c @@ -44,6 +44,10 @@ build_path (GskPathOperation op, gsk_path_builder_line_to (builder, pts[1].x, pts[1].y); break; + case GSK_PATH_QUAD: + gsk_path_builder_quad_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y); + break; + case GSK_PATH_CUBIC: gsk_path_builder_cubic_to (builder, pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y); break; diff --git a/testsuite/gsk/path.c b/testsuite/gsk/path.c index f50ce7dace..507549867e 100644 --- a/testsuite/gsk/path.c +++ b/testsuite/gsk/path.c @@ -248,7 +248,7 @@ add_standard_contour (GskPathBuilder *builder) n = g_test_rand_int_range (1, 20); for (i = 0; i < n; i++) { - switch (g_test_rand_int_range (0, 6)) + switch (g_test_rand_int_range (0, 8)) { case 0: gsk_path_builder_line_to (builder, @@ -263,6 +263,22 @@ add_standard_contour (GskPathBuilder *builder) break; case 2: + gsk_path_builder_quad_to (builder, + g_test_rand_double_range (-1000, 1000), + g_test_rand_double_range (-1000, 1000), + g_test_rand_double_range (-1000, 1000), + g_test_rand_double_range (-1000, 1000)); + break; + + case 3: + gsk_path_builder_rel_quad_to (builder, + g_test_rand_double_range (-1000, 1000), + g_test_rand_double_range (-1000, 1000), + g_test_rand_double_range (-1000, 1000), + g_test_rand_double_range (-1000, 1000)); + break; + + case 4: gsk_path_builder_cubic_to (builder, g_test_rand_double_range (-1000, 1000), g_test_rand_double_range (-1000, 1000), @@ -272,7 +288,7 @@ add_standard_contour (GskPathBuilder *builder) g_test_rand_double_range (-1000, 1000)); break; - case 3: + case 5: gsk_path_builder_rel_cubic_to (builder, g_test_rand_double_range (-1000, 1000), g_test_rand_double_range (-1000, 1000), @@ -282,7 +298,7 @@ add_standard_contour (GskPathBuilder *builder) g_test_rand_double_range (-1000, 1000)); break; - case 4: + case 6: gsk_path_builder_conic_to (builder, g_test_rand_double_range (-1000, 1000), g_test_rand_double_range (-1000, 1000), @@ -291,7 +307,7 @@ add_standard_contour (GskPathBuilder *builder) random_weight ()); break; - case 5: + case 7: gsk_path_builder_rel_conic_to (builder, g_test_rand_double_range (-1000, 1000), g_test_rand_double_range (-1000, 1000), @@ -381,6 +397,13 @@ path_operation_print (const PathOperation *p, _g_string_append_point (string, &p->pts[1]); break; + case GSK_PATH_QUAD: + g_string_append (string, " Q "); + _g_string_append_point (string, &p->pts[1]); + g_string_append (string, ", "); + _g_string_append_point (string, &p->pts[2]); + break; + case GSK_PATH_CUBIC: g_string_append (string, " C "); _g_string_append_point (string, &p->pts[1]); @@ -425,6 +448,10 @@ path_operation_equal (const PathOperation *p1, case GSK_PATH_CLOSE: return graphene_point_near (&p1->pts[1], &p2->pts[1], epsilon); + case GSK_PATH_QUAD: + return graphene_point_near (&p1->pts[1], &p2->pts[1], epsilon) + && graphene_point_near (&p1->pts[2], &p2->pts[2], epsilon); + case GSK_PATH_CUBIC: return graphene_point_near (&p1->pts[1], &p2->pts[1], epsilon) && graphene_point_near (&p1->pts[2], &p2->pts[2], epsilon) @@ -1063,6 +1090,11 @@ rotate_path_cb (GskPathOperation op, gsk_path_builder_line_to (builders[1], pts[1].y, -pts[1].x); break; + case GSK_PATH_QUAD: + gsk_path_builder_quad_to (builders[0], pts[1].x, pts[1].y, pts[2].x, pts[2].y); + gsk_path_builder_quad_to (builders[1], pts[1].y, -pts[1].x, pts[2].y, -pts[2].x); + break; + case GSK_PATH_CUBIC: gsk_path_builder_cubic_to (builders[0], pts[1].x, pts[1].y, pts[2].x, pts[2].y, pts[3].x, pts[3].y); gsk_path_builder_cubic_to (builders[1], pts[1].y, -pts[1].x, pts[2].y, -pts[2].x, pts[3].y, -pts[3].x);