[gtk/curve-ops: 48/97] path: Special-case rects and circles




commit d00cb79e84a696fb82572934d3af783683909ddb
Author: Matthias Clasen <mclasen redhat com>
Date:   Wed Nov 25 01:03:13 2020 -0500

    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.

 gsk/gskpath.c                      | 101 ++++++++++++++++++++++++++++++++++++-
 testsuite/gsk/path-special-cases.c |  31 ++++++++++++
 2 files changed, 130 insertions(+), 2 deletions(-)
---
diff --git a/gsk/gskpath.c b/gsk/gskpath.c
index 49d425a6c1..21e8857c46 100644
--- a/gsk/gskpath.c
+++ b/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')
                   {
diff --git a/testsuite/gsk/path-special-cases.c b/testsuite/gsk/path-special-cases.c
index 23f08d59d0..04a575ac1f 100644
--- a/testsuite/gsk/path-special-cases.c
+++ b/testsuite/gsk/path-special-cases.c
@@ -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 ();
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]