[clutter] Add PaintNode, an element on the render object tree



commit b83dc6abfaed4ac16af2cad412d6382f5b0aa427
Author: Emmanuele Bassi <ebassi linux intel com>
Date:   Wed Feb 1 15:51:27 2012 +0000

    Add PaintNode, an element on the render object tree
    
    Now that we have a proper scene graph API, we should split out the
    rendering part from the logical and event handling part.
    
    ClutterPaintNode is a lightweight fundamental type that encodes only the
    paint operations: pipeline state and geometry. At its most simple, is a
    way to structure setting up the programmable pipeline using a
    CoglPipeline, and submitting Cogl primitives. The important take away
    from this API is that you are not allowed to call Cogl API like
    cogl_set_source() or cogl_primitive_draw() directly.
    
    The interesting approach to this is that, in the future, we should be
    able to move to a purely retained mode: we will decide which actors need
    to be painted, they will update their own branch of the render graph,
    and we'll take the render graph and build all the rendering commands
    from that.
    
    For the 1.x API, we will have to maintain invariants and the existing
    behaviour, but as soon as we can break API, the old paint signal will
    just go away, and Actors will only be allowed to manipulate the render
    tree.

 clutter/Makefile.am                  |    5 +
 clutter/clutter-paint-node-private.h |  115 ++++
 clutter/clutter-paint-node.c         | 1137 ++++++++++++++++++++++++++++++++++
 clutter/clutter-paint-node.h         |   95 +++
 clutter/clutter-paint-nodes.c        | 1124 +++++++++++++++++++++++++++++++++
 clutter/clutter-paint-nodes.h        |  161 +++++
 clutter/clutter-types.h              |    1 +
 clutter/clutter.h                    |    2 +
 8 files changed, 2640 insertions(+), 0 deletions(-)
---
diff --git a/clutter/Makefile.am b/clutter/Makefile.am
index b1c9e05..6c22c42 100644
--- a/clutter/Makefile.am
+++ b/clutter/Makefile.am
@@ -100,6 +100,8 @@ source_h =					\
 	$(srcdir)/clutter-model.h		\
 	$(srcdir)/clutter-offscreen-effect.h	\
 	$(srcdir)/clutter-page-turn-effect.h	\
+	$(srcdir)/clutter-paint-nodes.h		\
+	$(srcdir)/clutter-paint-node.h		\
 	$(srcdir)/clutter-path-constraint.h	\
 	$(srcdir)/clutter-path.h		\
 	$(srcdir)/clutter-property-transition.h	\
@@ -173,6 +175,8 @@ source_c = \
 	$(srcdir)/clutter-model.c		\
 	$(srcdir)/clutter-offscreen-effect.c	\
 	$(srcdir)/clutter-page-turn-effect.c	\
+	$(srcdir)/clutter-paint-nodes.c		\
+	$(srcdir)/clutter-paint-node.c		\
 	$(srcdir)/clutter-path-constraint.c	\
 	$(srcdir)/clutter-path.c		\
 	$(srcdir)/clutter-property-transition.c	\
@@ -216,6 +220,7 @@ source_h_priv = \
 	$(srcdir)/clutter-master-clock.h		\
 	$(srcdir)/clutter-model-private.h		\
 	$(srcdir)/clutter-offscreen-effect-private.h	\
+	$(srcdir)/clutter-paint-node-private.h		\
 	$(srcdir)/clutter-paint-volume-private.h	\
 	$(srcdir)/clutter-private.h 			\
 	$(srcdir)/clutter-profile.h			\
diff --git a/clutter/clutter-paint-node-private.h b/clutter/clutter-paint-node-private.h
new file mode 100644
index 0000000..346bda8
--- /dev/null
+++ b/clutter/clutter-paint-node-private.h
@@ -0,0 +1,115 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2011  Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Emmanuele Bassi <ebassi linux intel com>
+ */
+
+#ifndef __CLUTTER_PAINT_NODE_PRIVATE_H__
+#define __CLUTTER_PAINT_NODE_PRIVATE_H__
+
+#include <glib-object.h>
+#include <clutter/clutter-paint-node.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_PAINT_NODE_CLASS(klass)         (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_PAINT_NODE, ClutterPaintNodeClass))
+#define CLUTTER_IS_PAINT_NODE_CLASS(klass)      (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_PAINT_NODE))
+#define CLUTTER_PAINT_NODE_GET_CLASS(obj)       (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_PAINT_NODE, ClutterPaintNodeClass))
+
+typedef struct _ClutterPaintOperation   ClutterPaintOperation;
+
+struct _ClutterPaintNode
+{
+  GTypeInstance parent_instance;
+
+  ClutterPaintNode *parent;
+
+  ClutterPaintNode *first_child;
+  ClutterPaintNode *prev_sibling;
+  ClutterPaintNode *next_sibling;
+  ClutterPaintNode *last_child;
+
+  guint n_children;
+
+  GArray *operations;
+
+  gchar *name;
+
+  volatile int ref_count;
+};
+
+struct _ClutterPaintNodeClass
+{
+  GTypeClass base_class;
+
+  void     (* finalize)  (ClutterPaintNode *node);
+
+  gboolean (* pre_draw)  (ClutterPaintNode *node);
+  void     (* draw)      (ClutterPaintNode *node);
+  void     (* post_draw) (ClutterPaintNode *node);
+};
+
+typedef enum {
+  PAINT_OP_INVALID = 0,
+  PAINT_OP_TEX_RECT,
+  PAINT_OP_PATH,
+  PAINT_OP_PRIMITIVE
+} PaintOpCode;
+
+struct _ClutterPaintOperation
+{
+  PaintOpCode opcode;
+
+  union {
+    float texrect[8];
+
+    CoglPath *path;
+
+    CoglPrimitive *primitive;
+  } op;
+};
+
+GType _clutter_root_node_get_type (void) G_GNUC_CONST;
+GType _clutter_transform_node_get_type (void) G_GNUC_CONST;
+GType _clutter_dummy_node_get_type (void) G_GNUC_CONST;
+
+void                    _clutter_paint_operation_paint_rectangle        (const ClutterPaintOperation *op);
+void                    _clutter_paint_operation_clip_rectangle         (const ClutterPaintOperation *op);
+void                    _clutter_paint_operation_paint_path             (const ClutterPaintOperation *op);
+void                    _clutter_paint_operation_clip_path              (const ClutterPaintOperation *op);
+void                    _clutter_paint_operation_paint_primitive        (const ClutterPaintOperation *op);
+
+void                    _clutter_paint_node_init_types                  (void);
+gpointer                _clutter_paint_node_internal                    (GType gtype);
+
+ClutterPaintNode *      _clutter_root_node_new                          (CoglFramebuffer             *framebuffer,
+                                                                         const ClutterColor          *clear_color,
+                                                                         CoglBufferBit                clear_flags,
+                                                                         const CoglMatrix            *matrix);
+ClutterPaintNode *      _clutter_transform_node_new                     (const CoglMatrix            *matrix);
+ClutterPaintNode *      _clutter_dummy_node_new                         (void);
+
+void                    _clutter_paint_node_paint                       (ClutterPaintNode            *root);
+void                    _clutter_paint_node_dump_tree                   (ClutterPaintNode            *root);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_PAINT_NODE_PRIVATE_H__ */
diff --git a/clutter/clutter-paint-node.c b/clutter/clutter-paint-node.c
new file mode 100644
index 0000000..dc11191
--- /dev/null
+++ b/clutter/clutter-paint-node.c
@@ -0,0 +1,1137 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2011  Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Emmanuele Bassi <ebassi linux intel com>
+ */
+
+/**
+ * SECTION:clutter-paint-node
+ * @Title: ClutterPaintNode
+ * @Short_Description: Paint objects
+ *
+ * #ClutterPaintNode is an element in the render graph.
+ *
+ * The render graph contains all the elements that need to be painted by
+ * Clutter when submitting a frame to the graphics system.
+ *
+ * The render graph is distinct from the scene graph: the scene graph is
+ * composed by actors, which can be visible or invisible; the scene graph
+ * elements also respond to events. The render graph, instead, is only
+ * composed by nodes that will be painted.
+ *
+ * Each #ClutterActor can submit multiple #ClutterPaintNode<!-- -->s to
+ * the render graph.
+ */
+
+/**
+ * ClutterPaintNode:
+ *
+ * The <structname>ClutterPaintNode</structname> structure contains only
+ * private data and it should be accessed using the provided API.
+ *
+ * Ref Func: clutter_paint_node_ref
+ * Unref Func: clutter_paint_node_unref
+ * Set Value Func: clutter_value_set_paint_node
+ * Get Value Func: clutter_value_get_paint_node
+ *
+ * Since: 1.10
+ */
+
+/**
+ * ClutterPaintNodeClass:
+ *
+ * The <structname>ClutterPaintNodeClass</structname> structure contains
+ * only private data.
+ *
+ * Since: 1.10
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <pango/pango.h>
+#include <cogl/cogl.h>
+
+#include "clutter-paint-node-private.h"
+
+#include "clutter-debug.h"
+#include "clutter-private.h"
+
+#include <gobject/gvaluecollector.h>
+
+static inline void      clutter_paint_operation_clear   (ClutterPaintOperation *op);
+
+static void
+value_paint_node_init (GValue *value)
+{
+  value->data[0].v_pointer = NULL;
+}
+
+static void
+value_paint_node_free_value (GValue *value)
+{
+  if (value->data[0].v_pointer != NULL)
+    clutter_paint_node_unref (value->data[0].v_pointer);
+}
+
+static void
+value_paint_node_copy_value (const GValue *src,
+                             GValue       *dst)
+{
+  if (src->data[0].v_pointer != NULL)
+    dst->data[0].v_pointer = clutter_paint_node_ref (src->data[0].v_pointer);
+  else
+    dst->data[0].v_pointer = NULL;
+}
+
+static gpointer
+value_paint_node_peek_pointer (const GValue *value)
+{
+  return value->data[0].v_pointer;
+}
+
+static gchar *
+value_paint_node_collect_value (GValue      *value,
+                                guint        n_collect_values,
+                                GTypeCValue *collect_values,
+                                guint        collect_flags)
+{
+  ClutterPaintNode *node;
+
+  node = collect_values[0].v_pointer;
+
+  if (node == NULL)
+    {
+      value->data[0].v_pointer = NULL;
+      return NULL;
+    }
+
+  if (node->parent_instance.g_class == NULL)
+    return g_strconcat ("invalid unclassed ClutterPaintNode pointer for "
+                        "value type '",
+                        G_VALUE_TYPE_NAME (value),
+                        "'",
+                        NULL);
+
+  value->data[0].v_pointer = clutter_paint_node_ref (node);
+
+  return NULL;
+}
+
+static gchar *
+value_paint_node_lcopy_value (const GValue *value,
+                              guint         n_collect_values,
+                              GTypeCValue  *collect_values,
+                              guint         collect_flags)
+{
+  ClutterPaintNode **node_p = collect_values[0].v_pointer;
+
+  if (node_p == NULL)
+    return g_strconcat ("value location for '",
+                        G_VALUE_TYPE_NAME (value),
+                        "' passed as NULL",
+                        NULL);
+
+  if (value->data[0].v_pointer == NULL)
+    *node_p = NULL;
+  else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
+    *node_p = value->data[0].v_pointer;
+  else
+    *node_p = clutter_paint_node_ref (value->data[0].v_pointer);
+
+  return NULL;
+}
+
+static void
+clutter_paint_node_class_base_init (ClutterPaintNodeClass *klass)
+{
+}
+
+static void
+clutter_paint_node_class_base_finalize (ClutterPaintNodeClass *klass)
+{
+}
+
+static void
+clutter_paint_node_real_finalize (ClutterPaintNode *node)
+{
+  ClutterPaintNode *iter;
+
+  g_free (node->name);
+
+  if (node->operations != NULL)
+    {
+      guint i;
+
+      for (i = 0; i < node->operations->len; i++)
+        {
+          ClutterPaintOperation *op;
+
+          op = &g_array_index (node->operations, ClutterPaintOperation, i);
+          clutter_paint_operation_clear (op);
+        }
+
+      g_array_unref (node->operations);
+    }
+
+  iter = node->first_child;
+  while (iter != NULL)
+    {
+      ClutterPaintNode *next = iter->next_sibling;
+
+      clutter_paint_node_remove_child (node, iter);
+
+      iter = next;
+    }
+
+  g_type_free_instance ((GTypeInstance *) node);
+}
+
+static gboolean
+clutter_paint_node_real_pre_draw (ClutterPaintNode *node)
+{
+  return FALSE;
+}
+
+static void
+clutter_paint_node_real_draw (ClutterPaintNode *node)
+{
+}
+
+static void
+clutter_paint_node_real_post_draw (ClutterPaintNode *node)
+{
+}
+
+static void
+clutter_paint_node_class_init (ClutterPaintNodeClass *klass)
+{
+  klass->pre_draw = clutter_paint_node_real_pre_draw;
+  klass->draw = clutter_paint_node_real_draw;
+  klass->post_draw = clutter_paint_node_real_post_draw;
+  klass->finalize = clutter_paint_node_real_finalize;
+}
+
+static void
+clutter_paint_node_init (ClutterPaintNode *self)
+{
+  self->ref_count = 1;
+}
+
+GType
+clutter_paint_node_get_type (void)
+{
+  static volatile gsize paint_node_type_id__volatile = 0;
+
+  if (g_once_init_enter (&paint_node_type_id__volatile))
+    {
+      static const GTypeFundamentalInfo finfo = {
+        (G_TYPE_FLAG_CLASSED |
+         G_TYPE_FLAG_INSTANTIATABLE |
+         G_TYPE_FLAG_DERIVABLE |
+         G_TYPE_FLAG_DEEP_DERIVABLE),
+      };
+
+      static const GTypeValueTable value_table = {
+        value_paint_node_init,
+        value_paint_node_free_value,
+        value_paint_node_copy_value,
+        value_paint_node_peek_pointer,
+        "p",
+        value_paint_node_collect_value,
+        "p",
+        value_paint_node_lcopy_value,
+      };
+
+      const GTypeInfo node_info = {
+        sizeof (ClutterPaintNodeClass),
+
+        (GBaseInitFunc) clutter_paint_node_class_base_init,
+        (GBaseFinalizeFunc) clutter_paint_node_class_base_finalize,
+        (GClassInitFunc) clutter_paint_node_class_init,
+        (GClassFinalizeFunc) NULL,
+        NULL,
+
+        sizeof (ClutterPaintNode),
+        0,
+        (GInstanceInitFunc) clutter_paint_node_init,
+
+        &value_table,
+      };
+
+      GType paint_node_type_id =
+        g_type_register_fundamental (g_type_fundamental_next (),
+                                     I_("ClutterPaintNode"),
+                                     &node_info, &finfo,
+                                     G_TYPE_FLAG_ABSTRACT);
+
+      g_once_init_leave (&paint_node_type_id__volatile, paint_node_type_id);
+    }
+
+  return paint_node_type_id__volatile;
+}
+
+/**
+ * clutter_paint_node_set_name:
+ * @node: a #ClutterPaintNode
+ * @name: a string annotating the @node
+ *
+ * Sets a user-readable @name for @node.
+ *
+ * The @name will be used for debugging purposes.
+ *
+ * The @node will copy the passed string.
+ *
+ * Since: 1.10
+ */
+void
+clutter_paint_node_set_name (ClutterPaintNode *node,
+                             const char       *name)
+{
+  g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
+
+  g_free (node->name);
+  node->name = g_strdup (name);
+}
+
+/**
+ * clutter_paint_node_ref:
+ * @node: a #ClutterPaintNode
+ *
+ * Acquires a reference on @node.
+ *
+ * Return value: (transfer full): the #ClutterPaintNode
+ *
+ * Since: 1.10
+ */
+ClutterPaintNode *
+clutter_paint_node_ref (ClutterPaintNode *node)
+{
+  g_return_val_if_fail (CLUTTER_IS_PAINT_NODE (node), NULL);
+
+  g_atomic_int_inc (&node->ref_count);
+
+  return node;
+}
+
+/**
+ * clutter_paint_node_unref:
+ * @node: a #ClutterPaintNode
+ *
+ * Releases a reference on @node.
+ *
+ * Since: 1.10
+ */
+void
+clutter_paint_node_unref (ClutterPaintNode *node)
+{
+  g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
+
+  if (g_atomic_int_dec_and_test (&node->ref_count))
+    {
+      ClutterPaintNodeClass *klass = CLUTTER_PAINT_NODE_GET_CLASS (node);
+
+      klass->finalize (node);
+    }
+}
+
+/**
+ * clutter_paint_node_add_child:
+ * @node: a #ClutterPaintNode
+ * @child: the child #ClutterPaintNode to add
+ *
+ * Adds @child to the list of children of @node.
+ *
+ * This function will acquire a reference on @child.
+ *
+ * Since: 1.10
+ */
+void
+clutter_paint_node_add_child (ClutterPaintNode *node,
+                              ClutterPaintNode *child)
+{
+  g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
+  g_return_if_fail (CLUTTER_IS_PAINT_NODE (child));
+  g_return_if_fail (node != child);
+  g_return_if_fail (child->parent == NULL);
+
+  child->parent = node;
+  clutter_paint_node_ref (child);
+
+  node->n_children += 1;
+
+  child->prev_sibling = node->last_child;
+
+  if (node->last_child != NULL)
+    {
+      ClutterPaintNode *tmp = node->last_child;
+
+      tmp->next_sibling = child;
+    }
+
+  if (child->prev_sibling == NULL)
+    node->first_child = child;
+
+  if (child->next_sibling == NULL)
+    node->last_child = child;
+}
+
+/**
+ * clutter_paint_node_remove_child:
+ * @node: a #ClutterPaintNode
+ * @child: the #ClutterPaintNode to remove
+ *
+ * Removes @child from the list of children of @node.
+ *
+ * This function will release the reference on @child acquired by
+ * using clutter_paint_node_add_child().
+ *
+ * Since: 1.10
+ */
+void
+clutter_paint_node_remove_child (ClutterPaintNode *node,
+                                 ClutterPaintNode *child)
+{
+  ClutterPaintNode *prev, *next;
+
+  g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
+  g_return_if_fail (CLUTTER_IS_PAINT_NODE (child));
+  g_return_if_fail (node != child);
+  g_return_if_fail (child->parent == node);
+
+  node->n_children -= 1;
+
+  prev = child->prev_sibling;
+  next = child->next_sibling;
+
+  if (prev != NULL)
+    prev->next_sibling = next;
+
+  if (next != NULL)
+    next->prev_sibling = prev;
+
+  if (node->first_child == child)
+    node->first_child = next;
+
+  if (node->last_child == child)
+    node->last_child = prev;
+
+  child->prev_sibling = NULL;
+  child->next_sibling = NULL;
+  child->parent = NULL;
+
+  clutter_paint_node_unref (child);
+}
+
+/**
+ * clutter_paint_node_replace_child:
+ * @node: a #ClutterPaintNode
+ * @old_child: the child replaced by @new_child
+ * @new_child: the child that replaces @old_child
+ *
+ * Atomically replaces @old_child with @new_child in the list of
+ * children of @node.
+ *
+ * This function will release the reference on @old_child acquired
+ * by @node, and will acquire a new reference on @new_child.
+ *
+ * Since: 1.10
+ */
+void
+clutter_paint_node_replace_child (ClutterPaintNode *node,
+                                  ClutterPaintNode *old_child,
+                                  ClutterPaintNode *new_child)
+{
+  ClutterPaintNode *prev, *next;
+
+  g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
+  g_return_if_fail (CLUTTER_IS_PAINT_NODE (old_child));
+  g_return_if_fail (old_child->parent == node);
+  g_return_if_fail (CLUTTER_IS_PAINT_NODE (new_child));
+  g_return_if_fail (new_child->parent == NULL);
+
+  prev = old_child->prev_sibling;
+  next = old_child->next_sibling;
+
+  new_child->parent = node;
+  new_child->prev_sibling = prev;
+  new_child->next_sibling = next;
+  clutter_paint_node_ref (new_child);
+
+  if (prev != NULL)
+    prev->next_sibling = new_child;
+
+  if (next != NULL)
+    next->prev_sibling = new_child;
+
+  if (node->first_child == old_child)
+    node->first_child = new_child;
+
+  if (node->last_child == old_child)
+    node->last_child = new_child;
+
+  old_child->prev_sibling = NULL;
+  old_child->next_sibling = NULL;
+  old_child->parent = NULL;
+  clutter_paint_node_unref (old_child);
+}
+
+/**
+ * clutter_paint_node_remove_all:
+ * @node: a #ClutterPaintNode
+ *
+ * Removes all children of @node.
+ *
+ * This function releases the reference acquired by @node on its
+ * children.
+ *
+ * Since: 1.10
+ */
+void
+clutter_paint_node_remove_all (ClutterPaintNode *node)
+{
+  ClutterPaintNode *iter;
+
+  g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
+
+  iter = node->first_child;
+  while (iter != NULL)
+    {
+      ClutterPaintNode *next = iter->next_sibling;
+
+      clutter_paint_node_remove_child (node, iter);
+
+      iter = next;
+    }
+}
+
+/**
+ * clutter_paint_node_get_first_child:
+ * @node: a #ClutterPaintNode
+ *
+ * Retrieves the first child of the @node.
+ *
+ * Return value: (transfer none): a pointer to the first child of
+ *   the #ClutterPaintNode.
+ *
+ * Since: 1.10
+ */
+ClutterPaintNode *
+clutter_paint_node_get_first_child (ClutterPaintNode *node)
+{
+  g_return_val_if_fail (CLUTTER_IS_PAINT_NODE (node), NULL);
+
+  return node->first_child;
+}
+
+/**
+ * clutter_paint_node_get_previous_sibling:
+ * @node: a #ClutterPaintNode
+ *
+ * Retrieves the previous sibling of @node.
+ *
+ * Return value: (transfer none): a pointer to the previous sibling
+ *   of the #ClutterPaintNode.
+ *
+ * Since: 1.10
+ */
+ClutterPaintNode *
+clutter_paint_node_get_previous_sibling (ClutterPaintNode *node)
+{
+  g_return_val_if_fail (CLUTTER_IS_PAINT_NODE (node), NULL);
+
+  return node->prev_sibling;
+}
+
+/**
+ * clutter_paint_node_get_next_sibling:
+ * @node: a #ClutterPaintNode
+ *
+ * Retrieves the next sibling of @node.
+ *
+ * Return value: (transfer none): a pointer to the next sibling
+ *   of a #ClutterPaintNode
+ *
+ * Since: 1.10
+ */
+ClutterPaintNode *
+clutter_paint_node_get_next_sibling (ClutterPaintNode *node)
+{
+  g_return_val_if_fail (CLUTTER_IS_PAINT_NODE (node), NULL);
+
+  return node->next_sibling;
+}
+
+/**
+ * clutter_paint_node_get_last_child:
+ * @node: a #ClutterPaintNode
+ *
+ * Retrieves the last child of @node.
+ *
+ * Return value: (transfer none): a pointer to the last child
+ *   of a #ClutterPaintNode
+ *
+ * Since: 1.10
+ */
+ClutterPaintNode *
+clutter_paint_node_get_last_child (ClutterPaintNode *node)
+{
+  g_return_val_if_fail (CLUTTER_IS_PAINT_NODE (node), NULL);
+
+  return node->last_child;
+}
+
+/**
+ * clutter_paint_node_get_parent:
+ * @node: a #ClutterPaintNode
+ *
+ * Retrieves the parent of @node.
+ *
+ * Return value: (transfer none): a pointer to the parent of
+ *   a #ClutterPaintNode
+ *
+ * Since: 1.10
+ */
+ClutterPaintNode *
+clutter_paint_node_get_parent (ClutterPaintNode *node)
+{
+  g_return_val_if_fail (CLUTTER_IS_PAINT_NODE (node), NULL);
+
+  return node->parent;
+}
+
+/**
+ * clutter_paint_node_get_n_children:
+ * @node: a #ClutterPaintNode
+ *
+ * Retrieves the number of children of @node.
+ *
+ * Return value: the number of children of a #ClutterPaintNode
+ *
+ * Since: 1.10
+ */
+guint
+clutter_paint_node_get_n_children (ClutterPaintNode *node)
+{
+  g_return_val_if_fail (CLUTTER_IS_PAINT_NODE (node), 0);
+
+  return node->n_children;
+}
+
+/**
+ * clutter_value_set_paint_node:
+ * @value: a #GValue initialized with %CLUTTER_TYPE_PAINT_NODE
+ * @node: (type Clutter.PaintNode) (allow-none): a #ClutterPaintNode, or %NULL
+ *
+ * Sets the contents of a #GValue initialized with %CLUTTER_TYPE_PAINT_NODE.
+ *
+ * This function increased the reference count of @node; if you do not wish
+ * to increase the reference count, use clutter_value_take_paint_node()
+ * instead. The reference count will be released by g_value_unset().
+ *
+ * Since: 1.10
+ */
+void
+clutter_value_set_paint_node (GValue   *value,
+                              gpointer  node)
+{
+  ClutterPaintNode *old_node;
+
+  g_return_if_fail (CLUTTER_VALUE_HOLDS_PAINT_NODE (value));
+
+  old_node = value->data[0].v_pointer;
+
+  if (node != NULL)
+    {
+      g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
+
+      value->data[0].v_pointer = clutter_paint_node_ref (node);
+    }
+  else
+    value->data[0].v_pointer = NULL;
+
+  if (old_node != NULL)
+    clutter_paint_node_unref (old_node);
+}
+
+/**
+ * clutter_value_take_paint_node:
+ * @value: a #GValue, initialized with %CLUTTER_TYPE_PAINT_NODE
+ * @node: (type Clutter.PaintNode) (allow-none): a #ClutterPaintNode, or %NULL
+ *
+ * Sets the contents of a #GValue initialized with %CLUTTER_TYPE_PAINT_NODE.
+ *
+ * Unlike clutter_value_set_paint_node(), this function will not take a
+ * reference on the passed @node: instead, it will take ownership of the
+ * current reference count.
+ *
+ * Since: 1.10
+ */
+void
+clutter_value_take_paint_node (GValue   *value,
+                               gpointer  node)
+{
+  ClutterPaintNode *old_node;
+
+  g_return_if_fail (CLUTTER_VALUE_HOLDS_PAINT_NODE (value));
+
+  old_node = value->data[0].v_pointer;
+
+  if (node != NULL)
+    {
+      g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
+
+      /* take over ownership */
+      value->data[0].v_pointer = node;
+    }
+  else
+    value->data[0].v_pointer = NULL;
+
+  if (old_node != NULL)
+    clutter_paint_node_unref (old_node);
+}
+
+/**
+ * clutter_value_get_paint_node:
+ * @value: a #GValue initialized with %CLUTTER_TYPE_PAINT_NODE
+ *
+ * Retrieves a pointer to the #ClutterPaintNode contained inside
+ * the passed #GValue.
+ *
+ * Return value: (transfer none) (type Clutter.PaintNode): a pointer to
+ *   a #ClutterPaintNode, or %NULL
+ *
+ * Since: 1.10
+ */
+gpointer
+clutter_value_get_paint_node (const GValue *value)
+{
+  g_return_val_if_fail (CLUTTER_VALUE_HOLDS_PAINT_NODE (value), NULL);
+
+  return value->data[0].v_pointer;
+}
+
+/**
+ * clutter_value_dup_paint_node:
+ * @value: a #GValue initialized with %CLUTTER_TYPE_PAINT_NODE
+ *
+ * Retrieves a pointer to the #ClutterPaintNode contained inside
+ * the passed #GValue, and if not %NULL it will increase the
+ * reference count.
+ *
+ * Return value: (transfer full) (type Clutter.PaintNode): a pointer
+ *   to the #ClutterPaintNode, with its reference count increased,
+ *   or %NULL
+ *
+ * Since: 1.10
+ */
+gpointer
+clutter_value_dup_paint_node (const GValue *value)
+{
+  g_return_val_if_fail (CLUTTER_VALUE_HOLDS_PAINT_NODE (value), NULL);
+
+  if (value->data[0].v_pointer != NULL)
+    return clutter_paint_node_ref (value->data[0].v_pointer);
+
+  return NULL;
+}
+
+static inline void
+clutter_paint_operation_clear (ClutterPaintOperation *op)
+{
+  switch (op->opcode)
+    {
+    case PAINT_OP_INVALID:
+      break;
+
+    case PAINT_OP_TEX_RECT:
+      break;
+
+    case PAINT_OP_PATH:
+      if (op->op.path != NULL)
+        cogl_object_unref (op->op.path);
+      break;
+
+    case PAINT_OP_PRIMITIVE:
+      if (op->op.primitive != NULL)
+        cogl_object_unref (op->op.primitive);
+      break;
+    }
+}
+
+static void
+clutter_paint_operation_to_string (const ClutterPaintOperation *op,
+                                   GString                     *buf,
+                                   int                          level)
+{
+  int i;
+
+  for (i = 0; i < level; i++)
+    g_string_append (buf, "  ");
+
+  g_string_append (buf, "{ ");
+
+  switch (op->opcode)
+    {
+    case PAINT_OP_INVALID:
+      break;
+
+    case PAINT_OP_TEX_RECT:
+      g_string_append_printf (buf, "\"texrect\" : [ %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f ]",
+                              op->op.texrect[0],
+                              op->op.texrect[1],
+                              op->op.texrect[2],
+                              op->op.texrect[3],
+                              op->op.texrect[4],
+                              op->op.texrect[5],
+                              op->op.texrect[6],
+                              op->op.texrect[7]);
+      break;
+
+    case PAINT_OP_PATH:
+      g_string_append_printf (buf, "\"path\" : \"0x%p\"", op->op.path);
+      break;
+
+    case PAINT_OP_PRIMITIVE:
+      g_string_append_printf (buf, "\"primitive\" : \"0x%p\"", op->op.primitive);
+      break;
+    }
+
+  g_string_append (buf, " }");
+}
+
+static inline void
+clutter_paint_op_init_tex_rect (ClutterPaintOperation *op,
+                                const ClutterActorBox *rect,
+                                float                  x_1,
+                                float                  y_1,
+                                float                  x_2,
+                                float                  y_2)
+{
+  clutter_paint_operation_clear (op);
+
+  op->opcode = PAINT_OP_TEX_RECT;
+  op->op.texrect[0] = rect->x1;
+  op->op.texrect[1] = rect->y1;
+  op->op.texrect[2] = rect->x2;
+  op->op.texrect[3] = rect->y2;
+  op->op.texrect[4] = x_1;
+  op->op.texrect[5] = y_1;
+  op->op.texrect[6] = x_2;
+  op->op.texrect[7] = y_2;
+}
+
+static inline void
+clutter_paint_op_init_path (ClutterPaintOperation *op,
+                            CoglPath              *path)
+{
+  clutter_paint_operation_clear (op);
+
+  op->opcode = PAINT_OP_PATH;
+  op->op.path = cogl_object_ref (path);
+}
+
+static inline void
+clutter_paint_op_init_primitive (ClutterPaintOperation *op,
+                                 CoglPrimitive         *primitive)
+{
+  clutter_paint_operation_clear (op);
+
+  op->opcode = PAINT_OP_PRIMITIVE;
+  op->op.primitive = cogl_object_ref (primitive);
+}
+
+static inline void
+clutter_paint_node_maybe_init_operations (ClutterPaintNode *node)
+{
+  if (node->operations != NULL)
+    return;
+
+  node->operations =
+    g_array_new (FALSE, FALSE, sizeof (ClutterPaintOperation));
+}
+
+/**
+ * clutter_paint_node_add_rectangle:
+ * @node: a #ClutterPaintNode
+ * @rect: a #ClutterActorBox
+ *
+ * Adds a rectangle region to the @node, as described by the
+ * passed @rect.
+ *
+ * Since: 1.10
+ */
+void
+clutter_paint_node_add_rectangle (ClutterPaintNode      *node,
+                                  const ClutterActorBox *rect)
+{
+  ClutterPaintOperation operation;
+
+  g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
+  g_return_if_fail (rect != NULL);
+
+  clutter_paint_node_maybe_init_operations (node);
+
+  clutter_paint_op_init_tex_rect (&operation, rect, 0.0, 0.0, 1.0, 1.0);
+  g_array_append_val (node->operations, operation);
+}
+
+/**
+ * clutter_paint_node_add_texture_rectangle:
+ * @node: a #ClutterPaintNode
+ * @rect: a #ClutterActorBox
+ * @x_1: the left X coordinate of the texture
+ * @y_1: the top Y coordinate of the texture
+ * @x_2: the right X coordinate of the texture
+ * @y_2: the bottom Y coordinate of the texture
+ *
+ * Adds a rectangle region to the @node, with texture coordinates.
+ *
+ * Since: 1.10
+ */
+void
+clutter_paint_node_add_texture_rectangle (ClutterPaintNode      *node,
+                                          const ClutterActorBox *rect,
+                                          float                  x_1,
+                                          float                  y_1,
+                                          float                  x_2,
+                                          float                  y_2)
+{
+  ClutterPaintOperation operation;
+
+  g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
+  g_return_if_fail (rect != NULL);
+
+  clutter_paint_node_maybe_init_operations (node);
+
+  clutter_paint_op_init_tex_rect (&operation, rect, x_1, y_1, x_2, y_2);
+  g_array_append_val (node->operations, operation);
+}
+
+/**
+ * clutter_paint_node_add_path:
+ * @node: a #ClutterPaintNode
+ * @path: a Cogl path
+ *
+ * Adds a region described as a path to the @node.
+ *
+ * This function acquires a reference on the passed @path, so it
+ * is safe to call cogl_object_unref() when it returns.
+ *
+ * Since: 1.10
+ * Stability: unstable
+ */
+void
+clutter_paint_node_add_path (ClutterPaintNode *node,
+                             CoglPath         *path)
+{
+  ClutterPaintOperation operation;
+
+  g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
+  g_return_if_fail (cogl_is_path (path));
+
+  clutter_paint_node_maybe_init_operations (node);
+
+  clutter_paint_op_init_path (&operation, path);
+  g_array_append_val (node->operations, operation);
+}
+
+/**
+ * clutter_paint_node_add_primitive:
+ * @node: a #ClutterPaintNode
+ * @primitive: a Cogl primitive
+ *
+ * Adds a region described by a Cogl primitive to the @node.
+ *
+ * This function acquires a reference on @primitive, so it is safe
+ * to call cogl_object_unref() when it returns.
+ *
+ * Since: 1.10
+ */
+void
+clutter_paint_node_add_primitive (ClutterPaintNode *node,
+                                  CoglPrimitive    *primitive)
+{
+  ClutterPaintOperation operation;
+
+  g_return_if_fail (CLUTTER_IS_PAINT_NODE (node));
+  g_return_if_fail (cogl_is_primitive (primitive));
+
+  clutter_paint_node_maybe_init_operations (node);
+
+  clutter_paint_op_init_primitive (&operation, primitive);
+  g_array_append_val (node->operations, operation);
+}
+
+/*< private >
+ * _clutter_paint_node_paint:
+ * @node: a #ClutterPaintNode
+ *
+ * Paints the @node using the class implementation, traversing
+ * its children, if any.
+ */
+void
+_clutter_paint_node_paint (ClutterPaintNode *node)
+{
+  ClutterPaintNodeClass *klass = CLUTTER_PAINT_NODE_GET_CLASS (node);
+  ClutterPaintNode *iter;
+  gboolean res;
+
+  res = klass->pre_draw (node);
+
+  if (res)
+    {
+      klass->draw (node);
+    }
+
+  for (iter = node->first_child;
+       iter != NULL;
+       iter = iter->next_sibling)
+    {
+      _clutter_paint_node_paint (iter);
+    }
+
+  if (res)
+    {
+      klass->post_draw (node);
+    }
+}
+
+static void
+clutter_paint_node_to_string (ClutterPaintNode *node,
+                              GString          *buf,
+                              int               level)
+{
+  ClutterPaintNode *iter;
+  int i;
+
+  for (i = 0; i < level; i++)
+    g_string_append (buf, "  ");
+
+  g_string_append_c (buf, '"');
+  g_string_append (buf, g_type_name (G_TYPE_FROM_INSTANCE (node)));
+  g_string_append_c (buf, '"');
+
+  g_string_append (buf, " : {\n");
+
+  if (node->name != NULL)
+    {
+      for (i = 0; i < level + 1; i++)
+        g_string_append (buf, "  ");
+
+      g_string_append_printf (buf, "\"name\" : \"%s\"", node->name);
+
+      if (node->operations != NULL ||
+          node->first_child != NULL)
+        g_string_append_c (buf, ',');
+
+      g_string_append_c (buf, '\n');
+    }
+
+  if (node->operations != NULL)
+    {
+      guint o;
+
+      for (i = 0; i < level + 1; i++)
+        g_string_append (buf, "  ");
+
+      g_string_append (buf, "\"operations\" : [\n");
+
+      for (o = 0; o < node->operations->len; o++)
+        {
+          const ClutterPaintOperation *op;
+
+          op = &g_array_index (node->operations, ClutterPaintOperation, o);
+          clutter_paint_operation_to_string (op, buf, level + 2);
+
+          if ((o + 1) != node->operations->len)
+            g_string_append_c (buf, ',');
+
+          g_string_append_c (buf, '\n');
+        }
+
+      for (i = 0; i < level + 1; i++)
+        g_string_append (buf, "  ");
+
+      g_string_append (buf, "]");
+
+      if (node->first_child != NULL)
+        g_string_append_c (buf, ',');
+
+      g_string_append_c (buf, '\n');
+    }
+
+  if (node->first_child == NULL)
+    goto out;
+
+  for (i = 0; i < level + 1; i++)
+    g_string_append (buf, "  ");
+
+  g_string_append (buf, "\"children\" : [\n");
+
+  for (iter = node->first_child;
+       iter != NULL;
+       iter = iter->next_sibling)
+    {
+      clutter_paint_node_to_string (iter, buf, level + 2);
+
+      if (iter->next_sibling != NULL)
+        g_string_append (buf, ",\n");
+      else
+        g_string_append (buf, "\n");
+    }
+
+  for (i = 0; i < level + 1; i++)
+    g_string_append (buf, "  ");
+
+  g_string_append (buf, "]\n");
+
+out:
+  for (i = 0; i < level; i++)
+    g_string_append (buf, "  ");
+
+  g_string_append (buf, "}");
+}
+
+void
+_clutter_paint_node_dump_tree (ClutterPaintNode *node)
+{
+#ifdef CLUTTER_ENABLE_DEBUG
+  GString *buf = g_string_sized_new (1024);
+
+  clutter_paint_node_to_string (node, buf, 0);
+
+  CLUTTER_NOTE (PAINT, "Render tree:\n%s", buf->str);
+
+  g_string_free (buf, TRUE);
+#endif /* CLUTTER_ENABLE_DEBUG */
+}
+
+gpointer
+_clutter_paint_node_internal (GType gtype)
+{
+  g_return_val_if_fail (g_type_is_a (gtype, CLUTTER_TYPE_PAINT_NODE), NULL);
+
+  _clutter_paint_node_init_types ();
+
+  return (gpointer) g_type_create_instance (gtype);
+}
diff --git a/clutter/clutter-paint-node.h b/clutter/clutter-paint-node.h
new file mode 100644
index 0000000..46cc7d3
--- /dev/null
+++ b/clutter/clutter-paint-node.h
@@ -0,0 +1,95 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2011  Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Emmanuele Bassi <ebassi linux intel com>
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only <clutter/clutter.h> can be included directly."
+#endif
+
+#ifndef __CLUTTER_PAINT_NODE_H__
+#define __CLUTTER_PAINT_NODE_H__
+
+#include <cogl/cogl.h>
+#include <clutter/clutter-types.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_PAINT_NODE                 (clutter_paint_node_get_type ())
+#define CLUTTER_PAINT_NODE(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_PAINT_NODE, ClutterPaintNode))
+#define CLUTTER_IS_PAINT_NODE(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_PAINT_NODE))
+
+typedef struct _ClutterPaintNodePrivate ClutterPaintNodePrivate;
+typedef struct _ClutterPaintNodeClass   ClutterPaintNodeClass;
+
+GType clutter_paint_node_get_type (void) G_GNUC_CONST;
+
+ClutterPaintNode *      clutter_paint_node_ref                          (ClutterPaintNode      *node);
+void                    clutter_paint_node_unref                        (ClutterPaintNode      *node);
+
+void                    clutter_paint_node_set_name                     (ClutterPaintNode      *node,
+                                                                         const char            *name);
+
+void                    clutter_paint_node_add_child                    (ClutterPaintNode      *node,
+                                                                         ClutterPaintNode      *child);
+void                    clutter_paint_node_remove_child                 (ClutterPaintNode      *node,
+                                                                         ClutterPaintNode      *child);
+void                    clutter_paint_node_replace_child                (ClutterPaintNode      *node,
+                                                                         ClutterPaintNode      *old_child,
+                                                                         ClutterPaintNode      *new_child);
+void                    clutter_paint_node_remove_all                   (ClutterPaintNode      *node);
+
+guint                   clutter_paint_node_get_n_children               (ClutterPaintNode      *node);
+
+ClutterPaintNode *      clutter_paint_node_get_first_child              (ClutterPaintNode      *node);
+ClutterPaintNode *      clutter_paint_node_get_previous_sibling         (ClutterPaintNode      *node);
+ClutterPaintNode *      clutter_paint_node_get_next_sibling             (ClutterPaintNode      *node);
+ClutterPaintNode *      clutter_paint_node_get_last_child               (ClutterPaintNode      *node);
+ClutterPaintNode *      clutter_paint_node_get_parent                   (ClutterPaintNode      *node);
+
+void                    clutter_paint_node_add_rectangle                (ClutterPaintNode      *node,
+                                                                         const ClutterActorBox *rect);
+void                    clutter_paint_node_add_texture_rectangle        (ClutterPaintNode      *node,
+                                                                         const ClutterActorBox *rect,
+                                                                         float                  x_1,
+                                                                         float                  y_1,
+                                                                         float                  x_2,
+                                                                         float                  y_2);
+#if defined(COGL_ENABLE_EXPERIMENTAL_2_0_API) && defined(CLUTTER_ENABLE_EXPERIMENTAL_API)
+void                    clutter_paint_node_add_path                     (ClutterPaintNode      *node,
+                                                                         CoglPath              *path);
+void                    clutter_paint_node_add_primitive                (ClutterPaintNode      *node,
+                                                                         CoglPrimitive         *primitive);
+#endif /* COGL_ENABLE_EXPERIMENTAL_2_0_API && CLUTTER_ENABLE_EXPERIMENTAL_API */
+
+#define CLUTTER_VALUE_HOLDS_PAINT_NODE(value)   (G_VALUE_HOLDS (value, CLUTTER_TYPE_PAINT_NODE))
+
+void                    clutter_value_set_paint_node                    (GValue                *value,
+                                                                         gpointer               node);
+void                    clutter_value_take_paint_node                   (GValue                *value,
+                                                                         gpointer               node);
+gpointer                clutter_value_get_paint_node                    (const GValue          *value);
+gpointer                clutter_value_dup_paint_node                    (const GValue          *value);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_PAINT_NODE_H__ */
diff --git a/clutter/clutter-paint-nodes.c b/clutter/clutter-paint-nodes.c
new file mode 100644
index 0000000..c0868b3
--- /dev/null
+++ b/clutter/clutter-paint-nodes.c
@@ -0,0 +1,1124 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2011  Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Emmanuele Bassi <ebassi linux intel com>
+ */
+
+/**
+ * SECTION:clutter-paint-nodes
+ * @Title: Paint Nodes
+ * @Short_Description: ClutterPaintNode implementations
+ *
+ * Clutter provides a set of predefined #ClutterPaintNode implementations
+ * that cover all the state changes available.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define CLUTTER_ENABLE_EXPERIMENTAL_API
+
+#include "clutter-paint-node-private.h"
+
+#include <pango/pango.h>
+#include <cogl/cogl.h>
+
+#include "clutter-color.h"
+#include "clutter-debug.h"
+#include "clutter-private.h"
+
+#include "clutter-paint-nodes.h"
+
+static CoglPipeline *default_color_pipeline   = NULL;
+static CoglPipeline *default_texture_pipeline = NULL;
+
+/*< private >
+ * _clutter_paint_node_init_types:
+ *
+ * Initializes the required types for ClutterPaintNode subclasses
+ */
+void
+_clutter_paint_node_init_types (void)
+{
+  CoglContext *ctx;
+  CoglColor cogl_color;
+  GType node_type G_GNUC_UNUSED;
+
+  if (G_LIKELY (default_color_pipeline != NULL))
+    return;
+
+  ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+
+  node_type = clutter_paint_node_get_type ();
+
+  cogl_color_init_from_4f (&cogl_color, 1.0, 1.0, 1.0, 1.0);
+
+  default_color_pipeline = cogl_pipeline_new (ctx);
+  cogl_pipeline_set_color (default_color_pipeline, &cogl_color);
+
+  default_texture_pipeline = cogl_pipeline_new (ctx);
+  cogl_pipeline_set_layer_null_texture (default_texture_pipeline, 0,
+                                        COGL_TEXTURE_TYPE_2D);
+  cogl_pipeline_set_color (default_texture_pipeline, &cogl_color);
+}
+
+/*
+ * Root node, private
+ *
+ * any frame can only have a since RootNode instance for each
+ * top-level actor.
+ */
+
+#define clutter_root_node_get_type      _clutter_root_node_get_type
+
+typedef struct _ClutterRootNode         ClutterRootNode;
+typedef struct _ClutterPaintNodeClass   ClutterRootNodeClass;
+
+struct _ClutterRootNode
+{
+  ClutterPaintNode parent_instance;
+
+  CoglFramebuffer *framebuffer;
+
+  CoglBufferBit clear_flags;
+  CoglColor clear_color;
+  CoglMatrix modelview;
+};
+
+GType _clutter_root_node_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (ClutterRootNode, clutter_root_node, CLUTTER_TYPE_PAINT_NODE)
+
+static gboolean
+clutter_root_node_pre_draw (ClutterPaintNode *node)
+{
+  ClutterRootNode *rnode = (ClutterRootNode *) node;
+
+  cogl_push_matrix ();
+
+  cogl_framebuffer_set_modelview_matrix (rnode->framebuffer,
+                                         &rnode->modelview);
+
+  cogl_framebuffer_clear (rnode->framebuffer,
+                          rnode->clear_flags,
+                          &rnode->clear_color);
+
+  return TRUE;
+}
+
+static void
+clutter_root_node_post_draw (ClutterPaintNode *node)
+{
+  cogl_pop_matrix ();
+}
+
+static void
+clutter_root_node_finalize (ClutterPaintNode *node)
+{
+  ClutterRootNode *rnode = (ClutterRootNode *) node;
+
+  cogl_object_unref (rnode->framebuffer);
+
+  CLUTTER_PAINT_NODE_CLASS (clutter_root_node_parent_class)->finalize (node);
+}
+
+static void
+clutter_root_node_class_init (ClutterRootNodeClass *klass)
+{
+  ClutterPaintNodeClass *node_class = CLUTTER_PAINT_NODE_CLASS (klass);
+
+  node_class->pre_draw = clutter_root_node_pre_draw;
+  node_class->post_draw = clutter_root_node_post_draw;
+  node_class->finalize = clutter_root_node_finalize;
+}
+
+static void
+clutter_root_node_init (ClutterRootNode *self)
+{
+  cogl_matrix_init_identity (&self->modelview);
+}
+
+ClutterPaintNode *
+_clutter_root_node_new (CoglFramebuffer    *framebuffer,
+                        const ClutterColor *clear_color,
+                        CoglBufferBit       clear_flags,
+                        const CoglMatrix   *matrix)
+{
+  ClutterRootNode *res;
+
+  res = _clutter_paint_node_internal (_clutter_root_node_get_type ());
+
+  cogl_color_init_from_4ub (&res->clear_color,
+                            clear_color->red,
+                            clear_color->green,
+                            clear_color->blue,
+                            clear_color->alpha);
+  cogl_color_premultiply (&res->clear_color);
+
+  res->framebuffer = cogl_object_ref (framebuffer);
+  res->clear_flags = clear_flags;
+  res->modelview = *matrix;
+
+  return (ClutterPaintNode *) res;
+}
+
+/*
+ * Transform node
+ *
+ * A private PaintNode, that changes the modelview of its child
+ * nodes.
+ */
+
+#define clutter_transform_node_get_type _clutter_transform_node_get_type
+
+typedef struct _ClutterTransformNode {
+  ClutterPaintNode parent_instance;
+
+  CoglMatrix modelview;
+} ClutterTransformNode;
+
+typedef struct _ClutterPaintNodeClass   ClutterTransformNodeClass;
+
+GType _clutter_transform_node_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (ClutterTransformNode, clutter_transform_node, CLUTTER_TYPE_PAINT_NODE)
+
+static gboolean
+clutter_transform_node_pre_draw (ClutterPaintNode *node)
+{
+  ClutterTransformNode *tnode = (ClutterTransformNode *) node;
+  CoglMatrix matrix;
+
+  cogl_push_matrix ();
+
+  cogl_get_modelview_matrix (&matrix);
+  cogl_matrix_multiply (&matrix, &matrix, &tnode->modelview);
+  cogl_set_modelview_matrix (&matrix);
+
+  return TRUE;
+}
+
+static void
+clutter_transform_node_post_draw (ClutterPaintNode *node)
+{
+  cogl_pop_matrix ();
+}
+
+static void
+clutter_transform_node_class_init (ClutterTransformNodeClass *klass)
+{
+  ClutterPaintNodeClass *node_class;
+
+  node_class = CLUTTER_PAINT_NODE_CLASS (klass);
+  node_class->pre_draw = clutter_transform_node_pre_draw;
+  node_class->post_draw = clutter_transform_node_post_draw;
+}
+
+static void
+clutter_transform_node_init (ClutterTransformNode *self)
+{
+  cogl_matrix_init_identity (&self->modelview);
+}
+
+ClutterPaintNode *
+_clutter_transform_node_new (const CoglMatrix *modelview)
+{
+  ClutterTransformNode *res;
+
+  res = _clutter_paint_node_internal (_clutter_transform_node_get_type ());
+
+  if (modelview != NULL)
+    res->modelview = *modelview;
+
+  return (ClutterPaintNode *) res;
+}
+
+/*
+ * Dummy node, private
+ *
+ * an empty node, used temporarily until we can drop API compatibility,
+ * and we'll be able to build a full render tree for each frame.
+ */
+
+#define clutter_dummy_node_get_type      _clutter_dummy_node_get_type
+
+typedef struct _ClutterDummyNode        ClutterDummyNode;
+typedef struct _ClutterPaintNodeClass   ClutterDummyNodeClass;
+
+struct _ClutterDummyNode
+{
+  ClutterPaintNode parent_instance;
+};
+
+GType _clutter_dummy_node_get_type (void) G_GNUC_CONST;
+
+G_DEFINE_TYPE (ClutterDummyNode, clutter_dummy_node, CLUTTER_TYPE_PAINT_NODE)
+
+static gboolean
+clutter_dummy_node_pre_draw (ClutterPaintNode *node)
+{
+  return TRUE;
+}
+
+static void
+clutter_dummy_node_class_init (ClutterDummyNodeClass *klass)
+{
+  ClutterPaintNodeClass *node_class = CLUTTER_PAINT_NODE_CLASS (klass);
+
+  node_class->pre_draw = clutter_dummy_node_pre_draw;
+}
+
+static void
+clutter_dummy_node_init (ClutterDummyNode *self)
+{
+}
+
+ClutterPaintNode *
+_clutter_dummy_node_new (void)
+{
+  return _clutter_paint_node_internal (_clutter_dummy_node_get_type ());
+}
+
+/*
+ * Pipeline node
+ */
+
+struct _ClutterPipelineNode
+{
+  ClutterPaintNode parent_instance;
+
+  CoglPipeline *pipeline;
+};
+
+struct _ClutterPipelineNodeClass
+{
+  ClutterPaintNodeClass parent_class;
+};
+
+G_DEFINE_TYPE (ClutterPipelineNode, clutter_pipeline_node, CLUTTER_TYPE_PAINT_NODE)
+
+static void
+clutter_pipeline_node_finalize (ClutterPaintNode *node)
+{
+  ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (node);
+
+  if (pnode->pipeline != NULL)
+    cogl_object_unref (pnode->pipeline);
+
+  CLUTTER_PAINT_NODE_CLASS (clutter_pipeline_node_parent_class)->finalize (node);
+}
+
+static gboolean
+clutter_pipeline_node_pre_draw (ClutterPaintNode *node)
+{
+  ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (node);
+
+  if (node->operations != NULL &&
+      pnode->pipeline != NULL)
+    {
+      cogl_push_source (pnode->pipeline);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+clutter_pipeline_node_draw (ClutterPaintNode *node)
+{
+  ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (node);
+  guint i;
+
+  if (pnode->pipeline == NULL)
+    return;
+
+  if (node->operations == NULL)
+    return;
+
+  for (i = 0; i < node->operations->len; i++)
+    {
+      const ClutterPaintOperation *op;
+
+      op = &g_array_index (node->operations, ClutterPaintOperation, i);
+
+      switch (op->opcode)
+        {
+        case PAINT_OP_INVALID:
+          break;
+
+        case PAINT_OP_TEX_RECT:
+          cogl_rectangle_with_texture_coords (op->op.texrect[0],
+                                              op->op.texrect[1],
+                                              op->op.texrect[2],
+                                              op->op.texrect[3],
+                                              op->op.texrect[4],
+                                              op->op.texrect[5],
+                                              op->op.texrect[6],
+                                              op->op.texrect[7]);
+          break;
+
+        case PAINT_OP_PATH:
+          cogl_path_fill (op->op.path);
+          break;
+
+        case PAINT_OP_PRIMITIVE:
+          {
+            CoglFramebuffer *fb = cogl_get_draw_framebuffer ();
+
+            cogl_framebuffer_draw_primitive (fb, pnode->pipeline,
+                                             op->op.primitive);
+          }
+          break;
+        }
+    }
+}
+
+static void
+clutter_pipeline_node_post_draw (ClutterPaintNode *node)
+{
+  cogl_pop_source ();
+}
+
+static void
+clutter_pipeline_node_class_init (ClutterPipelineNodeClass *klass)
+{
+  ClutterPaintNodeClass *node_class;
+
+  node_class = CLUTTER_PAINT_NODE_CLASS (klass);
+  node_class->pre_draw = clutter_pipeline_node_pre_draw;
+  node_class->draw = clutter_pipeline_node_draw;
+  node_class->post_draw = clutter_pipeline_node_post_draw;
+  node_class->finalize = clutter_pipeline_node_finalize;
+}
+
+static void
+clutter_pipeline_node_init (ClutterPipelineNode *self)
+{
+}
+
+/**
+ * clutter_pipeline_node_new:
+ * @pipeline: (allow-none): a Cogl pipeline state object, or %NULL
+ *
+ * Creates a new #ClutterPaintNode that will use the @pipeline to
+ * paint its contents.
+ *
+ * This function will acquire a reference on the passed @pipeline,
+ * so it is safe to call cogl_object_unref() when it returns.
+ *
+ * Return value: (transfer full): the newly created #ClutterPaintNode.
+ *   Use clutter_paint_node_unref() when done.
+ *
+ * Since: 1.10
+ */
+ClutterPaintNode *
+clutter_pipeline_node_new (CoglPipeline *pipeline)
+{
+  ClutterPipelineNode *res;
+
+  g_return_val_if_fail (pipeline == NULL || cogl_is_pipeline (pipeline), NULL);
+
+  res = _clutter_paint_node_internal (CLUTTER_TYPE_PIPELINE_NODE);
+
+  if (pipeline != NULL)
+    res->pipeline = cogl_object_ref (pipeline);
+
+  return (ClutterPaintNode *) res;
+}
+
+/*
+ * Color node
+ */
+
+/**
+ * ClutterColorNode:
+ *
+ * The <structname>ClutterColorNode</structname> structure is an opaque
+ * type whose members cannot be directly accessed.
+ *
+ * Since: 1.10
+ */
+struct _ClutterColorNode
+{
+  ClutterPipelineNode parent_instance;
+};
+
+/**
+ * ClutterColorNode:
+ *
+ * The <structname>ClutterColorNodeClass</structname> structure is an
+ * opaque type whose members cannot be directly accessed.
+ *
+ * Since: 1.10
+ */
+struct _ClutterColorNodeClass
+{
+  ClutterPipelineNodeClass parent_class;
+};
+
+G_DEFINE_TYPE (ClutterColorNode, clutter_color_node, CLUTTER_TYPE_PIPELINE_NODE)
+
+static void
+clutter_color_node_class_init (ClutterColorNodeClass *klass)
+{
+
+}
+
+static void
+clutter_color_node_init (ClutterColorNode *cnode)
+{
+  ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (cnode);
+
+  g_assert (default_color_pipeline != NULL);
+  pnode->pipeline = cogl_pipeline_copy (default_color_pipeline);
+}
+
+/**
+ * clutter_color_node_new:
+ * @color: (allow-none): the color to paint, or %NULL
+ *
+ * Creates a new #ClutterPaintNode that will paint a solid color
+ * fill using @color.
+ *
+ * Return value: (transfer full): the newly created #ClutterPaintNode. Use
+ *   clutter_paint_node_unref() when done
+ *
+ * Since: 1.10
+ */
+ClutterPaintNode *
+clutter_color_node_new (const ClutterColor *color)
+{
+  ClutterPipelineNode *cnode;
+
+  cnode = _clutter_paint_node_internal (CLUTTER_TYPE_COLOR_NODE);
+
+  if (color != NULL)
+    {
+      CoglColor cogl_color;
+
+      cogl_color_init_from_4ub (&cogl_color,
+                                color->red,
+                                color->green,
+                                color->blue,
+                                color->alpha);
+      cogl_color_premultiply (&cogl_color);
+
+      cogl_pipeline_set_color (cnode->pipeline, &cogl_color);
+    }
+
+  return (ClutterPaintNode *) cnode;
+}
+
+/*
+ * Texture node
+ */
+
+/**
+ * ClutterTextureNode:
+ *
+ * The <structname>ClutterTextureNode</structname> structure is an opaque
+ * type whose members cannot be directly accessed.
+ *
+ * Since: 1.10
+ */
+struct _ClutterTextureNode
+{
+  ClutterPipelineNode parent_instance;
+};
+
+/**
+ * ClutterTextureNode:
+ *
+ * The <structname>ClutterTextureNodeClass</structname> structure is an
+ * opaque type whose members cannot be directly accessed.
+ *
+ * Since: 1.10
+ */
+struct _ClutterTextureNodeClass
+{
+  ClutterPipelineNodeClass parent_class;
+};
+
+G_DEFINE_TYPE (ClutterTextureNode, clutter_texture_node, CLUTTER_TYPE_PIPELINE_NODE)
+
+static void
+clutter_texture_node_class_init (ClutterTextureNodeClass *klass)
+{
+}
+
+static void
+clutter_texture_node_init (ClutterTextureNode *self)
+{
+  ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (self);
+
+  g_assert (default_texture_pipeline != NULL);
+  pnode->pipeline = cogl_pipeline_copy (default_texture_pipeline);
+}
+
+/**
+ * clutter_texture_node_new:
+ * @texture: (allow-none): a #CoglTexture
+ * @color: (allow-none): a #ClutterColor
+ *
+ * Creates a new #ClutterPaintNode that will paint the passed @texture.
+ *
+ * This function will take a reference on @texture, so it is safe to
+ * call cogl_object_unref() on @texture when it returns.
+ *
+ * Return value: (transfer full): the newly created #ClutterPaintNode.
+ *   Use clutter_paint_node_unref() when done
+ *
+ * Since: 1.10
+ */
+ClutterPaintNode *
+clutter_texture_node_new (CoglTexture        *texture,
+                          const ClutterColor *color)
+{
+  ClutterPipelineNode *tnode;
+
+  g_return_val_if_fail (texture == NULL || cogl_is_texture (texture), NULL);
+
+  tnode = _clutter_paint_node_internal (CLUTTER_TYPE_TEXTURE_NODE);
+
+  if (texture != NULL)
+    cogl_pipeline_set_layer_texture (tnode->pipeline, 0, texture);
+
+  if (color != NULL)
+    {
+      CoglColor cogl_color;
+
+      cogl_color_init_from_4ub (&cogl_color,
+                                color->red,
+                                color->green,
+                                color->blue,
+                                color->alpha);
+      cogl_color_premultiply (&cogl_color);
+      cogl_pipeline_set_color (tnode->pipeline, &cogl_color);
+    }
+
+  return (ClutterPaintNode *) tnode;
+}
+
+/*
+ * Text node
+ */
+
+struct _ClutterTextNode
+{
+  ClutterPaintNode parent_instance;
+
+  PangoLayout *layout;
+  CoglColor color;
+};
+
+struct _ClutterTextNodeClass
+{
+  ClutterPaintNodeClass parent_class;
+};
+
+G_DEFINE_TYPE (ClutterTextNode, clutter_text_node, CLUTTER_TYPE_PAINT_NODE)
+
+static void
+clutter_text_node_finalize (ClutterPaintNode *node)
+{
+  ClutterTextNode *tnode = CLUTTER_TEXT_NODE (node);
+
+  if (tnode->layout != NULL)
+    g_object_unref (tnode->layout);
+
+  CLUTTER_PAINT_NODE_CLASS (clutter_text_node_parent_class)->finalize (node);
+}
+
+static gboolean
+clutter_text_node_pre_draw (ClutterPaintNode *node)
+{
+  ClutterTextNode *tnode = CLUTTER_TEXT_NODE (node);
+
+  return tnode->layout != NULL;
+}
+
+static void
+clutter_text_node_draw (ClutterPaintNode *node)
+{
+  ClutterTextNode *tnode = CLUTTER_TEXT_NODE (node);
+  PangoRectangle ink_extents;
+  guint i;
+
+  if (node->operations == NULL)
+    return;
+
+  pango_layout_get_pixel_extents (tnode->layout, &ink_extents, NULL);
+
+  for (i = 0; i < node->operations->len; i++)
+    {
+      const ClutterPaintOperation *op;
+      float op_width, op_height;
+      gboolean clipped = FALSE;
+
+      op = &g_array_index (node->operations, ClutterPaintOperation, i);
+
+      switch (op->opcode)
+        {
+        case PAINT_OP_TEX_RECT:
+          op_width = op->op.texrect[2] - op->op.texrect[0];
+          op_height = op->op.texrect[3] - op->op.texrect[1];
+
+          /* if the primitive size was smaller than the layout,
+           * we clip the layout when drawin, to avoid spilling
+           * it out
+           */
+          if (ink_extents.width > op_width ||
+              ink_extents.height > op_height)
+            {
+              cogl_clip_push_rectangle (op->op.texrect[0],
+                                        op->op.texrect[1],
+                                        op->op.texrect[2],
+                                        op->op.texrect[3]);
+              clipped = TRUE;
+            }
+
+          cogl_pango_render_layout (tnode->layout,
+                                    op->op.texrect[0],
+                                    op->op.texrect[1],
+                                    &tnode->color,
+                                    0);
+
+          if (clipped)
+            cogl_clip_pop ();
+          break;
+
+        case PAINT_OP_PATH:
+        case PAINT_OP_PRIMITIVE:
+        case PAINT_OP_INVALID:
+          break;
+        }
+    }
+}
+
+static void
+clutter_text_node_class_init (ClutterTextNodeClass *klass)
+{
+  ClutterPaintNodeClass *node_class = CLUTTER_PAINT_NODE_CLASS (klass);
+
+  node_class->pre_draw = clutter_text_node_pre_draw;
+  node_class->draw = clutter_text_node_draw;
+  node_class->finalize = clutter_text_node_finalize;
+}
+
+static void
+clutter_text_node_init (ClutterTextNode *self)
+{
+  cogl_color_init_from_4f (&self->color, 0.0, 0.0, 0.0, 1.0);
+}
+
+/**
+ * clutter_text_node_new:
+ * @layout: (allow-none): a #PangoLayout, or %NULL
+ * @color: (allow-none): the color used to paint the layout,
+ *   or %NULL
+ *
+ * Creates a new #ClutterPaintNode that will paint a #PangoLayout
+ * with the given color.
+ *
+ * This function takes a reference on the passed @layout, so it
+ * is safe to call g_object_unref() after it returns.
+ *
+ * Return value: (transfer full): the newly created #ClutterPaintNode.
+ *   Use clutter_paint_node_unref() when done
+ *
+ * Since: 1.10
+ */
+ClutterPaintNode *
+clutter_text_node_new (PangoLayout        *layout,
+                       const ClutterColor *color)
+{
+  ClutterTextNode *res;
+
+  g_return_val_if_fail (layout == NULL || PANGO_IS_LAYOUT (layout), NULL);
+
+  res = _clutter_paint_node_internal (CLUTTER_TYPE_TEXT_NODE);
+
+  if (layout != NULL)
+    res->layout = g_object_ref (layout);
+
+  if (color != NULL)
+    {
+      cogl_color_init_from_4ub (&res->color,
+                                color->red,
+                                color->green,
+                                color->blue,
+                                color->alpha);
+    }
+
+  return (ClutterPaintNode *) res;
+}
+
+/*
+ * Clip node
+ */
+struct _ClutterClipNode
+{
+  ClutterPaintNode parent_instance;
+};
+
+struct _ClutterClipNodeClass
+{
+  ClutterPaintNodeClass parent_class;
+};
+
+G_DEFINE_TYPE (ClutterClipNode, clutter_clip_node, CLUTTER_TYPE_PAINT_NODE)
+
+static gboolean
+clutter_clip_node_pre_draw (ClutterPaintNode *node)
+{
+  gboolean retval = FALSE;
+  CoglFramebuffer *fb;
+  guint i;
+
+  if (node->operations == NULL)
+    return FALSE;
+
+  fb = cogl_get_draw_framebuffer ();
+
+  for (i = 0; i < node->operations->len; i++)
+    {
+      const ClutterPaintOperation *op;
+
+      op = &g_array_index (node->operations, ClutterPaintOperation, i);
+
+      switch (op->opcode)
+        {
+        case PAINT_OP_TEX_RECT:
+          cogl_framebuffer_push_rectangle_clip (fb,
+                                                op->op.texrect[0],
+                                                op->op.texrect[1],
+                                                op->op.texrect[2],
+                                                op->op.texrect[3]);
+          retval = TRUE;
+          break;
+
+        case PAINT_OP_PATH:
+          cogl_framebuffer_push_path_clip (fb, op->op.path);
+          retval = TRUE;
+          break;
+
+        case PAINT_OP_PRIMITIVE:
+        case PAINT_OP_INVALID:
+          break;
+        }
+    }
+
+  return retval;
+}
+
+static void
+clutter_clip_node_post_draw (ClutterPaintNode *node)
+{
+  CoglFramebuffer *fb;
+  guint i;
+
+  if (node->operations == NULL)
+    return;
+
+  fb = cogl_get_draw_framebuffer ();
+
+  for (i = 0; i < node->operations->len; i++)
+    {
+      const ClutterPaintOperation *op;
+
+      op = &g_array_index (node->operations, ClutterPaintOperation, i);
+
+      switch (op->opcode)
+        {
+        case PAINT_OP_PATH:
+        case PAINT_OP_TEX_RECT:
+          cogl_framebuffer_pop_clip (fb);
+          break;
+
+        case PAINT_OP_PRIMITIVE:
+        case PAINT_OP_INVALID:
+          break;
+        }
+    }
+}
+
+static void
+clutter_clip_node_class_init (ClutterClipNodeClass *klass)
+{
+  ClutterPaintNodeClass *node_class;
+
+  node_class = CLUTTER_PAINT_NODE_CLASS (klass);
+  node_class->pre_draw = clutter_clip_node_pre_draw;
+  node_class->post_draw = clutter_clip_node_post_draw;
+}
+
+static void
+clutter_clip_node_init (ClutterClipNode *self)
+{
+}
+
+/**
+ * clutter_clip_node_new:
+ *
+ * Creates a new #ClutterPaintNode that will clip its child
+ * nodes to the 2D regions added to it.
+ *
+ * Return value: (transfer full): the newly created #ClutterPaintNode.
+ *   Use clutter_paint_node_unref() when done.
+ *
+ * Since: 1.10
+ */
+ClutterPaintNode *
+clutter_clip_node_new (void)
+{
+  return _clutter_paint_node_internal (CLUTTER_TYPE_CLIP_NODE);
+}
+
+/*
+ * ClutterLayerNode
+ */
+
+struct _ClutterLayerNode
+{
+  ClutterPaintNode parent_instance;
+
+  cairo_rectangle_t viewport;
+
+  CoglMatrix projection;
+
+  float fbo_width;
+  float fbo_height;
+
+  CoglPipeline *state;
+  CoglFramebuffer *offscreen;
+  CoglTexture *texture;
+
+  guint8 opacity;
+};
+
+struct _ClutterLayerNodeClass
+{
+  ClutterPaintNodeClass parent_class;
+};
+
+G_DEFINE_TYPE (ClutterLayerNode, clutter_layer_node, CLUTTER_TYPE_PAINT_NODE)
+
+static gboolean
+clutter_layer_node_pre_draw (ClutterPaintNode *node)
+{
+  ClutterLayerNode *lnode = (ClutterLayerNode *) node;
+  CoglMatrix matrix;
+
+  /* if we were unable to create an offscreen buffer for this node, then
+   * we simply ignore it
+   */
+  if (lnode->offscreen == NULL)
+    return FALSE;
+
+  /* if no geometry was submitted for this node then we simply ignore it */
+  if (node->operations == NULL)
+    return FALSE;
+
+  /* copy the same modelview from the current framebuffer to the one we
+   * are going to use
+   */
+  cogl_get_modelview_matrix (&matrix);
+
+  cogl_push_framebuffer (lnode->offscreen);
+
+  cogl_framebuffer_set_modelview_matrix (lnode->offscreen, &matrix);
+
+  cogl_framebuffer_set_viewport (lnode->offscreen,
+                                 lnode->viewport.x,
+                                 lnode->viewport.y,
+                                 lnode->viewport.width,
+                                 lnode->viewport.height);
+
+  cogl_framebuffer_set_projection_matrix (lnode->offscreen,
+                                          &lnode->projection);
+
+  /* clear out the target framebuffer */
+  cogl_framebuffer_clear4f (lnode->offscreen,
+                            COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH,
+                            0.f, 0.f, 0.f, 0.f);
+
+  cogl_push_matrix ();
+
+  /* every draw operation after this point will happen an offscreen
+   * framebuffer
+   */
+
+  return TRUE;
+}
+
+static void
+clutter_layer_node_post_draw (ClutterPaintNode *node)
+{
+  ClutterLayerNode *lnode = CLUTTER_LAYER_NODE (node);
+  CoglFramebuffer *fb;
+  guint i;
+
+  /* switch to the previous framebuffer */
+  cogl_pop_matrix ();
+  cogl_pop_framebuffer ();
+
+  fb = cogl_get_draw_framebuffer ();
+
+  for (i = 0; i < node->operations->len; i++)
+    {
+      const ClutterPaintOperation *op;
+
+      op = &g_array_index (node->operations, ClutterPaintOperation, i);
+      switch (op->opcode)
+        {
+        case PAINT_OP_INVALID:
+          break;
+
+        case PAINT_OP_TEX_RECT:
+          /* now we need to paint the texture */
+          cogl_push_source (lnode->state);
+          cogl_rectangle_with_texture_coords (op->op.texrect[0],
+                                              op->op.texrect[1],
+                                              op->op.texrect[2],
+                                              op->op.texrect[3],
+                                              op->op.texrect[4],
+                                              op->op.texrect[5],
+                                              op->op.texrect[6],
+                                              op->op.texrect[7]);
+          cogl_pop_source ();
+          break;
+
+        case PAINT_OP_PATH:
+          cogl_push_source (lnode->state);
+          cogl_path_fill (op->op.path);
+          cogl_pop_source ();
+          break;
+
+        case PAINT_OP_PRIMITIVE:
+          cogl_framebuffer_draw_primitive (fb, lnode->state, op->op.primitive);
+          break;
+        }
+    }
+}
+
+static void
+clutter_layer_node_finalize (ClutterPaintNode *node)
+{
+  ClutterLayerNode *lnode = CLUTTER_LAYER_NODE (node);
+
+  if (lnode->state != NULL)
+    cogl_object_unref (lnode->state);
+
+  if (lnode->offscreen != NULL)
+    cogl_object_unref (lnode->offscreen);
+
+  CLUTTER_PAINT_NODE_CLASS (clutter_layer_node_parent_class)->finalize (node);
+}
+
+static void
+clutter_layer_node_class_init (ClutterLayerNodeClass *klass)
+{
+  ClutterPaintNodeClass *node_class;
+
+  node_class = CLUTTER_PAINT_NODE_CLASS (klass);
+  node_class->pre_draw = clutter_layer_node_pre_draw;
+  node_class->post_draw = clutter_layer_node_post_draw;
+  node_class->finalize = clutter_layer_node_finalize;
+}
+
+static void
+clutter_layer_node_init (ClutterLayerNode *self)
+{
+  cogl_matrix_init_identity (&self->projection);
+}
+
+/**
+ * clutter_layer_node_new:
+ * @projection: the projection matrix to use to set up the layer
+ * @viewport: (type cairo.Rectangle): the viewport to use to set up the layer
+ * @width: the width of the layer
+ * @height: the height of the layer
+ * @opacity: the opacity to be used when drawing the layer
+ *
+ * Creates a new #ClutterLayerNode.
+ *
+ * All children of this node will be painted inside a separate
+ * framebuffer; the framebuffer will then be painted using the
+ * given @opacity.
+ *
+ * Return value: (transfer full): the newly created #ClutterLayerNode.
+ *   Use clutter_paint_node_unref() when done.
+ *
+ * Since: 1.10
+ */
+ClutterPaintNode *
+clutter_layer_node_new (const CoglMatrix        *projection,
+                        const cairo_rectangle_t *viewport,
+                        float                    width,
+                        float                    height,
+                        guint8                   opacity)
+{
+  ClutterLayerNode *res;
+  CoglColor color;
+
+  res = _clutter_paint_node_internal (CLUTTER_TYPE_LAYER_NODE);
+
+  res->projection = *projection;
+  res->viewport = *viewport;
+  res->fbo_width = width;
+  res->fbo_height = height;
+  res->opacity = opacity;
+
+  /* the texture backing the FBO */
+  res->texture = cogl_texture_new_with_size (MAX (res->fbo_width, 1),
+                                             MAX (res->fbo_height, 1),
+                                             COGL_TEXTURE_NO_SLICING,
+                                             COGL_PIXEL_FORMAT_RGBA_8888_PRE);
+
+  res->offscreen = cogl_offscreen_new_to_texture (res->texture);
+  if (res->offscreen == NULL)
+    {
+      g_critical ("%s: Unable to create an offscreen buffer", G_STRLOC);
+
+      cogl_object_unref (res->texture);
+      res->texture = NULL;
+
+      goto out;;
+    }
+
+  cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
+
+  /* the pipeline used to paint the texture; we use nearest
+   * interpolation filters because the texture is always
+   * going to be painted at a 1:1 texel:pixel ratio
+   */
+  res->state = cogl_pipeline_copy (default_texture_pipeline);
+  cogl_pipeline_set_layer_filters (res->state, 0,
+                                   COGL_PIPELINE_FILTER_NEAREST,
+                                   COGL_PIPELINE_FILTER_NEAREST);
+  cogl_pipeline_set_layer_texture (res->state, 0, res->texture);
+  cogl_pipeline_set_color (res->state, &color);
+  cogl_object_unref (res->texture);
+
+out:
+  return (ClutterPaintNode *) res;
+}
diff --git a/clutter/clutter-paint-nodes.h b/clutter/clutter-paint-nodes.h
new file mode 100644
index 0000000..8305e47
--- /dev/null
+++ b/clutter/clutter-paint-nodes.h
@@ -0,0 +1,161 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2011  Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Emmanuele Bassi <ebassi linux intel com>
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only <clutter/clutter.h> can be included directly."
+#endif
+
+#ifndef __CLUTTER_PAINT_NODES_H__
+#define __CLUTTER_PAINT_NODES_H__
+
+#include <cogl/cogl.h>
+#include <clutter/clutter-types.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_COLOR_NODE         (clutter_color_node_get_type ())
+#define CLUTTER_COLOR_NODE(obj)         (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_COLOR_NODE, ClutterColorNode))
+#define CLUTTER_IS_COLOR_NODE(obj)      (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_COLOR_NODE))
+
+/**
+ * ClutterColorNode:
+ *
+ * The <structname>ClutterTextNode</structname> structure is an opaque
+ * type whose members cannot be directly accessed.
+ *
+ * Since: 1.10
+ */
+typedef struct _ClutterColorNode                ClutterColorNode;
+typedef struct _ClutterColorNodeClass           ClutterColorNodeClass;
+
+GType clutter_color_node_get_type (void) G_GNUC_CONST;
+
+ClutterPaintNode *      clutter_color_node_new          (const ClutterColor    *color);
+
+#define CLUTTER_TYPE_TEXTURE_NODE               (clutter_texture_node_get_type ())
+#define CLUTTER_TEXTURE_NODE(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TEXTURE_NODE, ClutterTextureNode))
+#define CLUTTER_IS_TEXTURE_NODE(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TEXTURE_NODE))
+
+/**
+ * ClutterTextureNode:
+ *
+ * The <structname>ClutterTextNode</structname> structure is an opaque
+ * type whose members cannot be directly accessed.
+ *
+ * Since: 1.10
+ */
+typedef struct _ClutterTextureNode              ClutterTextureNode;
+typedef struct _ClutterTextureNodeClass         ClutterTextureNodeClass;
+
+GType clutter_texture_node_get_type (void) G_GNUC_CONST;
+
+ClutterPaintNode *      clutter_texture_node_new        (CoglTexture           *texture,
+                                                         const ClutterColor    *color);
+
+#define CLUTTER_TYPE_CLIP_NODE                  (clutter_clip_node_get_type ())
+#define CLUTTER_CLIP_NODE(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_CLIP_NODE, ClutterClipNode))
+#define CLUTTER_IS_CLIP_NODE(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_CLIP_NODE))
+
+/**
+ * ClutterClipNode:
+ *
+ * The <structname>ClutterTextNode</structname> structure is an opaque
+ * type whose members cannot be directly accessed.
+ *
+ * Since: 1.10
+ */
+typedef struct _ClutterClipNode                 ClutterClipNode;
+typedef struct _ClutterClipNodeClass            ClutterClipNodeClass;
+
+GType clutter_clip_node_get_type (void) G_GNUC_CONST;
+
+ClutterPaintNode *      clutter_clip_node_new           (void);
+
+#define CLUTTER_TYPE_PIPELINE_NODE              (clutter_pipeline_node_get_type ())
+#define CLUTTER_PIPELINE_NODE(obj)              (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_PIPELINE_NODE, ClutterPipelineNode))
+#define CLUTTER_IS_PIPELINE_NODE(obj)           (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_PIPELINE_NODE))
+
+/**
+ * ClutterPipelineNode:
+ *
+ * The <structname>ClutterTextNode</structname> structure is an opaque
+ * type whose members cannot be directly accessed.
+ *
+ * Since: 1.10
+ */
+typedef struct _ClutterPipelineNode             ClutterPipelineNode;
+typedef struct _ClutterPipelineNodeClass        ClutterPipelineNodeClass;
+
+GType clutter_pipeline_node_get_type (void) G_GNUC_CONST;
+
+#if defined(COGL_ENABLE_EXPERIMENTAL_2_0_API) && defined(CLUTTER_ENABLE_EXPERIMENTAL_API)
+ClutterPaintNode *      clutter_pipeline_node_new       (CoglPipeline          *pipeline);
+#endif /* COGL_ENABLE_EXPERIMENTAL_2_0_API && CLUTTER_ENABLE_EXPERIMENTAL_API */
+
+#define CLUTTER_TYPE_TEXT_NODE                  (clutter_text_node_get_type ())
+#define CLUTTER_TEXT_NODE(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TEXT_NODE, ClutterTextNode))
+#define CLUTTER_IS_TEXT_NODE(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TEXT_NODE))
+
+/**
+ * ClutterTextNode:
+ *
+ * The <structname>ClutterTextNode</structname> structure is an opaque
+ * type whose members cannot be directly accessed.
+ *
+ * Since: 1.10
+ */
+typedef struct _ClutterTextNode                 ClutterTextNode;
+typedef struct _ClutterTextNodeClass            ClutterTextNodeClass;
+
+GType clutter_text_node_get_type (void) G_GNUC_CONST;
+
+ClutterPaintNode *      clutter_text_node_new           (PangoLayout           *layout,
+                                                         const ClutterColor    *color);
+
+#define CLUTTER_TYPE_LAYER_NODE                 (clutter_layer_node_get_type ())
+#define CLUTTER_LAYER_NODE(obj)                 (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_LAYER_NODE, ClutterLayerNode))
+#define CLUTTER_IS_LAYER_NODE(obj)              (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_LAYER_NODE))
+
+/**
+ * ClutterLayerNode:
+ *
+ * The <structname>ClutterLayerNode</structname> structure is an opaque
+ * type whose members cannot be directly accessed.
+ *
+ * Since: 1.10
+ */
+typedef struct _ClutterLayerNode                ClutterLayerNode;
+typedef struct _ClutterLayerNodeClass           ClutterLayerNodeClass;
+
+GType clutter_layer_node_get_type (void) G_GNUC_CONST;
+
+ClutterPaintNode *      clutter_layer_node_new          (const CoglMatrix        *projection,
+                                                         const cairo_rectangle_t *viewport,
+                                                         float                    width,
+                                                         float                    height,
+                                                         guint8                   opacity);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_PAINT_NODES_H__ */
diff --git a/clutter/clutter-types.h b/clutter/clutter-types.h
index 226e2e8..3fd9335 100644
--- a/clutter/clutter-types.h
+++ b/clutter/clutter-types.h
@@ -53,6 +53,7 @@ typedef struct _ClutterLayoutMeta       ClutterLayoutMeta;
 typedef struct _ClutterActorMeta        ClutterActorMeta;
 typedef struct _ClutterLayoutManager    ClutterLayoutManager;
 typedef struct _ClutterActorIter        ClutterActorIter;
+typedef struct _ClutterPaintNode        ClutterPaintNode;
 
 typedef struct _ClutterAlpha            ClutterAlpha;
 typedef struct _ClutterAnimatable       ClutterAnimatable; /* dummy */
diff --git a/clutter/clutter.h b/clutter/clutter.h
index 0d8beff..52d443f 100644
--- a/clutter/clutter.h
+++ b/clutter/clutter.h
@@ -80,6 +80,8 @@
 #include "clutter-model.h"
 #include "clutter-offscreen-effect.h"
 #include "clutter-page-turn-effect.h"
+#include "clutter-paint-nodes.h"
+#include "clutter-paint-node.h"
 #include "clutter-path-constraint.h"
 #include "clutter-path.h"
 #include "clutter-property-transition.h"



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