path: Special-case rects and circles
Write out the commands for rects and circles in a special way, and add code in the parser to recognize this, so we can successfully round-trip these through the SVG path format. The special way - for people who want to use it for debugging - for now is that we use uppercase "Z" to close standard paths, but lowercase "z" to close our special paths. A test is included, but the random path serializations should take care of it, too.
This commit is contained in:
committed by
Benjamin Otte
parent
ce2505fe0e
commit
85e4bd95c1
101
gsk/gskpath.c
101
gsk/gskpath.c
@@ -2117,6 +2117,81 @@ 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_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_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") &&
|
||||
w2 == - *w)
|
||||
{
|
||||
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_string (p, "A") &&
|
||||
parse_coordinate_pair (p, r, &r1) &&
|
||||
parse_string (p, "0 0 0") &&
|
||||
parse_coordinate_pair (p, &mx, &my) &&
|
||||
parse_string (p, "A") &&
|
||||
parse_coordinate_pair (p, &r2, &r3) &&
|
||||
parse_string (p, "0 0 0") &&
|
||||
parse_coordinate_pair (p, &ex, &ey) &&
|
||||
parse_string (p, "z") &&
|
||||
*r == r1 && r1 == r2 && r2 == r3 &&
|
||||
*sx == ex && *sy == ey)
|
||||
{
|
||||
skip_whitespace (p);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
*p = o;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gsk_path_parse:
|
||||
* @string: a string
|
||||
@@ -2178,9 +2253,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')
|
||||
{
|
||||
|
||||
@@ -276,6 +276,36 @@ test_rsvg_parse (void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Test that circles and rectangles serialize as expected and can be
|
||||
* round-tripped through strings.
|
||||
*/
|
||||
static void
|
||||
test_serialize_custom_contours (void)
|
||||
{
|
||||
GskPathBuilder *builder;
|
||||
GskPath *path;
|
||||
GskPath *path1;
|
||||
char *string;
|
||||
char *string1;
|
||||
|
||||
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 ("M 150 100 A 50 50 0 0 0 50 100 A 50 50 0 0 0 150 100 z M 111 222 h 333 v 444 h -333 z", ==, string);
|
||||
|
||||
path1 = gsk_path_parse (string);
|
||||
string1 = gsk_path_to_string (path1);
|
||||
g_assert_cmpstr (string, ==, string1);
|
||||
|
||||
g_free (string);
|
||||
g_free (string1);
|
||||
gsk_path_unref (path);
|
||||
gsk_path_unref (path1);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc,
|
||||
char *argv[])
|
||||
@@ -283,6 +313,7 @@ main (int argc,
|
||||
gtk_test_init (&argc, &argv, NULL);
|
||||
|
||||
g_test_add_func ("/path/rsvg-parse", test_rsvg_parse);
|
||||
g_test_add_func ("/path/serialize-custom-contours", test_serialize_custom_contours);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user