From 0a28a5d53a8db84f1c166ed8a4e6f06e1bfc809f Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sun, 27 Aug 2023 11:17:24 -0400 Subject: [PATCH] Add a rectangle contour Add a contour that optimizes some things for rectangles. Also add rectangle detection to the path parser, and add tests similar to what we have for the other special contours. --- gsk/gskcontour.c | 538 +++++++++++++++++++++++++++++ gsk/gskcontourprivate.h | 1 + gsk/gskpath.c | 70 +++- gsk/gskpathbuilder.c | 14 +- testsuite/gsk/path-private.c | 83 ++++- testsuite/gsk/path-special-cases.c | 47 ++- 6 files changed, 712 insertions(+), 41 deletions(-) diff --git a/gsk/gskcontour.c b/gsk/gskcontour.c index 1210294d56..4268bac76d 100644 --- a/gsk/gskcontour.c +++ b/gsk/gskcontour.c @@ -1457,6 +1457,544 @@ gsk_circle_contour_new (const graphene_point_t *center, return (GskContour *) self; } +/* }}} */ +/* {{{ Rectangle */ + +typedef struct _GskRectContour GskRectContour; +struct _GskRectContour +{ + GskContour contour; + + float x; + float y; + float width; + float height; + float length; +}; + +static void +gsk_rect_contour_copy (const GskContour *contour, + GskContour *dest) +{ + const GskRectContour *self = (const GskRectContour *) contour; + GskRectContour *target = (GskRectContour *) dest; + + *target = *self; +} + +static GskPathFlags +gsk_rect_contour_get_flags (const GskContour *contour) +{ + return GSK_PATH_FLAT | GSK_PATH_CLOSED; +} + +static void +gsk_rect_contour_print (const GskContour *contour, + GString *string) +{ + const GskRectContour *self = (const GskRectContour *) contour; + + _g_string_append_point (string, "M ", &GRAPHENE_POINT_INIT (self->x, self->y)); + _g_string_append_double (string, " h ", self->width); + _g_string_append_double (string, " v ", self->height); + _g_string_append_double (string, " h ", - self->width); + g_string_append (string, " z"); +} + +static gboolean +gsk_rect_contour_get_bounds (const GskContour *contour, + GskBoundingBox *bounds) +{ + const GskRectContour *self = (const GskRectContour *) contour; + + gsk_bounding_box_init (bounds, + &GRAPHENE_POINT_INIT (self->x, self->y), + &GRAPHENE_POINT_INIT (self->x + self->width, self->y + self->height)); + + return TRUE; +} + +static gboolean +gsk_rect_contour_get_stroke_bounds (const GskContour *contour, + const GskStroke *stroke, + GskBoundingBox *bounds) +{ + const GskRectContour *self = (const GskRectContour *) contour; + graphene_rect_t rect; + + graphene_rect_init (&rect, self->x, self->y, self->width, self->height); + graphene_rect_inset (&rect, - stroke->line_width / 2, - stroke->line_width / 2); + gsk_bounding_box_init_from_rect (bounds, &rect); + + return TRUE; +} + +static gboolean +gsk_rect_contour_foreach (const GskContour *contour, + float tolerance, + GskPathForeachFunc func, + gpointer user_data) +{ + const GskRectContour *self = (const GskRectContour *) contour; + graphene_point_t pts[] = { + GRAPHENE_POINT_INIT (self->x, self->y), + GRAPHENE_POINT_INIT (self->x + self->width, self->y), + GRAPHENE_POINT_INIT (self->x + self->width, self->y + self->height), + GRAPHENE_POINT_INIT (self->x, self->y + self->height), + GRAPHENE_POINT_INIT (self->x, self->y) + }; + + return func (GSK_PATH_MOVE, &pts[0], 1, 0.f, user_data) && + func (GSK_PATH_LINE, &pts[0], 2, 0.f, user_data) && + func (GSK_PATH_LINE, &pts[1], 2, 0.f, user_data) && + func (GSK_PATH_LINE, &pts[2], 2, 0.f, user_data) && + func (GSK_PATH_CLOSE, &pts[3], 2, 0.f, user_data); +} + +static GskContour * +gsk_rect_contour_reverse (const GskContour *contour) +{ + const GskRectContour *self = (const GskRectContour *) contour; + + return gsk_rect_contour_new (&GRAPHENE_RECT_INIT (self->x + self->width, + self->y, + - self->width, + self->height)); +} + +static int +gsk_rect_contour_get_winding (const GskContour *contour, + const graphene_point_t *point) +{ + const GskRectContour *self = (const GskRectContour *) contour; + graphene_rect_t rect; + + graphene_rect_init (&rect, self->x, self->y, self->width, self->height); + + if (graphene_rect_contains_point (&rect, point)) + { + if ((self->width < 0) != (self->height < 0)) + return -1; + else + return 1; + } + + return 0; +} + +static gsize +gsk_rect_contour_get_n_ops (const GskContour *contour) +{ + return 2; +} + +static gboolean +gsk_rect_contour_closest_point (const GskRectContour *self, + const graphene_point_t *point, + float threshold, + float *out_distance, + float *out_offset) +{ + graphene_point_t t, p; + float distance; + + /* offset coords to be relative to rectangle */ + t.x = point->x - self->x; + t.y = point->y - self->y; + + if (self->width) + { + /* do unit square math */ + t.x /= self->width; + /* move point onto the square */ + t.x = CLAMP (t.x, 0.f, 1.f); + } + else + t.x = 0.f; + + if (self->height) + { + t.y /= self->height; + t.y = CLAMP (t.y, 0.f, 1.f); + } + else + t.y = 0.f; + + if (t.x > 0 && t.x < 1 && t.y > 0 && t.y < 1) + { + float diff = MIN (t.x, 1.f - t.x) * fabsf (self->width) - MIN (t.y, 1.f - t.y) * fabsf (self->height); + + if (diff < 0.f) + t.x = ceilf (t.x - 0.5f); /* round 0.5 down */ + else if (diff > 0.f) + t.y = roundf (t.y); /* round 0.5 up */ + else + { + /* at least 2 points match, return the first one in the stroke */ + if (t.y <= 1.f - t.y) + t.y = 0.f; + else if (1.f - t.x <= t.x) + t.x = 1.f; + else + t.y = 1.f; + } + } + + /* Don't let -0 confuse us */ + t.x = fabsf (t.x); + t.y = fabsf (t.y); + + p = GRAPHENE_POINT_INIT (self->x + t.x * self->width, + self->y + t.y * self->height); + + distance = graphene_point_distance (point, &p, NULL, NULL); + if (distance > threshold) + return FALSE; + + *out_distance = distance; + + if (t.y == 0) + *out_offset = t.x * fabsf (self->width); + else if (t.y == 1) + *out_offset = (2 - t.x) * fabsf (self->width) + fabsf (self->height); + else if (t.x == 1) + *out_offset = fabsf (self->width) + t.y * fabsf (self->height); + else + *out_offset = 2 * fabsf (self->width) + (2 - t.y) * fabsf (self->height); + + return TRUE; +} + +static gboolean +gsk_rect_contour_get_closest_point (const GskContour *contour, + const graphene_point_t *point, + float threshold, + GskRealPathPoint *result, + float *out_dist) +{ + const GskRectContour *self = (const GskRectContour *) contour; + float distance; + + if (gsk_rect_contour_closest_point (self, point, threshold, out_dist, &distance)) + { + result->idx = 1; + result->t = distance / self->length; + + return TRUE; + } + + return FALSE; +} + +static void +set_tangent (float xs, + float ys, + float xe, + float ye, + GskPathDirection direction, + graphene_vec2_t *tangent) +{ + if (direction == GSK_PATH_TO_START || direction == GSK_PATH_FROM_START) + graphene_vec2_init (tangent, xs, ys); + else + graphene_vec2_init (tangent, xe, ye); + + if (direction == GSK_PATH_TO_START || direction == GSK_PATH_FROM_END) + graphene_vec2_negate (tangent, tangent); +} + +static void +gsk_rect_contour_pos_tangent (const GskRectContour *self, + float distance, + GskPathDirection direction, + graphene_point_t *pos, + graphene_vec2_t *tangent) +{ + if (distance == 0) + { + if (pos) + *pos = GRAPHENE_POINT_INIT (self->x, self->y); + + if (tangent) + set_tangent (0.f, - copysignf (1.f, self->height), + copysignf (1.f, self->width), 0.f, + direction, tangent); + return; + } + + if (distance < fabsf (self->width)) + { + if (pos) + *pos = GRAPHENE_POINT_INIT (self->x + copysignf (distance, self->width), self->y); + if (tangent) + set_tangent (copysignf (1.f, self->width), 0.f, + copysignf (1.f, self->width), 0.f, + direction, tangent); + return; + } + distance -= fabsf (self->width); + + if (distance == 0) + { + if (pos) + *pos = GRAPHENE_POINT_INIT (self->x + self->width, self->y); + + if (tangent) + set_tangent (copysignf (1.f, self->width), 0.f, + 0.f, copysignf (1.f, self->height), + direction, tangent); + return; + } + + if (distance < fabsf (self->height)) + { + if (pos) + *pos = GRAPHENE_POINT_INIT (self->x + self->width, self->y + copysignf (distance, self->height)); + if (tangent) + set_tangent (0.f, copysignf (1.f, self->height), + 0.f, copysignf (1.f, self->height), + direction, tangent); + return; + } + distance -= fabs (self->height); + + if (distance == 0) + { + if (pos) + *pos = GRAPHENE_POINT_INIT (self->x + self->width, self->y + self->height); + + if (tangent) + set_tangent (0.f, copysignf (1.f, self->height), + - copysignf (1.f, self->width), 0.f, + direction, tangent); + return; + } + + if (distance < fabsf (self->width)) + { + if (pos) + *pos = GRAPHENE_POINT_INIT (self->x + self->width - copysignf (distance, self->width), self->y + self->height); + if (tangent) + set_tangent (- copysignf (1.f, self->width), 0.f, + - copysignf (1.f, self->width), 0.f, + direction, tangent); + return; + } + distance -= fabsf (self->width); + + if (distance == 0) + { + if (pos) + *pos = GRAPHENE_POINT_INIT (self->x, self->y + self->height); + + if (tangent) + set_tangent (- copysignf (1.f, self->width), 0.f, + 0.f, - copysignf (1.f, self->height), + direction, tangent); + return; + } + + if (distance < fabsf (self->height)) + { + if (pos) + *pos = GRAPHENE_POINT_INIT (self->x, self->y + self->height - copysignf (distance, self->height)); + if (tangent) + set_tangent (0.f, - copysignf (1.f, self->height), + 0.f, - copysignf (1.f, self->height), + direction, tangent); + return; + } + + if (pos) + *pos = GRAPHENE_POINT_INIT (self->x, self->y); + + if (tangent) + set_tangent (0.f, - copysignf (1.f, self->height), + copysignf (1.f, self->width), 0.f, + direction, tangent); +} + +static void +gsk_rect_contour_get_position (const GskContour *contour, + GskRealPathPoint *point, + graphene_point_t *position) +{ + const GskRectContour *self = (const GskRectContour *) contour; + + gsk_rect_contour_pos_tangent (self, point->t * self->length, GSK_PATH_TO_END, position, NULL); +} + +static void +gsk_rect_contour_get_tangent (const GskContour *contour, + GskRealPathPoint *point, + GskPathDirection direction, + graphene_vec2_t *tangent) +{ + const GskRectContour *self = (const GskRectContour *) contour; + + gsk_rect_contour_pos_tangent (self, point->t * self->length, direction, NULL, tangent); +} + +static float +gsk_rect_contour_get_curvature (const GskContour *contour, + GskRealPathPoint *point, + GskPathDirection direction, + graphene_point_t *center) +{ + return 0; +} + +static void +gsk_rect_contour_add_segment (const GskContour *contour, + GskPathBuilder *builder, + gboolean emit_move_to, + GskRealPathPoint *start_point, + GskRealPathPoint *end_point) +{ + const GskRectContour *self = (const GskRectContour *) contour; + float w = fabsf (self->width); + float h = fabsf (self->height); + float start = start_point->t * self->length; + float end = end_point->t * self->length; + + if (start < w) + { + if (emit_move_to) + gsk_path_builder_move_to (builder, self->x + start * (w / self->width), self->y); + if (end <= w) + { + gsk_path_builder_line_to (builder, self->x + end * (w / self->width), self->y); + return; + } + gsk_path_builder_line_to (builder, self->x + self->width, self->y); + } + start -= w; + end -= w; + + if (start < h) + { + if (start >= 0 && emit_move_to) + gsk_path_builder_move_to (builder, self->x + self->width, self->y + start * (h / self->height)); + if (end <= h) + { + gsk_path_builder_line_to (builder, self->x + self->width, self->y + end * (h / self->height)); + return; + } + gsk_path_builder_line_to (builder, self->x + self->width, self->y + self->height); + } + start -= h; + end -= h; + + if (start < w) + { + if (start >= 0 && emit_move_to) + gsk_path_builder_move_to (builder, self->x + (w - start) * (w / self->width), self->y + self->height); + if (end <= w) + { + gsk_path_builder_line_to (builder, self->x + (w - end) * (w / self->width), self->y + self->height); + return; + } + gsk_path_builder_line_to (builder, self->x, self->y + self->height); + } + start -= w; + end -= w; + + if (start < h) + { + if (start >= 0 && emit_move_to) + gsk_path_builder_move_to (builder, self->x, self->y + (h - start) * (h / self->height)); + if (end <= h) + { + gsk_path_builder_line_to (builder, self->x, self->y + (h - end) * (h / self->height)); + return; + } + gsk_path_builder_line_to (builder, self->x, self->y); + } +} + +static gpointer +gsk_rect_contour_init_measure (const GskContour *contour, + float tolerance, + float *out_length) +{ + const GskRectContour *self = (const GskRectContour *) contour; + + *out_length = self->length; + + return NULL; +} + +static void +gsk_rect_contour_free_measure (const GskContour *contour, + gpointer data) +{ +} + +static void +gsk_rect_contour_get_point (const GskContour *contour, + gpointer measure_data, + float distance, + GskRealPathPoint *result) +{ + const GskRectContour *self = (const GskRectContour *) contour; + + result->idx = 1; + result->t = CLAMP (distance / self->length, 0, 1); +} + +static float +gsk_rect_contour_get_distance (const GskContour *contour, + GskRealPathPoint *point, + gpointer measure_data) +{ + const GskRectContour *self = (const GskRectContour *) contour; + + return point->t * self->length; +} + +static const GskContourClass GSK_RECT_CONTOUR_CLASS = +{ + sizeof (GskRectContour), + "GskRectContour", + gsk_rect_contour_copy, + gsk_contour_get_size_default, + gsk_rect_contour_get_flags, + gsk_rect_contour_print, + gsk_rect_contour_get_bounds, + gsk_rect_contour_get_stroke_bounds, + gsk_rect_contour_foreach, + gsk_rect_contour_reverse, + gsk_rect_contour_get_winding, + gsk_rect_contour_get_n_ops, + gsk_rect_contour_get_closest_point, + gsk_rect_contour_get_position, + gsk_rect_contour_get_tangent, + gsk_rect_contour_get_curvature, + gsk_rect_contour_add_segment, + gsk_rect_contour_init_measure, + gsk_rect_contour_free_measure, + gsk_rect_contour_get_point, + gsk_rect_contour_get_distance, +}; + +GskContour * +gsk_rect_contour_new (const graphene_rect_t *rect) +{ + GskRectContour *self; + + self = g_new0 (GskRectContour, 1); + + self->contour.klass = &GSK_RECT_CONTOUR_CLASS; + + self->x = rect->origin.x; + self->y = rect->origin.y; + self->width = rect->size.width; + self->height = rect->size.height; + self->length = 2 * (fabsf (self->width) + fabsf (self->height)); + + return (GskContour *) self; +} + /* }}} */ /* {{{ Rounded Rectangle */ diff --git a/gsk/gskcontourprivate.h b/gsk/gskcontourprivate.h index 73ffef8c5d..19da13688f 100644 --- a/gsk/gskcontourprivate.h +++ b/gsk/gskcontourprivate.h @@ -36,6 +36,7 @@ GskContour * gsk_standard_contour_new (GskPathFlags GskContour * gsk_circle_contour_new (const graphene_point_t *center, float radius); +GskContour * gsk_rect_contour_new (const graphene_rect_t *rect); GskContour * gsk_rounded_rect_contour_new (const GskRoundedRect *rounded_rect); const char * gsk_contour_get_type_name (const GskContour *self); diff --git a/gsk/gskpath.c b/gsk/gskpath.c index 20fd595b5c..607adb766e 100644 --- a/gsk/gskpath.c +++ b/gsk/gskpath.c @@ -995,6 +995,35 @@ is_line (double x0, double y0, NEAR (x0, x1) && NEAR (x0, x2) && NEAR (x0, x3); } +static gboolean +parse_rectangle (const char **p, + double *x, + double *y, + double *w, + double *h) +{ + const char *o = *p; + double w2; + + if (parse_coordinate_pair (p, x, y) && + parse_string (p, "h") && + parse_coordinate (p, w) && + parse_string (p, "v") && + parse_coordinate (p, h) && + parse_string (p, "h") && + parse_coordinate (p, &w2) && + parse_string (p, "z") && + NEAR (w2, -*w)) + { + skip_whitespace (p); + + return TRUE; + } + + *p = o; + return FALSE; +} + static gboolean parse_circle (const char **p, double *cx, @@ -1101,6 +1130,9 @@ parse_rounded_rect (const char **p, rr->corner[GSK_CORNER_TOP_RIGHT] = GRAPHENE_SIZE_INIT (x2 - x1, y3 - y2); rr->corner[GSK_CORNER_BOTTOM_RIGHT] = GRAPHENE_SIZE_INIT (x5 - x6, y5 - y4); rr->corner[GSK_CORNER_BOTTOM_LEFT] = GRAPHENE_SIZE_INIT (x7 - x8, y8 - y7); + + skip_whitespace (p); + return TRUE; } } @@ -1195,35 +1227,47 @@ gsk_path_parse (const char *string) case 'M': case 'm': { - double x1, y1, r; + double x1, y1, w, h, r; GskRoundedRect rr; /* Look for special contours */ - if (parse_circle (&p, &x1, &y1, &r)) + if (parse_rectangle (&p, &x1, &y1, &w, &h)) + { + gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (x1, y1, w, h)); + if (_strchr ("zZX", prev_cmd)) + { + path_x = x1; + path_y = y1; + } + + x = x1; + y = y1; + } + else if (parse_circle (&p, &x1, &y1, &r)) { gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (x1, y1), r); - x = x1 + r; - y = y1; - if (_strchr ("zZX", prev_cmd)) { - path_x = x; - path_y = y; + path_x = x1 + r; + path_y = y1; } + + x = x1 + r; + y = y1; } else if (parse_rounded_rect (&p, &rr)) { gsk_path_builder_add_rounded_rect (builder, &rr); - x = rr.bounds.origin.x + rr.corner[GSK_CORNER_TOP_LEFT].width; - y = rr.bounds.origin.y; - if (_strchr ("zZX", prev_cmd)) { - path_x = x; - path_y = y; + path_x = rr.bounds.origin.x + rr.corner[GSK_CORNER_TOP_LEFT].width; + path_y = rr.bounds.origin.y; } + + x = rr.bounds.origin.x + rr.corner[GSK_CORNER_TOP_LEFT].width; + y = rr.bounds.origin.y; } else if (parse_coordinate_pair (&p, &x1, &y1)) { @@ -1232,6 +1276,7 @@ gsk_path_parse (const char *string) x1 += x; y1 += y; } + if (repeat) gsk_path_builder_line_to (builder, x1, y1); else @@ -1243,6 +1288,7 @@ gsk_path_parse (const char *string) path_y = y1; } } + x = x1; y = y1; } diff --git a/gsk/gskpathbuilder.c b/gsk/gskpathbuilder.c index efdac61463..b612d4ebf8 100644 --- a/gsk/gskpathbuilder.c +++ b/gsk/gskpathbuilder.c @@ -460,20 +460,10 @@ void gsk_path_builder_add_rect (GskPathBuilder *self, const graphene_rect_t *rect) { - graphene_point_t current; - g_return_if_fail (self != NULL); + g_return_if_fail (rect != NULL); - current = self->current_point; - - gsk_path_builder_move_to (self, rect->origin.x, rect->origin.y); - - gsk_path_builder_rel_line_to (self, rect->size.width, 0); - gsk_path_builder_rel_line_to (self, 0, rect->size.height); - gsk_path_builder_rel_line_to (self, - rect->size.width, 0); - - gsk_path_builder_close (self); - self->current_point = current; + gsk_path_builder_add_contour (self, gsk_rect_contour_new (rect)); } /** diff --git a/testsuite/gsk/path-private.c b/testsuite/gsk/path-private.c index 1c040d90e2..21d2a3ccfa 100644 --- a/testsuite/gsk/path-private.c +++ b/testsuite/gsk/path-private.c @@ -116,18 +116,14 @@ test_circle_winding (void) path1 = convert_to_standard_contour (path); contour1 = gsk_path_get_contour (path1, 0); - g_assert_true (gsk_contour_get_winding (contour, &GRAPHENE_POINT_INIT (100, 100)) - == - gsk_contour_get_winding (contour1, &GRAPHENE_POINT_INIT (100, 100))); - builder = gsk_path_builder_new (); gsk_path_builder_add_reverse_path (builder, path); path2 = gsk_path_builder_free_to_path (builder); contour2 = gsk_path_get_contour (path2, 0); - g_assert_true (gsk_contour_get_winding (contour, &GRAPHENE_POINT_INIT (100, 100)) - == - - gsk_contour_get_winding (contour2, &GRAPHENE_POINT_INIT (100, 100))); + g_assert_true (gsk_contour_get_winding (contour, &GRAPHENE_POINT_INIT (100, 100)) == 1); + g_assert_true (gsk_contour_get_winding (contour1, &GRAPHENE_POINT_INIT (100, 100)) == 1); + g_assert_true (gsk_contour_get_winding (contour2, &GRAPHENE_POINT_INIT (100, 100)) == -1); gsk_path_unref (path2); gsk_path_unref (path1); @@ -189,22 +185,79 @@ test_rounded_rect_winding (void) path1 = convert_to_standard_contour (path); contour1 = gsk_path_get_contour (path1, 0); - g_assert_true (gsk_contour_get_winding (contour, &GRAPHENE_POINT_INIT (150, 150)) - == - gsk_contour_get_winding (contour1, &GRAPHENE_POINT_INIT (150, 150))); + builder = gsk_path_builder_new (); + gsk_path_builder_add_reverse_path (builder, path); + path2 = gsk_path_builder_free_to_path (builder); + contour2 = gsk_path_get_contour (path2, 0); + + g_assert_true (gsk_contour_get_winding (contour, &GRAPHENE_POINT_INIT (150, 150)) == 1); + g_assert_true (gsk_contour_get_winding (contour1, &GRAPHENE_POINT_INIT (150, 150)) == 1); + g_assert_true (gsk_contour_get_winding (contour2, &GRAPHENE_POINT_INIT (150, 150)) == -1); + + gsk_path_unref (path2); + gsk_path_unref (path1); +} + +static void +test_rect_roundtrip (void) +{ + graphene_rect_t rect; + GskPathBuilder *builder; + GskPath *path, *path2; + const GskContour *contour; + char *s; + + rect = GRAPHENE_RECT_INIT (100, 100, 200, 150); + + builder = gsk_path_builder_new (); + gsk_path_builder_add_rect (builder, &rect); + path = gsk_path_builder_free_to_path (builder); + contour = gsk_path_get_contour (path, 0); + + g_assert_cmpstr (gsk_contour_get_type_name (contour), ==, "GskRectContour"); + + s = gsk_path_to_string (path); + path2 = gsk_path_parse (s); + contour = gsk_path_get_contour (path2, 0); + + g_assert_cmpstr (gsk_contour_get_type_name (contour), ==, "GskRectContour"); + + g_free (s); + gsk_path_unref (path2); + gsk_path_unref (path); +} + +static void +test_rect_winding (void) +{ + GskPathBuilder *builder; + GskPath *path, *path1, *path2, *path3; + const GskContour *contour, *contour1, *contour2, *contour3; + + builder = gsk_path_builder_new (); + gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (100, 100, 200, 150)); + path = gsk_path_builder_free_to_path (builder); + contour = gsk_path_get_contour (path, 0); + + path1 = convert_to_standard_contour (path); + contour1 = gsk_path_get_contour (path1, 0); builder = gsk_path_builder_new (); gsk_path_builder_add_reverse_path (builder, path); path2 = gsk_path_builder_free_to_path (builder); contour2 = gsk_path_get_contour (path2, 0); - g_assert_true (gsk_contour_get_winding (contour, &GRAPHENE_POINT_INIT (150, 150)) - == - - gsk_contour_get_winding (contour2, &GRAPHENE_POINT_INIT (150, 150))); + path3 = convert_to_standard_contour (path2); + contour3 = gsk_path_get_contour (path3, 0); + g_assert_true (gsk_contour_get_winding (contour, &GRAPHENE_POINT_INIT (150, 150)) == 1); + g_assert_true (gsk_contour_get_winding (contour1, &GRAPHENE_POINT_INIT (150, 150)) == 1); + g_assert_true (gsk_contour_get_winding (contour2, &GRAPHENE_POINT_INIT (150, 150)) == -1); + g_assert_true (gsk_contour_get_winding (contour3, &GRAPHENE_POINT_INIT (150, 150)) == -1); + + gsk_path_unref (path3); gsk_path_unref (path2); gsk_path_unref (path1); - gsk_path_unref (path); } int @@ -216,6 +269,8 @@ main (int argc, char *argv[]) g_test_add_func ("/path/circle/winding", test_circle_winding); g_test_add_func ("/path/rounded-rect/roundtrip", test_rounded_rect_roundtrip); g_test_add_func ("/path/rounded-rect/winding", test_rounded_rect_winding); + g_test_add_func ("/path/rect/roundtrip", test_rect_roundtrip); + g_test_add_func ("/path/rect/winding", test_rect_winding); return g_test_run (); } diff --git a/testsuite/gsk/path-special-cases.c b/testsuite/gsk/path-special-cases.c index f7d570c4d0..17a242de39 100644 --- a/testsuite/gsk/path-special-cases.c +++ b/testsuite/gsk/path-special-cases.c @@ -323,7 +323,7 @@ test_rect_path (void) g_assert_true (gsk_path_is_closed (path)); s = gsk_path_to_string (path); - g_assert_cmpstr (s, ==, "M 0 0 L 200 0 L 200 100 L 0 100 Z"); + g_assert_cmpstr (s, ==, "M 0 0 h 200 v 100 h -200 z"); g_free (s); g_assert_true (gsk_path_get_bounds (path, &bounds)); @@ -819,6 +819,42 @@ test_rounded_rect (void) gsk_path_unref (path); } +static void +test_rect (void) +{ + graphene_rect_t rect; + GskPathBuilder *builder; + GskPath *path; + GskPathPoint point; + graphene_point_t p; + + rect = GRAPHENE_RECT_INIT (10, 10, 100, 50); + + builder = gsk_path_builder_new (); + + gsk_path_builder_add_rect (builder, &rect); + + path = gsk_path_builder_free_to_path (builder); + + for (int i = 0; i < 100; i++) + { + p = GRAPHENE_POINT_INIT (g_test_rand_double_range (0, 200), + g_test_rand_double_range (0, 200)); + + g_assert_true (graphene_rect_contains_point (&rect, &p) == gsk_path_in_fill (path, &p, GSK_FILL_RULE_WINDING)); + } + + gsk_path_get_start_point (path, &point); + gsk_path_point_get_position (&point, path, &p); + g_assert_true (graphene_point_equal (&p, &GRAPHENE_POINT_INIT (10, 10))); + + gsk_path_get_end_point (path, &point); + gsk_path_point_get_position (&point, path, &p); + g_assert_true (graphene_point_equal (&p, &GRAPHENE_POINT_INIT (10, 10))); + + gsk_path_unref (path); +} + static void test_circle (void) { @@ -883,7 +919,11 @@ test_circle (void) 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)); + gsk_path_builder_move_to (builder, -2, -2); + gsk_path_builder_line_to (builder, 2, 0); + gsk_path_builder_line_to (builder, 2, 2); + gsk_path_builder_line_to (builder, -2, 2); + gsk_path_builder_close (builder); 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)); @@ -1002,7 +1042,7 @@ main (int argc, char *argv[]) g_test_add_func ("/path/rsvg-parse", test_rsvg_parse); g_test_add_func ("/path/empty", test_empty_path); - g_test_add_func ("/path/rect", test_rect_path); + g_test_add_func ("/path/rect-path", test_rect_path); g_test_add_func ("/path/foreach", test_foreach); g_test_add_func ("/path/point", test_path_point); g_test_add_func ("/path/segments", test_path_segments); @@ -1011,6 +1051,7 @@ main (int argc, char *argv[]) g_test_add_func ("/path/builder/add", test_path_builder_add); g_test_add_func ("/path/rotated-arc", test_rotated_arc); g_test_add_func ("/path/rounded-rect", test_rounded_rect); + g_test_add_func ("/path/rect", test_rect); g_test_add_func ("/path/circle", test_circle); g_test_add_func ("/path/length", test_length); g_test_add_func ("/path/rect/segment", test_rect_segment);