From 0fce24674ac09e6dbb2298990355cd9ccee8ee00 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 26 Aug 2023 14:05:47 -0400 Subject: [PATCH 1/4] path: Add a circle contour This special contour takes advantage of the circle definition to speed up things like hit testing and closest point determination. --- gsk/gskcontour.c | 539 ++++++++++++++++++++++++++++++++++++++++ gsk/gskcontourprivate.h | 3 + gsk/gskpathbuilder.c | 25 +- 3 files changed, 543 insertions(+), 24 deletions(-) diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c index 03b2b80764..8e054466d4 100644 --- a/gsk/gskcontour.c +++ b/gsk/gskcontour.c @@ -96,6 +96,12 @@ struct _GskContourClass gpointer measure_data); }; +static gsize +gsk_contour_get_size_default (const GskContour *contour) +{ + return contour->klass->struct_size; +} + /* {{{ Utilities */ #define DEG_TO_RAD(x) ((x) * (G_PI / 180.f)) @@ -120,6 +126,59 @@ _g_string_append_point (GString *string, _g_string_append_double (string, pt->y); } +static gboolean +add_segment (GskPathOperation op, + const graphene_point_t *pts, + gsize n_pts, + float weight, + gpointer user_data) +{ + GskPathBuilder *builder = user_data; + + switch (op) + { + case GSK_PATH_MOVE: + gsk_path_builder_move_to (builder, pts[0].x, pts[0].y); + break; + case GSK_PATH_LINE: + 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; + case GSK_PATH_CONIC: + gsk_path_builder_conic_to (builder, + pts[1].x, pts[1].y, + pts[2].x, pts[2].y, + weight); + break; + case GSK_PATH_CLOSE: + gsk_path_builder_close (builder); + break; + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static GskPath * +convert_to_standard_contour (const GskContour *contour) +{ + GskPathBuilder *builder; + + builder = gsk_path_builder_new (); + gsk_contour_foreach (contour, 0.5, add_segment, builder); + return gsk_path_builder_free_to_path (builder); +} /* }}} */ /* {{{ Standard */ @@ -1025,6 +1084,486 @@ gsk_standard_contour_new (GskPathFlags flags, return contour; } +/* }}} */ +/* {{{ Circle */ + +typedef struct _GskCircleContour GskCircleContour; +struct _GskCircleContour +{ + GskContour contour; + + graphene_point_t center; + float radius; + gboolean ccw; +}; + +static void +gsk_circle_contour_copy (const GskContour *contour, + GskContour *dest) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + GskCircleContour *target = (GskCircleContour *) dest; + + *target = *self; +} + +static GskPathFlags +gsk_circle_contour_get_flags (const GskContour *contour) +{ + return GSK_PATH_CLOSED; +} + +static gboolean +foreach_print (GskPathOperation op, + const graphene_point_t *pts, + gsize n_pts, + float weight, + gpointer data) +{ + GString *string = data; + + switch (op) + { + case GSK_PATH_MOVE: + g_string_append (string, "M "); + _g_string_append_point (string, &pts[0]); + break; + + case GSK_PATH_CLOSE: + g_string_append (string, " Z"); + break; + + case GSK_PATH_LINE: + g_string_append (string, " L "); + _g_string_append_point (string, &pts[1]); + break; + + case GSK_PATH_QUAD: + g_string_append (string, " Q "); + _g_string_append_point (string, &pts[1]); + g_string_append (string, ", "); + _g_string_append_point (string, &pts[2]); + break; + + case GSK_PATH_CUBIC: + g_string_append (string, " C "); + _g_string_append_point (string, &pts[1]); + g_string_append (string, ", "); + _g_string_append_point (string, &pts[2]); + g_string_append (string, ", "); + _g_string_append_point (string, &pts[3]); + break; + + case GSK_PATH_CONIC: + g_string_append (string, " O "); + _g_string_append_point (string, &pts[1]); + g_string_append (string, ", "); + _g_string_append_point (string, &pts[2]); + g_string_append (string, ", "); + _g_string_append_double (string, weight); + break; + + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static void +gsk_circle_contour_print (const GskContour *contour, + GString *string) +{ + gsk_contour_foreach (contour, 0.5, foreach_print, string); +} + +static gboolean +gsk_circle_contour_get_bounds (const GskContour *contour, + GskBoundingBox *bounds) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + + gsk_bounding_box_init (bounds, + &GRAPHENE_POINT_INIT (self->center.x - self->radius, + self->center.y - self->radius), + &GRAPHENE_POINT_INIT (self->center.x + self->radius, + self->center.y + self->radius)); + + return TRUE; +} + +static gboolean +gsk_circle_contour_get_stroke_bounds (const GskContour *contour, + const GskStroke *stroke, + GskBoundingBox *bounds) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + + gsk_bounding_box_init (bounds, + &GRAPHENE_POINT_INIT (self->center.x - self->radius - stroke->line_width/2, + self->center.y - self->radius - stroke->line_width/2), + &GRAPHENE_POINT_INIT (self->center.x + self->radius + stroke->line_width/2, + self->center.y + self->radius + stroke->line_width/2)); + + return TRUE; +} + +static void +gsk_circle_contour_get_start_end (const GskContour *contour, + graphene_point_t *start, + graphene_point_t *end) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + + if (start) + *start = GRAPHENE_POINT_INIT (self->center.x + self->radius, self->center.y); + + if (end) + *end = GRAPHENE_POINT_INIT (self->center.x + self->radius, self->center.y); +} + +static gboolean +gsk_circle_contour_foreach (const GskContour *contour, + float tolerance, + GskPathForeachFunc func, + gpointer user_data) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + float rx, ry; + + rx = ry = self->radius; + if (self->ccw) + ry = - self->radius; + + if (!func (GSK_PATH_MOVE, + (const graphene_point_t[1]) { + GRAPHENE_POINT_INIT (self->center.x + rx, self->center.y), + }, + 1, 0.f, user_data)) + return FALSE; + + if (!func (GSK_PATH_CONIC, + (const graphene_point_t[3]) { + GRAPHENE_POINT_INIT (self->center.x + rx, self->center.y), + GRAPHENE_POINT_INIT (self->center.x + rx, self->center.y + ry), + GRAPHENE_POINT_INIT (self->center.x, self->center.y + ry), + }, + 3, M_SQRT1_2, user_data)) + return FALSE; + + if (!func (GSK_PATH_CONIC, + (const graphene_point_t[3]) { + GRAPHENE_POINT_INIT (self->center.x, self->center.y + ry), + GRAPHENE_POINT_INIT (self->center.x - rx, self->center.y + ry), + GRAPHENE_POINT_INIT (self->center.x - rx, self->center.y), + }, + 3, M_SQRT1_2, user_data)) + return FALSE; + + if (!func (GSK_PATH_CONIC, + (const graphene_point_t[3]) { + GRAPHENE_POINT_INIT (self->center.x - rx, self->center.y), + GRAPHENE_POINT_INIT (self->center.x - rx, self->center.y - ry), + GRAPHENE_POINT_INIT (self->center.x, self->center.y - ry), + }, + 3, M_SQRT1_2, user_data)) + return FALSE; + + if (!func (GSK_PATH_CONIC, + (const graphene_point_t[3]) { + GRAPHENE_POINT_INIT (self->center.x, self->center.y - ry), + GRAPHENE_POINT_INIT (self->center.x + rx, self->center.y - ry), + GRAPHENE_POINT_INIT (self->center.x + rx, self->center.y), + }, + 3, M_SQRT1_2, user_data)) + return FALSE; + + if (!func (GSK_PATH_CLOSE, + (const graphene_point_t[2]) { + GRAPHENE_POINT_INIT (self->center.x + rx, self->center.y), + GRAPHENE_POINT_INIT (self->center.x + rx, self->center.y), + }, + 2, 0.f, user_data)) + return FALSE; + + return TRUE; +} + +static GskContour * +gsk_circle_contour_reverse (const GskContour *contour) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + GskCircleContour *copy; + + copy = g_new0 (GskCircleContour, 1); + gsk_circle_contour_copy (contour, (GskContour *)copy); + copy->ccw = !self->ccw; + + return (GskContour *)copy; +} + +static int +gsk_circle_contour_get_winding (const GskContour *contour, + const graphene_point_t *point) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + + if (graphene_point_distance (point, &self->center, NULL, NULL) >= self->radius) + return 0; + + if (self->ccw) + return -1; + else + return 1; +} + +static gsize +gsk_circle_contour_get_n_ops (const GskContour *contour) +{ + /* Not related to how many curves foreach produces. + * GskPath assumes that the start- and endpoints + * of a contour are { x, 1, 0 } and { x, n_ops - 1, 1 }. + * + * The circle contour uses a single 'segment' in path + * points, with a t that ranges from 0 to 1 to cover + * the angles from 0 to 360 (or 360 to 0 in the ccw + * case). + */ + + return 2; +} + +static gboolean +gsk_circle_contour_get_closest_point (const GskContour *contour, + const graphene_point_t *point, + float threshold, + GskRealPathPoint *result, + float *out_dist) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + float dist, angle, t; + + dist = fabsf (graphene_point_distance (&self->center, point, NULL, NULL) - self->radius); + + if (dist > threshold) + return FALSE; + + angle = RAD_TO_DEG (atan2f (point->y - self->center.y, point->x - self->center.x)); + + if (angle < 0) + angle = 360 - angle; + + t = CLAMP (angle / 360, 0, 1); + + if (self->ccw) + t = 1 - t; + + result->idx = 1; + result->t = t; + + return TRUE; +} + +#define GSK_CIRCLE_POINT_INIT(self, angle) \ + GRAPHENE_POINT_INIT ((self)->center.x + cosf (DEG_TO_RAD (angle)) * self->radius, \ + (self)->center.y + sinf (DEG_TO_RAD (angle)) * self->radius) + +static void +gsk_circle_contour_get_position (const GskContour *contour, + GskRealPathPoint *point, + graphene_point_t *position) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + float t; + + t = point->t; + + if (self->ccw) + t = 1 - t; + + if (t == 0 || t == 1) + *position = GRAPHENE_POINT_INIT (self->center.x + self->radius, self->center.y); + else + *position = GSK_CIRCLE_POINT_INIT (self, t * 360); +} + +static void +gsk_circle_contour_get_tangent (const GskContour *contour, + GskRealPathPoint *point, + GskPathDirection direction, + graphene_vec2_t *tangent) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + graphene_point_t p; + + gsk_circle_contour_get_position (contour, point, &p); + + graphene_vec2_init (tangent, p.y - self->center.y, - p.x + self->center.x); + graphene_vec2_normalize (tangent, tangent); +} + +static float +gsk_circle_contour_get_curvature (const GskContour *contour, + GskRealPathPoint *point, + GskPathDirection direction, + graphene_point_t *center) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + + if (center) + *center = self->center; + + return 1 / self->radius; +} + +static void +gsk_circle_contour_add_segment (const GskContour *contour, + GskPathBuilder *builder, + gboolean emit_move_to, + GskRealPathPoint *start, + GskRealPathPoint *end) +{ + GskPath *path; + graphene_point_t p; + GskRealPathPoint start2, end2; + const GskContour *std; + float dist; + + /* This is a cheesy way of doing things: convert to a standard contour, + * and translate the path points from circle to standard. We just have + * to be careful to tell start- and endpoint apart. + */ + + path = convert_to_standard_contour (contour); + std = gsk_path_get_contour (path, 0); + + start2.contour = 0; + + if (start->idx == 1 && start->t == 0) + { + start2.idx = 1; + start2.t = 0; + } + else + { + gsk_circle_contour_get_position (contour, start, &p); + gsk_standard_contour_get_closest_point (std, &p, INFINITY, &start2, &dist); + } + + end2.contour = 0; + + if (end->idx == 1 && end->t == 1) + { + end2.idx = 4; + end2.t = 1; + } + else + { + gsk_circle_contour_get_position (contour, end, &p); + gsk_standard_contour_get_closest_point (std, &p, INFINITY, &end2, &dist); + } + + gsk_standard_contour_add_segment (std, builder, emit_move_to, &start2, &end2); + + gsk_path_unref (path); +} + +static gpointer +gsk_circle_contour_init_measure (const GskContour *contour, + float tolerance, + float *out_length) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + + *out_length = 2 * M_PI * self->radius; + + return NULL; +} + +static void +gsk_circle_contour_free_measure (const GskContour *contour, + gpointer data) +{ +} + +static void +gsk_circle_contour_get_point (const GskContour *contour, + gpointer measure_data, + float distance, + GskRealPathPoint *result) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + float t; + + t = distance / (2 * M_PI * self->radius); + + if (self->ccw) + t = 1 - t; + + result->idx = 1; + result->t = t; +} + +static float +gsk_circle_contour_get_distance (const GskContour *contour, + GskRealPathPoint *point, + gpointer measure_data) +{ + const GskCircleContour *self = (const GskCircleContour *) contour; + float t; + + t = point->t; + + if (self->ccw) + t = 1 - t; + + return 2 * M_PI * self->radius * t; +} + +static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS = +{ + sizeof (GskCircleContour), + "GskCircleContour", + gsk_circle_contour_copy, + gsk_contour_get_size_default, + gsk_circle_contour_get_flags, + gsk_circle_contour_print, + gsk_circle_contour_get_bounds, + gsk_circle_contour_get_stroke_bounds, + gsk_circle_contour_get_start_end, + gsk_circle_contour_foreach, + gsk_circle_contour_reverse, + gsk_circle_contour_get_winding, + gsk_circle_contour_get_n_ops, + gsk_circle_contour_get_closest_point, + gsk_circle_contour_get_position, + gsk_circle_contour_get_tangent, + gsk_circle_contour_get_curvature, + gsk_circle_contour_add_segment, + gsk_circle_contour_init_measure, + gsk_circle_contour_free_measure, + gsk_circle_contour_get_point, + gsk_circle_contour_get_distance, +}; + +GskContour * +gsk_circle_contour_new (const graphene_point_t *center, + float radius) +{ + GskCircleContour *self; + + self = g_new0 (GskCircleContour, 1); + + self->contour.klass = &GSK_CIRCLE_CONTOUR_CLASS; + + self->contour.klass = &GSK_CIRCLE_CONTOUR_CLASS; + self->center = *center; + self->radius = radius; + self->ccw = FALSE; + + return (GskContour *) self; +} + /* }}} */ /* {{{ API */ diff --git a/gsk/gskcontourprivate.h b/gsk/gskcontourprivate.h index 25415a99c6..58e10bc5e0 100644 --- a/gsk/gskcontourprivate.h +++ b/gsk/gskcontourprivate.h @@ -34,6 +34,9 @@ GskContour * gsk_standard_contour_new (GskPathFlags gsize n_ops, gssize offset); +GskContour * gsk_circle_contour_new (const graphene_point_t *center, + float radius); + void gsk_contour_copy (GskContour * dest, const GskContour *src); GskContour * gsk_contour_dup (const GskContour *src); diff --git a/gsk/gskpathbuilder.c b/gsk/gskpathbuilder.c index 5857bdf5d4..3622ba8544 100644 --- a/gsk/gskpathbuilder.c +++ b/gsk/gskpathbuilder.c @@ -560,34 +560,11 @@ gsk_path_builder_add_circle (GskPathBuilder *self, const graphene_point_t *center, float radius) { - graphene_point_t current; - g_return_if_fail (self != NULL); g_return_if_fail (center != NULL); g_return_if_fail (radius > 0); - current = self->current_point; - - gsk_path_builder_move_to (self, center->x + radius, center->y); - // bottom right quarter - gsk_path_builder_arc_to (self, - center->x + radius, center->y + radius, - center->x, center->y + radius); - // bottom left quarter - gsk_path_builder_arc_to (self, - center->x - radius, center->y + radius, - center->x - radius, center->y); - // top left quarter - gsk_path_builder_arc_to (self, - center->x - radius, center->y - radius, - center->x, center->y - radius); - // top right quarter - gsk_path_builder_arc_to (self, - center->x + radius, center->y - radius, - center->x + radius, center->y); - // done - gsk_path_builder_close (self); - self->current_point = current; + gsk_path_builder_add_contour (self, gsk_circle_contour_new (center, radius)); } /** From b8a3d7fa00914c74946a8efef5846c9460cd86fc Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 26 Aug 2023 19:34:50 -0400 Subject: [PATCH 2/4] path: Recognize circles when parsing We can look out for the tell-tale M-O-O-O-O-Z and turn it into a circle contour. --- gsk/gskpath.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 2 deletions(-) diff --git a/gsk/gskpath.c b/gsk/gskpath.c index 52601f8b94..99797be3be 100644 --- a/gsk/gskpath.c +++ b/gsk/gskpath.c @@ -953,6 +953,78 @@ parse_command (const char **p, return FALSE; } +static gboolean +parse_string (const char **p, + const char *s) +{ + int len = strlen (s); + if (strncmp (*p, s, len) != 0) + return FALSE; + (*p) += len; + return TRUE; +} + +static gboolean +parse_circle (const char **p, + double *cx, + double *cy, + double *r) +{ + const char *o = *p; + double x0, y0, x1, y1, x2, y2, x3, y3; + double x4, y4, x5, y5, x6, y6, x7, y7; + double x8, y8, w0, w1, w2, w3; + double xx, yy; + + if (parse_coordinate_pair (p, &x0, &y0) && + parse_string (p, "O") && + parse_coordinate_pair (p, &x1, &y1) && + parse_coordinate_pair (p, &x2, &y2) && + parse_nonnegative_number (p, &w0) && + parse_string (p, "O") && + parse_coordinate_pair (p, &x3, &y3) && + parse_coordinate_pair (p, &x4, &y4) && + parse_nonnegative_number (p, &w1) && + parse_string (p, "O") && + parse_coordinate_pair (p, &x5, &y5) && + parse_coordinate_pair (p, &x6, &y6) && + parse_nonnegative_number (p, &w2) && + parse_string (p, "O") && + parse_coordinate_pair (p, &x7, &y7) && + parse_coordinate_pair (p, &x8, &y8) && + parse_nonnegative_number (p, &w3) && + parse_string (p, "Z")) + { + xx = (x0 + x4) / 2; + yy = (y2 + y6) / 2; + +#define NEAR(x, y) (fabs ((x) - (y)) < 0.001) + + if (NEAR (x0, x1) && NEAR (x0, x8) && NEAR (x0, x7) && + NEAR (x2, x6) && NEAR (x3, x4) && NEAR (x4, x5) && + NEAR (y5, y6) && NEAR (y6, y7) && NEAR (y4, y8) && + NEAR (y8, y0) && NEAR (y3, y2) && NEAR (y2, y1) && + NEAR (x2, xx) && NEAR (yy, y4) && + NEAR (w0, M_SQRT1_2) && NEAR (w1, M_SQRT1_2) && + NEAR (w2, M_SQRT1_2) && NEAR (w3, M_SQRT1_2) && + x1 > x2 && x2 > x3 && y3 > y4 && y4 > y5) + { + *cx = xx; + *cy = yy; + *r = x0 - xx; + + skip_whitespace (p); + + return TRUE; + } + +#undef NEAR + } + + *p = o; + return FALSE; +} + /** * gsk_path_parse: * @string: a string @@ -1037,9 +1109,20 @@ gsk_path_parse (const char *string) case 'M': case 'm': { - double x1, y1; + double x1, y1, r; - if (parse_coordinate_pair (&p, &x1, &y1)) + if (parse_circle (&p, &x1, &y1, &r)) + { + gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (x1, y1), r); + if (_strchr ("zZX", prev_cmd)) + { + path_x = x1 + r; + path_y = y1; + } + x = x1 + r; + y = y1; + } + else if (parse_coordinate_pair (&p, &x1, &y1)) { if (cmd == 'm') { From ead88c36eceb1896eff10f12812145146ed26737 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 26 Aug 2023 20:22:02 -0400 Subject: [PATCH 3/4] path: Add circle tests --- testsuite/gsk/path-special-cases.c | 69 ++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/testsuite/gsk/path-special-cases.c b/testsuite/gsk/path-special-cases.c index 3671503cbe..df4c5714ea 100644 --- a/testsuite/gsk/path-special-cases.c +++ b/testsuite/gsk/path-special-cases.c @@ -813,9 +813,10 @@ static void test_circle (void) { GskPathBuilder *builder; - GskPath *path; - GskPathMeasure *measure; - float length; + GskPath *path, *path1, *path2, *path3, *path4, *path5, *path6; + GskPathMeasure *measure, *measure1, *measure2, *measure3; + float length, length1, length2, length3; + GskPathPoint point0, point1; builder = gsk_path_builder_new (); gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (0, 0), 1); @@ -826,8 +827,70 @@ test_circle (void) g_assert_cmpfloat_with_epsilon (length, 2 * M_PI, 0.001); + gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (1, 1), INFINITY, &point0); + gsk_path_get_closest_point (path, &GRAPHENE_POINT_INIT (-1, 1), INFINITY, &point1); + + builder = gsk_path_builder_new (); + gsk_path_builder_add_segment (builder, path, &point0, &point1); + path1 = gsk_path_builder_free_to_path (builder); + + measure1 = gsk_path_measure_new (path1); + length1 = gsk_path_measure_get_length (measure1); + + g_assert_cmpfloat_with_epsilon (length1, 2 * M_PI * 0.25, 0.001); + + builder = gsk_path_builder_new (); + gsk_path_builder_add_segment (builder, path, &point1, &point0); + path2 = gsk_path_builder_free_to_path (builder); + + measure2 = gsk_path_measure_new (path2); + length2 = gsk_path_measure_get_length (measure2); + + g_assert_cmpfloat_with_epsilon (length2, 2 * M_PI * 0.75, 0.001); + + builder = gsk_path_builder_new (); + gsk_path_builder_add_reverse_path (builder, path); + path3 = gsk_path_builder_free_to_path (builder); + + measure3 = gsk_path_measure_new (path3); + length3 = gsk_path_measure_get_length (measure3); + + g_assert_cmpfloat_with_epsilon (length3, 2 * M_PI, 0.001); + + g_assert_true (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_WINDING)); + g_assert_true (gsk_path_in_fill (path, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_EVEN_ODD)); + g_assert_true (gsk_path_in_fill (path3, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_WINDING)); + g_assert_true (gsk_path_in_fill (path3, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_EVEN_ODD)); + + builder = gsk_path_builder_new (); + gsk_path_builder_add_path (builder, path); + gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (-2, -2, 4, 4)); + path4 = gsk_path_builder_free_to_path (builder); + + g_assert_true (gsk_path_in_fill (path4, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_WINDING)); + g_assert_false (gsk_path_in_fill (path4, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_EVEN_ODD)); + + path5 = gsk_path_parse ("M 2 0 O 2 2 0 2 0.707 O -2 2 -2 0 0.707 O -2 -2 0 -2 0.707 O 2 -2 2 0 0.707 Z"); + builder = gsk_path_builder_new (); + gsk_path_builder_add_path (builder, path); + gsk_path_builder_add_path (builder, path5); + path6 = gsk_path_builder_free_to_path (builder); + + g_assert_true (gsk_path_in_fill (path6, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_WINDING)); + g_assert_false (gsk_path_in_fill (path6, &GRAPHENE_POINT_INIT (0, 0), GSK_FILL_RULE_EVEN_ODD)); + + gsk_path_measure_unref (measure); + gsk_path_measure_unref (measure1); + gsk_path_measure_unref (measure2); + gsk_path_measure_unref (measure3); gsk_path_unref (path); + gsk_path_unref (path1); + gsk_path_unref (path2); + gsk_path_unref (path3); + gsk_path_unref (path4); + gsk_path_unref (path5); + gsk_path_unref (path6); } static void From 167b38dfa145e3953a84a44a6592a36b9f16c1b2 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 26 Aug 2023 23:35:40 -0400 Subject: [PATCH 4/4] contour: Some reactoring Make a default print implementation, and use it. --- gsk/gskcontour.c | 208 ++++++++++++++++------------------------------- 1 file changed, 72 insertions(+), 136 deletions(-) diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c index 8e054466d4..9e4f173cd7 100644 --- a/gsk/gskcontour.c +++ b/gsk/gskcontour.c @@ -96,34 +96,30 @@ struct _GskContourClass gpointer measure_data); }; -static gsize -gsk_contour_get_size_default (const GskContour *contour) -{ - return contour->klass->struct_size; -} - /* {{{ Utilities */ #define DEG_TO_RAD(x) ((x) * (G_PI / 180.f)) #define RAD_TO_DEG(x) ((x) / (G_PI / 180.f)) static void -_g_string_append_double (GString *string, - double d) +_g_string_append_double (GString *string, + const char *prefix, + double d) { char buf[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, d); + g_string_append (string, prefix); g_string_append (string, buf); } static void _g_string_append_point (GString *string, + const char *prefix, const graphene_point_t *pt) { - _g_string_append_double (string, pt->x); - g_string_append_c (string, ' '); - _g_string_append_double (string, pt->y); + _g_string_append_double (string, prefix, pt->x); + _g_string_append_double (string, " ", pt->y); } static gboolean @@ -180,6 +176,69 @@ convert_to_standard_contour (const GskContour *contour) return gsk_path_builder_free_to_path (builder); } +/* }}} */ +/* {{{ Default implementations */ + +static gsize +gsk_contour_get_size_default (const GskContour *contour) +{ + return contour->klass->struct_size; +} + +static gboolean +foreach_print (GskPathOperation op, + const graphene_point_t *pts, + gsize n_pts, + float weight, + gpointer data) +{ + GString *string = data; + + switch (op) + { + case GSK_PATH_MOVE: + _g_string_append_point (string, "M ", &pts[0]); + break; + + case GSK_PATH_CLOSE: + g_string_append (string, " Z"); + break; + + case GSK_PATH_LINE: + _g_string_append_point (string, " L ", &pts[1]); + break; + + case GSK_PATH_QUAD: + _g_string_append_point (string, " Q ", &pts[1]); + _g_string_append_point (string, ", ", &pts[2]); + break; + + case GSK_PATH_CUBIC: + _g_string_append_point (string, " C ", &pts[1]); + _g_string_append_point (string, ", ", &pts[2]); + _g_string_append_point (string, ", ", &pts[3]); + break; + + case GSK_PATH_CONIC: + _g_string_append_point (string, " O ", &pts[1]); + _g_string_append_point (string, ", ", &pts[2]); + _g_string_append_double (string, ", ", weight); + break; + + default: + g_assert_not_reached (); + } + + return TRUE; +} + +static void +gsk_contour_print_default (const GskContour *contour, + GString *string) +{ + gsk_contour_foreach (contour, 0.5, foreach_print, string); +} + /* }}} */ /* {{{ Standard */ @@ -316,65 +375,6 @@ gsk_standard_contour_get_flags (const GskContour *contour) return self->flags; } -static void -gsk_standard_contour_print (const GskContour *contour, - GString *string) -{ - const GskStandardContour *self = (const GskStandardContour *) contour; - gsize i; - - for (i = 0; i < self->n_ops; i ++) - { - const graphene_point_t *pt = gsk_pathop_points (self->ops[i]); - - switch (gsk_pathop_op (self->ops[i])) - { - case GSK_PATH_MOVE: - g_string_append (string, "M "); - _g_string_append_point (string, &pt[0]); - break; - - case GSK_PATH_CLOSE: - g_string_append (string, " Z"); - break; - - case GSK_PATH_LINE: - g_string_append (string, " L "); - _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]); - g_string_append (string, ", "); - _g_string_append_point (string, &pt[2]); - g_string_append (string, ", "); - _g_string_append_point (string, &pt[3]); - break; - - case GSK_PATH_CONIC: - 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; - } - } -} - static gboolean gsk_standard_contour_get_bounds (const GskContour *contour, GskBoundingBox *bounds) @@ -1015,7 +1015,7 @@ static const GskContourClass GSK_STANDARD_CONTOUR_CLASS = gsk_standard_contour_copy, gsk_standard_contour_get_size, gsk_standard_contour_get_flags, - gsk_standard_contour_print, + gsk_contour_print_default, gsk_standard_contour_get_bounds, gsk_standard_contour_get_stroke_bounds, gsk_standard_contour_get_start_end, @@ -1113,70 +1113,6 @@ gsk_circle_contour_get_flags (const GskContour *contour) return GSK_PATH_CLOSED; } -static gboolean -foreach_print (GskPathOperation op, - const graphene_point_t *pts, - gsize n_pts, - float weight, - gpointer data) -{ - GString *string = data; - - switch (op) - { - case GSK_PATH_MOVE: - g_string_append (string, "M "); - _g_string_append_point (string, &pts[0]); - break; - - case GSK_PATH_CLOSE: - g_string_append (string, " Z"); - break; - - case GSK_PATH_LINE: - g_string_append (string, " L "); - _g_string_append_point (string, &pts[1]); - break; - - case GSK_PATH_QUAD: - g_string_append (string, " Q "); - _g_string_append_point (string, &pts[1]); - g_string_append (string, ", "); - _g_string_append_point (string, &pts[2]); - break; - - case GSK_PATH_CUBIC: - g_string_append (string, " C "); - _g_string_append_point (string, &pts[1]); - g_string_append (string, ", "); - _g_string_append_point (string, &pts[2]); - g_string_append (string, ", "); - _g_string_append_point (string, &pts[3]); - break; - - case GSK_PATH_CONIC: - g_string_append (string, " O "); - _g_string_append_point (string, &pts[1]); - g_string_append (string, ", "); - _g_string_append_point (string, &pts[2]); - g_string_append (string, ", "); - _g_string_append_double (string, weight); - break; - - default: - g_assert_not_reached (); - } - - return TRUE; -} - -static void -gsk_circle_contour_print (const GskContour *contour, - GString *string) -{ - gsk_contour_foreach (contour, 0.5, foreach_print, string); -} - static gboolean gsk_circle_contour_get_bounds (const GskContour *contour, GskBoundingBox *bounds) @@ -1527,7 +1463,7 @@ static const GskContourClass GSK_CIRCLE_CONTOUR_CLASS = gsk_circle_contour_copy, gsk_contour_get_size_default, gsk_circle_contour_get_flags, - gsk_circle_contour_print, + gsk_contour_print_default, gsk_circle_contour_get_bounds, gsk_circle_contour_get_stroke_bounds, gsk_circle_contour_get_start_end,