diff options
-rw-r--r-- | gsk/gskpath.c | 172 | ||||
-rw-r--r-- | testsuite/gsk/path.c | 153 |
2 files changed, 304 insertions, 21 deletions
diff --git a/gsk/gskpath.c b/gsk/gskpath.c index 7da1ed1f3b..00c8aaf949 100644 --- a/gsk/gskpath.c +++ b/gsk/gskpath.c @@ -195,14 +195,11 @@ gsk_rect_contour_print (const GskContour *contour, { const GskRectContour *self = (const GskRectContour *) contour; - g_string_append (string, "M "); - _g_string_append_point (string, &GRAPHENE_POINT_INIT (self->x, self->y)); - g_string_append (string, " h "); - _g_string_append_double (string, self->width); - g_string_append (string, " v "); - _g_string_append_double (string, self->height); - g_string_append (string, " h "); - _g_string_append_double (string, - self->width); + /* Write commands in a form that gsk_path_new_from_string() recognizes */ + g_string_append_printf (string, "M%g,%gh%gv%gh%gz", + self->x, self->y, + self->width, self->height, + -self->width); } static gboolean @@ -554,18 +551,20 @@ gsk_circle_contour_print (const GskContour *contour, const GskCircleContour *self = (const GskCircleContour *) contour; float mid_angle = (self->end_angle - self->start_angle) / 2; - g_string_append (string, "M "); + g_string_append_printf (string, "M%g,%g", + self->center.x + cos (DEG_TO_RAD (self->start_angle)) * self->radius, + self->center.y + sin (DEG_TO_RAD (self->start_angle)) * self->radius); _g_string_append_point (string, &GSK_CIRCLE_POINT_INIT (self, self->start_angle)); - g_string_append (string, " A "); - _g_string_append_point (string, &GRAPHENE_POINT_INIT (self->radius, self->radius)); - g_string_append_printf (string, " 0 0 %u", - self->start_angle < self->end_angle ? 0 : 1); - _g_string_append_point (string, &GSK_CIRCLE_POINT_INIT (self, mid_angle)); - g_string_append (string, " A "); - _g_string_append_point (string, &GRAPHENE_POINT_INIT (self->radius, self->radius)); - g_string_append_printf (string, " 0 0 %u", - self->start_angle < self->end_angle ? 0 : 1); - _g_string_append_point (string, &GSK_CIRCLE_POINT_INIT (self, self->end_angle)); + g_string_append_printf (string, "A%g,%g,0,0,%u,%g,%g", + self->radius, self->radius, + self->start_angle < self->end_angle ? 0 : 1, + self->center.x + cos (DEG_TO_RAD (mid_angle)) * self->radius, + self->center.y + sin (DEG_TO_RAD (mid_angle)) * self->radius); + g_string_append_printf (string, "A%g,%g,0,0,%u,%g,%g", + self->radius, self->radius, + self->start_angle < self->end_angle ? 0 : 1, + self->center.x + cos (DEG_TO_RAD (self->end_angle)) * self->radius, + self->center.y + sin (DEG_TO_RAD (self->end_angle)) * self->radius); if (fabs (self->start_angle - self->end_angle >= 360)) g_string_append (string, "Z"); } @@ -2194,6 +2193,115 @@ parse_command (const char **p, return FALSE; } +static gboolean +parse_char (const char **p, + char ch) +{ + if (**p != ch) + return FALSE; + (*p)++; + return TRUE; +} + +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_rectangle (const char **p, + double *x, + double *y, + double *w, + double *h) +{ + const char *o = *p; + double w2; + + /* Check for M%g,%gh%gv%gh%gz without any intervening whitespace */ + if (parse_coordinate_pair (p, x, y) && + parse_char (p, 'h') && + parse_coordinate (p, w) && + parse_char (p, 'v') && + parse_coordinate (p, h) && + parse_char (p, 'h') && + parse_coordinate (p, &w2) && + parse_char (p, 'z') && + w2 == - *w) + { + const char *s; + + for (s = o; s != *p; s++) + { + if (g_ascii_isspace (*s)) + { + *p = o; + return FALSE; + } + } + + skip_whitespace (p); + + return TRUE; + } + + *p = o; + return FALSE; +} + +static gboolean +parse_circle (const char **p, + double *sx, + double *sy, + double *r) +{ + const char *o = *p; + double r1, r2, r3, mx, my, ex, ey; + + /* Check for M%g,%gA%g,%g,0,1,0,%g,%gA%g,%g,0,1,0,%g,%g + * without any intervening whitespace + */ + if (parse_coordinate_pair (p, sx, sy) && + parse_char (p, 'A') && + parse_coordinate_pair (p, r, &r1) && + parse_string (p, "0,1,0,") && + parse_coordinate_pair (p, &mx, &my) && + parse_char (p, 'A') && + parse_coordinate_pair (p, &r2, &r3) && + parse_string (p, "0,1,0,") && + parse_coordinate_pair (p, &ex, &ey) && + parse_char (p, 'Z') && + *r == r1 && r1 == r2 && r2 == r3 && + *sx == ex && *sy == ey) + { + const char *s; + + for (s = o; s != *p; s++) + { + if (g_ascii_isspace (*s)) + { + *p = o; + return FALSE; + } + } + + *r = r1; + + skip_whitespace (p); + + return TRUE; + } + + *p = o; + return FALSE; +} + /** * gsk_path_parse: * @string: a string @@ -2255,9 +2363,31 @@ gsk_path_parse (const char *string) case 'M': case 'm': { - double x1, y1; + double x1, y1, w, h, r; - if (parse_coordinate_pair (&p, &x1, &y1)) + 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 - r, y1), r); + if (strchr ("zZX", prev_cmd)) + { + path_x = x1; + path_y = y1; + } + x = x1; + y = y1; + } + else if (parse_coordinate_pair (&p, &x1, &y1)) { if (cmd == 'm') { diff --git a/testsuite/gsk/path.c b/testsuite/gsk/path.c index 7eca487f4a..119bae4433 100644 --- a/testsuite/gsk/path.c +++ b/testsuite/gsk/path.c @@ -859,6 +859,157 @@ test_from_string (void) } } +/* test that the parser can handle random paths */ +static void +test_from_random_string (void) +{ + int i; + + for (i = 0; i < 1000; i++) + { + GskPath *path = create_random_path (G_MAXUINT); + char *string = gsk_path_to_string (path); + GskPath *path1; + + g_assert_nonnull (string); + + path1 = gsk_path_parse (string); + g_assert_nonnull (path1); + + gsk_path_unref (path1); + g_free (string); + gsk_path_unref (path); + } +} + +typedef struct +{ + GskPathOperation op; + graphene_point_t pts[4]; + gsize n_pts; +} Contour; + +static gboolean +append_contour (GskPathOperation op, + const graphene_point_t *pts, + gsize n_pts, + float weight, + gpointer user_data) +{ + GArray *a = user_data; + Contour c; + int i; + + g_assert (n_pts <= 4); + c.op = op; + c.n_pts = n_pts; + for (i = 0; i < n_pts; i++) + c.pts[i] = pts[i]; + + g_array_append_val (a, c); + return TRUE; +} + +static GArray * +path_to_contours (GskPath *path) +{ + GArray *a; + + a = g_array_new (FALSE, FALSE, sizeof (Contour)); + gsk_path_foreach (path, append_contour, a); + + return a; +} + +static gboolean +contour_equal (Contour *c1, + Contour *c2) +{ + int i; + + if (c1->op != c2->op) + return FALSE; + + if (c1->n_pts != c2->n_pts) + return FALSE; + + for (i = 0; i < c1->n_pts; i++) + { + if (c1->pts[i].x != c2->pts[i].x || + c1->pts[i].y != c2->pts[i].y) + return FALSE; + } + + return TRUE; +} + +static gboolean +contours_equal (GArray *a1, + GArray *a2) +{ + int i; + + if (a1->len != a2->len) + return FALSE; + + for (i = 0; i < a1->len; i++) + { + Contour *c1 = &g_array_index (a1, Contour, i); + Contour *c2 = &g_array_index (a2, Contour, i); + + if (!contour_equal (c1, c2)) + return FALSE; + } + + return TRUE; +} + +static gboolean +path_equal (GskPath *path1, + GskPath *path2) +{ + GArray *a1, *a2; + gboolean ret; + + a1 = path_to_contours (path1); + a2 = path_to_contours (path2); + + ret = contours_equal (a1, a2); + + g_array_unref (a1); + g_array_unref (a2); + + return ret; +} + +/* Test that circles and rectangles serialize as expected and can be + * round-tripped through strings. + */ +static void +test_serialize (void) +{ + GskPathBuilder *builder; + GskPath *path; + GskPath *path1; + char *string; + + builder = gsk_path_builder_new (); + gsk_path_builder_add_circle (builder, &GRAPHENE_POINT_INIT (100, 100), 50); + gsk_path_builder_add_rect (builder, &GRAPHENE_RECT_INIT (111, 222, 333, 444)); + path = gsk_path_builder_free_to_path (builder); + + string = gsk_path_to_string (path); + g_assert_cmpstr ("M150,100A50,50,0,1,0,50,100A50,50,0,1,0,150,100Z M111,222h333v444h-333z", ==, string); + + path1 = gsk_path_parse (string); + + g_assert_true (path_equal (path, path1)); + + g_free (string); + gsk_path_unref (path); + gsk_path_unref (path1); +} + int main (int argc, char *argv[]) @@ -874,6 +1025,8 @@ main (int argc, g_test_add_func ("/path/closest_point", test_closest_point); g_test_add_func ("/path/closest_point_for_point", test_closest_point_for_point); g_test_add_func ("/path/from-string", test_from_string); + g_test_add_func ("/path/from-random-string", test_from_random_string); + g_test_add_func ("/path/serialize", test_serialize); return g_test_run (); } |