[gtk+/wip/ebassi/gsk-1] Snapshot/WIP
- From: Emmanuele Bassi <ebassi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/wip/ebassi/gsk-1] Snapshot/WIP
- Date: Mon, 28 Mar 2016 18:07:19 +0000 (UTC)
commit f3bd57a014087e6a8a3f1bbccaf7fd8b540069a0
Author: Emmanuele Bassi <ebassi gnome org>
Date: Thu Mar 17 13:48:19 2016 +0000
Snapshot/WIP
gsk/Makefile.am | 81 +++-
gsk/gsk.h | 33 ++
gsk/gskcairorenderer.c | 147 +++++++
gsk/gskcairorendererprivate.h | 26 ++
gsk/gskdebug.c | 34 ++
gsk/gskdebugprivate.h | 34 ++
gsk/gskenums.h | 31 ++
gsk/gskenumtypes.c.template | 38 ++
gsk/gskenumtypes.h.template | 24 ++
gsk/gskglrenderer.c | 337 ++++++++++++++++
gsk/gskglrendererprivate.h | 23 ++
gsk/gskrenderer.c | 873 +++++++++++++++++++++++++++++++++++++++++
gsk/gskrenderer.h | 86 ++++
gsk/gskrendererprivate.h | 78 ++++
gsk/gskrendernode.c | 767 ++++++++++++++++++++++++++++++++++++
gsk/gskrendernode.h | 111 ++++++
gsk/gskrendernodeiter.c | 148 +++++++
gsk/gskrendernodeiter.h | 45 +++
gsk/gskrendernodeprivate.h | 63 +++
gsk/gsktypes.h | 29 ++
tests/Makefile.am | 7 +
tests/testgskrenderer.c | 178 +++++++++
22 files changed, 3175 insertions(+), 18 deletions(-)
---
diff --git a/gsk/Makefile.am b/gsk/Makefile.am
index 2b20fcd..f89155d 100644
--- a/gsk/Makefile.am
+++ b/gsk/Makefile.am
@@ -1,23 +1,12 @@
include $(top_srcdir)/Makefile.decl
--include $(INTROSPECTION_MAKEFILE)
-
-# Preamble
-INTROSPECTION_GIRS =
-INTROSPECTION_SCANNER_ARGS = \
- --add-include-path=../gdk \
- --warn-all
-INTROSPECTION_COMPILER_ARGS = \
- --includedir=$(srcdir) \
- --includedir=. \
- --includedir=../gdk
AM_CPPFLAGS = \
-DG_LOG_DOMAIN=\"Gsk\" \
-DGSK_COMPILATION \
- -I$(top_builddir) \
- -I$(top_builddir)/gsk \
-I$(top_srcdir) \
-I$(top_srcdir)/gdk \
+ -I$(top_builddir) \
+ -I$(top_builddir)/gsk \
$(GTK_DEBUG_FLAGS) \
$(GSK_DEP_CFLAGS)
@@ -34,15 +23,59 @@ LDADD = \
BUILT_SOURCES =
CLEANFILES =
+DISTCLEANFILES =
lib_LTLIBRARIES =
-gsk_public_source_h =
-gsk_private_source_h =
+gsk_public_source_h = \
+ gskenums.h \
+ gskrenderer.h \
+ gskrendernode.h \
+ gskrendernodeiter.h \
+ gsktypes.h
+gsk_private_source_h = \
+ gskcairorendererprivate.h \
+ gskdebugprivate.h \
+ gskglrendererprivate.h \
+ gskrendererprivate.h \
+ gskrendernodeprivate.h
gsk_private_source_c =
-gsk_source_c =
+gsk_built_source_h = \
+ gskenumtypes.h
+gsk_built_source_c = \
+ gskenumtypes.c
+gsk_source_c = \
+ gskcairorenderer.c \
+ gskdebug.c \
+ gskglrenderer.c \
+ gskrenderer.c \
+ gskrendernode.c \
+ gskrendernodeiter.c
+
+all_sources = \
+ $(gsk_public_source_h) \
+ $(gsk_private_source_h) \
+ $(gsk_built_source_h) \
+ $(gsk_private_source_c) \
+ $(gsk_source_c)
+
+BUILT_SOURCES += $(gsk_built_source_h) $(gsk_built_source_c)
+
+gskenumtypes.h: $(gsk_public_source_h) gskenumtypes.h.template
+ $(AM_V_GEN) $(GLIB_MKENUMS) --template $(filter %.template,$^) $(filter-out %.template,$^) > \
+ gskenumtypes.h.tmp && \
+ mv gskenumtypes.h.tmp gskenumtypes.h
+
+gskenumtypes.c: $(gsk_public_source_h) gskenumtypes.c.template
+ $(AM_V_GEN) $(GLIB_MKENUMS) --template $(filter %.template,$^) $(filter-out %.template,$^) > \
+ gskenumtypes.c.tmp && \
+ mv gskenumtypes.c.tmp gskenumtypes.c
+
+EXTRA_DIST += gskenumtypes.h.template gskenumtypes.c.template
+DISTCLEANFILES += gskenumtypes.h gskenumtypes.c
libgsk_3_la_SOURCES = $(all_sources)
+nodist_libgsk_3_la_SOURCES = $(gsk_built_source_h) $(gsk_built_source_c)
libgsk_3_la_CFLAGS = $(AM_CFLAGS) $(GDK_HIDDEN_VISIBILITY_CFLAGS)
libgsk_3_la_LIBADD = $(GSK_DEP_LIBS) $(top_builddir)/gdk/libgdk-3.la
libgsk_3_la_LDFLAGS = $(LDADD)
@@ -50,11 +83,23 @@ libgsk_3_la_LDFLAGS = $(LDADD)
lib_LTLIBRARIES += libgsk-3.la
gskincludedir = $(includedir)/gtk-3.0/gsk
-gskinclude_HEADERS = $(gsk_public_source_h) gsk.h
+gskinclude_HEADERS = $(gsk_public_source_h) gskenumtypes.h gsk.h
+
+-include $(INTROSPECTION_MAKEFILE)
+INTROSPECTION_GIRS =
+INTROSPECTION_SCANNER_ENV = \
+ CC="$(CC)"
+INTROSPECTION_SCANNER_ARGS = \
+ --add-include-path=../gdk \
+ --warn-all
+INTROSPECTION_COMPILER_ARGS = \
+ --includedir=$(srcdir) \
+ --includedir=. \
+ --includedir=../gdk
if HAVE_INTROSPECTION
-introspection_files = $(gsk_source_c) $(gsk_public_source_h)
+introspection_files = $(filter-out $(wildcard *private.h),$(all_sources))
Gsk-3.0.gir: libgsk-3.la Makefile
Gsk_3_0_gir_SCANNERFLAGS = \
diff --git a/gsk/gsk.h b/gsk/gsk.h
new file mode 100644
index 0000000..01c4569
--- /dev/null
+++ b/gsk/gsk.h
@@ -0,0 +1,33 @@
+/* GSK - The GTK Scene Kit
+ * Copyright 2016 Endless
+ *
+ * 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/>.
+ */
+
+#ifndef __GSK_H__
+#define __GSK_H__
+
+#define __GSK_H_INSIDE__
+
+#include <gsk/gskenums.h>
+#include <gsk/gskrenderer.h>
+#include <gsk/gskrendernode.h>
+#include <gsk/gskrendernodeiter.h>
+
+#include <gsk/gsktypes.h>
+#include <gsk/gskenumtypes.h>
+
+#undef __GSK_H_INSIDE__
+
+#endif /* __GSK_H__ */
diff --git a/gsk/gskcairorenderer.c b/gsk/gskcairorenderer.c
new file mode 100644
index 0000000..2d5527d
--- /dev/null
+++ b/gsk/gskcairorenderer.c
@@ -0,0 +1,147 @@
+#include "config.h"
+
+#include "gskcairorendererprivate.h"
+
+#include "gskdebugprivate.h"
+#include "gskrendererprivate.h"
+#include "gskrendernodeiter.h"
+#include "gskrendernodeprivate.h"
+
+struct _GskCairoRenderer
+{
+ GskRenderer parent_instance;
+};
+
+struct _GskCairoRendererClass
+{
+ GskRendererClass parent_class;
+};
+
+G_DEFINE_TYPE (GskCairoRenderer, gsk_cairo_renderer, GSK_TYPE_RENDERER)
+
+static gboolean
+gsk_cairo_renderer_realize (GskRenderer *renderer)
+{
+ return TRUE;
+}
+
+static void
+gsk_cairo_renderer_unrealize (GskRenderer *renderer)
+{
+
+}
+
+static void
+gsk_cairo_renderer_render_node (GskCairoRenderer *self,
+ GskRenderNode *node,
+ cairo_t *cr)
+{
+ GskRenderNodeIter iter;
+ GskRenderNode *child;
+ gboolean pop_group = FALSE;
+ graphene_matrix_t mvp;
+ cairo_matrix_t ctm;
+ graphene_rect_t frame;
+
+ if (gsk_render_node_is_hidden (node))
+ return;
+
+ cairo_save (cr);
+
+ gsk_render_node_get_world_matrix (node, &mvp);
+ if (graphene_matrix_to_2d (&mvp, &ctm.xx, &ctm.yx, &ctm.xy, &ctm.yy, &ctm.x0, &ctm.y0))
+ {
+ GSK_NOTE (CAIRO, g_print ("CTM = { .xx = %g, .yx = %g, .xy = %g, .yy = %g, .x0 = %g, .y0 = %g }\n",
+ ctm.xx, ctm.yx,
+ ctm.xy, ctm.yy,
+ ctm.x0, ctm.y0));
+ cairo_transform (cr, &ctm);
+ }
+
+ gsk_render_node_get_bounds (node, &frame);
+ GSK_NOTE (CAIRO, g_print ("CLIP = { .x = %g, .y = %g, .width = %g, .height = %g }\n",
+ frame.origin.x, frame.origin.y,
+ frame.size.width, frame.size.height));
+ cairo_rectangle (cr, frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
+ cairo_clip (cr);
+
+ if (!gsk_render_node_is_opaque (node))
+ {
+ cairo_push_group (cr);
+ pop_group = TRUE;
+ }
+
+ cairo_set_source_surface (cr, gsk_render_node_get_surface (node), frame.origin.x, frame.origin.y);
+ cairo_paint (cr);
+
+ cairo_matrix_invert (&ctm);
+ cairo_transform (cr, &ctm);
+
+ if (gsk_render_node_get_n_children (node) != 0)
+ {
+ cairo_save (cr);
+
+ gsk_render_node_iter_init (&iter, node);
+ while (gsk_render_node_iter_next (&iter, &child))
+ gsk_cairo_renderer_render_node (self, child, cr);
+
+ cairo_restore (cr);
+ }
+
+ if (pop_group)
+ {
+ cairo_pop_group_to_source (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_paint_with_alpha (cr, gsk_render_node_get_opacity (node));
+ }
+
+ cairo_restore (cr);
+}
+
+static void
+gsk_cairo_renderer_render (GskRenderer *renderer)
+{
+ GskCairoRenderer *self = GSK_CAIRO_RENDERER (renderer);
+ cairo_surface_t *target = gsk_renderer_get_surface (renderer);
+ GskRenderNode *root = gsk_renderer_get_root_node (renderer);
+ cairo_t *cr = cairo_create (target);
+
+ gsk_cairo_renderer_render_node (self, root, cr);
+
+ cairo_destroy (cr);
+}
+
+static void
+gsk_cairo_renderer_clear (GskRenderer *renderer)
+{
+ cairo_surface_t *surface = gsk_renderer_get_surface (renderer);
+ cairo_t *cr = cairo_create (surface);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+ if (gsk_renderer_get_use_alpha (renderer))
+ cairo_set_source_rgba (cr, 0, 0, 0, 0);
+ else
+ cairo_set_source_rgb (cr, 0, 0, 0);
+
+ cairo_paint (cr);
+
+ cairo_destroy (cr);
+}
+
+static void
+gsk_cairo_renderer_class_init (GskCairoRendererClass *klass)
+{
+ GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass);
+
+ renderer_class->realize = gsk_cairo_renderer_realize;
+ renderer_class->unrealize = gsk_cairo_renderer_unrealize;
+ renderer_class->clear = gsk_cairo_renderer_clear;
+ renderer_class->render = gsk_cairo_renderer_render;
+}
+
+static void
+gsk_cairo_renderer_init (GskCairoRenderer *self)
+{
+
+}
diff --git a/gsk/gskcairorendererprivate.h b/gsk/gskcairorendererprivate.h
new file mode 100644
index 0000000..7a9bd23
--- /dev/null
+++ b/gsk/gskcairorendererprivate.h
@@ -0,0 +1,26 @@
+#ifndef __GSK_CAIRO_RENDERER_PRIVATE_H__
+#define __GSK_CAIRO_RENDERER_PRIVATE_H__
+
+#include <cairo.h>
+#include <gsk/gskrenderer.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_CAIRO_RENDERER (gsk_cairo_renderer_get_type ())
+
+#define GSK_CAIRO_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_CAIRO_RENDERER,
GskCairoRenderer))
+#define GSK_IS_CAIRO_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_CAIRO_RENDERER))
+#define GSK_CAIRO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_CAIRO_RENDERER,
GskCairoRendererClass))
+#define GSK_IS_CAIRO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_CAIRO_RENDERER))
+#define GSK_CAIRO_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_CAIRO_RENDERER,
GskCairoRendererClass))
+
+typedef struct _GskCairoRenderer GskCairoRenderer;
+typedef struct _GskCairoRendererClass GskCairoRendererClass;
+
+GType gsk_cairo_renderer_get_type (void) G_GNUC_CONST;
+
+GskRenderer *gsk_cairo_renderer_new (void);
+
+G_END_DECLS
+
+#endif /* __GSK_CAIRO_RENDERER_PRIVATE_H__ */
diff --git a/gsk/gskdebug.c b/gsk/gskdebug.c
new file mode 100644
index 0000000..2ecbebb
--- /dev/null
+++ b/gsk/gskdebug.c
@@ -0,0 +1,34 @@
+#include "gskdebugprivate.h"
+
+#ifdef G_ENABLE_DEBUG
+static const GDebugKey gsk_debug_keys[] = {
+ { "rendernode", GSK_DEBUG_RENDER_NODE },
+ { "renderer", GSK_DEBUG_RENDERER },
+ { "cairo", GSK_DEBUG_CAIRO },
+ { "opengl", GSK_DEBUG_OPENGL },
+};
+#endif
+
+gboolean
+gsk_check_debug_flags (GskDebugFlags flags)
+{
+#ifdef G_ENABLE_DEBUG
+ static volatile gsize gsk_debug_flags_set;
+ static guint gsk_debug_flags;
+
+ if (g_once_init_enter (&gsk_debug_flags_set))
+ {
+ const char *debug_string = g_getenv ("GSK_DEBUG");
+
+ gsk_debug_flags = g_parse_debug_string (debug_string,
+ (GDebugKey *) gsk_debug_keys,
+ G_N_ELEMENTS (gsk_debug_keys));
+
+ g_once_init_leave (&gsk_debug_flags_set, TRUE);
+ }
+
+ return (gsk_debug_flags & flags) != 0;
+#else
+ return FALSE;
+#endif
+}
diff --git a/gsk/gskdebugprivate.h b/gsk/gskdebugprivate.h
new file mode 100644
index 0000000..ec1f618
--- /dev/null
+++ b/gsk/gskdebugprivate.h
@@ -0,0 +1,34 @@
+#ifndef __GSK_DEBUG_PRIVATE_H__
+#define __GSK_DEBUG_PRIVATE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ GSK_DEBUG_RENDER_NODE = 1 << 0,
+ GSK_DEBUG_RENDERER = 1 << 1,
+ GSK_DEBUG_CAIRO = 1 << 2,
+ GSK_DEBUG_OPENGL = 1 << 3
+} GskDebugFlags;
+
+gboolean gsk_check_debug_flags (GskDebugFlags flags);
+
+#ifdef G_ENABLE_DEBUG
+
+#define GSK_DEBUG_CHECK(type) G_UNLIKELY (gsk_check_debug_flags (GSK_DEBUG_ ## type))
+#define GSK_NOTE(type,action) G_STMT_START { \
+ if (GSK_DEBUG_CHECK (type)) { \
+ action; \
+ } } G_STMT_END
+
+#else
+
+#define GSK_DEBUG_CHECK(type) 0
+#define GSK_NOTE(type,action)
+
+#endif
+
+G_END_DECLS
+
+#endif /* __GSK_DEBUG_PRIVATE_H__ */
diff --git a/gsk/gskenums.h b/gsk/gskenums.h
new file mode 100644
index 0000000..6aef13b
--- /dev/null
+++ b/gsk/gskenums.h
@@ -0,0 +1,31 @@
+/* GSK - The GTK Scene Kit
+ * Copyright 2016 Endless
+ *
+ * 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/>.
+ */
+
+#ifndef __GSK_ENUMS_H__
+#define __GSK_ENUMS_H__
+
+#if !defined (__GSK_H_INSIDE__) && !defined (GSK_COMPILATION)
+#error "Only <gsk/gsk.h> can be included directly."
+#endif
+
+typedef enum {
+ GSK_SCALING_FILTER_LINEAR,
+ GSK_SCALING_FILTER_NEAREST,
+ GSK_SCALING_FILTER_TRILINEAR
+} GskScalingFilter;
+
+#endif /* __GSK_TYPES_H__ */
diff --git a/gsk/gskenumtypes.c.template b/gsk/gskenumtypes.c.template
new file mode 100644
index 0000000..430ea8f
--- /dev/null
+++ b/gsk/gskenumtypes.c.template
@@ -0,0 +1,38 @@
+/*** BEGIN file-header ***/
+#include "config.h"
+#include "gskenumtypes.h"
+#include <gsk.h>
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+ enum_name@_get_type (void)
+{
+ static volatile gsize g_define_type_id__volatile = 0;
+
+ if (g_once_init_enter (&g_define_type_id__volatile))
+ {
+ static const G Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+ GType g_define_type_id =
+ g_ type@_register_static (g_intern_static_string ("@EnumName@"), values);
+ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ }
+
+ return g_define_type_id__volatile;
+}
+
+/*** END value-tail ***/
diff --git a/gsk/gskenumtypes.h.template b/gsk/gskenumtypes.h.template
new file mode 100644
index 0000000..15a8ac6
--- /dev/null
+++ b/gsk/gskenumtypes.h.template
@@ -0,0 +1,24 @@
+/*** BEGIN file-header ***/
+#ifndef __GSK_ENUM_TYPES_H__
+#define __GSK_ENUM_TYPES_H__
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GDK_AVAILABLE_IN_ALL GType @enum_name _get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX _TYPE_@ENUMSHORT@ (@enum_name _get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* __GSK_ENUM_TYPES_H__ */
+/*** END file-tail ***/
diff --git a/gsk/gskglrenderer.c b/gsk/gskglrenderer.c
new file mode 100644
index 0000000..0870a22
--- /dev/null
+++ b/gsk/gskglrenderer.c
@@ -0,0 +1,337 @@
+#include "config.h"
+
+#include "gskglrendererprivate.h"
+
+#include "gskrendererprivate.h"
+#include "gskrendernodeprivate.h"
+#include "gskrendernodeiter.h"
+
+#include <epoxy/gl.h>
+
+typedef struct {
+ GskRenderNode *node;
+ guint texture_id;
+ graphene_rect_t frame;
+ graphene_matrix_t modelview;
+ float opacity;
+} RenderItem;
+
+struct _GskGLRenderer
+{
+ GskRenderer parent_instance;
+
+ GdkGLContext *context;
+
+ graphene_matrix_t mvp;
+ graphene_frustum_t frustum;
+
+ guint frame_buffer;
+ guint render_buffer;
+ guint depth_stencil_buffer;
+ guint texture_id;
+
+ guint vao_id;
+
+ GArray *opaque_render_items;
+ GArray *transparent_render_items;
+
+ gboolean has_buffers : 1;
+ gboolean has_alpha : 1;
+ gboolean has_stencil_buffer : 1;
+ gboolean has_depth_buffer : 1;
+};
+
+struct _GskGLRendererClass
+{
+ GskRendererClass parent_class;
+};
+
+G_DEFINE_TYPE (GskGLRenderer, gsk_gl_renderer, GSK_TYPE_RENDERER)
+
+static void
+gsk_gl_renderer_dispose (GObject *gobject)
+{
+ GskGLRenderer *self = GSK_GL_RENDERER (gobject);
+
+ g_clear_object (&self->context);
+ g_clear_pointer (&self->opaque_render_items, g_array_unref);
+ g_clear_pointer (&self->transparent_render_items, g_array_unref);
+
+ G_OBJECT_CLASS (gsk_gl_renderer_parent_class)->dispose (gobject);
+}
+
+static void
+gsk_gl_renderer_create_buffers (GskGLRenderer *self)
+{
+ glGenFramebuffersEXT (1, &self->frame_buffer);
+
+ if (self->has_alpha)
+ {
+ if (self->texture_id == 0)
+ glGenTextures (1, &self->texture_id);
+
+ if (self->render_buffer != 0)
+ {
+ glDeleteRenderbuffersEXT (1, &self->render_buffer);
+ self->render_buffer = 0;
+ }
+ }
+ else
+ {
+ if (self->render_buffer == 0)
+ glGenRenderbuffersEXT (1, &self->render_buffer);
+
+ if (self->texture_id != 0)
+ {
+ glDeleteTextures (1, &self->texture_id);
+ self->texture_id = 0;
+ }
+ }
+
+ if (self->has_depth_buffer || self->has_stencil_buffer)
+ {
+ if (self->depth_stencil_buffer == 0)
+ glGenRenderbuffersEXT (1, &self->depth_stencil_buffer);
+ }
+ else
+ {
+ if (self->depth_stencil_buffer != 0)
+ {
+ glDeleteRenderbuffersEXT (1, &self->depth_stencil_buffer);
+ self->depth_stencil_buffer = 0;
+ }
+ }
+
+ self->has_buffers = TRUE;
+}
+
+static void
+gsk_gl_renderer_destroy_buffers (GskGLRenderer *self)
+{
+ if (!self->has_buffers)
+ return;
+
+ if (self->depth_stencil_buffer != 0)
+ {
+ glDeleteRenderbuffersEXT (1, &self->depth_stencil_buffer);
+ self->depth_stencil_buffer = 0;
+ }
+
+ if (self->render_buffer != 0)
+ {
+ glDeleteRenderbuffersEXT (1, &self->render_buffer);
+ self->render_buffer = 0;
+ }
+
+ if (self->texture_id != 0)
+ {
+ glDeleteTextures (1, &self->texture_id);
+ self->texture_id = 0;
+ }
+
+ if (self->frame_buffer != 0)
+ {
+ glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);
+ glDeleteFramebuffersEXT (1, &self->frame_buffer);
+ self->frame_buffer = 0;
+ }
+
+ self->has_buffers = FALSE;
+}
+
+static gboolean
+gsk_gl_renderer_realize (GskRenderer *renderer)
+{
+ GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+ GError *error = NULL;
+
+ if (self->context == NULL)
+ return FALSE;
+
+ gdk_gl_context_realize (self->context, &error);
+ if (error != NULL)
+ {
+ g_critical ("Unable to realize GL renderer: %s", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ gdk_gl_context_make_current (self->context);
+ gsk_gl_renderer_create_buffers (self);
+
+ return TRUE;
+}
+
+static void
+gsk_gl_renderer_unrealize (GskRenderer *renderer)
+{
+ GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+
+ if (self->context == NULL)
+ return;
+
+ gdk_gl_context_make_current (self->context);
+ gsk_gl_renderer_destroy_buffers (self);
+
+ if (self->context == gdk_gl_context_get_current ())
+ gdk_gl_context_clear_current ();
+}
+
+static void
+gsk_gl_renderer_resize_viewport (GskRenderer *renderer,
+ const graphene_rect_t *viewport)
+{
+ GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+
+ if (self->context == NULL)
+ return;
+
+ gdk_gl_context_make_current (self->context);
+
+ glViewport (0, 0, viewport->size.width, viewport->size.height);
+}
+
+static void
+gsk_gl_renderer_update (GskRenderer *renderer,
+ const graphene_matrix_t *modelview,
+ const graphene_matrix_t *projection)
+{
+ GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+
+ graphene_matrix_multiply (modelview, projection, &self->mvp);
+ graphene_frustum_init_from_matrix (&self->frustum, &self->mvp);
+}
+
+static void
+render_item_clear (gpointer data_)
+{
+}
+
+static void
+gsk_gl_renderer_add_render_item (GskGLRenderer *self,
+ GskRenderNode *node)
+{
+ cairo_surface_t *surface;
+ GskRenderNodeIter iter;
+ GskRenderNode *child;
+ RenderItem item;
+
+ if (gsk_render_node_is_hidden (node))
+ return;
+
+ item.node = g_object_ref (node);
+ gsk_render_node_get_bounds (node, &item.frame);
+ gsk_render_node_get_world_matrix (node, &item.modelview);
+ item.opacity = gsk_render_node_get_opacity (node);
+
+#if 0
+ /* TODO: This should really be an asset atlas */
+ surface = gsk_render_node_get_surface (node);
+ if (surface != NULL)
+ gdk_cairo_surface_to_texture (surface, &item.texture_id);
+#endif
+
+ if (gsk_render_node_is_opaque (node))
+ g_array_append_val (self->opaque_render_items, item);
+ else
+ g_array_prepend_val (self->transparent_render_items, item);
+
+ gsk_render_node_iter_init (&iter, node);
+ while (gsk_render_node_iter_next (&iter, &child))
+ gsk_gl_renderer_add_render_item (self, child);
+}
+
+static void
+gsk_gl_renderer_validate_tree (GskRenderer *renderer,
+ GskRenderNode *root)
+{
+ GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+
+ g_array_set_size (self->opaque_render_items, 0);
+ g_array_set_size (self->transparent_render_items, 0);
+
+ gsk_gl_renderer_add_render_item (self, gsk_renderer_get_root_node (renderer));
+}
+
+static void
+gsk_gl_renderer_clear (GskRenderer *renderer)
+{
+ GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+
+ gdk_gl_context_make_current (self->context);
+
+ glClearColor (0, 0, 0, 0);
+ glClear (GL_COLOR_BUFFER_BIT);
+}
+
+static void
+gsk_gl_renderer_render (GskRenderer *renderer)
+{
+ GskGLRenderer *self = GSK_GL_RENDERER (renderer);
+ guint i;
+
+ gdk_gl_context_make_current (self->context);
+
+ /* Opaque pass: front-to-back */
+ for (i = 0; i < self->opaque_render_items->len; i++)
+ {
+ RenderItem *item = &g_array_index (self->opaque_render_items, RenderItem, i);
+ }
+
+ /* Transparent pass: back-to-front */
+ for (i = 0; i < self->transparent_render_items->len; i++)
+ {
+ RenderItem *item = &g_array_index (self->transparent_render_items, RenderItem, i);
+ }
+}
+
+static void
+gsk_gl_renderer_class_init (GskGLRendererClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GskRendererClass *renderer_class = GSK_RENDERER_CLASS (klass);
+
+ gobject_class->dispose = gsk_gl_renderer_dispose;
+
+ renderer_class->realize = gsk_gl_renderer_realize;
+ renderer_class->unrealize = gsk_gl_renderer_unrealize;
+ renderer_class->resize_viewport = gsk_gl_renderer_resize_viewport;
+ renderer_class->update = gsk_gl_renderer_update;
+ renderer_class->clear = gsk_gl_renderer_clear;
+ renderer_class->validate_tree = gsk_gl_renderer_validate_tree;
+ renderer_class->render = gsk_gl_renderer_render;
+}
+
+static void
+gsk_gl_renderer_init (GskGLRenderer *self)
+{
+ self->opaque_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), 16);
+ g_array_set_clear_func (self->opaque_render_items, render_item_clear);
+
+ self->transparent_render_items = g_array_sized_new (FALSE, FALSE, sizeof (RenderItem), 16);
+ g_array_set_clear_func (self->opaque_render_items, render_item_clear);
+}
+
+void
+gsk_gl_renderer_set_context (GskGLRenderer *renderer,
+ GdkGLContext *context)
+{
+ g_return_if_fail (GSK_IS_GL_RENDERER (renderer));
+ g_return_if_fail (context == NULL || GDK_IS_GL_CONTEXT (context));
+
+ if (gsk_renderer_is_realized (GSK_RENDERER (renderer)))
+ return;
+
+ if (gdk_gl_context_get_display (context) != gsk_renderer_get_display (GSK_RENDERER (renderer)))
+ return;
+
+ g_set_object (&renderer->context, context);
+}
+
+GdkGLContext *
+gsk_gl_renderer_get_context (GskGLRenderer *renderer)
+{
+ g_return_val_if_fail (GSK_IS_GL_RENDERER (renderer), NULL);
+
+ return renderer->context;
+}
diff --git a/gsk/gskglrendererprivate.h b/gsk/gskglrendererprivate.h
new file mode 100644
index 0000000..a30b201
--- /dev/null
+++ b/gsk/gskglrendererprivate.h
@@ -0,0 +1,23 @@
+#ifndef __GSK_GL_RENDERER_PRIVATE_H__
+#define __GSK_GL_RENDERER_PRIVATE_H__
+
+#include <gsk/gskrenderer.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_GL_RENDERER (gsk_gl_renderer_get_type ())
+
+#define GSK_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_GL_RENDERER,
GskGLRenderer))
+#define GSK_IS_GL_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_GL_RENDERER))
+#define GSK_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_GL_RENDERER,
GskGLRendererClass))
+#define GSK_IS_GL_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_GL_RENDERER))
+#define GSK_GL_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_GL_RENDERER,
GskGLRendererClass))
+
+typedef struct _GskGLRenderer GskGLRenderer;
+typedef struct _GskGLRendererClass GskGLRendererClass;
+
+GType gsk_gl_renderer_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GSK_GL_RENDERER_PRIVATE_H__ */
diff --git a/gsk/gskrenderer.c b/gsk/gskrenderer.c
new file mode 100644
index 0000000..e415c4e
--- /dev/null
+++ b/gsk/gskrenderer.c
@@ -0,0 +1,873 @@
+#include "config.h"
+
+#include "gskrendererprivate.h"
+
+#include "gskcairorendererprivate.h"
+#include "gskglrendererprivate.h"
+#include "gskrendernodeprivate.h"
+
+#include "gskenumtypes.h"
+
+#include <graphene-gobject.h>
+#include <cairo-gobject.h>
+#include <gdk/gdk.h>
+
+/**
+ * SECTION:GskRenderer
+ * @title: GskRenderer
+ * @Short_desc: Renders a scene with a simplified graph
+ *
+ * XXX
+ */
+
+typedef struct
+{
+ GObject parent_instance;
+
+ GdkDisplay *display;
+ GdkWindow *window;
+
+ graphene_rect_t viewport;
+ graphene_matrix_t modelview;
+ graphene_matrix_t projection;
+
+ GskScalingFilter min_filter;
+ GskScalingFilter mag_filter;
+
+ GskRenderNode *root_node;
+
+ cairo_surface_t *surface;
+
+ gboolean is_realized : 1;
+ gboolean needs_viewport_resize : 1;
+ gboolean needs_modelview_update : 1;
+ gboolean needs_projection_update : 1;
+ gboolean needs_tree_validation : 1;
+ gboolean auto_clear : 1;
+ gboolean use_alpha : 1;
+} GskRendererPrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GskRenderer, gsk_renderer, G_TYPE_OBJECT)
+
+enum {
+ PROP_VIEWPORT = 1,
+ PROP_MODELVIEW,
+ PROP_PROJECTION,
+ PROP_MINIFICATION_FILTER,
+ PROP_MAGNIFICATION_FILTER,
+ PROP_AUTO_CLEAR,
+ PROP_ROOT_NODE,
+ PROP_DISPLAY,
+ PROP_WINDOW,
+ PROP_SURFACE,
+ PROP_USE_ALPHA,
+
+ N_PROPS
+};
+
+static GParamSpec *gsk_renderer_properties[N_PROPS];
+
+#define GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD(obj,method) \
+ g_critical ("Renderer of type '%s' does not implement GskRenderer::" # method, G_OBJECT_TYPE_NAME (obj))
+
+static gboolean
+gsk_renderer_real_realize (GskRenderer *self)
+{
+ GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD (self, realize);
+ return FALSE;
+}
+
+static void
+gsk_renderer_real_unrealize (GskRenderer *self)
+{
+ GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD (self, unrealize);
+}
+
+static void
+gsk_renderer_real_render (GskRenderer *self)
+{
+ GSK_RENDERER_WARN_NOT_IMPLEMENTED_METHOD (self, render);
+}
+
+static void
+gsk_renderer_real_resize_viewport (GskRenderer *self,
+ const graphene_rect_t *viewport)
+{
+}
+
+static void
+gsk_renderer_real_update (GskRenderer *self,
+ const graphene_matrix_t *mv,
+ const graphene_matrix_t *proj)
+{
+}
+
+static void
+gsk_renderer_real_validate_tree (GskRenderer *self,
+ GskRenderNode *root)
+{
+}
+
+static void
+gsk_renderer_dispose (GObject *gobject)
+{
+ GskRenderer *self = GSK_RENDERER (gobject);
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (self);
+
+ gsk_renderer_unrealize (self);
+
+ g_clear_object (&priv->root_node);
+ g_clear_object (&priv->display);
+
+ G_OBJECT_CLASS (gsk_renderer_parent_class)->dispose (gobject);
+}
+
+static void
+gsk_renderer_set_property (GObject *gobject,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GskRenderer *self = GSK_RENDERER (gobject);
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_VIEWPORT:
+ gsk_renderer_set_viewport (self, g_value_get_boxed (value));
+ break;
+
+ case PROP_MODELVIEW:
+ gsk_renderer_set_modelview (self, g_value_get_boxed (value));
+ break;
+
+ case PROP_PROJECTION:
+ gsk_renderer_set_projection (self, g_value_get_boxed (value));
+ break;
+
+ case PROP_MINIFICATION_FILTER:
+ gsk_renderer_set_scaling_filters (self, g_value_get_enum (value), priv->mag_filter);
+ break;
+
+ case PROP_MAGNIFICATION_FILTER:
+ gsk_renderer_set_scaling_filters (self, priv->min_filter, g_value_get_enum (value));
+ break;
+
+ case PROP_AUTO_CLEAR:
+ gsk_renderer_set_auto_clear (self, g_value_get_boolean (value));
+ break;
+
+ case PROP_ROOT_NODE:
+ gsk_renderer_set_root_node (self, g_value_get_object (value));
+ break;
+
+ case PROP_SURFACE:
+ gsk_renderer_set_surface (self, g_value_get_boxed (value));
+ break;
+
+ case PROP_WINDOW:
+ gsk_renderer_set_window (self, g_value_get_object (value));
+ break;
+
+ case PROP_DISPLAY:
+ priv->display = g_value_dup_object (value);
+ break;
+
+ case PROP_USE_ALPHA:
+ gsk_renderer_set_use_alpha (self, g_value_get_boolean (value));
+ break;
+ }
+}
+
+static void
+gsk_renderer_get_property (GObject *gobject,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GskRenderer *self = GSK_RENDERER (gobject);
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (self);
+
+ switch (prop_id)
+ {
+ case PROP_VIEWPORT:
+ g_value_set_boxed (value, &priv->viewport);
+ break;
+
+ case PROP_MODELVIEW:
+ g_value_set_boxed (value, &priv->modelview);
+ break;
+
+ case PROP_PROJECTION:
+ g_value_set_boxed (value, &priv->projection);
+ break;
+
+ case PROP_MINIFICATION_FILTER:
+ g_value_set_enum (value, priv->min_filter);
+ break;
+
+ case PROP_MAGNIFICATION_FILTER:
+ g_value_set_enum (value, priv->mag_filter);
+ break;
+
+ case PROP_AUTO_CLEAR:
+ g_value_set_boolean (value, priv->auto_clear);
+ break;
+
+ case PROP_ROOT_NODE:
+ g_value_set_object (value, priv->root_node);
+ break;
+
+ case PROP_SURFACE:
+ g_value_set_boxed (value, priv->surface);
+ break;
+
+ case PROP_DISPLAY:
+ g_value_set_object (value, priv->display);
+ break;
+
+ case PROP_WINDOW:
+ g_value_set_object (value, priv->window);
+ break;
+
+ case PROP_USE_ALPHA:
+ g_value_set_boolean (value, priv->use_alpha);
+ break;
+ }
+}
+
+static void
+gsk_renderer_constructed (GObject *gobject)
+{
+ GskRenderer *self = GSK_RENDERER (gobject);
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (self);
+
+ if (priv->display == NULL)
+ {
+ GdkDisplayManager *manager = gdk_display_manager_get ();
+
+ priv->display = gdk_display_manager_get_default_display (manager);
+ g_assert (priv->display != NULL);
+ }
+
+ G_OBJECT_CLASS (gsk_renderer_parent_class)->constructed (gobject);
+}
+
+static void
+gsk_renderer_class_init (GskRendererClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ klass->realize = gsk_renderer_real_realize;
+ klass->unrealize = gsk_renderer_real_unrealize;
+ klass->resize_viewport = gsk_renderer_real_resize_viewport;
+ klass->update = gsk_renderer_real_update;
+ klass->validate_tree = gsk_renderer_real_validate_tree;
+ klass->render = gsk_renderer_real_render;
+
+ gobject_class->constructed = gsk_renderer_constructed;
+ gobject_class->set_property = gsk_renderer_set_property;
+ gobject_class->get_property = gsk_renderer_get_property;
+ gobject_class->dispose = gsk_renderer_dispose;
+
+ /**
+ * GskRenderer:viewport:
+ *
+ * The visible area used by the #GskRenderer to render its contents.
+ *
+ * Since: 3.20
+ */
+ gsk_renderer_properties[PROP_VIEWPORT] =
+ g_param_spec_boxed ("viewport",
+ "Viewport",
+ "The visible area used by the renderer",
+ GRAPHENE_TYPE_RECT,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GskRenderer:modelview:
+ *
+ * The initial modelview matrix used by the #GskRenderer.
+ *
+ * If set to %NULL, the identity matrix:
+ *
+ * |[<!-- language="plain"
+ * | 1.0, 0.0, 0.0, 0.0 |
+ * | 0.0, 1.0, 0.0, 0.0 |
+ * | 0.0, 0.0, 1.0, 0.0 |
+ * | 0.0, 0.0, 0.0, 1.0 |
+ * ]|
+ *
+ * Is used instead.
+ *
+ * Since: 3.20
+ */
+ gsk_renderer_properties[PROP_MODELVIEW] =
+ g_param_spec_boxed ("modelview",
+ "Modelview",
+ "The modelview matrix used by the renderer",
+ GRAPHENE_TYPE_MATRIX,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ /**
+ * GskRenderer:projection:
+ *
+ * The projection matrix used by the #GskRenderer.
+ *
+ * If set to %NULL, the identity matrix:
+ *
+ * |[<!-- language="plain"
+ * | 1.0, 0.0, 0.0, 0.0 |
+ * | 0.0, 1.0, 0.0, 0.0 |
+ * | 0.0, 0.0, 1.0, 0.0 |
+ * | 0.0, 0.0, 0.0, 1.0 |
+ * ]|
+ *
+ * Is used instead.
+ *
+ * Since: 3.20
+ */
+ gsk_renderer_properties[PROP_PROJECTION] =
+ g_param_spec_boxed ("projection",
+ "Projection",
+ "The projection matrix used by the renderer",
+ GRAPHENE_TYPE_MATRIX,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ gsk_renderer_properties[PROP_MINIFICATION_FILTER] =
+ g_param_spec_enum ("minification-filter",
+ "Minification Filter",
+ "The minification filter used by the renderer for texture targets",
+ GSK_TYPE_SCALING_FILTER,
+ GSK_SCALING_FILTER_LINEAR,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ gsk_renderer_properties[PROP_MAGNIFICATION_FILTER] =
+ g_param_spec_enum ("magnification-filter",
+ "Magnification Filter",
+ "The magnification filter used by the renderer for texture targets",
+ GSK_TYPE_SCALING_FILTER,
+ GSK_SCALING_FILTER_LINEAR,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ gsk_renderer_properties[PROP_AUTO_CLEAR] =
+ g_param_spec_boolean ("auto-clear",
+ "Auto Clear",
+ "Automatically clears the rendering target on render",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ gsk_renderer_properties[PROP_ROOT_NODE] =
+ g_param_spec_object ("root-node",
+ "Root Node",
+ "The root render node to render",
+ GSK_TYPE_RENDER_NODE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ gsk_renderer_properties[PROP_SURFACE] =
+ g_param_spec_boxed ("surface",
+ "Surface",
+ "The Cairo surface used to render to",
+ CAIRO_GOBJECT_TYPE_SURFACE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ gsk_renderer_properties[PROP_DISPLAY] =
+ g_param_spec_object ("display",
+ "Display",
+ "The GdkDisplay object used by the renderer",
+ GDK_TYPE_DISPLAY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS);
+
+ gsk_renderer_properties[PROP_WINDOW] =
+ g_param_spec_object ("window",
+ "Window",
+ "The GdkWindow associated to the renderer",
+ GDK_TYPE_WINDOW,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ gsk_renderer_properties[PROP_USE_ALPHA] =
+ g_param_spec_boolean ("use-alpha",
+ "Use Alpha",
+ "Whether the renderer should use the alpha channel when rendering",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_EXPLICIT_NOTIFY);
+
+ g_object_class_install_properties (gobject_class, N_PROPS, gsk_renderer_properties);
+}
+
+static void
+gsk_renderer_init (GskRenderer *self)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (self);
+
+ graphene_matrix_init_identity (&priv->modelview);
+ graphene_matrix_init_identity (&priv->projection);
+
+ priv->auto_clear = TRUE;
+
+ priv->min_filter = GSK_SCALING_FILTER_LINEAR;
+ priv->mag_filter = GSK_SCALING_FILTER_LINEAR;
+}
+
+void
+gsk_renderer_set_viewport (GskRenderer *renderer,
+ const graphene_rect_t *viewport)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (GSK_IS_RENDERER (renderer));
+
+ if (viewport == NULL)
+ {
+ graphene_rect_init (&priv->viewport, 0.f, 0.f, 0.f, 0.f);
+ g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_VIEWPORT]);
+ return;
+ }
+
+ if (graphene_rect_equal (viewport, &priv->viewport))
+ return;
+
+ graphene_rect_init_from_rect (&priv->viewport, viewport);
+ priv->needs_viewport_resize = TRUE;
+
+ g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_VIEWPORT]);
+}
+
+void
+gsk_renderer_get_viewport (GskRenderer *renderer,
+ graphene_rect_t *viewport)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (GSK_IS_RENDERER (renderer));
+ g_return_if_fail (viewport != NULL);
+
+ graphene_rect_init_from_rect (viewport, &priv->viewport);
+}
+
+void
+gsk_renderer_set_modelview (GskRenderer *renderer,
+ const graphene_matrix_t *modelview)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (GSK_IS_RENDERER (renderer));
+
+ if (modelview == NULL)
+ graphene_matrix_init_identity (&priv->modelview);
+ else
+ graphene_matrix_init_from_matrix (&priv->modelview, modelview);
+
+ priv->needs_modelview_update = TRUE;
+
+ g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_MODELVIEW]);
+}
+
+void
+gsk_renderer_get_modelview (GskRenderer *renderer,
+ graphene_matrix_t *modelview)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (GSK_IS_RENDERER (renderer));
+ g_return_if_fail (modelview != NULL);
+
+ graphene_matrix_init_from_matrix (modelview, &priv->modelview);
+}
+
+void
+gsk_renderer_set_projection (GskRenderer *renderer,
+ const graphene_matrix_t *projection)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (GSK_IS_RENDERER (renderer));
+
+ if (projection == NULL)
+ graphene_matrix_init_identity (&priv->projection);
+ else
+ graphene_matrix_init_from_matrix (&priv->projection, projection);
+
+ priv->needs_projection_update = TRUE;
+
+ g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_PROJECTION]);
+}
+
+void
+gsk_renderer_get_projection (GskRenderer *renderer,
+ graphene_matrix_t *projection)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (GSK_IS_RENDERER (renderer));
+ g_return_if_fail (projection != NULL);
+
+ graphene_matrix_init_from_matrix (projection, &priv->projection);
+}
+
+void
+gsk_renderer_set_root_node (GskRenderer *renderer,
+ GskRenderNode *root)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (GSK_IS_RENDERER (renderer));
+ g_return_if_fail (GSK_IS_RENDER_NODE (root));
+
+ if (g_set_object (&priv->root_node, root))
+ {
+ priv->needs_tree_validation = TRUE;
+ g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_ROOT_NODE]);
+ }
+}
+
+void
+gsk_renderer_set_scaling_filters (GskRenderer *renderer,
+ GskScalingFilter min_filter,
+ GskScalingFilter mag_filter)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+ GObject *gobject;
+
+ g_return_if_fail (GSK_IS_RENDERER (renderer));
+
+ gobject = G_OBJECT (renderer);
+
+ g_object_freeze_notify (gobject);
+
+ if (priv->min_filter != min_filter)
+ {
+ priv->min_filter = min_filter;
+ g_object_notify_by_pspec (gobject, gsk_renderer_properties[PROP_MINIFICATION_FILTER]);
+ }
+
+ if (priv->mag_filter != mag_filter)
+ {
+ priv->mag_filter = mag_filter;
+ g_object_notify_by_pspec (gobject, gsk_renderer_properties[PROP_MAGNIFICATION_FILTER]);
+ }
+
+ g_object_thaw_notify (gobject);
+}
+
+void
+gsk_renderer_get_scaling_filters (GskRenderer *renderer,
+ GskScalingFilter *min_filter,
+ GskScalingFilter *mag_filter)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (GSK_IS_RENDERER (renderer));
+
+ if (min_filter != NULL)
+ *min_filter = priv->min_filter;
+
+ if (mag_filter != NULL)
+ *mag_filter = priv->mag_filter;
+}
+
+void
+gsk_renderer_set_surface (GskRenderer *renderer,
+ cairo_surface_t *surface)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (GSK_IS_RENDERER (renderer));
+ g_return_if_fail (surface != NULL);
+ g_return_if_fail (!priv->is_realized);
+
+ if (priv->surface == surface)
+ return;
+
+ g_clear_pointer (&priv->surface, cairo_surface_destroy);
+ priv->surface = cairo_surface_reference (surface);
+
+ g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_SURFACE]);
+}
+
+cairo_surface_t *
+gsk_renderer_get_surface (GskRenderer *renderer)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
+
+ if (priv->surface != NULL)
+ return priv->surface;
+
+ if (priv->window != NULL)
+ {
+ int scale = gdk_window_get_scale_factor (priv->window);
+ int width = gdk_window_get_width (priv->window);
+ int height = gdk_window_get_height (priv->window);
+ cairo_format_t format;
+
+ if (priv->use_alpha)
+ format = CAIRO_FORMAT_ARGB32;
+ else
+ format = CAIRO_FORMAT_RGB24;
+
+ priv->surface = gdk_window_create_similar_image_surface (priv->window,
+ format,
+ width, height,
+ scale);
+ }
+
+ return priv->surface;
+}
+
+GdkDisplay *
+gsk_renderer_get_display (GskRenderer *renderer)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
+
+ return priv->display;
+}
+
+GskRenderNode *
+gsk_renderer_get_root_node (GskRenderer *renderer)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
+
+ return priv->root_node;
+}
+
+gboolean
+gsk_renderer_is_realized (GskRenderer *renderer)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_val_if_fail (GSK_IS_RENDERER (renderer), FALSE);
+
+ return priv->is_realized;
+}
+
+void
+gsk_renderer_set_window (GskRenderer *renderer,
+ GdkWindow *window)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (GSK_IS_RENDERER (renderer));
+ g_return_if_fail (GDK_IS_WINDOW (window));
+ g_return_if_fail (!priv->is_realized);
+
+ if (g_set_object (&priv->window, window))
+ g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_WINDOW]);
+}
+
+GdkWindow *
+gsk_renderer_get_window (GskRenderer *renderer)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_val_if_fail (GSK_IS_RENDERER (renderer), NULL);
+
+ return priv->window;
+}
+
+gboolean
+gsk_renderer_realize (GskRenderer *renderer)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_val_if_fail (GSK_IS_RENDERER (renderer), FALSE);
+ g_return_val_if_fail (priv->window != NULL, FALSE);
+
+ if (priv->is_realized)
+ return TRUE;
+
+ priv->is_realized = GSK_RENDERER_GET_CLASS (renderer)->realize (renderer);
+
+ return priv->is_realized;
+}
+
+void
+gsk_renderer_unrealize (GskRenderer *renderer)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (GSK_IS_RENDERER (renderer));
+
+ if (!priv->is_realized)
+ return;
+
+ GSK_RENDERER_GET_CLASS (renderer)->unrealize (renderer);
+
+ priv->is_realized = FALSE;
+}
+
+void
+gsk_renderer_maybe_resize_viewport (GskRenderer *renderer)
+{
+ GskRendererClass *renderer_class = GSK_RENDERER_GET_CLASS (renderer);
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ if (priv->needs_viewport_resize)
+ {
+ renderer_class->resize_viewport (renderer, &priv->viewport);
+ priv->needs_viewport_resize = FALSE;
+
+ /* If the target surface has been created by a window, then we
+ * need to clear it
+ */
+ if (priv->window != NULL && priv->surface != NULL)
+ g_clear_pointer (&priv->surface, cairo_surface_destroy);
+ }
+}
+
+void
+gsk_renderer_maybe_update (GskRenderer *renderer)
+{
+ GskRendererClass *renderer_class = GSK_RENDERER_GET_CLASS (renderer);
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ if (priv->needs_modelview_update || priv->needs_projection_update)
+ {
+ renderer_class->update (renderer, &priv->modelview, &priv->projection);
+ priv->needs_modelview_update = FALSE;
+ priv->needs_projection_update = FALSE;
+ }
+}
+
+void
+gsk_renderer_maybe_validate_tree (GskRenderer *renderer)
+{
+ GskRendererClass *renderer_class = GSK_RENDERER_GET_CLASS (renderer);
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ if (priv->needs_tree_validation)
+ {
+ renderer_class->validate_tree (renderer, priv->root_node);
+ priv->needs_tree_validation = FALSE;
+ }
+}
+
+void
+gsk_renderer_maybe_clear (GskRenderer *renderer)
+{
+ GskRendererClass *renderer_class = GSK_RENDERER_GET_CLASS (renderer);
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ if (priv->auto_clear)
+ renderer_class->clear (renderer);
+}
+
+void
+gsk_renderer_render (GskRenderer *renderer)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (GSK_IS_RENDERER (renderer));
+ g_return_if_fail (priv->is_realized);
+ g_return_if_fail (priv->root_node != NULL);
+
+ gsk_renderer_maybe_resize_viewport (renderer);
+
+ gsk_renderer_maybe_update (renderer);
+
+ gsk_renderer_maybe_validate_tree (renderer);
+
+ gsk_renderer_maybe_clear (renderer);
+
+ GSK_RENDERER_GET_CLASS (renderer)->render (renderer);
+}
+
+void
+gsk_renderer_set_auto_clear (GskRenderer *renderer,
+ gboolean clear)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (GSK_IS_RENDERER (renderer));
+
+ clear = !!clear;
+
+ if (clear == priv->auto_clear)
+ return;
+
+ priv->auto_clear = clear;
+
+ g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_AUTO_CLEAR]);
+}
+
+gboolean
+gsk_renderer_get_auto_clear (GskRenderer *renderer)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_val_if_fail (GSK_IS_RENDERER (renderer), FALSE);
+
+ return priv->auto_clear;
+}
+
+void
+gsk_renderer_set_use_alpha (GskRenderer *renderer,
+ gboolean use_alpha)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_if_fail (GSK_IS_RENDERER (renderer));
+ g_return_if_fail (!priv->is_realized);
+
+ use_alpha = !!use_alpha;
+
+ if (use_alpha != priv->use_alpha)
+ return;
+
+ priv->use_alpha = use_alpha;
+
+ g_object_notify_by_pspec (G_OBJECT (renderer), gsk_renderer_properties[PROP_USE_ALPHA]);
+}
+
+gboolean
+gsk_renderer_get_use_alpha (GskRenderer *renderer)
+{
+ GskRendererPrivate *priv = gsk_renderer_get_instance_private (renderer);
+
+ g_return_val_if_fail (GSK_IS_RENDERER (renderer), FALSE);
+
+ return priv->use_alpha;
+}
+
+/**
+ * gsk_renderer_get_for_display:
+ * @display: a #GdkDisplay
+ *
+ * Creates an appropriate #GskRenderer instance for the given @display.
+ *
+ * Returns: (transfer full): a #GskRenderer
+ *
+ * Since: 3.20
+ */
+GskRenderer *
+gsk_renderer_get_for_display (GdkDisplay *display)
+{
+ return g_object_new (GSK_TYPE_CAIRO_RENDERER, "display", display, NULL);
+}
diff --git a/gsk/gskrenderer.h b/gsk/gskrenderer.h
new file mode 100644
index 0000000..c96a4be
--- /dev/null
+++ b/gsk/gskrenderer.h
@@ -0,0 +1,86 @@
+/* GSK - The GTK Scene Kit
+ *
+ * Copyright 2016 Endless
+ *
+ * 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/>.
+ */
+
+#ifndef __GSK_RENDERER_H__
+#define __GSK_RENDERER_H__
+
+#if !defined (__GSK_H_INSIDE__) && !defined (GSK_COMPILATION)
+#error "Only <gsk/gsk.h> can be included directly."
+#endif
+
+#include <gsk/gsktypes.h>
+#include <gsk/gskrendernode.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_RENDERER (gsk_renderer_get_type ())
+
+#define GSK_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_RENDERER, GskRenderer))
+#define GSK_IS_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_RENDERER))
+
+typedef struct _GskRenderer GskRenderer;
+typedef struct _GskRendererClass GskRendererClass;
+
+GDK_AVAILABLE_IN_3_20
+GType gsk_renderer_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_20
+GskRenderer * gsk_renderer_get_for_display (GdkDisplay *display);
+
+GDK_AVAILABLE_IN_3_20
+void gsk_renderer_set_viewport (GskRenderer *renderer,
+ const graphene_rect_t *viewport);
+GDK_AVAILABLE_IN_3_20
+void gsk_renderer_set_projection (GskRenderer *renderer,
+ const graphene_matrix_t *projection);
+GDK_AVAILABLE_IN_3_20
+void gsk_renderer_set_modelview (GskRenderer *renderer,
+ const graphene_matrix_t *modelview);
+GDK_AVAILABLE_IN_3_20
+void gsk_renderer_set_scaling_filters (GskRenderer *renderer,
+ GskScalingFilter min_filter,
+ GskScalingFilter mag_filter);
+GDK_AVAILABLE_IN_3_20
+void gsk_renderer_set_auto_clear (GskRenderer *renderer,
+ gboolean clear);
+GDK_AVAILABLE_IN_3_20
+void gsk_renderer_set_root_node (GskRenderer *renderer,
+ GskRenderNode *root);
+GDK_AVAILABLE_IN_3_20
+void gsk_renderer_set_surface (GskRenderer *renderer,
+ cairo_surface_t *surface);
+GDK_AVAILABLE_IN_3_20
+void gsk_renderer_set_window (GskRenderer *renderer,
+ GdkWindow *window);
+GDK_AVAILABLE_IN_3_20
+void gsk_renderer_set_use_alpha (GskRenderer *renderer,
+ gboolean use_alpha);
+
+GDK_AVAILABLE_IN_3_20
+cairo_surface_t * gsk_renderer_get_surface (GskRenderer *renderer);
+
+GDK_AVAILABLE_IN_3_20
+gboolean gsk_renderer_realize (GskRenderer *renderer);
+GDK_AVAILABLE_IN_3_20
+void gsk_renderer_unrealize (GskRenderer *renderer);
+GDK_AVAILABLE_IN_3_20
+void gsk_renderer_render (GskRenderer *renderer);
+
+G_END_DECLS
+
+#endif /* __GSK_RENDERER_H__ */
diff --git a/gsk/gskrendererprivate.h b/gsk/gskrendererprivate.h
new file mode 100644
index 0000000..47ed218
--- /dev/null
+++ b/gsk/gskrendererprivate.h
@@ -0,0 +1,78 @@
+/* GSK - The GTK Scene Kit
+ *
+ * Copyright 2016 Endless
+ *
+ * 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/>.
+ */
+
+#ifndef __GSK_RENDERER_PRIVATE_H__
+#define __GSK_RENDERER_PRIVATE_H__
+
+#include "gskrenderer.h"
+
+G_BEGIN_DECLS
+
+#define GSK_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSK_TYPE_RENDERER,
GskRendererClass))
+#define GSK_IS_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSK_TYPE_RENDERER))
+#define GSK_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GSK_TYPE_RENDERER,
GskRendererClass))
+
+struct _GskRenderer
+{
+ GObject parent_instance;
+};
+
+struct _GskRendererClass
+{
+ GObjectClass parent_class;
+
+ gboolean (* realize) (GskRenderer *renderer);
+ void (* unrealize) (GskRenderer *renderer);
+
+ void (* resize_viewport) (GskRenderer *renderer,
+ const graphene_rect_t *viewport);
+ void (* update) (GskRenderer *renderer,
+ const graphene_matrix_t *modelview,
+ const graphene_matrix_t *projection);
+ void (* validate_tree) (GskRenderer *renderer,
+ GskRenderNode *root);
+ void (* clear) (GskRenderer *renderer);
+ void (* render) (GskRenderer *renderer);
+};
+
+void gsk_renderer_get_viewport (GskRenderer *renderer,
+ graphene_rect_t *viewport);
+void gsk_renderer_get_modelview (GskRenderer *renderer,
+ graphene_matrix_t *modelview);
+void gsk_renderer_get_projection (GskRenderer *renderer,
+ graphene_matrix_t *projection);
+void gsk_renderer_get_scaling_filters (GskRenderer *renderer,
+ GskScalingFilter *min_filter,
+ GskScalingFilter *mag_filter);
+gboolean gsk_renderer_get_auto_clear (GskRenderer *renderer);
+gboolean gsk_renderer_get_use_alpha (GskRenderer *renderer);
+
+GskRenderNode *gsk_renderer_get_root_node (GskRenderer *renderer);
+GdkDisplay *gsk_renderer_get_display (GskRenderer *renderer);
+GdkWindow *gsk_renderer_get_window (GskRenderer *renderer);
+
+gboolean gsk_renderer_is_realized (GskRenderer *renderer);
+
+void gsk_renderer_maybe_resize_viewport (GskRenderer *renderer);
+void gsk_renderer_maybe_update (GskRenderer *renderer);
+void gsk_renderer_maybe_validate_tree (GskRenderer *renderer);
+void gsk_renderer_maybe_clear (GskRenderer *renderer);
+
+G_END_DECLS
+
+#endif /* __GSK_RENDERER_PRIVATE_H__ */
diff --git a/gsk/gskrendernode.c b/gsk/gskrendernode.c
new file mode 100644
index 0000000..1a1a46a
--- /dev/null
+++ b/gsk/gskrendernode.c
@@ -0,0 +1,767 @@
+#include "config.h"
+
+#include "gskrendernodeprivate.h"
+
+#include "gskdebugprivate.h"
+#include "gskrendernodeiter.h"
+
+#include <graphene-gobject.h>
+
+G_DEFINE_TYPE (GskRenderNode, gsk_render_node, G_TYPE_OBJECT)
+
+static void
+gsk_render_node_dispose (GObject *gobject)
+{
+ GskRenderNode *self = GSK_RENDER_NODE (gobject);
+ GskRenderNodeIter iter;
+
+ gsk_render_node_iter_init (&iter, self);
+ while (gsk_render_node_iter_next (&iter, NULL))
+ gsk_render_node_iter_remove (&iter);
+
+ G_OBJECT_CLASS (gsk_render_node_parent_class)->dispose (gobject);
+}
+
+static void
+gsk_render_node_class_init (GskRenderNodeClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->dispose = gsk_render_node_dispose;
+}
+
+static void
+gsk_render_node_init (GskRenderNode *self)
+{
+ graphene_rect_init_from_rect (&self->bounds, graphene_rect_zero ());
+
+ graphene_matrix_init_identity (&self->transform);
+ graphene_matrix_init_identity (&self->child_transform);
+
+ self->opacity = 1.0;
+}
+
+/**
+ * gsk_render_node_new:
+ *
+ * Creates a new #GskRenderNode, to be used with #GskRenderer.
+ *
+ * Returns: (transfer full): the newly created #GskRenderNode
+ *
+ * Since: 3.20
+ */
+GskRenderNode *
+gsk_render_node_new (void)
+{
+ return g_object_new (GSK_TYPE_RENDER_NODE, NULL);
+}
+
+/**
+ * gsk_render_node_get_parent:
+ * @node: a #GskRenderNode
+ *
+ * Returns the parent of the @node.
+ *
+ * Returns: (transfer none): the parent of the #GskRenderNode
+ *
+ * Since: 3.20
+ */
+GskRenderNode *
+gsk_render_node_get_parent (GskRenderNode *node)
+{
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+
+ return node->parent;
+}
+
+/**
+ * gsk_render_node_get_first_child:
+ * @node: a #GskRenderNode
+ *
+ * Returns the first child of @node.
+ *
+ * Returns: (transfer none): the first child of the #GskRenderNode
+ *
+ * Since: 3.20
+ */
+GskRenderNode *
+gsk_render_node_get_first_child (GskRenderNode *node)
+{
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+
+ return node->first_child;
+}
+
+/**
+ * gsk_render_node_get_last_child:
+ *
+ * Returns the last child of @node.
+ *
+ * Returns: (transfer none): the last child of the #GskRenderNode
+ *
+ * Since: 3.20
+ */
+GskRenderNode *
+gsk_render_node_get_last_child (GskRenderNode *node)
+{
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+
+ return node->last_child;
+}
+
+/**
+ * gsk_render_node_get_next_sibling:
+ *
+ * Returns the next sibling of @node.
+ *
+ * Returns: (transfer none): the next sibling of the #GskRenderNode
+ *
+ * Since: 3.20
+ */
+GskRenderNode *
+gsk_render_node_get_next_sibling (GskRenderNode *node)
+{
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+
+ return node->next_sibling;
+}
+
+/**
+ * gsk_render_node_get_previous_sibling:
+ *
+ * Returns the previous sibling of @node.
+ *
+ * Returns: (transfer none): the previous sibling of the #GskRenderNode
+ *
+ * Since: 3.20
+ */
+GskRenderNode *
+gsk_render_node_get_previous_sibling (GskRenderNode *node)
+{
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+
+ return node->prev_sibling;
+}
+
+typedef void (* InsertChildFunc) (GskRenderNode *node,
+ GskRenderNode *child,
+ gpointer user_data);
+
+static void
+gsk_render_node_insert_child_internal (GskRenderNode *node,
+ GskRenderNode *child,
+ InsertChildFunc insert_func,
+ gpointer insert_func_data)
+{
+ if (child->parent != NULL)
+ {
+ g_critical ("The render node of type '%s' already has a parent of type '%s'; "
+ "render nodes cannot be added to multiple parents.",
+ G_OBJECT_TYPE_NAME (child),
+ G_OBJECT_TYPE_NAME (node));
+ return;
+ }
+
+ insert_func (node, child, insert_func_data);
+
+ g_object_ref (child);
+
+ child->parent = node;
+ child->age = 0;
+ child->needs_world_matrix_update = TRUE;
+
+ node->n_children += 1;
+ node->age += 1;
+ node->needs_world_matrix_update = TRUE;
+
+ if (child->prev_sibling == NULL)
+ node->first_child = child;
+ if (child->next_sibling == NULL)
+ node->last_child = child;
+}
+
+static void
+insert_child_at_pos (GskRenderNode *node,
+ GskRenderNode *child,
+ gpointer user_data)
+{
+ int pos = GPOINTER_TO_INT (user_data);
+
+ if (pos == 0)
+ {
+ GskRenderNode *tmp = node->first_child;
+
+ if (tmp != NULL)
+ tmp->prev_sibling = child;
+
+ child->prev_sibling = NULL;
+ child->next_sibling = tmp;
+
+ return;
+ }
+
+ if (pos < 0 || pos >= node->n_children)
+ {
+ GskRenderNode *tmp = node->last_child;
+
+ if (tmp != NULL)
+ tmp->next_sibling = child;
+
+ child->prev_sibling = tmp;
+ child->next_sibling = NULL;
+
+ return;
+ }
+
+ {
+ GskRenderNode *iter;
+ int i;
+
+ for (iter = node->first_child, i = 0;
+ iter != NULL;
+ iter = iter->next_sibling, i++)
+ {
+ if (i == pos)
+ {
+ GskRenderNode *tmp = iter->prev_sibling;
+
+ child->prev_sibling = tmp;
+ child->next_sibling = iter;
+
+ iter->prev_sibling = child;
+
+ if (tmp != NULL)
+ tmp->next_sibling = child;
+
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * gsk_render_node_insert_child_at_pos:
+ * @node: a #GskRenderNode
+ * @child: a #GskRenderNode
+ * @index_: the index in the list of children where @child should be inserted at
+ *
+ * Inserts @child into the list of children of @node, using the given @index_.
+ *
+ * If @index_ is 0, the @child will be prepended to the list of children.
+ *
+ * If @index_ is less than zero, or equal to the number of children, the @child
+ * will be appended to the list of children.
+ *
+ * This function acquires a reference on @child.
+ *
+ * Returns: (transfer none): the #GskRenderNode
+ *
+ * Since: 3.20
+ */
+GskRenderNode *
+gsk_render_node_insert_child_at_pos (GskRenderNode *node,
+ GskRenderNode *child,
+ int index_)
+{
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
+
+ gsk_render_node_insert_child_internal (node, child,
+ insert_child_at_pos,
+ GINT_TO_POINTER (index_));
+
+ return node;
+}
+
+static void
+insert_child_before (GskRenderNode *node,
+ GskRenderNode *child,
+ gpointer user_data)
+{
+ GskRenderNode *sibling = user_data;
+
+ if (sibling == NULL)
+ sibling = node->first_child;
+
+ child->next_sibling = sibling;
+
+ if (sibling != NULL)
+ {
+ GskRenderNode *tmp = sibling->prev_sibling;
+
+ child->prev_sibling = tmp;
+
+ if (tmp != NULL)
+ tmp->next_sibling = child;
+
+ sibling->prev_sibling = child;
+ }
+ else
+ child->prev_sibling = NULL;
+}
+
+/**
+ * gsk_render_node_insert_child_before:
+ * @node: a #GskRenderNode
+ * @child: a #GskRenderNode
+ * @sibling: (nullable): a #GskRenderNode, or %NULL
+ *
+ * Inserts @child in the list of children of @node, before @sibling.
+ *
+ * If @sibling is %NULL, the @child will be inserted at the beginning of the
+ * list of children.
+ *
+ * This function acquires a reference of @child.
+ *
+ * Returns: (transfer none): the #GskRenderNode
+ *
+ * Since: 3.20
+ */
+GskRenderNode *
+gsk_render_node_insert_child_before (GskRenderNode *node,
+ GskRenderNode *child,
+ GskRenderNode *sibling)
+{
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
+ g_return_val_if_fail (sibling == NULL || GSK_IS_RENDER_NODE (sibling), node);
+
+ gsk_render_node_insert_child_internal (node, child, insert_child_before, sibling);
+
+ return node;
+}
+
+static void
+insert_child_after (GskRenderNode *node,
+ GskRenderNode *child,
+ gpointer user_data)
+{
+ GskRenderNode *sibling = user_data;
+
+ if (sibling == NULL)
+ sibling = node->last_child;
+
+ child->prev_sibling = sibling;
+
+ if (sibling != NULL)
+ {
+ GskRenderNode *tmp = sibling->next_sibling;
+
+ child->next_sibling = tmp;
+
+ if (tmp != NULL)
+ tmp->prev_sibling = child;
+
+ sibling->next_sibling = child;
+ }
+ else
+ child->next_sibling = NULL;
+}
+
+/**
+ * gsk_render_node_insert_child_after:
+ * @node: a #GskRenderNode
+ * @child: a #GskRenderNode
+ * @sibling: (nullable): a #GskRenderNode, or %NULL
+ *
+ * Inserts @child in the list of children of @node, after @sibling.
+ *
+ * If @sibling is %NULL, the @child will be inserted at the end of the list
+ * of children.
+ *
+ * This function acquires a reference of @child.
+ *
+ * Returns: (transfer none): the #GskRenderNode
+ *
+ * Since: 3.20
+ */
+GskRenderNode *
+gsk_render_node_insert_child_after (GskRenderNode *node,
+ GskRenderNode *child,
+ GskRenderNode *sibling)
+{
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
+ g_return_val_if_fail (sibling == NULL || GSK_IS_RENDER_NODE (sibling), node);
+
+ if (sibling != NULL)
+ g_return_val_if_fail (sibling->parent == node, node);
+
+ gsk_render_node_insert_child_internal (node, child, insert_child_after, sibling);
+
+ return node;
+}
+
+typedef struct {
+ GskRenderNode *prev_sibling;
+ GskRenderNode *next_sibling;
+} InsertBetween;
+
+static void
+insert_child_between (GskRenderNode *node,
+ GskRenderNode *child,
+ gpointer data_)
+{
+ InsertBetween *data = data_;
+
+ child->prev_sibling = data->prev_sibling;
+ child->next_sibling = data->next_sibling;
+
+ if (data->prev_sibling != NULL)
+ data->prev_sibling->next_sibling = child;
+
+ if (data->next_sibling != NULL)
+ data->next_sibling->prev_sibling = child;
+}
+
+/**
+ * gsk_render_node_replace_child:
+ * @node: a #GskRenderNode
+ * @new_child: the #GskRenderNode to add
+ * @old_child: the #GskRenderNode to replace
+ *
+ * Replaces @old_child with @new_child in the list of children of @node.
+ *
+ * This function acquires a reference to @new_child, and releases a reference
+ * of @old_child.
+ *
+ * Returns: (transfer none): the #GskRenderNode
+ *
+ * Since: 3.20
+ */
+GskRenderNode *
+gsk_render_node_replace_child (GskRenderNode *node,
+ GskRenderNode *new_child,
+ GskRenderNode *old_child)
+{
+ InsertBetween clos;
+
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (new_child), node);
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (old_child), node);
+
+ g_return_val_if_fail (new_child->parent == NULL, node);
+ g_return_val_if_fail (old_child->parent == node, node);
+
+ clos.prev_sibling = old_child->prev_sibling;
+ clos.next_sibling = old_child->next_sibling;
+ gsk_render_node_remove_child (node, old_child);
+
+ gsk_render_node_insert_child_internal (node, new_child, insert_child_between, &clos);
+
+ return node;
+}
+
+/**
+ * gsk_render_node_remove_child:
+ * @node: a #GskRenderNode
+ * @child: a #GskRenderNode child of @node
+ *
+ * Removes @child from the list of children of @node.
+ *
+ * This function releases the reference acquired when adding @child to the
+ * list of children.
+ *
+ * Returns: (transfer none): the #GskRenderNode
+ */
+GskRenderNode *
+gsk_render_node_remove_child (GskRenderNode *node,
+ GskRenderNode *child)
+{
+ GskRenderNode *prev_sibling, *next_sibling;
+
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (child), node);
+
+ if (child->parent != node)
+ {
+ g_critical ("The render node of type '%s' is not a child of the render node of type '%s'",
+ G_OBJECT_TYPE_NAME (child),
+ G_OBJECT_TYPE_NAME (node));
+ return node;
+ }
+
+ prev_sibling = child->prev_sibling;
+ next_sibling = child->next_sibling;
+
+ child->parent = NULL;
+ child->prev_sibling = NULL;
+ child->next_sibling = NULL;
+ child->age = 0;
+
+ if (prev_sibling)
+ prev_sibling->next_sibling = next_sibling;
+ if (next_sibling)
+ next_sibling->prev_sibling = prev_sibling;
+
+ node->age += 1;
+ node->n_children -= 1;
+
+ if (node->first_child == child)
+ node->first_child = next_sibling;
+ if (node->last_child == child)
+ node->last_child = prev_sibling;
+
+ g_object_unref (child);
+
+ return node;
+}
+
+/**
+ * gsk_render_node_remove_all_children:
+ * @node: a #GskRenderNode
+ *
+ * Removes all children of @node.
+ *
+ * See also: gsk_render_node_remove_child()
+ *
+ * Returns: (transfer none): the #GskRenderNode
+ *
+ * Since: 3.20
+ */
+GskRenderNode *
+gsk_render_node_remove_all_children (GskRenderNode *node)
+{
+ GskRenderNodeIter iter;
+
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+
+ if (node->n_children == 0)
+ return node;
+
+ gsk_render_node_iter_init (&iter, node);
+ while (gsk_render_node_iter_next (&iter, NULL))
+ gsk_render_node_iter_remove (&iter);
+
+ g_assert (node->n_children == 0);
+ g_assert (node->first_child == NULL);
+ g_assert (node->last_child == NULL);
+
+ return node;
+}
+
+guint
+gsk_render_node_get_n_children (GskRenderNode *node)
+{
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), 0);
+
+ return node->n_children;
+}
+
+void
+gsk_render_node_set_bounds (GskRenderNode *node,
+ const graphene_rect_t *bounds)
+{
+ g_return_if_fail (GSK_IS_RENDER_NODE (node));
+
+ if (bounds == NULL)
+ graphene_rect_init_from_rect (&node->bounds, graphene_rect_zero ());
+ else
+ graphene_rect_init_from_rect (&node->bounds, bounds);
+}
+
+void
+gsk_render_node_get_bounds (GskRenderNode *node,
+ graphene_rect_t *bounds)
+{
+ g_return_if_fail (GSK_IS_RENDER_NODE (node));
+ g_return_if_fail (bounds != NULL);
+
+ *bounds = node->bounds;
+}
+
+void
+gsk_render_node_set_transform (GskRenderNode *node,
+ const graphene_matrix_t *transform)
+{
+ g_return_if_fail (GSK_IS_RENDER_NODE (node));
+
+ if (transform == NULL)
+ graphene_matrix_init_identity (&node->transform);
+ else
+ graphene_matrix_init_from_matrix (&node->transform, transform);
+
+ node->transform_set = !graphene_matrix_is_identity (&node->transform);
+ node->needs_world_matrix_update = TRUE;
+}
+
+void
+gsk_render_node_set_child_transform (GskRenderNode *node,
+ const graphene_matrix_t *transform)
+{
+ g_return_if_fail (GSK_IS_RENDER_NODE (node));
+
+ if (transform == NULL)
+ graphene_matrix_init_identity (&node->child_transform);
+ else
+ graphene_matrix_init_from_matrix (&node->child_transform, transform);
+
+ node->child_transform_set = !graphene_matrix_is_identity (&node->child_transform);
+ node->needs_world_matrix_update = TRUE;
+}
+
+void
+gsk_render_node_set_opacity (GskRenderNode *node,
+ double opacity)
+{
+ g_return_if_fail (GSK_IS_RENDER_NODE (node));
+
+ node->opacity = CLAMP (opacity, 0.0, 1.0);
+}
+
+double
+gsk_render_node_get_opacity (GskRenderNode *node)
+{
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), 0.0);
+
+ return node->opacity;
+}
+
+void
+gsk_render_node_set_hidden (GskRenderNode *node,
+ gboolean hidden)
+{
+ g_return_if_fail (GSK_IS_RENDER_NODE (node));
+
+ node->hidden = !!hidden;
+}
+
+gboolean
+gsk_render_node_is_hidden (GskRenderNode *node)
+{
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), TRUE);
+
+ return node->hidden;
+}
+
+void
+gsk_render_node_set_opaque (GskRenderNode *node,
+ gboolean opaque)
+{
+ g_return_if_fail (GSK_IS_RENDER_NODE (node));
+
+ node->opaque = !!opaque;
+}
+
+gboolean
+gsk_render_node_is_opaque (GskRenderNode *node)
+{
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), TRUE);
+
+ return node->opaque;
+}
+
+gboolean
+gsk_render_node_contains (GskRenderNode *node,
+ GskRenderNode *descendant)
+{
+ GskRenderNode *tmp;
+
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), FALSE);
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (descendant), FALSE);
+
+ for (tmp = descendant; tmp != NULL; tmp = tmp->parent)
+ if (tmp == node)
+ return TRUE;
+
+ return FALSE;
+}
+
+GskRenderNode *
+gsk_render_node_get_toplevel (GskRenderNode *node)
+{
+ GskRenderNode *parent;
+
+ parent = node->parent;
+ if (parent == NULL)
+ return node;
+
+ while (parent != NULL)
+ {
+ if (parent->parent == NULL)
+ return parent;
+
+ parent = parent->parent;
+ }
+
+ return NULL;
+}
+
+void
+gsk_render_node_update_world_matrix (GskRenderNode *node,
+ gboolean force)
+{
+ GskRenderNodeIter iter;
+ GskRenderNode *child;
+
+ if (force || node->needs_world_matrix_update)
+ {
+ GSK_NOTE (RENDER_NODE, g_print ("Updating cached world matrix on node %p [parent=%p, t_set=%s,
ct_set=%s]\n",
+ node,
+ node->parent != NULL ? node->parent : 0,
+ node->transform_set ? "y" : "n",
+ node->parent != NULL && node->parent->child_transform_set ? "y" :
"n"));
+
+ if (node->parent == NULL)
+ graphene_matrix_init_from_matrix (&node->world_matrix, &node->transform);
+ else
+ {
+ GskRenderNode *parent = node->parent;
+ graphene_matrix_t tmp;
+
+ if (parent->child_transform_set)
+ graphene_matrix_init_from_matrix (&tmp, &parent->child_transform);
+ else
+ graphene_matrix_init_identity (&tmp);
+
+ if (node->transform_set)
+ graphene_matrix_multiply (&tmp, &node->transform, &tmp);
+
+ graphene_matrix_multiply (&tmp, &parent->world_matrix, &node->world_matrix);
+ }
+
+ node->needs_world_matrix_update = FALSE;
+ }
+
+ gsk_render_node_iter_init (&iter, node);
+ while (gsk_render_node_iter_next (&iter, &child))
+ gsk_render_node_update_world_matrix (child, TRUE);
+}
+
+void
+gsk_render_node_set_surface (GskRenderNode *node,
+ cairo_surface_t *surface)
+{
+ g_return_if_fail (GSK_IS_RENDER_NODE (node));
+ g_return_if_fail (surface != NULL);
+
+ g_clear_pointer (&node->surface, cairo_surface_destroy);
+ node->surface = cairo_surface_reference (surface);
+}
+
+cairo_surface_t *
+gsk_render_node_get_surface (GskRenderNode *node)
+{
+ g_return_val_if_fail (GSK_IS_RENDER_NODE (node), NULL);
+
+ return node->surface;
+}
+
+void
+gsk_render_node_get_world_matrix (GskRenderNode *node,
+ graphene_matrix_t *mv)
+{
+ g_return_if_fail (GSK_IS_RENDER_NODE (node));
+ g_return_if_fail (mv != NULL);
+
+ if (node->needs_world_matrix_update)
+ {
+ GskRenderNode *tmp = gsk_render_node_get_toplevel (node);
+
+ gsk_render_node_update_world_matrix (tmp, TRUE);
+
+ g_assert (!node->needs_world_matrix_update);
+ }
+
+ *mv = node->world_matrix;
+}
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
new file mode 100644
index 0000000..43280e7
--- /dev/null
+++ b/gsk/gskrendernode.h
@@ -0,0 +1,111 @@
+/* GSK - The GTK Scene Kit
+ *
+ * Copyright 2016 Endless
+ *
+ * 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/>.
+ */
+
+#ifndef __GSK_RENDER_NODE_H__
+#define __GSK_RENDER_NODE_H__
+
+#if !defined (__GSK_H_INSIDE__) && !defined (GSK_COMPILATION)
+#error "Only <gsk/gsk.h> can be included directly."
+#endif
+
+#include <gsk/gsktypes.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_RENDER_NODE (gsk_render_node_get_type ())
+
+#define GSK_RENDER_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSK_TYPE_RENDER_NODE, GskRenderNode))
+#define GSK_IS_RENDER_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSK_TYPE_RENDER_NODE))
+
+typedef struct _GskRenderNode GskRenderNode;
+typedef struct _GskRenderNodeClass GskRenderNodeClass;
+
+GDK_AVAILABLE_IN_3_20
+GType gsk_render_node_get_type (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_20
+GskRenderNode * gsk_render_node_new (void);
+
+GDK_AVAILABLE_IN_3_20
+GskRenderNode * gsk_render_node_get_parent (GskRenderNode *node);
+GDK_AVAILABLE_IN_3_20
+GskRenderNode * gsk_render_node_get_first_child (GskRenderNode *node);
+GDK_AVAILABLE_IN_3_20
+GskRenderNode * gsk_render_node_get_last_child (GskRenderNode *node);
+GDK_AVAILABLE_IN_3_20
+GskRenderNode * gsk_render_node_get_next_sibling (GskRenderNode *node);
+GDK_AVAILABLE_IN_3_20
+GskRenderNode * gsk_render_node_get_previous_sibling (GskRenderNode *node);
+
+GDK_AVAILABLE_IN_3_20
+GskRenderNode * gsk_render_node_insert_child_at_pos (GskRenderNode *node,
+ GskRenderNode *child,
+ int index_);
+GDK_AVAILABLE_IN_3_20
+GskRenderNode * gsk_render_node_insert_child_before (GskRenderNode *node,
+ GskRenderNode *child,
+ GskRenderNode *sibling);
+GDK_AVAILABLE_IN_3_20
+GskRenderNode * gsk_render_node_insert_child_after (GskRenderNode *node,
+ GskRenderNode *child,
+ GskRenderNode *sibling);
+GDK_AVAILABLE_IN_3_20
+GskRenderNode * gsk_render_node_remove_child (GskRenderNode *node,
+ GskRenderNode *child);
+GDK_AVAILABLE_IN_3_20
+GskRenderNode * gsk_render_node_replace_child (GskRenderNode *node,
+ GskRenderNode *new_child,
+ GskRenderNode *old_child);
+GDK_AVAILABLE_IN_3_20
+GskRenderNode * gsk_render_node_remove_all_children (GskRenderNode *node);
+GDK_AVAILABLE_IN_3_20
+guint gsk_render_node_get_n_children (GskRenderNode *node);
+
+GDK_AVAILABLE_IN_3_20
+gboolean gsk_render_node_contains (GskRenderNode *node,
+ GskRenderNode *descendant);
+
+GDK_AVAILABLE_IN_3_20
+void gsk_render_node_set_bounds (GskRenderNode *node,
+ const graphene_rect_t *bounds);
+GDK_AVAILABLE_IN_3_20
+void gsk_render_node_set_transform (GskRenderNode *node,
+ const graphene_matrix_t *transform);
+GDK_AVAILABLE_IN_3_20
+void gsk_render_node_set_child_transform (GskRenderNode *node,
+ const graphene_matrix_t *transform);
+GDK_AVAILABLE_IN_3_20
+void gsk_render_node_set_opacity (GskRenderNode *node,
+ double opacity);
+GDK_AVAILABLE_IN_3_20
+void gsk_render_node_set_hidden (GskRenderNode *node,
+ gboolean hidden);
+GDK_AVAILABLE_IN_3_20
+gboolean gsk_render_node_is_hidden (GskRenderNode *node);
+GDK_AVAILABLE_IN_3_20
+void gsk_render_node_set_opaque (GskRenderNode *node,
+ gboolean opaque);
+GDK_AVAILABLE_IN_3_20
+gboolean gsk_render_node_is_opaque (GskRenderNode *node);
+GDK_AVAILABLE_IN_3_20
+void gsk_render_node_set_surface (GskRenderNode *node,
+ cairo_surface_t *surface);
+
+G_END_DECLS
+
+#endif /* __GSK_RENDER_NODE_H__ */
diff --git a/gsk/gskrendernodeiter.c b/gsk/gskrendernodeiter.c
new file mode 100644
index 0000000..cdcf29c
--- /dev/null
+++ b/gsk/gskrendernodeiter.c
@@ -0,0 +1,148 @@
+#include "config.h"
+#include "gskrendernodeiter.h"
+#include "gskrendernodeprivate.h"
+
+typedef struct {
+ GskRenderNode *root;
+ GskRenderNode *current;
+ gint64 age;
+ gpointer reserved1;
+ gpointer reserved2;
+} RealIter;
+
+#define REAL_ITER(iter) ((RealIter *) (iter))
+
+GskRenderNodeIter *
+gsk_render_node_iter_new (void)
+{
+ return g_slice_new (GskRenderNodeIter);
+}
+
+static GskRenderNodeIter *
+gsk_render_node_iter_copy (GskRenderNodeIter *src)
+{
+ return g_slice_dup (GskRenderNodeIter, src);
+}
+
+void
+gsk_render_node_iter_free (GskRenderNodeIter *iter)
+{
+ g_slice_free (GskRenderNodeIter, iter);
+}
+
+G_DEFINE_BOXED_TYPE (GskRenderNodeIter, gsk_render_node_iter,
+ gsk_render_node_iter_copy,
+ gsk_render_node_iter_free)
+
+void
+gsk_render_node_iter_init (GskRenderNodeIter *iter,
+ GskRenderNode *node)
+{
+ RealIter *riter = REAL_ITER (iter);
+
+ g_return_if_fail (iter != NULL);
+ g_return_if_fail (GSK_IS_RENDER_NODE (node));
+
+ riter->root = node;
+ riter->age = node->age;
+ riter->current = NULL;
+}
+
+gboolean
+gsk_render_node_iter_is_valid (GskRenderNodeIter *iter)
+{
+ RealIter *riter = REAL_ITER (iter);
+
+ g_return_val_if_fail (iter != NULL, FALSE);
+
+ if (riter->root == NULL)
+ return FALSE;
+
+ return riter->root->age == riter->age;
+}
+
+/**
+ * gsk_render_node_iter_next:
+ * @iter: a #GskRenderNodeIter
+ * @child: (out) (transfer none): return location for a #GskRenderNode
+ *
+ * Advances the @iter and retrieves the next child of the root #GskRenderNode
+ * used to initialize the #GskRenderNodeIter.
+ *
+ * If the iterator could advance, this function returns %TRUE and sets the
+ * @child argument with the child #GskRenderNode.
+ *
+ * If the iterator could not advance, this function returns %FALSE and the
+ * contents of the @child argument are undefined.
+ *
+ * Returns: %TRUE if the iterator could advance, and %FALSE otherwise
+ *
+ * Since: 3.20
+ */
+gboolean
+gsk_render_node_iter_next (GskRenderNodeIter *iter,
+ GskRenderNode **child)
+{
+ RealIter *riter = REAL_ITER (iter);
+
+ g_return_val_if_fail (riter != NULL, FALSE);
+ g_return_val_if_fail (riter->root != NULL, FALSE);
+ g_return_val_if_fail (riter->root->age == riter->age, FALSE);
+
+ if (riter->current == NULL)
+ riter->current = riter->root->first_child;
+ else
+ riter->current = riter->current->next_sibling;
+
+ if (child != NULL)
+ *child = riter->current;
+
+ return riter->current != NULL;
+}
+
+gboolean
+gsk_render_node_iter_prev (GskRenderNodeIter *iter,
+ GskRenderNode **child)
+{
+ RealIter *riter = REAL_ITER (iter);
+
+ g_return_val_if_fail (riter != NULL, FALSE);
+ g_return_val_if_fail (riter->root != NULL, FALSE);
+ g_return_val_if_fail (riter->root->age == riter->age, FALSE);
+
+ if (riter->current == NULL)
+ riter->current = riter->root->last_child;
+ else
+ riter->current = riter->current->prev_sibling;
+
+ if (child != NULL)
+ *child = riter->current;
+
+ return riter->current != NULL;
+}
+
+void
+gsk_render_node_iter_remove (GskRenderNodeIter *iter)
+{
+ RealIter *riter = REAL_ITER (iter);
+ GskRenderNode *tmp;
+
+ g_return_if_fail (riter != NULL);
+ g_return_if_fail (riter->root != NULL);
+ g_return_if_fail (riter->root->age == riter->age);
+ g_return_if_fail (riter->current != NULL);
+
+ tmp = riter->current;
+
+ if (tmp != NULL)
+ {
+ riter->current = tmp->prev_sibling;
+
+ gsk_render_node_remove_child (riter->root, tmp);
+
+ riter->age += 1;
+
+ /* Safety net */
+ g_assert (riter->age == riter->root->age);
+ }
+}
diff --git a/gsk/gskrendernodeiter.h b/gsk/gskrendernodeiter.h
new file mode 100644
index 0000000..ed9f9a8
--- /dev/null
+++ b/gsk/gskrendernodeiter.h
@@ -0,0 +1,45 @@
+#ifndef __GSK_RENDER_NODE_ITER_H__
+#define __GSK_RENDER_NODE_ITER_H__
+
+#include <gsk/gskrendernode.h>
+
+G_BEGIN_DECLS
+
+#define GSK_TYPE_RENDER_NODE_ITER (gsk_render_node_iter_get_type())
+
+typedef struct _GskRenderNodeIter GskRenderNodeIter;
+
+struct _GskRenderNodeIter
+{
+ /*< private >*/
+ gpointer dummy1;
+ gpointer dummy2;
+ gint64 dummy3;
+ gpointer dummy4;
+ gpointer dummy5;
+};
+
+GDK_AVAILABLE_IN_3_20
+GType gsk_render_node_iter_get_type (void) G_GNUC_CONST;
+GDK_AVAILABLE_IN_3_20
+GskRenderNodeIter *gsk_render_node_iter_new (void);
+GDK_AVAILABLE_IN_3_20
+void gsk_render_node_iter_free (GskRenderNodeIter *iter);
+GDK_AVAILABLE_IN_3_20
+void gsk_render_node_iter_init (GskRenderNodeIter *iter,
+ GskRenderNode *node);
+
+GDK_AVAILABLE_IN_3_20
+gboolean gsk_render_node_iter_is_valid (GskRenderNodeIter *iter);
+GDK_AVAILABLE_IN_3_20
+gboolean gsk_render_node_iter_prev (GskRenderNodeIter *iter,
+ GskRenderNode **child);
+GDK_AVAILABLE_IN_3_20
+gboolean gsk_render_node_iter_next (GskRenderNodeIter *iter,
+ GskRenderNode **child);
+GDK_AVAILABLE_IN_3_20
+void gsk_render_node_iter_remove (GskRenderNodeIter *iter);
+
+G_END_DECLS
+
+#endif /* GSK_RENDER_NODE_ITER_H */
diff --git a/gsk/gskrendernodeprivate.h b/gsk/gskrendernodeprivate.h
new file mode 100644
index 0000000..2a5a9a1
--- /dev/null
+++ b/gsk/gskrendernodeprivate.h
@@ -0,0 +1,63 @@
+#ifndef __GSK_RENDER_NODE_PRIVATE_H__
+#define __GSK_RENDER_NODE_PRIVATE_H__
+
+#include "gskrendernode.h"
+#include <cairo.h>
+
+G_BEGIN_DECLS
+
+struct _GskRenderNode
+{
+ GObject parent_instance;
+
+ GskRenderNode *parent;
+ GskRenderNode *first_child;
+ GskRenderNode *last_child;
+ GskRenderNode *prev_sibling;
+ GskRenderNode *next_sibling;
+
+ int n_children;
+ gint64 age;
+
+ cairo_surface_t *surface;
+
+ double opacity;
+
+ graphene_rect_t bounds;
+
+ graphene_matrix_t world_matrix;
+
+ graphene_matrix_t transform;
+ graphene_matrix_t child_transform;
+
+ gboolean hidden : 1;
+ gboolean opaque : 1;
+ gboolean transform_set : 1;
+ gboolean child_transform_set : 1;
+ gboolean needs_world_matrix_update : 1;
+};
+
+struct _GskRenderNodeClass
+{
+ GObjectClass parent_class;
+};
+
+void gsk_render_node_get_bounds (GskRenderNode *node,
+ graphene_rect_t *frame);
+void gsk_render_node_get_transform (GskRenderNode *node,
+ graphene_matrix_t *mv);
+double gsk_render_node_get_opacity (GskRenderNode *node);
+
+cairo_surface_t *gsk_render_node_get_surface (GskRenderNode *node);
+
+GskRenderNode *gsk_render_node_get_toplevel (GskRenderNode *node);
+
+void gsk_render_node_update_world_matrix (GskRenderNode *node,
+ gboolean force);
+
+void gsk_render_node_get_world_matrix (GskRenderNode *node,
+ graphene_matrix_t *mv);
+
+G_END_DECLS
+
+#endif /* __GSK_RENDER_NODE_PRIVATE_H__ */
diff --git a/gsk/gsktypes.h b/gsk/gsktypes.h
new file mode 100644
index 0000000..8513328
--- /dev/null
+++ b/gsk/gsktypes.h
@@ -0,0 +1,29 @@
+/* GSK - The GTK Scene Kit
+ * Copyright 2016 Endless
+ *
+ * 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/>.
+ */
+
+#ifndef __GSK_TYPES_H__
+#define __GSK_TYPES_H__
+
+#if !defined (__GSK_H_INSIDE__) && !defined (GSK_COMPILATION)
+#error "Only <gsk/gsk.h> can be included directly."
+#endif
+
+#include <graphene.h>
+#include <gdk/gdk.h>
+#include <gsk/gskenums.h>
+
+#endif /* __GSK_TYPES_H__ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 681807d..8ffd722 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -7,6 +7,8 @@ AM_CPPFLAGS = \
-I$(top_srcdir) \
-I$(top_builddir)/gdk \
-I$(top_srcdir)/gdk \
+ -I$(top_builddir)/gsk \
+ -I$(top_srcdir)/gsk \
$(GTK_DEBUG_FLAGS) \
$(GTK_DEP_CFLAGS) \
$(GDK_DEP_CFLAGS)
@@ -16,6 +18,7 @@ DEPS = \
LDADD = \
$(top_builddir)/gtk/libgtk-3.la \
+ $(top_builddir)/gsk/libgsk-3.la \
$(top_builddir)/gdk/libgdk-3.la \
$(GTK_DEP_LIBS) \
$(GDK_DEP_LIBS) \
@@ -172,6 +175,7 @@ noinst_PROGRAMS = $(TEST_PROGS) \
gdkgears \
listmodel \
testpopup \
+ testgskrenderer \
$(NULL)
if USE_X11
@@ -316,6 +320,7 @@ testtitlebar_DEPENDENCIES = $(TEST_DEPS)
testwindowsize_DEPENDENCIES = $(TEST_DEPS)
listmodel_DEPENDENCIES = $(TEST_DEPS)
foreigndrawing_DEPENDENCIES = $(TEST_DEPS)
+testgskrenderer_DEPENDENCIES = $(TEST_DEPS)
animated_resizing_SOURCES = \
animated-resizing.c \
@@ -556,6 +561,8 @@ listmodel_SOURCES = listmodel.c
foreigndrawing_SOURCES = foreigndrawing.c
+testgskrenderer_SOURCES = testgskrenderer.c
+
EXTRA_DIST += \
gradient1.png \
testgtk.1 \
diff --git a/tests/testgskrenderer.c b/tests/testgskrenderer.c
new file mode 100644
index 0000000..b1e74da
--- /dev/null
+++ b/tests/testgskrenderer.c
@@ -0,0 +1,178 @@
+#include "config.h"
+
+#include <graphene.h>
+#include <cairo.h>
+#include <gsk/gsk.h>
+#include <gtk/gtk.h>
+
+static cairo_surface_t *
+create_color_surface (GdkRGBA *color, int w, int h)
+{
+ cairo_surface_t *res;
+ cairo_t *cr;
+
+ res = cairo_image_surface_create (CAIRO_FORMAT_RGB24, w, h);
+ cr = cairo_create (res);
+
+ cairo_set_source_rgb (cr, color->red, color->green, color->blue);
+ cairo_rectangle (cr, 0, 0, w, h);
+ cairo_fill (cr);
+
+ cairo_destroy (cr);
+
+ return res;
+}
+
+static GskRenderer *
+get_renderer (GtkWidget *widget)
+{
+ GskRenderer *res;
+
+ res = g_object_get_data (G_OBJECT (widget), "-gsk-renderer");
+ if (res == NULL)
+ {
+ res = gsk_renderer_get_for_display (gtk_widget_get_display (widget));
+ g_object_set_data_full (G_OBJECT (widget), "-gsk-renderer",
+ res,
+ (GDestroyNotify) g_object_unref);
+ }
+
+ return res;
+}
+
+static void
+create_scene (GskRenderer *renderer)
+{
+ GskRenderNode *root, *node;
+ cairo_surface_t *surface;
+ graphene_matrix_t ctm;
+
+ root = gsk_render_node_new ();
+ gsk_render_node_set_bounds (root, &(graphene_rect_t) {
+ .origin.x = 0.f,
+ .origin.y = 0.f,
+ .size.width = 120.f,
+ .size.height = 120.f
+ });
+ surface = create_color_surface (&(GdkRGBA) { .red = 1, .green = 0, .blue = 0, .alpha = 1 }, 120, 120);
+ gsk_render_node_set_surface (root, surface);
+ cairo_surface_destroy (surface);
+ gsk_renderer_set_root_node (renderer, root);
+ g_object_set_data (G_OBJECT (renderer), "-gsk-renderer-root-node", root);
+
+ g_object_unref (root);
+
+ node = gsk_render_node_new ();
+ gsk_render_node_set_bounds (node, &(graphene_rect_t) {
+ .origin.x = 0.f,
+ .origin.y = 0.f,
+ .size.width = 50.f,
+ .size.height = 50.f
+ });
+ surface = create_color_surface (&(GdkRGBA) { .red = 0, .green = 1, .blue = 0, .alpha = 1 }, 50, 50);
+ gsk_render_node_set_surface (node, surface);
+ cairo_surface_destroy (surface);
+ graphene_matrix_init_translate (&ctm, &(graphene_point3d_t) { .x = 10.f, .y = 10.f, .z = 0.f });
+ gsk_render_node_set_transform (node, &ctm);
+ gsk_render_node_insert_child_at_pos (root, node, 0);
+ g_object_unref (node);
+
+ node = gsk_render_node_new ();
+ gsk_render_node_set_bounds (node, &(graphene_rect_t) {
+ .origin.x = 0.f,
+ .origin.y = 0.f,
+ .size.width = 50.f,
+ .size.height = 50.f
+ });
+ surface = create_color_surface (&(GdkRGBA) { .red = 0, .green = 0, .blue = 1, .alpha = 1 }, 50, 50);
+ gsk_render_node_set_surface (node, surface);
+ cairo_surface_destroy (surface);
+ graphene_matrix_init_translate (&ctm, &(graphene_point3d_t) { .x = 60.f, .y = 60.f, .z = 0.f });
+ gsk_render_node_set_transform (node, &ctm);
+ gsk_render_node_insert_child_at_pos (root, node, 1);
+ g_object_unref (node);
+}
+
+static void
+realize (GtkWidget *widget)
+{
+ GskRenderer *renderer = get_renderer (widget);
+
+ gsk_renderer_set_window (renderer, gtk_widget_get_window (widget));
+ gsk_renderer_set_use_alpha (renderer, TRUE);
+ gsk_renderer_realize (renderer);
+
+ create_scene (renderer);
+}
+
+static void
+unrealize (GtkWidget *widget)
+{
+ g_object_set_data (G_OBJECT (widget), "-gsk-renderer", NULL);
+}
+
+static void
+size_allocate (GtkWidget *widget, GtkAllocation *allocation)
+{
+ GskRenderer *renderer = get_renderer (widget);
+ GskRenderNode *root;
+ graphene_matrix_t ctm;
+
+ gsk_renderer_set_viewport (renderer, &(graphene_rect_t) {
+ .origin.x = 0.f,
+ .origin.y = 0.f,
+ .size.width = allocation->width,
+ .size.height = allocation->height
+ });
+
+ root = g_object_get_data (G_OBJECT (renderer), "-gsk-renderer-root-node");
+ if (root == NULL)
+ {
+ create_scene (renderer);
+ root = g_object_get_data (G_OBJECT (renderer), "-gsk-renderer-root-node");
+ }
+
+ graphene_matrix_init_translate (&ctm, &(graphene_point3d_t) {
+ .x = (allocation->width - 120.f) / 2.f,
+ .y = (allocation->height - 120.f) / 2.f,
+ .z = 0.f
+ });
+ gsk_render_node_set_transform (root, &ctm);
+}
+
+static gboolean
+draw (GtkWidget *widget, cairo_t *cr)
+{
+ GskRenderer *renderer = get_renderer (widget);
+
+ gsk_renderer_render (renderer);
+
+ cairo_set_source_surface (cr, gsk_renderer_get_surface (renderer), 0, 0);
+ cairo_paint (cr);
+
+ return TRUE;
+}
+
+int
+main (int argc, char *argv[])
+{
+ gtk_init (NULL, NULL);
+
+ GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size (GTK_WINDOW (window), 400, 400);
+ g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
+
+ GtkWidget *area = gtk_drawing_area_new ();
+ gtk_widget_set_hexpand (area, TRUE);
+ gtk_widget_set_vexpand (area, TRUE);
+ gtk_container_add (GTK_CONTAINER (window), area);
+
+ g_signal_connect (area, "realize", G_CALLBACK (realize), NULL);
+ g_signal_connect (area, "unrealize", G_CALLBACK (unrealize), NULL);
+ g_signal_connect (area, "size-allocate", G_CALLBACK (size_allocate), NULL);
+ g_signal_connect (area, "draw", G_CALLBACK (draw), NULL);
+
+ gtk_widget_show_all (window);
+
+ gtk_main ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]