[gtk/wip/baedert/nodeeditor: 11/24] Parse render nodes from text files



commit 7fbf096d8f1e7d0be70f59e4ff0a3a6315558ab9
Author: Timm Bäder <mail baedert org>
Date:   Sat Mar 2 16:55:17 2019 +0100

    Parse render nodes from text files

 gsk/gskrendernode.c              |   44 +-
 gsk/gskrendernodeparser.c        | 1214 ++++++++++++++++++++++++++++++++++++++
 gsk/gskrendernodeparserprivate.h |   10 +
 gsk/meson.build                  |    1 +
 4 files changed, 1230 insertions(+), 39 deletions(-)
---
diff --git a/gsk/gskrendernode.c b/gsk/gskrendernode.c
index 343fd4b8ce..f785db11c0 100644
--- a/gsk/gskrendernode.c
+++ b/gsk/gskrendernode.c
@@ -42,6 +42,7 @@
 
 #include "gskdebugprivate.h"
 #include "gskrendererprivate.h"
+#include "gskrendernodeparserprivate.h"
 
 #include <graphene-gobject.h>
 
@@ -328,19 +329,11 @@ gsk_render_node_diff (GskRenderNode  *node1,
 GBytes *
 gsk_render_node_serialize (GskRenderNode *node)
 {
-  GVariant *node_variant, *variant;
   GBytes *result;
+  char *str;
 
-  node_variant = gsk_render_node_serialize_node (node);
-
-  variant = g_variant_new ("(suuv)",
-                           GSK_RENDER_NODE_SERIALIZATION_ID,
-                           (guint32) GSK_RENDER_NODE_SERIALIZATION_VERSION,
-                           (guint32) gsk_render_node_get_node_type (node),
-                           node_variant);
-
-  result = g_variant_get_data_as_bytes (variant);
-  g_variant_unref (variant);
+  str = gsk_render_node_serialize_to_string (node);
+  result = g_bytes_new_take (str, strlen (str));
 
   return result;
 }
@@ -397,36 +390,9 @@ GskRenderNode *
 gsk_render_node_deserialize (GBytes  *bytes,
                              GError **error)
 {
-  char *id_string;
-  guint32 version, node_type;
-  GVariant *variant, *node_variant;
   GskRenderNode *node = NULL;
 
-  variant = g_variant_new_from_bytes (G_VARIANT_TYPE ("(suuv)"), bytes, FALSE);
-
-  g_variant_get (variant, "(suuv)", &id_string, &version, &node_type, &node_variant);
-
-  if (!g_str_equal (id_string, GSK_RENDER_NODE_SERIALIZATION_ID))
-    {
-      g_set_error (error, GSK_SERIALIZATION_ERROR, GSK_SERIALIZATION_UNSUPPORTED_FORMAT,
-                   "Data not in GskRenderNode serialization format.");
-      goto out;
-    }
-
-  if (version != GSK_RENDER_NODE_SERIALIZATION_VERSION)
-    {
-      g_set_error (error, GSK_SERIALIZATION_ERROR, GSK_SERIALIZATION_UNSUPPORTED_VERSION,
-                   "Format version %u not supported.", version);
-      goto out;
-    }
-
-  node = gsk_render_node_deserialize_node (node_type, node_variant, error);
-
-out:
-  g_free (id_string);
-  g_variant_unref (node_variant);
-  g_variant_unref (variant);
+  node = gsk_render_node_deserialize_from_bytes (bytes);
 
   return node;
 }
-
diff --git a/gsk/gskrendernodeparser.c b/gsk/gskrendernodeparser.c
new file mode 100644
index 0000000000..0170a49756
--- /dev/null
+++ b/gsk/gskrendernodeparser.c
@@ -0,0 +1,1214 @@
+
+#include "gskrendernodeparserprivate.h"
+
+#include "gskcsstokenizerprivate.h"
+#include "gskroundedrectprivate.h"
+#include "gskrendernodeprivate.h"
+#include "gsktransform.h"
+
+typedef struct
+{
+  int n_tokens;
+  GskCssToken *tokens;
+
+  int pos;
+  const GskCssToken *cur;
+} Parser;
+
+static void
+skip (Parser *p)
+{
+  p->pos ++;
+
+  g_assert_cmpint (p->pos, <, p->n_tokens);
+  p->cur = &p->tokens[p->pos];
+}
+
+static const GskCssToken *
+lookahead (Parser *p,
+           int     lookahead)
+{
+  g_assert_cmpint (p->pos, <, p->n_tokens - lookahead);
+
+  return &p->tokens[p->pos + lookahead];
+}
+
+static void
+expect (Parser *p,
+        int     expected_type)
+{
+  if (p->cur->type != expected_type)
+    g_error ("Expected token type %d but found %d ('%s')",
+             expected_type, p->cur->type, gsk_css_token_to_string (p->cur));
+}
+
+static void
+expect_skip (Parser *p,
+             int     expected_type)
+{
+  expect (p, expected_type);
+  skip (p);
+}
+
+static void
+expect_skip_ident (Parser     *p,
+                   const char *ident)
+{
+  if (!gsk_css_token_is_ident (p->cur, ident))
+    g_error ("Expected ident '%s', but found token %s",
+             ident, p->cur->string.string);
+
+  skip (p);
+}
+
+static void
+parser_init (Parser      *p,
+             GskCssToken *tokens,
+             int          n_tokens)
+{
+  p->tokens = tokens;
+  p->pos = 0;
+  p->cur = &tokens[p->pos];
+  p->n_tokens = n_tokens;
+}
+
+static GskCssToken *
+tokenize (GBytes *bytes,
+          int    *n_tokens)
+{
+  GskCssTokenizer *tokenizer;
+  GArray *tokens;
+  GskCssToken token;
+
+  tokenizer = gsk_css_tokenizer_new (bytes, NULL, NULL, NULL);
+  tokens = g_array_new (FALSE, TRUE, sizeof (GskCssToken));
+
+  for (gsk_css_tokenizer_read_token (tokenizer, &token);
+       !gsk_css_token_is (&token, GSK_CSS_TOKEN_EOF);
+       gsk_css_tokenizer_read_token (tokenizer, &token))
+    {
+      g_array_append_val (tokens, token);
+    }
+
+  g_array_append_val (tokens, token);
+
+  *n_tokens = (int) tokens->len;
+
+  return (GskCssToken *) g_array_free (tokens, FALSE);
+}
+
+static double
+number_value (Parser *p)
+{
+  if (!gsk_css_token_is (p->cur, GSK_CSS_TOKEN_SIGNED_INTEGER) &&
+      !gsk_css_token_is (p->cur, GSK_CSS_TOKEN_SIGNLESS_INTEGER) &&
+      !gsk_css_token_is (p->cur, GSK_CSS_TOKEN_SIGNED_NUMBER) &&
+      !gsk_css_token_is (p->cur, GSK_CSS_TOKEN_SIGNLESS_NUMBER))
+    expect (p, GSK_CSS_TOKEN_SIGNED_NUMBER);
+
+  return p->cur->number.number;
+}
+
+static void
+parse_double4 (Parser *p,
+               double *out_values)
+{
+  int i;
+
+  expect_skip (p, GSK_CSS_TOKEN_OPEN_PARENS);
+  out_values[0] = number_value (p);
+  skip (p);
+
+  for (i = 0; i < 3; i ++)
+    {
+      expect_skip (p, GSK_CSS_TOKEN_COMMA);
+      out_values[1 + i] = number_value (p);
+      skip (p);
+    }
+
+  expect_skip (p, GSK_CSS_TOKEN_CLOSE_PARENS);
+}
+
+static void
+parse_tuple (Parser *p,
+             double *out_values)
+{
+  expect_skip (p, GSK_CSS_TOKEN_OPEN_PARENS);
+  out_values[0] = number_value (p);
+  skip (p);
+
+  expect_skip (p, GSK_CSS_TOKEN_COMMA);
+
+  out_values[1] = number_value (p);
+  skip (p);
+
+  expect_skip (p, GSK_CSS_TOKEN_CLOSE_PARENS);
+}
+
+/*
+ * The cases we allow are:
+ * (x, y, w, h) (w1, h1) (w2, h2) (w3, h3) (w4, h4) for the full rect
+ * (x, y, w, h) s1 s2 s3 s4                         for rect + quad corners
+ * (x, y, w, h) s                                   for rect + all corners the same size
+ * (x, y, w, h)                                     for just the rect with 0-sized corners
+ */
+static void
+parse_rounded_rect (Parser         *p,
+                    GskRoundedRect *result)
+{
+  double rect[4];
+  double corner0[2];
+  double corner1[2];
+  double corner2[2];
+  double corner3[2];
+
+  parse_double4 (p, rect);
+
+  if (gsk_css_token_is (p->cur, GSK_CSS_TOKEN_OPEN_PARENS))
+    {
+      parse_tuple (p, corner0);
+      parse_tuple (p, corner1);
+      parse_tuple (p, corner2);
+      parse_tuple (p, corner3);
+    }
+  else if (gsk_css_token_is (p->cur, GSK_CSS_TOKEN_SIGNED_INTEGER) ||
+           gsk_css_token_is (p->cur, GSK_CSS_TOKEN_SIGNLESS_INTEGER) ||
+           gsk_css_token_is (p->cur, GSK_CSS_TOKEN_SIGNED_NUMBER) ||
+           gsk_css_token_is (p->cur, GSK_CSS_TOKEN_SIGNLESS_NUMBER))
+    {
+      double val = number_value (p);
+
+      corner0[0] = corner0[1] = val;
+
+      skip (p);
+
+      if (gsk_css_token_is (p->cur, GSK_CSS_TOKEN_SIGNED_INTEGER) ||
+          gsk_css_token_is (p->cur, GSK_CSS_TOKEN_SIGNLESS_INTEGER) ||
+          gsk_css_token_is (p->cur, GSK_CSS_TOKEN_SIGNED_NUMBER) ||
+          gsk_css_token_is (p->cur, GSK_CSS_TOKEN_SIGNLESS_NUMBER))
+        {
+          corner1[0] = corner1[1] = number_value (p);
+          skip (p);
+          corner2[0] = corner2[1] = number_value (p);
+          skip (p);
+          corner3[0] = corner3[1] = number_value (p);
+          skip (p);
+        }
+      else
+        {
+          corner1[0] = corner1[1] = val;
+          corner2[0] = corner2[1] = val;
+          corner3[0] = corner3[1] = val;
+        }
+    }
+  else
+    {
+      corner0[0] = corner0[1] = 0.0;
+      corner1[0] = corner1[1] = 0.0;
+      corner2[0] = corner2[1] = 0.0;
+      corner3[0] = corner3[1] = 0.0;
+    }
+
+  gsk_rounded_rect_init (result,
+                         &GRAPHENE_RECT_INIT (rect[0], rect[1], rect[2], rect[3]),
+                         &(graphene_size_t) { corner0[0], corner0[1] },
+                         &(graphene_size_t) { corner1[0], corner1[1] },
+                         &(graphene_size_t) { corner2[0], corner2[1] },
+                         &(graphene_size_t) { corner3[0], corner3[1] });
+}
+
+static void
+parse_matrix (Parser            *p,
+              graphene_matrix_t *matrix)
+{
+  float vals[16];
+  int i;
+
+  expect_skip (p, GSK_CSS_TOKEN_OPEN_PARENS);
+
+  vals[0] = number_value (p);
+  skip (p);
+
+  for (i = 1; i < 16; i ++)
+    {
+      expect_skip (p, GSK_CSS_TOKEN_COMMA);
+      vals[i] = number_value (p);
+      skip (p);
+    }
+
+  expect_skip (p, GSK_CSS_TOKEN_CLOSE_PARENS);
+
+  graphene_matrix_init_from_float (matrix, vals);
+}
+
+static double
+parse_float_param (Parser     *p,
+                   const char *param_name)
+{
+  double value;
+
+  expect_skip_ident (p, param_name);
+  expect_skip (p, GSK_CSS_TOKEN_COLON);
+  value = number_value (p);
+  skip (p);
+
+  return value;
+}
+
+static void
+parse_tuple_param (Parser     *p,
+                   const char *param_name,
+                   double     *values)
+{
+  expect_skip_ident (p, param_name);
+  expect_skip (p, GSK_CSS_TOKEN_COLON);
+
+  parse_tuple (p, values);
+}
+
+static void
+parse_double4_param (Parser     *p,
+                     const char *param_name,
+                     double     *values)
+{
+  expect_skip_ident (p, param_name);
+  expect_skip (p, GSK_CSS_TOKEN_COLON);
+
+  parse_double4 (p, values);
+}
+
+static void
+parse_rounded_rect_param (Parser         *p,
+                          const char     *param_name,
+                          GskRoundedRect *rect)
+{
+  expect_skip_ident (p, param_name);
+  expect_skip (p, GSK_CSS_TOKEN_COLON);
+
+  parse_rounded_rect (p, rect);
+}
+
+static void
+parse_matrix_param (Parser            *p,
+                    const char        *param_name,
+                    graphene_matrix_t *matrix)
+{
+  expect_skip_ident (p, param_name);
+  expect_skip (p, GSK_CSS_TOKEN_COLON);
+
+  parse_matrix (p, matrix);
+}
+
+static GskRenderNode *
+parse_node (Parser *p)
+{
+  GskRenderNode *result = NULL;
+
+  if (gsk_css_token_is_ident (p->cur, "color"))
+    {
+      double color[4];
+      double bounds[4];
+
+      skip (p);
+      expect_skip (p, GSK_CSS_TOKEN_OPEN_CURLY);
+
+      parse_double4_param (p, "bounds", bounds);
+
+      expect_skip_ident (p, "color");
+      expect_skip (p, GSK_CSS_TOKEN_COLON);
+      parse_double4 (p, color);
+
+      expect_skip (p, GSK_CSS_TOKEN_CLOSE_CURLY);
+      result = gsk_color_node_new (&(GdkRGBA) { color[0], color[1], color[2], color[3] },
+                                   &GRAPHENE_RECT_INIT (bounds[0], bounds[1], bounds[2], bounds[3]));
+    }
+  else if (gsk_css_token_is_ident (p->cur, "opacity"))
+    {
+      double opacity = 0.0;
+      GskRenderNode *child;
+
+      skip (p);
+      expect_skip (p, GSK_CSS_TOKEN_OPEN_CURLY);
+      expect_skip_ident (p, "opacity");
+      expect_skip (p, GSK_CSS_TOKEN_COLON);
+      opacity = number_value (p);
+      skip (p);
+
+      child = parse_node (p);
+
+      expect_skip (p, GSK_CSS_TOKEN_CLOSE_CURLY);
+      result = gsk_opacity_node_new (child, opacity);
+      gsk_render_node_unref (child);
+    }
+  else if (gsk_css_token_is_ident (p->cur, "container"))
+    {
+      GPtrArray *children = g_ptr_array_new ();
+      guint i;
+
+      skip (p);
+      expect_skip (p, GSK_CSS_TOKEN_OPEN_CURLY);
+      while (p->cur->type != GSK_CSS_TOKEN_CLOSE_CURLY)
+        g_ptr_array_add (children, parse_node (p));
+
+
+      expect_skip (p, GSK_CSS_TOKEN_CLOSE_CURLY);
+      result = gsk_container_node_new ((GskRenderNode **)children->pdata, children->len);
+
+      for (i = 0; i < children->len; i ++)
+        gsk_render_node_unref (g_ptr_array_index (children, i));
+
+      g_ptr_array_free (children, TRUE);
+    }
+  else if (gsk_css_token_is_ident (p->cur, "outset_shadow"))
+    {
+      GskRoundedRect outline;
+      double color[4];
+      float dx, dy, spread, blur_radius;
+
+      skip (p);
+      expect_skip (p, GSK_CSS_TOKEN_OPEN_CURLY);
+      parse_rounded_rect_param (p, "outline", &outline);
+
+      parse_double4_param (p, "color", color);
+      dx = parse_float_param (p, "dx");
+      dy = parse_float_param (p, "dy");
+      spread = parse_float_param (p, "spread");
+      blur_radius = parse_float_param (p, "blur_radius");
+
+      expect_skip (p, GSK_CSS_TOKEN_CLOSE_CURLY);
+      result = gsk_outset_shadow_node_new (&outline,
+                                           &(GdkRGBA) { color[0], color[1], color[2], color[3] },
+                                           dx, dy,
+                                           spread,
+                                           blur_radius);
+    }
+  else if (gsk_css_token_is_ident (p->cur, "cross_fade"))
+    {
+      double progress;
+      GskRenderNode *start_child;
+      GskRenderNode *end_child;
+
+      skip (p);
+      expect_skip (p, GSK_CSS_TOKEN_OPEN_CURLY);
+
+      progress = parse_float_param (p, "progress");
+      start_child = parse_node (p);
+      end_child = parse_node (p);
+
+      expect_skip (p, GSK_CSS_TOKEN_CLOSE_CURLY);
+      result = gsk_cross_fade_node_new (start_child, end_child, progress);
+
+      gsk_render_node_unref (start_child);
+      gsk_render_node_unref (end_child);
+    }
+  else if (gsk_css_token_is_ident (p->cur, "clip"))
+    {
+      double clip_rect[4];
+      GskRenderNode *child;
+
+      skip (p);
+      expect_skip (p, GSK_CSS_TOKEN_OPEN_CURLY);
+
+      parse_double4_param (p, "clip", clip_rect);
+      child = parse_node (p);
+
+      expect_skip (p, GSK_CSS_TOKEN_CLOSE_CURLY);
+      result = gsk_clip_node_new (child,
+                                  &GRAPHENE_RECT_INIT (
+                                    clip_rect[0], clip_rect[1],
+                                    clip_rect[2], clip_rect[3]
+                                  ));
+
+      gsk_render_node_unref (child);
+    }
+  else if (gsk_css_token_is_ident (p->cur, "rounded_clip"))
+    {
+      GskRoundedRect clip_rect;
+      GskRenderNode *child;
+
+      skip (p);
+      expect_skip (p, GSK_CSS_TOKEN_OPEN_CURLY);
+
+      parse_rounded_rect_param (p, "clip", &clip_rect);
+      child = parse_node (p);
+
+
+      expect_skip (p, GSK_CSS_TOKEN_CLOSE_CURLY);
+      result = gsk_rounded_clip_node_new (child, &clip_rect);
+
+      gsk_render_node_unref (child);
+    }
+  else if (gsk_css_token_is_ident (p->cur, "linear_gradient"))
+    {
+      GArray *stops = g_array_new (FALSE, TRUE, sizeof (GskColorStop));
+      double bounds[4];
+      double start[2];
+      double end[2];
+
+      skip (p);
+      expect_skip (p, GSK_CSS_TOKEN_OPEN_CURLY);
+      parse_double4_param (p, "bounds", bounds);
+      parse_tuple_param (p, "start", start);
+      parse_tuple_param (p, "end", end);
+
+      expect_skip_ident (p, "stops");
+      expect_skip (p, GSK_CSS_TOKEN_COLON);
+      while (p->cur->type == GSK_CSS_TOKEN_OPEN_PARENS)
+        {
+          GskColorStop stop;
+          double color[4];
+
+          skip (p);
+          stop.offset = number_value (p);
+          skip (p);
+          expect_skip (p, GSK_CSS_TOKEN_COMMA);
+          parse_double4 (p, color);
+          expect_skip (p, GSK_CSS_TOKEN_CLOSE_PARENS);
+
+          stop.color = (GdkRGBA) { color[0], color[1], color[2], color[3] };
+          g_array_append_val (stops, stop);
+        }
+
+      expect_skip (p, GSK_CSS_TOKEN_CLOSE_CURLY);
+      result = gsk_linear_gradient_node_new (&GRAPHENE_RECT_INIT (
+                                               bounds[0], bounds[1],
+                                               bounds[2], bounds[3]
+                                             ),
+                                             &(graphene_point_t) { start[0], start[1] },
+                                             &(graphene_point_t) { end[0], end[1] },
+                                             (GskColorStop *)stops->data,
+                                             stops->len);
+      g_array_free (stops, TRUE);
+    }
+  else if (gsk_css_token_is_ident (p->cur, "transform"))
+    {
+      GskTransform *transform;
+      GskRenderNode *child;
+
+      skip (p);
+      expect_skip (p, GSK_CSS_TOKEN_OPEN_CURLY);
+      expect_skip_ident (p, "transform");
+      expect_skip (p, GSK_CSS_TOKEN_COLON);
+
+      if (p->cur->type == GSK_CSS_TOKEN_OPEN_PARENS)
+        {
+          graphene_matrix_t matrix;
+          parse_matrix (p, &matrix);
+
+          transform = gsk_transform_matrix (NULL, &matrix);
+        }
+      else
+        {
+          expect (p, GSK_CSS_TOKEN_IDENT);
+
+          for (transform = NULL;;)
+            {
+              /* Transform name */
+              expect (p, GSK_CSS_TOKEN_IDENT);
+
+              if (lookahead (p, 1)->type == GSK_CSS_TOKEN_OPEN_CURLY) /* Start of child node */
+                break;
+
+              if (gsk_css_token_is_ident (p->cur, "translate"))
+                {
+                  double offset[2];
+                  skip (p);
+                  parse_tuple (p, offset);
+                  transform = gsk_transform_translate (transform,
+                                                       &(graphene_point_t) { offset[0], offset[1] });
+
+                }
+              else
+                {
+                  g_error ("Unknown transform type: %s", gsk_css_token_to_string (p->cur));
+                }
+            }
+        }
+
+      child = parse_node (p);
+
+      expect_skip (p, GSK_CSS_TOKEN_CLOSE_CURLY);
+      result = gsk_transform_node_new (child, transform);
+
+      gsk_transform_unref (transform);
+      gsk_render_node_unref (child);
+    }
+  else if (gsk_css_token_is_ident (p->cur, "color_matrix"))
+    {
+      double offset_values[4];
+      graphene_matrix_t matrix;
+      graphene_vec4_t offset;
+      GskRenderNode *child;
+
+      skip (p);
+      expect_skip (p, GSK_CSS_TOKEN_OPEN_CURLY);
+
+      parse_matrix_param (p, "matrix", &matrix);
+      parse_double4_param (p, "offset", offset_values);
+
+      graphene_vec4_init (&offset,
+                          offset_values[0],
+                          offset_values[1],
+                          offset_values[2],
+                          offset_values[3]);
+
+      child = parse_node (p);
+
+      expect_skip (p, GSK_CSS_TOKEN_CLOSE_CURLY);
+      result = gsk_color_matrix_node_new (child, &matrix, &offset);
+
+      gsk_render_node_unref (child);
+    }
+  else if (gsk_css_token_is_ident (p->cur, "texture"))
+    {
+      G_GNUC_UNUSED guchar *texture_data;
+      gsize texture_data_len;
+      GdkTexture *texture;
+      double bounds[4];
+
+      skip (p);
+      expect_skip (p, GSK_CSS_TOKEN_OPEN_CURLY);
+
+      parse_double4_param (p, "bounds", bounds);
+
+      expect_skip (p, GSK_CSS_TOKEN_IDENT);
+      expect_skip (p, GSK_CSS_TOKEN_COLON);
+      expect (p, GSK_CSS_TOKEN_STRING);
+
+      texture_data = g_base64_decode (p->cur->string.string, &texture_data_len);
+      guchar data[] = {1, 0, 0, 1, 0, 0,
+                       0, 0, 1, 0, 0, 1};
+      GBytes *b = g_bytes_new_static (data, 12);
+
+      /* TODO: :( */
+      texture = gdk_memory_texture_new (2, 2, GDK_MEMORY_R8G8B8,
+                                        b, 6);
+
+      expect_skip (p, GSK_CSS_TOKEN_STRING);
+
+      expect_skip (p, GSK_CSS_TOKEN_CLOSE_CURLY);
+      result = gsk_texture_node_new (texture,
+                                     &GRAPHENE_RECT_INIT (
+                                       bounds[0], bounds[1],
+                                       bounds[2], bounds[3]
+                                     ));
+    }
+  else if (gsk_css_token_is_ident (p->cur, "inset_shadow"))
+    {
+      GskRoundedRect outline;
+      double color[4];
+      float dx, dy, spread, blur_radius;
+
+      skip (p);
+      expect_skip (p, GSK_CSS_TOKEN_OPEN_CURLY);
+      parse_rounded_rect_param (p, "outline", &outline);
+
+      parse_double4_param (p, "color", color);
+      dx = parse_float_param (p, "dx");
+      dy = parse_float_param (p, "dy");
+      spread = parse_float_param (p, "spread");
+      blur_radius = parse_float_param (p, "blur_radius");
+
+      expect_skip (p, GSK_CSS_TOKEN_CLOSE_CURLY);
+      result = gsk_inset_shadow_node_new (&outline,
+                                          &(GdkRGBA) { color[0], color[1], color[2], color[3] },
+                                          dx, dy,
+                                          spread,
+                                          blur_radius);
+    }
+  else if (gsk_css_token_is_ident (p->cur, "border"))
+    {
+      GskRoundedRect outline;
+      double widths[4];
+      double colors[4][4];
+
+      skip (p);
+      expect_skip (p, GSK_CSS_TOKEN_OPEN_CURLY);
+
+      parse_rounded_rect_param (p, "outline", &outline);
+      parse_double4_param (p, "widths", widths);
+
+      expect_skip_ident (p, "colors");
+      expect_skip (p, GSK_CSS_TOKEN_COLON);
+
+      parse_double4 (p, colors[0]);
+      parse_double4 (p, colors[1]);
+      parse_double4 (p, colors[2]);
+      parse_double4 (p, colors[3]);
+
+      expect_skip (p, GSK_CSS_TOKEN_CLOSE_CURLY);
+      result = gsk_border_node_new (&outline,
+                                    (float[4]) { widths[0], widths[1], widths[2], widths[3] },
+                                    (GdkRGBA[4]) {
+                                      (GdkRGBA) { colors[0][0], colors[0][1], colors[0][2], colors[0][3] },
+                                      (GdkRGBA) { colors[1][0], colors[1][1], colors[1][2], colors[1][3] },
+                                      (GdkRGBA) { colors[2][0], colors[2][1], colors[2][2], colors[2][3] },
+                                      (GdkRGBA) { colors[3][0], colors[3][1], colors[3][2], colors[3][3] },
+                                    });
+    }
+  else if (gsk_css_token_is_ident (p->cur, "text"))
+    {
+      skip (p);
+      expect_skip (p, GSK_CSS_TOKEN_OPEN_CURLY);
+
+      expect_skip (p, GSK_CSS_TOKEN_CLOSE_CURLY);
+
+      result = gsk_color_node_new (
+                                   &(GdkRGBA) { 0, 1, 0, 1 },
+                                   &GRAPHENE_RECT_INIT (0, 0, 0, 0));
+    }
+  else
+    {
+      g_error ("Unknown render node type: %s", gsk_css_token_to_string (p->cur));
+    }
+
+  return result;
+}
+
+/**
+ * All errors are fatal.
+ */
+GskRenderNode *
+gsk_render_node_deserialize_from_bytes (GBytes *bytes)
+{
+  GskRenderNode *root = NULL;
+  GskCssToken *tokens;
+  int n_tokens;
+  Parser parser;
+
+  tokens = tokenize (bytes, &n_tokens);
+
+  parser_init (&parser, tokens, n_tokens);
+  root = parse_node (&parser);
+
+  g_free (tokens);
+
+  return root;
+}
+
+
+typedef struct
+{
+  int indentation_level;
+  GString *str;
+} Printer;
+
+static void
+printer_init (Printer *self)
+{
+  self->indentation_level = 0;
+  self->str = g_string_new (NULL);
+}
+
+#define IDENT_LEVEL 2 /* Spaces per level */
+static void
+_indent (Printer *self)
+{
+  if (self->indentation_level > 0)
+    g_string_append_printf (self->str, "%*s", self->indentation_level * IDENT_LEVEL, " ");
+}
+#undef IDENT
+
+static void
+start_node (Printer    *self,
+            const char *node_name)
+{
+  _indent (self);
+  g_string_append_printf (self->str, "%s {\n", node_name);
+  self->indentation_level ++;
+}
+
+static void
+end_node (Printer *self)
+{
+  self->indentation_level --;
+  _indent (self);
+  g_string_append (self->str, "}\n");
+}
+
+static void
+append_rect (GString               *str,
+             const graphene_rect_t *r)
+{
+  g_string_append_printf (str, "(%f, %f, %f, %f)",
+                          r->origin.x,
+                          r->origin.y,
+                          r->size.width,
+                          r->size.height);
+}
+
+static void
+append_rounded_rect (GString              *str,
+                     const GskRoundedRect *r)
+{
+  append_rect (str, &r->bounds);
+
+  if (!gsk_rounded_rect_is_rectilinear (r))
+    {
+      g_string_append_printf (str, " (%f, %f) (%f, %f) (%f, %f) (%f, %f)",
+                              r->corner[0].width,
+                              r->corner[0].height,
+                              r->corner[1].width,
+                              r->corner[1].height,
+                              r->corner[2].width,
+                              r->corner[2].height,
+                              r->corner[3].width,
+                              r->corner[3].height);
+    }
+}
+
+static void
+append_rgba (GString         *str,
+             const GdkRGBA   *rgba)
+{
+  g_string_append_printf (str, "(%f, %f, %f, %f)",
+                          rgba->red,
+                          rgba->green,
+                          rgba->blue,
+                          rgba->alpha);
+}
+
+static void
+append_point (GString                *str,
+              const graphene_point_t *p)
+{
+  g_string_append_printf (str, "(%f, %f)", p->x, p->y);
+}
+
+static void
+append_matrix (GString                 *str,
+               const graphene_matrix_t *m)
+{
+  float v[16];
+
+  graphene_matrix_to_float (m, v);
+
+  g_string_append_printf (str, "(%f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f, %f)",
+                          v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7],
+                          v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]);
+}
+
+static void
+append_vec4 (GString               *str,
+             const graphene_vec4_t *v)
+{
+  g_string_append_printf (str, "(%f, %f, %f, %f)",
+                          graphene_vec4_get_x (v),
+                          graphene_vec4_get_y (v),
+                          graphene_vec4_get_z (v),
+                          graphene_vec4_get_w (v));
+}
+
+static void
+append_float_param (Printer    *p,
+                    const char *param_name,
+                    float       value)
+{
+  _indent (p);
+  g_string_append_printf (p->str, "%s = %f\n", param_name, value);
+}
+
+static void
+append_rgba_param (Printer       *p,
+                   const char    *param_name,
+                   const GdkRGBA *value)
+{
+  _indent (p);
+  g_string_append_printf (p->str, "%s = ", param_name);
+  append_rgba (p->str, value);
+  g_string_append_c (p->str, '\n');
+}
+
+static void
+append_rect_param (Printer               *p,
+                   const char            *param_name,
+                   const graphene_rect_t *value)
+{
+  _indent (p);
+  g_string_append_printf (p->str, "%s = ", param_name);
+  append_rect (p->str, value);
+  g_string_append_c (p->str, '\n');
+}
+
+static void
+append_rounded_rect_param (Printer              *p,
+                           const char           *param_name,
+                           const GskRoundedRect *value)
+{
+  _indent (p);
+  g_string_append_printf (p->str, "%s = ", param_name);
+  append_rounded_rect (p->str, value);
+  g_string_append_c (p->str, '\n');
+}
+
+static void
+append_point_param (Printer                *p,
+                    const char             *param_name,
+                    const graphene_point_t *value)
+{
+  _indent (p);
+  g_string_append_printf (p->str, "%s = ", param_name);
+  append_point (p->str, value);
+  g_string_append_c (p->str, '\n');
+}
+
+static void
+append_vec4_param (Printer               *p,
+                   const char            *param_name,
+                   const graphene_vec4_t *value)
+{
+  _indent (p);
+  g_string_append_printf (p->str, "%s = ", param_name);
+  append_vec4 (p->str, value);
+  g_string_append_c (p->str, '\n');
+}
+
+static void
+append_matrix_param (Printer                 *p,
+                     const char              *param_name,
+                     const graphene_matrix_t *value)
+{
+  _indent (p);
+  g_string_append_printf (p->str, "%s = ", param_name);
+  append_matrix (p->str, value);
+  g_string_append_c (p->str, '\n');
+}
+
+static void
+render_node_print (Printer       *p,
+                   GskRenderNode *node)
+{
+  switch (gsk_render_node_get_node_type (node))
+    {
+    case GSK_CONTAINER_NODE:
+      {
+        guint i;
+
+        start_node (p, "container");
+        for (i = 0; i < gsk_container_node_get_n_children (node); i ++)
+          {
+            GskRenderNode *child = gsk_container_node_get_child (node, i);
+
+            render_node_print (p, child);
+          }
+        end_node (p);
+      }
+    break;
+
+    case GSK_COLOR_NODE:
+      {
+        start_node (p, "color");
+        append_rect_param (p, "bounds", &node->bounds);
+        append_rgba_param (p, "color", gsk_color_node_peek_color (node));
+        end_node (p);
+      }
+    break;
+
+    case GSK_CROSS_FADE_NODE:
+      {
+        start_node (p, "cross_fade");
+
+        append_float_param (p, "progress", gsk_cross_fade_node_get_progress (node));
+        render_node_print (p, gsk_cross_fade_node_get_start_child (node));
+        render_node_print (p, gsk_cross_fade_node_get_end_child (node));
+
+        end_node (p);
+      }
+    break;
+
+    case GSK_LINEAR_GRADIENT_NODE:
+      {
+        int i;
+
+        start_node (p, "linear_gradient");
+
+        append_rect_param (p, "bounds", &node->bounds);
+        append_point_param (p, "start", gsk_linear_gradient_node_peek_start (node));
+        append_point_param (p, "end", gsk_linear_gradient_node_peek_end (node));
+
+        _indent (p);
+        g_string_append (p->str, "stops =");
+        for (i = 0; i < gsk_linear_gradient_node_get_n_color_stops (node); i ++)
+          {
+            const GskColorStop *stop = gsk_linear_gradient_node_peek_color_stops (node) + i;
+
+            g_string_append_printf (p->str, " (%f, ", stop->offset);
+            append_rgba (p->str, &stop->color);
+            g_string_append_c (p->str, ')');
+          }
+        g_string_append (p->str, "\n");
+
+        end_node (p);
+      }
+    break;
+
+    case GSK_REPEATING_LINEAR_GRADIENT_NODE:
+      {
+        g_error ("Add public api to access the repeating linear gradient node data");
+      }
+    break;
+
+    case GSK_OPACITY_NODE:
+      {
+        start_node (p, "opacity");
+
+        append_float_param (p, "opacity", gsk_opacity_node_get_opacity (node));
+        render_node_print (p, gsk_opacity_node_get_child (node));
+
+        end_node (p);
+      }
+    break;
+
+    case GSK_OUTSET_SHADOW_NODE:
+      {
+        start_node (p, "outset_shadow");
+
+        append_rounded_rect_param (p, "outline", gsk_outset_shadow_node_peek_outline (node));
+        append_rgba_param (p, "color", gsk_outset_shadow_node_peek_color (node));
+        append_float_param (p, "dx", gsk_outset_shadow_node_get_dx (node));
+        append_float_param (p, "dy", gsk_outset_shadow_node_get_dy (node));
+        append_float_param (p, "spread", gsk_outset_shadow_node_get_spread (node));
+        append_float_param (p, "blur_radius", gsk_outset_shadow_node_get_blur_radius (node));
+
+        end_node (p);
+      }
+    break;
+
+    case GSK_CLIP_NODE:
+      {
+        start_node (p, "clip");
+
+        append_rect_param (p, "clip", gsk_clip_node_peek_clip (node));
+        render_node_print (p, gsk_clip_node_get_child (node));
+
+        end_node (p);
+      }
+    break;
+
+    case GSK_ROUNDED_CLIP_NODE:
+      {
+        start_node (p, "rounded_clip");
+
+        append_rounded_rect_param (p, "clip", gsk_rounded_clip_node_peek_clip (node));
+        render_node_print (p, gsk_rounded_clip_node_get_child (node));
+
+        end_node (p);
+      }
+    break;
+
+    case GSK_TRANSFORM_NODE:
+      {
+        graphene_matrix_t matrix;
+
+        start_node (p, "transform");
+
+        gsk_transform_to_matrix (gsk_transform_node_get_transform (node), &matrix);
+        append_matrix_param (p, "transform", &matrix);
+        render_node_print (p, gsk_transform_node_get_child (node));
+
+        end_node (p);
+      }
+    break;
+
+    case GSK_COLOR_MATRIX_NODE:
+      {
+        start_node (p, "color_matrix");
+
+        append_matrix_param (p, "matrix", gsk_color_matrix_node_peek_color_matrix (node));
+        append_vec4_param (p, "offset", gsk_color_matrix_node_peek_color_offset (node));
+        render_node_print (p, gsk_color_matrix_node_get_child (node));
+
+        end_node (p);
+      }
+    break;
+
+    case GSK_BORDER_NODE:
+      {
+        start_node (p, "border");
+
+        append_rounded_rect_param (p, "outline", gsk_border_node_peek_outline (node));
+
+        _indent (p);
+        g_string_append (p->str, "widths = (");
+        g_string_append_printf (p->str, "%f, ", gsk_border_node_peek_widths (node)[0]);
+        g_string_append_printf (p->str, "%f, ", gsk_border_node_peek_widths (node)[1]);
+        g_string_append_printf (p->str, "%f, ", gsk_border_node_peek_widths (node)[2]);
+        g_string_append_printf (p->str, "%f", gsk_border_node_peek_widths (node)[3]);
+        g_string_append (p->str, ")\n");
+
+        _indent (p);
+        g_string_append (p->str, "colors = ");
+        append_rgba (p->str, &gsk_border_node_peek_colors (node)[0]);
+        g_string_append (p->str, " ");
+        append_rgba (p->str, &gsk_border_node_peek_colors (node)[1]);
+        g_string_append (p->str, " ");
+        append_rgba (p->str, &gsk_border_node_peek_colors (node)[2]);
+        g_string_append (p->str, " ");
+        append_rgba (p->str, &gsk_border_node_peek_colors (node)[3]);
+        g_string_append (p->str, "\n");
+
+        end_node (p);
+      }
+    break;
+
+    case GSK_SHADOW_NODE:
+      {
+        int i;
+
+        start_node (p, "shadow");
+
+        _indent (p);
+        g_string_append (p->str, "shadows = ");
+        for (i = 0; i < gsk_shadow_node_get_n_shadows (node); i ++)
+          {
+            const GskShadow *s = gsk_shadow_node_peek_shadow (node, i);
+
+            g_string_append_printf (p->str, "(%f, (%f, %f), ",
+                                    s->radius, s->dx, s->dy);
+            append_rgba (p->str, &s->color);
+            g_string_append (p->str, ", ");
+            /* TODO: One too many commas */
+          }
+
+        end_node (p);
+      }
+    break;
+
+    case GSK_INSET_SHADOW_NODE:
+      {
+        start_node (p, "inset_shadow");
+
+        append_rounded_rect_param (p, "outline", gsk_inset_shadow_node_peek_outline (node));
+        append_rgba_param (p, "color", gsk_inset_shadow_node_peek_color (node));
+        append_float_param (p, "dx", gsk_inset_shadow_node_get_dx (node));
+        append_float_param (p, "dy", gsk_inset_shadow_node_get_dy (node));
+        append_float_param (p, "spread", gsk_inset_shadow_node_get_spread (node));
+        append_float_param (p, "blur_radius", gsk_inset_shadow_node_get_blur_radius (node));
+
+        end_node (p);
+      }
+    break;
+
+    case GSK_TEXTURE_NODE:
+      {
+        GdkTexture *texture = gsk_texture_node_get_texture (node);
+        int stride;
+        int len;
+        guchar *data;
+        char *b64;
+
+        start_node (p, "texture");
+        append_rect_param (p, "bounds", &node->bounds);
+
+        stride = 4 * gdk_texture_get_width (texture);
+        len = sizeof (guchar) * stride * gdk_texture_get_height (texture);
+        data = g_malloc (len);
+        gdk_texture_download (texture, data, stride);
+
+        b64 = g_base64_encode (data, len);
+
+        _indent (p);
+        g_string_append_printf (p->str, "data = \"%s\"\n", b64);
+        end_node (p);
+
+        g_free (b64);
+        g_free (data);
+      }
+    break;
+
+    case GSK_CAIRO_NODE:
+      {
+
+        /*start_node (p, "cairo");*/
+        /*append_rect_param (p, "bounds", &node->bounds);*/
+        /*g_string_append (p->str, "?????");*/
+        /*end_node (p);*/
+      }
+    break;
+
+    case GSK_TEXT_NODE:
+      {
+        start_node (p, "text");
+
+        _indent (p);
+        g_string_append_printf (p->str, "font = \"%s\"\n",
+                                pango_font_description_to_string (pango_font_describe (
+                                     (PangoFont *)gsk_text_node_peek_font (node))));
+
+        append_float_param (p, "x", gsk_text_node_get_x (node));
+        append_float_param (p, "y", gsk_text_node_get_y (node));
+        append_rgba_param (p, "color", gsk_text_node_peek_color (node));
+
+        end_node (p);
+      }
+    break;
+
+    case GSK_DEBUG_NODE:
+      {
+        start_node (p, "debug");
+
+        _indent (p);
+        /* TODO: We potentially need to escape certain characters in the message */
+        g_string_append_printf (p->str, "message = \"%s\"\n", gsk_debug_node_get_message (node));
+
+        render_node_print (p, gsk_debug_node_get_child (node));
+        end_node (p);
+      }
+    break;
+
+    case GSK_BLUR_NODE:
+      {
+        start_node (p, "blur");
+
+        append_float_param (p, "radius", gsk_blur_node_get_radius (node));
+        render_node_print (p, gsk_blur_node_get_child (node));
+
+        end_node (p);
+      }
+    break;
+
+    case GSK_REPEAT_NODE:
+      {
+        start_node (p, "repeat");
+        append_rect_param (p, "bounds", &node->bounds);
+        append_rect_param (p, "child_bounds", gsk_repeat_node_peek_child_bounds (node));
+
+        render_node_print (p, gsk_repeat_node_get_child (node));
+
+        end_node (p);
+      }
+    break;
+
+    case GSK_BLEND_NODE:
+      {
+        start_node (p, "blend");
+
+        _indent (p);
+        /* TODO: (de)serialize enums! */
+        g_string_append_printf (p->str, "mode = %d\n", gsk_blend_node_get_blend_mode (node));
+        render_node_print (p, gsk_blend_node_get_bottom_child (node));
+        render_node_print (p, gsk_blend_node_get_top_child (node));
+
+        end_node (p);
+      }
+    break;
+
+    case GSK_NOT_A_RENDER_NODE:
+      g_assert_not_reached ();
+      break;
+
+    default:
+      g_error ("Unhandled node: %s", node->node_class->type_name);
+    }
+}
+
+char *
+gsk_render_node_serialize_to_string (GskRenderNode *root)
+{
+  Printer p;
+
+  printer_init (&p);
+  render_node_print (&p, root);
+
+  return g_string_free (p.str, FALSE);
+}
diff --git a/gsk/gskrendernodeparserprivate.h b/gsk/gskrendernodeparserprivate.h
new file mode 100644
index 0000000000..cd5052dcf9
--- /dev/null
+++ b/gsk/gskrendernodeparserprivate.h
@@ -0,0 +1,10 @@
+
+#ifndef __GSK_RENDER_NODE_PARSER_PRIVATE_H__
+#define __GSK_RENDER_NODE_PARSER_PRIVATE_H__
+
+#include "gskrendernode.h"
+
+GskRenderNode * gsk_render_node_deserialize_from_bytes  (GBytes        *bytes);
+char *          gsk_render_node_serialize_to_string     (GskRenderNode *root);
+
+#endif
diff --git a/gsk/meson.build b/gsk/meson.build
index 912c5ed0f8..bd0830b7cf 100644
--- a/gsk/meson.build
+++ b/gsk/meson.build
@@ -34,6 +34,7 @@ gsk_private_sources = files([
   'gskdebug.c',
   'gskprivate.c',
   'gskprofiler.c',
+  'gskrendernodeparser.c',
   'gl/gskshaderbuilder.c',
   'gl/gskglprofiler.c',
   'gl/gskglrenderer.c',



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