diff --git a/clutter/Makefile.am b/clutter/Makefile.am
index b1c9e0598..6c22c42ad 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 000000000..346bda8f3
--- /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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ */
+
+#ifndef __CLUTTER_PAINT_NODE_PRIVATE_H__
+#define __CLUTTER_PAINT_NODE_PRIVATE_H__
+
+#include
+#include
+
+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 000000000..dc11191ea
--- /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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ */
+
+/**
+ * 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 #ClutterPaintNodes to
+ * the render graph.
+ */
+
+/**
+ * ClutterPaintNode:
+ *
+ * The ClutterPaintNode 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 ClutterPaintNodeClass structure contains
+ * only private data.
+ *
+ * Since: 1.10
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include
+#include
+
+#include "clutter-paint-node-private.h"
+
+#include "clutter-debug.h"
+#include "clutter-private.h"
+
+#include
+
+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 000000000..46cc7d30a
--- /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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#ifndef __CLUTTER_PAINT_NODE_H__
+#define __CLUTTER_PAINT_NODE_H__
+
+#include
+#include
+
+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 000000000..c0868b3b5
--- /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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ */
+
+/**
+ * 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
+#include
+
+#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 ClutterColorNode structure is an opaque
+ * type whose members cannot be directly accessed.
+ *
+ * Since: 1.10
+ */
+struct _ClutterColorNode
+{
+ ClutterPipelineNode parent_instance;
+};
+
+/**
+ * ClutterColorNode:
+ *
+ * The ClutterColorNodeClass 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 ClutterTextureNode structure is an opaque
+ * type whose members cannot be directly accessed.
+ *
+ * Since: 1.10
+ */
+struct _ClutterTextureNode
+{
+ ClutterPipelineNode parent_instance;
+};
+
+/**
+ * ClutterTextureNode:
+ *
+ * The ClutterTextureNodeClass 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 000000000..8305e47cc
--- /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 .
+ *
+ * Author:
+ * Emmanuele Bassi
+ */
+
+#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only can be included directly."
+#endif
+
+#ifndef __CLUTTER_PAINT_NODES_H__
+#define __CLUTTER_PAINT_NODES_H__
+
+#include
+#include
+
+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 ClutterTextNode 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 ClutterTextNode 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 ClutterTextNode 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 ClutterTextNode 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 ClutterTextNode 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 ClutterLayerNode 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 226e2e812..3fd93350e 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 0d8befff9..52d443fa8 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"