[gtk+/wip/otte/rendernode: 126/127] gsk: Add gsk_render_node_serialize/deserialize()



commit b4b8d4757d6601e8046f06df919b6aafaefccf84
Author: Benjamin Otte <otte redhat com>
Date:   Wed Dec 21 05:43:14 2016 +0100

    gsk: Add gsk_render_node_serialize/deserialize()
    
    This does a conversion to/from GBytes and is intended for writing tests.
    
    It's really crude but it works.
    And that probably means Alex will (ab)use it for broadway.

 gsk/gskrendernode.c        |   50 +++
 gsk/gskrendernode.h        |    6 +
 gsk/gskrendernodeimpl.c    |  959 +++++++++++++++++++++++++++++++++++++++++++-
 gsk/gskrendernodeprivate.h |    6 +
 4 files changed, 1005 insertions(+), 16 deletions(-)
---
diff --git a/gsk/gskrendernode.c b/gsk/gskrendernode.c
index 20d9bef..bf2d9be 100644
--- a/gsk/gskrendernode.c
+++ b/gsk/gskrendernode.c
@@ -312,4 +312,54 @@ gsk_render_node_draw (GskRenderNode *node,
   cairo_restore (cr);
 }
 
+#define GSK_RENDER_NODE_SERIALIZATION_VERSION 0
+#define GSK_RENDER_NODE_SERIALIZATION_ID "GskRenderNode"
+
+GBytes *
+gsk_render_node_serialize (GskRenderNode *node)
+{
+  GVariant *node_variant, *variant;
+  GBytes *result;
+
+  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);
+
+  return result;
+}
+
+GskRenderNode *
+gsk_render_node_deserialize (GBytes *bytes)
+{
+  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))
+    goto out;
+
+  if (version != GSK_RENDER_NODE_SERIALIZATION_VERSION)
+    goto out;
+
+  node = gsk_render_node_deserialize_node (node_type, node_variant);
+
+out:
+  g_free (id_string);
+  g_variant_unref (node_variant);
+  g_variant_unref (variant);
+
+  return node;
+}
 
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index 39c134a..0cbbfd5 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -170,6 +170,12 @@ const char *            gsk_render_node_get_name                (GskRenderNode *
 GDK_AVAILABLE_IN_3_90
 void                    gsk_render_node_draw                    (GskRenderNode *node,
                                                                  cairo_t       *cr);
+
+GDK_AVAILABLE_IN_3_90
+GBytes *                gsk_render_node_serialize               (GskRenderNode *node);
+GDK_AVAILABLE_IN_3_90
+GskRenderNode *         gsk_render_node_deserialize             (GBytes        *bytes);
+
 G_END_DECLS
 
 #endif /* __GSK_RENDER_NODE_H__ */
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index 20ff948..6350a95 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -69,6 +69,36 @@ gsk_color_node_get_bounds (GskRenderNode   *node,
   graphene_rect_init_from_rect (bounds, &self->bounds); 
 }
 
+#define GSK_COLOR_NODE_VARIANT_TYPE "(dddddddd)"
+
+static GVariant *
+gsk_color_node_serialize (GskRenderNode *node)
+{
+  GskColorNode *self = (GskColorNode *) node;
+
+  return g_variant_new (GSK_COLOR_NODE_VARIANT_TYPE,
+                        self->color.red, self->color.green,
+                        self->color.blue, self->color.alpha,
+                        (double) self->bounds.origin.x, (double) self->bounds.origin.y,
+                        (double) self->bounds.size.width, (double) self->bounds.size.height);
+}
+
+static GskRenderNode *
+gsk_color_node_deserialize (GVariant *variant)
+{
+  double x, y, w, h;
+  GdkRGBA color;
+
+  if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (GSK_COLOR_NODE_VARIANT_TYPE)))
+    return NULL;
+
+  g_variant_get (variant, GSK_COLOR_NODE_VARIANT_TYPE,
+                 &color.red, &color.green, &color.blue, &color.alpha,
+                 &x, &y, &w, &h);
+
+  return gsk_color_node_new (&color, &GRAPHENE_RECT_INIT (x, y, w, h));
+}
+
 static const GskRenderNodeClass GSK_COLOR_NODE_CLASS = {
   GSK_COLOR_NODE,
   sizeof (GskColorNode),
@@ -76,7 +106,9 @@ static const GskRenderNodeClass GSK_COLOR_NODE_CLASS = {
   gsk_color_node_finalize,
   gsk_color_node_make_immutable,
   gsk_color_node_draw,
-  gsk_color_node_get_bounds
+  gsk_color_node_get_bounds,
+  gsk_color_node_serialize,
+  gsk_color_node_deserialize,
 };
 
 const GdkRGBA *
@@ -188,6 +220,81 @@ gsk_linear_gradient_node_get_bounds (GskRenderNode   *node,
   graphene_rect_init_from_rect (bounds, &self->bounds); 
 }
 
+#define GSK_LINEAR_GRADIENT_NODE_VARIANT_TYPE "(dddddddda(ddddd))"
+
+static GVariant *
+gsk_linear_gradient_node_serialize (GskRenderNode *node)
+{
+  GskLinearGradientNode *self = (GskLinearGradientNode *) node;
+  GVariantBuilder builder;
+  guint i;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ddddd)"));
+  for (i = 0; i < self->n_stops; i++)
+    {
+      g_variant_builder_add  (&builder, "(ddddd)",
+                              (double) self->stops[i].offset,
+                              self->stops[i].color.red, self->stops[i].color.green,
+                              self->stops[i].color.blue, self->stops[i].color.alpha);
+    }
+
+  return g_variant_new (GSK_LINEAR_GRADIENT_NODE_VARIANT_TYPE,
+                        (double) self->bounds.origin.x, (double) self->bounds.origin.y,
+                        (double) self->bounds.size.width, (double) self->bounds.size.height,
+                        (double) self->start.x, (double) self->start.y,
+                        (double) self->end.x, (double) self->end.y,
+                        &builder);
+}
+
+static GskRenderNode *
+gsk_linear_gradient_node_real_deserialize (GVariant *variant,
+                                           gboolean  repeating)
+{
+  GVariantIter *iter;
+  double x, y, w, h, start_x, start_y, end_x, end_y;
+  gsize i, n_stops;
+
+  if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (GSK_LINEAR_GRADIENT_NODE_VARIANT_TYPE)))
+    return NULL;
+
+  g_variant_get (variant, GSK_LINEAR_GRADIENT_NODE_VARIANT_TYPE,
+                 &x, &y, &w, &h,
+                 &start_x, &start_y, &end_x, &end_y,
+                 &iter);
+
+  n_stops = g_variant_iter_n_children (iter);
+  GskColorStop stops[n_stops];
+  for (i = 0; i < n_stops; i++)
+    {
+      double offset;
+      g_variant_iter_next (iter, "(ddddd)",
+                           &offset,
+                           &stops[i].color.red, &stops[i].color.green,
+                           &stops[i].color.blue, &stops[i].color.alpha);
+      stops[i].offset = offset;
+    }
+  g_variant_iter_free (iter);
+
+  return (repeating ? gsk_repeating_linear_gradient_node_new : gsk_linear_gradient_node_new)
+                      (&GRAPHENE_RECT_INIT (x, y, w, h),
+                       &GRAPHENE_POINT_INIT (start_x, start_y),
+                       &GRAPHENE_POINT_INIT (end_x, end_y),
+                       stops,
+                       n_stops);
+}
+
+static GskRenderNode *
+gsk_linear_gradient_node_deserialize (GVariant *variant)
+{
+  return gsk_linear_gradient_node_real_deserialize (variant, FALSE);
+}
+
+static GskRenderNode *
+gsk_repeating_linear_gradient_node_deserialize (GVariant *variant)
+{
+  return gsk_linear_gradient_node_real_deserialize (variant, TRUE);
+}
+
 static const GskRenderNodeClass GSK_LINEAR_GRADIENT_NODE_CLASS = {
   GSK_LINEAR_GRADIENT_NODE,
   sizeof (GskLinearGradientNode),
@@ -195,7 +302,9 @@ static const GskRenderNodeClass GSK_LINEAR_GRADIENT_NODE_CLASS = {
   gsk_linear_gradient_node_finalize,
   gsk_linear_gradient_node_make_immutable,
   gsk_linear_gradient_node_draw,
-  gsk_linear_gradient_node_get_bounds
+  gsk_linear_gradient_node_get_bounds,
+  gsk_linear_gradient_node_serialize,
+  gsk_linear_gradient_node_deserialize,
 };
 
 static const GskRenderNodeClass GSK_REPEATING_LINEAR_GRADIENT_NODE_CLASS = {
@@ -205,7 +314,9 @@ static const GskRenderNodeClass GSK_REPEATING_LINEAR_GRADIENT_NODE_CLASS = {
   gsk_linear_gradient_node_finalize,
   gsk_linear_gradient_node_make_immutable,
   gsk_linear_gradient_node_draw,
-  gsk_linear_gradient_node_get_bounds
+  gsk_linear_gradient_node_get_bounds,
+  gsk_linear_gradient_node_serialize,
+  gsk_repeating_linear_gradient_node_deserialize,
 };
 
 /**
@@ -387,6 +498,64 @@ gsk_border_node_get_bounds (GskRenderNode   *node,
   graphene_rect_init_from_rect (bounds, &self->outline.bounds);
 }
 
+#define GSK_BORDER_NODE_VARIANT_TYPE "(dddddddddddddddddddddddddddddddd)"
+
+static GVariant *
+gsk_border_node_serialize (GskRenderNode *node)
+{
+  GskBorderNode *self = (GskBorderNode *) node;
+
+  return g_variant_new (GSK_BORDER_NODE_VARIANT_TYPE,
+                        (double) self->outline.bounds.origin.x, (double) self->outline.bounds.origin.y,
+                        (double) self->outline.bounds.size.width, (double) self->outline.bounds.size.height,
+                        (double) self->outline.corner[0].width, (double) self->outline.corner[0].height,
+                        (double) self->outline.corner[1].width, (double) self->outline.corner[1].height,
+                        (double) self->outline.corner[2].width, (double) self->outline.corner[2].height,
+                        (double) self->outline.corner[3].width, (double) self->outline.corner[3].height,
+                        (double) self->border_width[0], (double) self->border_width[1],
+                        (double) self->border_width[2], (double) self->border_width[3],
+                        self->border_color[0].red, self->border_color[0].green,
+                        self->border_color[0].blue, self->border_color[0].alpha,
+                        self->border_color[1].red, self->border_color[1].green,
+                        self->border_color[1].blue, self->border_color[1].alpha,
+                        self->border_color[2].red, self->border_color[2].green,
+                        self->border_color[2].blue, self->border_color[2].alpha,
+                        self->border_color[3].red, self->border_color[3].green,
+                        self->border_color[3].blue, self->border_color[3].alpha);
+}
+
+static GskRenderNode *
+gsk_border_node_deserialize (GVariant *variant)
+{
+  double doutline[12], dwidths[4];
+  GdkRGBA colors[4];
+
+  if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (GSK_BORDER_NODE_VARIANT_TYPE)))
+    return NULL;
+
+  g_variant_get (variant, GSK_BORDER_NODE_VARIANT_TYPE,
+                 &doutline[0], &doutline[1], &doutline[2], &doutline[3],
+                 &doutline[4], &doutline[5], &doutline[6], &doutline[7],
+                 &doutline[8], &doutline[9], &doutline[10], &doutline[11],
+                 &dwidths[0], &dwidths[1], &dwidths[2], &dwidths[3],
+                 &colors[0].red, &colors[0].green, &colors[0].blue, &colors[0].alpha,
+                 &colors[1].red, &colors[1].green, &colors[1].blue, &colors[1].alpha,
+                 &colors[2].red, &colors[2].green, &colors[2].blue, &colors[2].alpha,
+                 &colors[3].red, &colors[3].green, &colors[3].blue, &colors[3].alpha);
+
+  return gsk_border_node_new (&(GskRoundedRect) {
+                                  .bounds = GRAPHENE_RECT_INIT(doutline[0], doutline[1], doutline[2], 
doutline[3]),
+                                  .corner = {
+                                      GRAPHENE_SIZE_INIT (doutline[4], doutline[5]),
+                                      GRAPHENE_SIZE_INIT (doutline[6], doutline[7]),
+                                      GRAPHENE_SIZE_INIT (doutline[8], doutline[9]),
+                                      GRAPHENE_SIZE_INIT (doutline[10], doutline[11])
+                                  }
+                              },
+                              (float[4]) { dwidths[0], dwidths[1], dwidths[2], dwidths[3] },
+                              colors);
+}
+
 static const GskRenderNodeClass GSK_BORDER_NODE_CLASS = {
   GSK_BORDER_NODE,
   sizeof (GskBorderNode),
@@ -394,7 +563,9 @@ static const GskRenderNodeClass GSK_BORDER_NODE_CLASS = {
   gsk_border_node_finalize,
   gsk_border_node_make_immutable,
   gsk_border_node_draw,
-  gsk_border_node_get_bounds
+  gsk_border_node_get_bounds,
+  gsk_border_node_serialize,
+  gsk_border_node_deserialize
 };
 
 const GskRoundedRect *
@@ -516,6 +687,64 @@ gsk_texture_node_get_bounds (GskRenderNode   *node,
   graphene_rect_init_from_rect (bounds, &self->bounds); 
 }
 
+#define GSK_TEXTURE_NODE_VARIANT_TYPE "(dddduuau)"
+
+static GVariant *
+gsk_texture_node_serialize (GskRenderNode *node)
+{
+  GskTextureNode *self = (GskTextureNode *) node;
+  cairo_surface_t *surface;
+  GVariant *result;
+
+  surface = gsk_texture_download (self->texture);
+
+  g_assert (cairo_image_surface_get_width (surface) * 4 == cairo_image_surface_get_stride (surface));
+
+  result = g_variant_new ("(dddduu@au)",
+                          (double) self->bounds.origin.x, (double) self->bounds.origin.y,
+                          (double) self->bounds.size.width, (double) self->bounds.size.height,
+                          (guint32) gsk_texture_get_width (self->texture),
+                          (guint32) gsk_texture_get_height (self->texture),
+                          g_variant_new_fixed_array (G_VARIANT_TYPE ("u"),
+                                                     cairo_image_surface_get_data (surface),
+                                                     gsk_texture_get_width (self->texture)
+                                                     * gsk_texture_get_height (self->texture),
+                                                     sizeof (guint32)));
+
+  cairo_surface_destroy (surface);
+
+  return result;
+}
+
+static GskRenderNode *
+gsk_texture_node_deserialize (GVariant *variant)
+{
+  GskRenderNode *node;
+  GskTexture *texture;
+  double bounds[4];
+  guint32 width, height;
+  GVariant *pixel_variant;
+  gsize n_pixels;
+
+  if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (GSK_TEXTURE_NODE_VARIANT_TYPE)))
+    return NULL;
+
+  g_variant_get (variant, "(dddduu@au)",
+                 &bounds[0], &bounds[1], &bounds[2], &bounds[3],
+                 &width, &height, &pixel_variant);
+
+  /* XXX: Make this work without copying the data */
+  texture = gsk_texture_new_for_data (g_variant_get_fixed_array (pixel_variant, &n_pixels, sizeof (guint32)),
+                                      width, height, width * 4);
+  g_variant_unref (pixel_variant);
+
+  node = gsk_texture_node_new (texture, &GRAPHENE_RECT_INIT(bounds[0], bounds[1], bounds[2], bounds[3]));
+
+  gsk_texture_unref (texture);
+
+  return node;
+}
+
 static const GskRenderNodeClass GSK_TEXTURE_NODE_CLASS = {
   GSK_TEXTURE_NODE,
   sizeof (GskTextureNode),
@@ -523,7 +752,9 @@ static const GskRenderNodeClass GSK_TEXTURE_NODE_CLASS = {
   gsk_texture_node_finalize,
   gsk_texture_node_make_immutable,
   gsk_texture_node_draw,
-  gsk_texture_node_get_bounds
+  gsk_texture_node_get_bounds,
+  gsk_texture_node_serialize,
+  gsk_texture_node_deserialize
 };
 
 GskTexture *
@@ -983,6 +1214,54 @@ gsk_inset_shadow_node_get_bounds (GskRenderNode   *node,
   graphene_rect_init_from_rect (bounds, &self->outline.bounds); 
 }
 
+#define GSK_INSET_SHADOW_NODE_VARIANT_TYPE "(dddddddddddddddddddd)"
+
+static GVariant *
+gsk_inset_shadow_node_serialize (GskRenderNode *node)
+{
+  GskInsetShadowNode *self = (GskInsetShadowNode *) node;
+
+  return g_variant_new (GSK_INSET_SHADOW_NODE_VARIANT_TYPE,
+                        (double) self->outline.bounds.origin.x, (double) self->outline.bounds.origin.y,
+                        (double) self->outline.bounds.size.width, (double) self->outline.bounds.size.height,
+                        (double) self->outline.corner[0].width, (double) self->outline.corner[0].height,
+                        (double) self->outline.corner[1].width, (double) self->outline.corner[1].height,
+                        (double) self->outline.corner[2].width, (double) self->outline.corner[2].height,
+                        (double) self->outline.corner[3].width, (double) self->outline.corner[3].height,
+                        self->color.red, self->color.green,
+                        self->color.blue, self->color.alpha,
+                        (double) self->dx, (double) self->dy,
+                        (double) self->spread, (double) self->blur_radius);
+}
+
+static GskRenderNode *
+gsk_inset_shadow_node_deserialize (GVariant *variant)
+{
+  double doutline[12], dx, dy, spread, radius;
+  GdkRGBA color;
+
+  if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (GSK_INSET_SHADOW_NODE_VARIANT_TYPE)))
+    return NULL;
+
+  g_variant_get (variant, GSK_INSET_SHADOW_NODE_VARIANT_TYPE,
+                 &doutline[0], &doutline[1], &doutline[2], &doutline[3],
+                 &doutline[4], &doutline[5], &doutline[6], &doutline[7],
+                 &doutline[8], &doutline[9], &doutline[10], &doutline[11],
+                 &color.red, &color.green, &color.blue, &color.alpha,
+                 &dx, &dy, &spread, &radius);
+
+  return gsk_inset_shadow_node_new (&(GskRoundedRect) {
+                                        .bounds = GRAPHENE_RECT_INIT(doutline[0], doutline[1], doutline[2], 
doutline[3]),
+                                        .corner = {
+                                            GRAPHENE_SIZE_INIT (doutline[4], doutline[5]),
+                                            GRAPHENE_SIZE_INIT (doutline[6], doutline[7]),
+                                            GRAPHENE_SIZE_INIT (doutline[8], doutline[9]),
+                                            GRAPHENE_SIZE_INIT (doutline[10], doutline[11])
+                                        }
+                                    },
+                                    &color, dx, dy, spread, radius);
+}
+
 static const GskRenderNodeClass GSK_INSET_SHADOW_NODE_CLASS = {
   GSK_INSET_SHADOW_NODE,
   sizeof (GskInsetShadowNode),
@@ -990,7 +1269,9 @@ static const GskRenderNodeClass GSK_INSET_SHADOW_NODE_CLASS = {
   gsk_inset_shadow_node_finalize,
   gsk_inset_shadow_node_make_immutable,
   gsk_inset_shadow_node_draw,
-  gsk_inset_shadow_node_get_bounds
+  gsk_inset_shadow_node_get_bounds,
+  gsk_inset_shadow_node_serialize,
+  gsk_inset_shadow_node_deserialize
 };
 
 /**
@@ -1197,6 +1478,54 @@ gsk_outset_shadow_node_get_bounds (GskRenderNode   *node,
   bounds->size.height += top + bottom;
 }
 
+#define GSK_OUTSET_SHADOW_NODE_VARIANT_TYPE "(dddddddddddddddddddd)"
+
+static GVariant *
+gsk_outset_shadow_node_serialize (GskRenderNode *node)
+{
+  GskOutsetShadowNode *self = (GskOutsetShadowNode *) node;
+
+  return g_variant_new (GSK_OUTSET_SHADOW_NODE_VARIANT_TYPE,
+                        (double) self->outline.bounds.origin.x, (double) self->outline.bounds.origin.y,
+                        (double) self->outline.bounds.size.width, (double) self->outline.bounds.size.height,
+                        (double) self->outline.corner[0].width, (double) self->outline.corner[0].height,
+                        (double) self->outline.corner[1].width, (double) self->outline.corner[1].height,
+                        (double) self->outline.corner[2].width, (double) self->outline.corner[2].height,
+                        (double) self->outline.corner[3].width, (double) self->outline.corner[3].height,
+                        self->color.red, self->color.green,
+                        self->color.blue, self->color.alpha,
+                        (double) self->dx, (double) self->dy,
+                        (double) self->spread, (double) self->blur_radius);
+}
+
+static GskRenderNode *
+gsk_outset_shadow_node_deserialize (GVariant *variant)
+{
+  double doutline[12], dx, dy, spread, radius;
+  GdkRGBA color;
+
+  if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (GSK_OUTSET_SHADOW_NODE_VARIANT_TYPE)))
+    return NULL;
+
+  g_variant_get (variant, GSK_INSET_SHADOW_NODE_VARIANT_TYPE,
+                 &doutline[0], &doutline[1], &doutline[2], &doutline[3],
+                 &doutline[4], &doutline[5], &doutline[6], &doutline[7],
+                 &doutline[8], &doutline[9], &doutline[10], &doutline[11],
+                 &color.red, &color.green, &color.blue, &color.alpha,
+                 &dx, &dy, &spread, &radius);
+
+  return gsk_outset_shadow_node_new (&(GskRoundedRect) {
+                                         .bounds = GRAPHENE_RECT_INIT(doutline[0], doutline[1], doutline[2], 
doutline[3]),
+                                         .corner = {
+                                             GRAPHENE_SIZE_INIT (doutline[4], doutline[5]),
+                                             GRAPHENE_SIZE_INIT (doutline[6], doutline[7]),
+                                             GRAPHENE_SIZE_INIT (doutline[8], doutline[9]),
+                                             GRAPHENE_SIZE_INIT (doutline[10], doutline[11])
+                                         }
+                                     },
+                                     &color, dx, dy, spread, radius);
+}
+
 static const GskRenderNodeClass GSK_OUTSET_SHADOW_NODE_CLASS = {
   GSK_OUTSET_SHADOW_NODE,
   sizeof (GskOutsetShadowNode),
@@ -1204,7 +1533,9 @@ static const GskRenderNodeClass GSK_OUTSET_SHADOW_NODE_CLASS = {
   gsk_outset_shadow_node_finalize,
   gsk_outset_shadow_node_make_immutable,
   gsk_outset_shadow_node_draw,
-  gsk_outset_shadow_node_get_bounds
+  gsk_outset_shadow_node_get_bounds,
+  gsk_outset_shadow_node_serialize,
+  gsk_outset_shadow_node_deserialize
 };
 
 /**
@@ -1296,6 +1627,84 @@ gsk_cairo_node_get_bounds (GskRenderNode   *node,
   graphene_rect_init_from_rect (bounds, &self->bounds); 
 }
 
+#define GSK_CAIRO_NODE_VARIANT_TYPE "(dddduuau)"
+
+static GVariant *
+gsk_cairo_node_serialize (GskRenderNode *node)
+{
+  GskCairoNode *self = (GskCairoNode *) node;
+
+  if (self->surface == NULL)
+    {
+      return g_variant_new ("(dddduu@au)",
+                            (double) self->bounds.origin.x, (double) self->bounds.origin.y,
+                            (double) self->bounds.size.width, (double) self->bounds.size.height,
+                            (guint32) 0, (guint32) 0,
+                            g_variant_new_array (G_VARIANT_TYPE ("u"), NULL, 0));
+    }
+  else if (cairo_image_surface_get_width (self->surface) * 4 == cairo_image_surface_get_stride 
(self->surface))
+    {
+      return g_variant_new ("(dddduu@au)",
+                            (double) self->bounds.origin.x, (double) self->bounds.origin.y,
+                            (double) self->bounds.size.width, (double) self->bounds.size.height,
+                            (guint32) cairo_image_surface_get_width (self->surface),
+                            (guint32) cairo_image_surface_get_height (self->surface),
+                            g_variant_new_fixed_array (G_VARIANT_TYPE ("u"),
+                                                       cairo_image_surface_get_data (self->surface),
+                                                       cairo_image_surface_get_width (self->surface)
+                                                       * cairo_image_surface_get_height (self->surface),
+                                                       sizeof (guint32)));
+    }
+  else
+    {
+      /* FIXME: implement! */
+      g_assert_not_reached ();
+      return NULL;
+    }
+}
+
+const cairo_user_data_key_t gsk_surface_variant_key;
+
+static GskRenderNode *
+gsk_cairo_node_deserialize (GVariant *variant)
+{
+  GskRenderNode *result;
+  cairo_surface_t *surface;
+  double x, y, width, height;
+  guint32 surface_width, surface_height;
+  GVariant *pixel_variant;
+  gsize n_pixels;
+
+  if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (GSK_CAIRO_NODE_VARIANT_TYPE)))
+    return NULL;
+
+  g_variant_get (variant, "(dddduu@au)",
+                 &x, &y, &width, &height,
+                 &surface_width, &surface_height,
+                 &pixel_variant);
+
+  if (surface_width == 0 || surface_height == 0)
+    {
+      g_variant_unref (pixel_variant);
+      return gsk_cairo_node_new (&GRAPHENE_RECT_INIT (x, y, width, height));
+    }
+
+  /* XXX: Make this work without copying the data */
+  surface = cairo_image_surface_create_for_data ((guchar *) g_variant_get_fixed_array (pixel_variant, 
&n_pixels, sizeof (guint32)),
+                                                 CAIRO_FORMAT_ARGB32,
+                                                 surface_width, surface_height, surface_width * 4);
+  cairo_surface_set_user_data (surface,
+                               &gsk_surface_variant_key,
+                               pixel_variant,
+                               (cairo_destroy_func_t) g_variant_unref);
+
+  result = gsk_cairo_node_new_for_surface (&GRAPHENE_RECT_INIT (x, y, width, height), surface);
+
+  cairo_surface_destroy (surface);
+
+  return result;
+}
+
 static const GskRenderNodeClass GSK_CAIRO_NODE_CLASS = {
   GSK_CAIRO_NODE,
   sizeof (GskCairoNode),
@@ -1303,7 +1712,9 @@ static const GskRenderNodeClass GSK_CAIRO_NODE_CLASS = {
   gsk_cairo_node_finalize,
   gsk_cairo_node_make_immutable,
   gsk_cairo_node_draw,
-  gsk_cairo_node_get_bounds
+  gsk_cairo_node_get_bounds,
+  gsk_cairo_node_serialize,
+  gsk_cairo_node_deserialize
 };
 
 /*< private >
@@ -1324,6 +1735,22 @@ gsk_cairo_node_get_surface (GskRenderNode *node)
   return self->surface;
 }
 
+GskRenderNode *
+gsk_cairo_node_new_for_surface (const graphene_rect_t *bounds,
+                                cairo_surface_t       *surface)
+{
+  GskCairoNode *self;
+
+  g_return_val_if_fail (bounds != NULL, NULL);
+
+  self = (GskCairoNode *) gsk_render_node_new (&GSK_CAIRO_NODE_CLASS);
+
+  graphene_rect_init_from_rect (&self->bounds, bounds);
+  self->surface = cairo_surface_reference (surface);
+
+  return &self->render_node;
+}
+
 /**
  * gsk_cairo_node_new:
  * @bounds: the rectangle to render the to
@@ -1508,6 +1935,65 @@ gsk_container_node_get_bounds (GskRenderNode   *node,
     }
 }
 
+#define GSK_CONTAINER_NODE_VARIANT_TYPE "a(uv)"
+
+static GVariant *
+gsk_container_node_serialize (GskRenderNode *node)
+{
+  GskContainerNode *self = (GskContainerNode *) node;
+  GVariantBuilder builder;
+  guint i;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE (GSK_CONTAINER_NODE_VARIANT_TYPE));
+  
+  for (i = 0; i < self->n_children; i++)
+    {
+      g_variant_builder_add (&builder, "(uv)",
+                             (guint32) gsk_render_node_get_node_type (self->children[i]),
+                             gsk_render_node_serialize_node (self->children[i]));
+    }
+
+  return g_variant_builder_end (&builder);
+}
+
+static GskRenderNode *
+gsk_container_node_deserialize (GVariant *variant)
+{
+  GskRenderNode *result;
+  GVariantIter iter;
+  gsize i, n_children;
+  guint32 child_type;
+  GVariant *child_variant;
+
+  if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (GSK_CONTAINER_NODE_VARIANT_TYPE)))
+    return NULL;
+
+  i = 0;
+  n_children = g_variant_iter_init (&iter, variant);
+  GskRenderNode *children[n_children];
+
+  while (g_variant_iter_loop (&iter, "(uv)", &child_type, &child_variant))
+    {
+      children[i] = gsk_render_node_deserialize_node (child_type, child_variant);
+      if (children[i] == NULL)
+        {
+          guint j;
+          for (j = 0; j < i; j++)
+            gsk_render_node_unref (children[j]);
+          g_variant_unref (child_variant);
+          return NULL;
+        }
+      i++;
+    }
+
+  result = gsk_container_node_new (children, n_children);
+
+  for (i = 0; i < n_children; i++)
+    gsk_render_node_unref (children[i]);
+
+  return result;
+}
+
 static const GskRenderNodeClass GSK_CONTAINER_NODE_CLASS = {
   GSK_CONTAINER_NODE,
   sizeof (GskContainerNode),
@@ -1515,7 +2001,9 @@ static const GskRenderNodeClass GSK_CONTAINER_NODE_CLASS = {
   gsk_container_node_finalize,
   gsk_container_node_make_immutable,
   gsk_container_node_draw,
-  gsk_container_node_get_bounds
+  gsk_container_node_get_bounds,
+  gsk_container_node_serialize,
+  gsk_container_node_deserialize
 };
 
 /**
@@ -1651,6 +2139,65 @@ gsk_transform_node_get_bounds (GskRenderNode   *node,
                                     bounds);
 }
 
+#define GSK_TRANSFORM_NODE_VARIANT_TYPE "(dddddddddddddddduv)"
+
+static GVariant *
+gsk_transform_node_serialize (GskRenderNode *node)
+{
+  GskTransformNode *self = (GskTransformNode *) node;
+  float mat[16];
+
+  graphene_matrix_to_float (&self->transform, mat);
+
+  return g_variant_new (GSK_TRANSFORM_NODE_VARIANT_TYPE,
+                        (double) mat[0], (double) mat[1], (double) mat[2], (double) mat[3],
+                        (double) mat[4], (double) mat[5], (double) mat[6], (double) mat[7],
+                        (double) mat[8], (double) mat[9], (double) mat[10], (double) mat[11],
+                        (double) mat[12], (double) mat[13], (double) mat[14], (double) mat[15],
+                        (guint32) gsk_render_node_get_node_type (self->child),
+                        gsk_render_node_serialize_node (self->child));
+}
+
+static GskRenderNode *
+gsk_transform_node_deserialize (GVariant *variant)
+{
+  graphene_matrix_t transform;
+  double mat[16];
+  guint32 child_type;
+  GVariant *child_variant;
+  GskRenderNode *result, *child;
+
+  if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (GSK_TRANSFORM_NODE_VARIANT_TYPE)))
+    return NULL;
+
+  g_variant_get (variant, GSK_TRANSFORM_NODE_VARIANT_TYPE,
+                 &mat[0], &mat[1], &mat[2], &mat[3],
+                 &mat[4], &mat[5], &mat[6], &mat[7],
+                 &mat[8], &mat[9], &mat[10], &mat[11],
+                 &mat[12], &mat[13], &mat[14], &mat[15],
+                 &child_type, &child_variant);
+
+  child = gsk_render_node_deserialize_node (child_type, child_variant);
+  g_variant_unref (child_variant);
+
+  if (child == NULL)
+    return NULL;
+
+  graphene_matrix_init_from_float (&transform,
+                                   (float[16]) {
+                                       mat[0], mat[1], mat[2], mat[3],
+                                       mat[4], mat[5], mat[6], mat[7],
+                                       mat[8], mat[9], mat[10], mat[11],
+                                       mat[12], mat[13], mat[14], mat[15]
+                                   });
+                                    
+  result = gsk_transform_node_new (child, &transform);
+
+  gsk_render_node_unref (child);
+
+  return result;
+}
+
 static const GskRenderNodeClass GSK_TRANSFORM_NODE_CLASS = {
   GSK_TRANSFORM_NODE,
   sizeof (GskTransformNode),
@@ -1658,7 +2205,9 @@ static const GskRenderNodeClass GSK_TRANSFORM_NODE_CLASS = {
   gsk_transform_node_finalize,
   gsk_transform_node_make_immutable,
   gsk_transform_node_draw,
-  gsk_transform_node_get_bounds
+  gsk_transform_node_get_bounds,
+  gsk_transform_node_serialize,
+  gsk_transform_node_deserialize
 };
 
 /**
@@ -1780,6 +2329,47 @@ gsk_opacity_node_get_bounds (GskRenderNode   *node,
   gsk_render_node_get_bounds (self->child, bounds);
 }
 
+#define GSK_OPACITY_NODE_VARIANT_TYPE "(duv)"
+
+static GVariant *
+gsk_opacity_node_serialize (GskRenderNode *node)
+{
+  GskOpacityNode *self = (GskOpacityNode *) node;
+
+  return g_variant_new (GSK_OPACITY_NODE_VARIANT_TYPE,
+                        (double) self->opacity,
+                        (guint32) gsk_render_node_get_node_type (self->child),
+                        gsk_render_node_serialize_node (self->child));
+}
+
+static GskRenderNode *
+gsk_opacity_node_deserialize (GVariant *variant)
+{
+  double opacity;
+  guint32 child_type;
+  GVariant *child_variant;
+  GskRenderNode *result, *child;
+
+  if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (GSK_OPACITY_NODE_VARIANT_TYPE)))
+    return NULL;
+
+  g_variant_get (variant, GSK_OPACITY_NODE_VARIANT_TYPE,
+                 &opacity,
+                 &child_type, &child_variant);
+
+  child = gsk_render_node_deserialize_node (child_type, child_variant);
+  g_variant_unref (child_variant);
+
+  if (child == NULL)
+    return NULL;
+
+  result = gsk_opacity_node_new (child, opacity);
+
+  gsk_render_node_unref (child);
+
+  return result;
+}
+
 static const GskRenderNodeClass GSK_OPACITY_NODE_CLASS = {
   GSK_OPACITY_NODE,
   sizeof (GskOpacityNode),
@@ -1787,7 +2377,9 @@ static const GskRenderNodeClass GSK_OPACITY_NODE_CLASS = {
   gsk_opacity_node_finalize,
   gsk_opacity_node_make_immutable,
   gsk_opacity_node_draw,
-  gsk_opacity_node_get_bounds
+  gsk_opacity_node_get_bounds,
+  gsk_opacity_node_serialize,
+  gsk_opacity_node_deserialize
 };
 
 /**
@@ -1904,6 +2496,48 @@ gsk_clip_node_get_bounds (GskRenderNode   *node,
   graphene_rect_intersection (&self->clip, &child_bounds, bounds);
 }
 
+#define GSK_CLIP_NODE_VARIANT_TYPE "(dddduv)"
+
+static GVariant *
+gsk_clip_node_serialize (GskRenderNode *node)
+{
+  GskClipNode *self = (GskClipNode *) node;
+
+  return g_variant_new (GSK_CLIP_NODE_VARIANT_TYPE,
+                        (double) self->clip.origin.x, (double) self->clip.origin.y,
+                        (double) self->clip.size.width, (double) self->clip.size.height,
+                        (guint32) gsk_render_node_get_node_type (self->child),
+                        gsk_render_node_serialize_node (self->child));
+}
+
+static GskRenderNode *
+gsk_clip_node_deserialize (GVariant *variant)
+{
+  double x, y, width, height;
+  guint32 child_type;
+  GVariant *child_variant;
+  GskRenderNode *result, *child;
+
+  if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (GSK_CLIP_NODE_VARIANT_TYPE)))
+    return NULL;
+
+  g_variant_get (variant, GSK_CLIP_NODE_VARIANT_TYPE,
+                 &x, &y, &width, &height,
+                 &child_type, &child_variant);
+
+  child = gsk_render_node_deserialize_node (child_type, child_variant);
+  g_variant_unref (child_variant);
+
+  if (child == NULL)
+    return NULL;
+
+  result = gsk_clip_node_new (child, &GRAPHENE_RECT_INIT(x, y, width, height));
+
+  gsk_render_node_unref (child);
+
+  return result;
+}
+
 static const GskRenderNodeClass GSK_CLIP_NODE_CLASS = {
   GSK_CLIP_NODE,
   sizeof (GskClipNode),
@@ -1911,7 +2545,9 @@ static const GskRenderNodeClass GSK_CLIP_NODE_CLASS = {
   gsk_clip_node_finalize,
   gsk_clip_node_make_immutable,
   gsk_clip_node_draw,
-  gsk_clip_node_get_bounds
+  gsk_clip_node_get_bounds,
+  gsk_clip_node_serialize,
+  gsk_clip_node_deserialize
 };
 
 /**
@@ -2027,6 +2663,63 @@ gsk_rounded_clip_node_get_bounds (GskRenderNode   *node,
   graphene_rect_intersection (&self->clip.bounds, &child_bounds, bounds);
 }
 
+#define GSK_ROUNDED_CLIP_NODE_VARIANT_TYPE "(dddddddddddduv)"
+
+static GVariant *
+gsk_rounded_clip_node_serialize (GskRenderNode *node)
+{
+  GskRoundedClipNode *self = (GskRoundedClipNode *) node;
+
+  return g_variant_new (GSK_ROUNDED_CLIP_NODE_VARIANT_TYPE,
+                        (double) self->clip.bounds.origin.x, (double) self->clip.bounds.origin.y,
+                        (double) self->clip.bounds.size.width, (double) self->clip.bounds.size.height,
+                        (double) self->clip.corner[0].width, (double) self->clip.corner[0].height,
+                        (double) self->clip.corner[1].width, (double) self->clip.corner[1].height,
+                        (double) self->clip.corner[2].width, (double) self->clip.corner[2].height,
+                        (double) self->clip.corner[3].width, (double) self->clip.corner[3].height,
+                        (guint32) gsk_render_node_get_node_type (self->child),
+                        gsk_render_node_serialize_node (self->child));
+}
+
+static GskRenderNode *
+gsk_rounded_clip_node_deserialize (GVariant *variant)
+{
+  double doutline[12];
+  guint32 child_type;
+  GVariant *child_variant;
+  GskRenderNode *child, *result;
+
+  if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (GSK_ROUNDED_CLIP_NODE_VARIANT_TYPE)))
+    return NULL;
+
+  g_variant_get (variant, GSK_ROUNDED_CLIP_NODE_VARIANT_TYPE,
+                 &doutline[0], &doutline[1], &doutline[2], &doutline[3],
+                 &doutline[4], &doutline[5], &doutline[6], &doutline[7],
+                 &doutline[8], &doutline[9], &doutline[10], &doutline[11],
+                 &child_type, &child_variant);
+
+  child = gsk_render_node_deserialize_node (child_type, child_variant);
+  g_variant_unref (child_variant);
+
+  if (child == NULL)
+    return NULL;
+
+  result = gsk_rounded_clip_node_new (child,
+                                      &(GskRoundedRect) {
+                                          .bounds = GRAPHENE_RECT_INIT(doutline[0], doutline[1], 
doutline[2], doutline[3]),
+                                          .corner = {
+                                              GRAPHENE_SIZE_INIT (doutline[4], doutline[5]),
+                                              GRAPHENE_SIZE_INIT (doutline[6], doutline[7]),
+                                              GRAPHENE_SIZE_INIT (doutline[8], doutline[9]),
+                                              GRAPHENE_SIZE_INIT (doutline[10], doutline[11])
+                                          }
+                                      });
+
+  gsk_render_node_unref (child);
+
+  return result;
+}
+
 static const GskRenderNodeClass GSK_ROUNDED_CLIP_NODE_CLASS = {
   GSK_ROUNDED_CLIP_NODE,
   sizeof (GskRoundedClipNode),
@@ -2034,7 +2727,9 @@ static const GskRenderNodeClass GSK_ROUNDED_CLIP_NODE_CLASS = {
   gsk_rounded_clip_node_finalize,
   gsk_rounded_clip_node_make_immutable,
   gsk_rounded_clip_node_draw,
-  gsk_rounded_clip_node_get_bounds
+  gsk_rounded_clip_node_get_bounds,
+  gsk_rounded_clip_node_serialize,
+  gsk_rounded_clip_node_deserialize
 };
 
 /**
@@ -2188,6 +2883,78 @@ gsk_shadow_node_get_bounds (GskRenderNode   *node,
   bounds->size.height += top + bottom;
 }
 
+#define GSK_SHADOW_NODE_VARIANT_TYPE "(uva(ddddddd))"
+
+static GVariant *
+gsk_shadow_node_serialize (GskRenderNode *node)
+{
+  GskShadowNode *self = (GskShadowNode *) node;
+  GVariantBuilder builder;
+  gsize i;
+
+  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ddddddd)"));
+  for (i = 0; i < self->n_shadows; i++)
+    {
+      g_variant_builder_add  (&builder, "(ddddddd)",
+                              self->shadows[i].color.red, self->shadows[i].color.green,
+                              self->shadows[i].color.blue, self->shadows[i].color.alpha,
+                              self->shadows[i].dx, self->shadows[i].dy,
+                              self->shadows[i].radius);
+    }
+
+  return g_variant_new (GSK_SHADOW_NODE_VARIANT_TYPE,
+                        (guint32) gsk_render_node_get_node_type (self->child),
+                        gsk_render_node_serialize_node (self->child),
+                        &builder);
+}
+
+static GskRenderNode *
+gsk_shadow_node_deserialize (GVariant *variant)
+{
+  gsize n_shadows;
+  guint32 child_type;
+  GVariant *child_variant;
+  GskRenderNode *result, *child;
+  GVariantIter *iter;
+  gsize i;
+
+  if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (GSK_SHADOW_NODE_VARIANT_TYPE)))
+    return NULL;
+
+  g_variant_get (variant, GSK_SHADOW_NODE_VARIANT_TYPE,
+                 &child_type, &child_variant, &iter);
+
+  child = gsk_render_node_deserialize_node (child_type, child_variant);
+  g_variant_unref (child_variant);
+
+  if (child == NULL)
+    {
+      g_variant_iter_free (iter);
+      return NULL;
+    }
+
+  n_shadows = g_variant_iter_n_children (iter);
+  GskShadow shadows[n_shadows];
+  for (i = 0; i < n_shadows; i++)
+    {
+      double dx, dy, radius;
+      g_variant_iter_next (iter, "(ddddddd)",
+                           &shadows[i].color.red, &shadows[i].color.green,
+                           &shadows[i].color.blue, &shadows[i].color.alpha,
+                           &dx, &dy, &radius);
+      shadows[i].dx = dx;
+      shadows[i].dy = dy;
+      shadows[i].radius = radius;
+    }
+  g_variant_iter_free (iter);
+
+  result = gsk_shadow_node_new (child, shadows, n_shadows);
+
+  gsk_render_node_unref (child);
+
+  return result;
+}
+
 static const GskRenderNodeClass GSK_SHADOW_NODE_CLASS = {
   GSK_SHADOW_NODE,
   sizeof (GskShadowNode),
@@ -2195,7 +2962,9 @@ static const GskRenderNodeClass GSK_SHADOW_NODE_CLASS = {
   gsk_shadow_node_finalize,
   gsk_shadow_node_make_immutable,
   gsk_shadow_node_draw,
-  gsk_shadow_node_get_bounds
+  gsk_shadow_node_get_bounds,
+  gsk_shadow_node_serialize,
+  gsk_shadow_node_deserialize
 };
 
 /**
@@ -2369,6 +3138,60 @@ gsk_blend_node_get_bounds (GskRenderNode   *node,
   graphene_rect_union (&bottom_bounds, &top_bounds, bounds);
 }
 
+#define GSK_BLEND_NODE_VARIANT_TYPE "(uvuvu)"
+
+static GVariant *
+gsk_blend_node_serialize (GskRenderNode *node)
+{
+  GskBlendNode *self = (GskBlendNode *) node;
+
+  return g_variant_new (GSK_BLEND_NODE_VARIANT_TYPE,
+                        (guint32) gsk_render_node_get_node_type (self->bottom),
+                        gsk_render_node_serialize_node (self->bottom),
+                        (guint32) gsk_render_node_get_node_type (self->top),
+                        gsk_render_node_serialize_node (self->top),
+                        (guint32) self->blend_mode);
+}
+
+static GskRenderNode *
+gsk_blend_node_deserialize (GVariant *variant)
+{
+  guint32 bottom_child_type, top_child_type, blend_mode;
+  GVariant *bottom_child_variant, *top_child_variant;
+  GskRenderNode *bottom_child, *top_child, *result;
+
+  if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (GSK_BLEND_NODE_VARIANT_TYPE)))
+    return NULL;
+
+  g_variant_get (variant, GSK_BLEND_NODE_VARIANT_TYPE,
+                 &bottom_child_type, &bottom_child_variant,
+                 &top_child_type, &top_child_variant,
+                 &blend_mode);
+
+  bottom_child = gsk_render_node_deserialize_node (bottom_child_type, bottom_child_variant);
+  g_variant_unref (bottom_child_variant);
+  if (bottom_child == NULL)
+    {
+      g_variant_unref (top_child_variant);
+      return NULL;
+    }
+
+  top_child = gsk_render_node_deserialize_node (top_child_type, top_child_variant);
+  g_variant_unref (top_child_variant);
+  if (top_child == NULL)
+    {
+      gsk_render_node_unref (bottom_child);
+      return NULL;
+    }
+
+  result = gsk_blend_node_new (bottom_child, top_child, blend_mode);
+
+  gsk_render_node_unref (top_child);
+  gsk_render_node_unref (bottom_child);
+
+  return result;
+}
+
 static const GskRenderNodeClass GSK_BLEND_NODE_CLASS = {
   GSK_BLEND_NODE,
   sizeof (GskBlendNode),
@@ -2376,7 +3199,9 @@ static const GskRenderNodeClass GSK_BLEND_NODE_CLASS = {
   gsk_blend_node_finalize,
   gsk_blend_node_make_immutable,
   gsk_blend_node_draw,
-  gsk_blend_node_get_bounds
+  gsk_blend_node_get_bounds,
+  gsk_blend_node_serialize,
+  gsk_blend_node_deserialize
 };
 
 /**
@@ -2505,6 +3330,61 @@ gsk_cross_fade_node_get_bounds (GskRenderNode   *node,
   graphene_rect_union (&start_bounds, &end_bounds, bounds);
 }
 
+#define GSK_CROSS_FADE_NODE_VARIANT_TYPE "(uvuvd)"
+
+static GVariant *
+gsk_cross_fade_node_serialize (GskRenderNode *node)
+{
+  GskCrossFadeNode *self = (GskCrossFadeNode *) node;
+
+  return g_variant_new (GSK_CROSS_FADE_NODE_VARIANT_TYPE,
+                        (guint32) gsk_render_node_get_node_type (self->start),
+                        gsk_render_node_serialize_node (self->start),
+                        (guint32) gsk_render_node_get_node_type (self->end),
+                        gsk_render_node_serialize_node (self->end),
+                        (double) self->progress);
+}
+
+static GskRenderNode *
+gsk_cross_fade_node_deserialize (GVariant *variant)
+{
+  guint32 start_child_type, end_child_type;
+  GVariant *start_child_variant, *end_child_variant;
+  GskRenderNode *start_child, *end_child, *result;
+  double progress;
+
+  if (!g_variant_is_of_type (variant, G_VARIANT_TYPE (GSK_CROSS_FADE_NODE_VARIANT_TYPE)))
+    return NULL;
+
+  g_variant_get (variant, GSK_CROSS_FADE_NODE_VARIANT_TYPE,
+                 &start_child_type, &start_child_variant,
+                 &end_child_type, &end_child_variant,
+                 &progress);
+
+  start_child = gsk_render_node_deserialize_node (start_child_type, start_child_variant);
+  g_variant_unref (start_child_variant);
+  if (start_child == NULL)
+    {
+      g_variant_unref (end_child_variant);
+      return NULL;
+    }
+
+  end_child = gsk_render_node_deserialize_node (end_child_type, end_child_variant);
+  g_variant_unref (end_child_variant);
+  if (end_child == NULL)
+    {
+      gsk_render_node_unref (start_child);
+      return NULL;
+    }
+
+  result = gsk_cross_fade_node_new (start_child, end_child, progress);
+
+  gsk_render_node_unref (end_child);
+  gsk_render_node_unref (start_child);
+
+  return result;
+}
+
 static const GskRenderNodeClass GSK_CROSS_FADE_NODE_CLASS = {
   GSK_CROSS_FADE_NODE,
   sizeof (GskCrossFadeNode),
@@ -2512,7 +3392,9 @@ static const GskRenderNodeClass GSK_CROSS_FADE_NODE_CLASS = {
   gsk_cross_fade_node_finalize,
   gsk_cross_fade_node_make_immutable,
   gsk_cross_fade_node_draw,
-  gsk_cross_fade_node_get_bounds
+  gsk_cross_fade_node_get_bounds,
+  gsk_cross_fade_node_serialize,
+  gsk_cross_fade_node_deserialize
 };
 
 /**
@@ -2577,3 +3459,48 @@ gsk_cross_fade_node_get_progress (GskRenderNode *node)
   return self->progress;
 }
 
+static const GskRenderNodeClass *klasses[] = {
+  [GSK_CONTAINER_NODE] = &GSK_CONTAINER_NODE_CLASS,
+  [GSK_CAIRO_NODE] = &GSK_CAIRO_NODE_CLASS,
+  [GSK_COLOR_NODE] = &GSK_COLOR_NODE_CLASS,
+  [GSK_LINEAR_GRADIENT_NODE] = &GSK_LINEAR_GRADIENT_NODE_CLASS,
+  [GSK_REPEATING_LINEAR_GRADIENT_NODE] = &GSK_REPEATING_LINEAR_GRADIENT_NODE_CLASS,
+  [GSK_BORDER_NODE] = &GSK_BORDER_NODE_CLASS,
+  [GSK_TEXTURE_NODE] = &GSK_TEXTURE_NODE_CLASS,
+  [GSK_INSET_SHADOW_NODE] = &GSK_INSET_SHADOW_NODE_CLASS,
+  [GSK_OUTSET_SHADOW_NODE] = &GSK_OUTSET_SHADOW_NODE_CLASS,
+  [GSK_TRANSFORM_NODE] = &GSK_TRANSFORM_NODE_CLASS,
+  [GSK_OPACITY_NODE] = &GSK_OPACITY_NODE_CLASS,
+  [GSK_CLIP_NODE] = &GSK_CLIP_NODE_CLASS,
+  [GSK_ROUNDED_CLIP_NODE] = &GSK_ROUNDED_CLIP_NODE_CLASS,
+  [GSK_SHADOW_NODE] = &GSK_SHADOW_NODE_CLASS,
+  [GSK_BLEND_NODE] = &GSK_BLEND_NODE_CLASS,
+  [GSK_CROSS_FADE_NODE] = &GSK_CROSS_FADE_NODE_CLASS
+};
+
+GskRenderNode *
+gsk_render_node_deserialize_node (GskRenderNodeType  type,
+                                  GVariant          *variant)
+{
+  const GskRenderNodeClass *klass;
+  GskRenderNode *result;
+
+  if (type < G_N_ELEMENTS (klasses))
+    klass = klasses[type];
+  else
+    klass = NULL;
+
+  if (klass == NULL)
+    return NULL;
+
+  result = klass->deserialize (variant);
+
+  return result;
+}
+
+GVariant *
+gsk_render_node_serialize_node (GskRenderNode *node)
+{
+  return node->node_class->serialize (node);
+}
+
diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h
index d56c85c..a35e58f 100644
--- a/gsk/gskrendernodeprivate.h
+++ b/gsk/gskrendernodeprivate.h
@@ -38,6 +38,8 @@ struct _GskRenderNodeClass
                  cairo_t       *cr);
   void (* get_bounds) (GskRenderNode   *node,
                        graphene_rect_t *bounds);
+  GVariant * (* serialize) (GskRenderNode *node);
+  GskRenderNode * (* deserialize) (GVariant *variant);
 };
 
 GskRenderNode *gsk_render_node_new (const GskRenderNodeClass *node_class);
@@ -46,12 +48,16 @@ void gsk_render_node_make_immutable (GskRenderNode *node);
 
 void gsk_render_node_get_bounds (GskRenderNode   *node,
                                  graphene_rect_t *frame);
+GVariant * gsk_render_node_serialize_node (GskRenderNode *node);
+GskRenderNode * gsk_render_node_deserialize_node (GskRenderNodeType type, GVariant *variant);
+
 double gsk_opacity_node_get_opacity (GskRenderNode *node);
 
 const GskRoundedRect * gsk_border_node_peek_outline (GskRenderNode *node);
 float gsk_border_node_get_width (GskRenderNode *node, guint i);
 const GdkRGBA * gsk_border_node_peek_color (GskRenderNode *node, guint i);
 
+GskRenderNode *gsk_cairo_node_new_for_surface (const graphene_rect_t *bounds, cairo_surface_t *surface);
 cairo_surface_t *gsk_cairo_node_get_surface (GskRenderNode *node);
 
 GskTexture *gsk_texture_node_get_texture (GskRenderNode *node);



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