[clutter/wip/apocalypses/apocalypse-3: 17/35] Add ClutterCanvas, a drawing content



commit e8b223a6d84c11317516dc2e98ef625a2e805e44
Author: Emmanuele Bassi <ebassi linux intel com>
Date:   Thu Mar 8 10:31:21 2012 +0000

    Add ClutterCanvas, a drawing content

 clutter/Makefile.am                        |    2 +
 clutter/clutter-cairo-texture.h            |   20 --
 clutter/clutter-canvas.c                   |  507 ++++++++++++++++++++++++++++
 clutter/clutter-canvas.h                   |   91 +++++
 clutter/clutter-macros.h                   |   20 ++
 clutter/clutter.h                          |    1 +
 clutter/clutter.symbols                    |    3 +
 doc/reference/clutter/clutter-docs.xml.in  |    6 +
 doc/reference/clutter/clutter-sections.txt |   17 +
 doc/reference/clutter/clutter.types        |    1 +
 tests/interactive/Makefile.am              |    3 +-
 tests/interactive/test-canvas.c            |  135 ++++++++
 12 files changed, 785 insertions(+), 21 deletions(-)
---
diff --git a/clutter/Makefile.am b/clutter/Makefile.am
index 7bb480f..6e138fc 100644
--- a/clutter/Makefile.am
+++ b/clutter/Makefile.am
@@ -65,6 +65,7 @@ source_h =					\
 	$(srcdir)/clutter-box-layout.h		\
 	$(srcdir)/clutter-brightness-contrast-effect.h	\
 	$(srcdir)/clutter-cairo-texture.h	\
+	$(srcdir)/clutter-canvas.h		\
 	$(srcdir)/clutter-child-meta.h		\
 	$(srcdir)/clutter-click-action.h	\
 	$(srcdir)/clutter-cogl-compat.h 	\
@@ -143,6 +144,7 @@ source_c = \
 	$(srcdir)/clutter-box-layout.c		\
 	$(srcdir)/clutter-brightness-contrast-effect.c	\
 	$(srcdir)/clutter-cairo-texture.c       \
+	$(srcdir)/clutter-canvas.c		\
 	$(srcdir)/clutter-child-meta.c		\
 	$(srcdir)/clutter-click-action.c	\
 	$(srcdir)/clutter-clone.c		\
diff --git a/clutter/clutter-cairo-texture.h b/clutter/clutter-cairo-texture.h
index c26b741..78f341b 100644
--- a/clutter/clutter-cairo-texture.h
+++ b/clutter/clutter-cairo-texture.h
@@ -43,26 +43,6 @@ G_BEGIN_DECLS
 #define CLUTTER_IS_CAIRO_TEXTURE_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_CAIRO_TEXTURE))
 #define CLUTTER_CAIRO_TEXTURE_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_CAIRO_TEXTURE, ClutterCairoTextureClass))
 
-/**
- * CLUTTER_CAIRO_FORMAT_ARGB32:
- *
- * The #CoglPixelFormat to be used when uploading image data from
- * and to a Cairo image surface using %CAIRO_FORMAT_ARGB32 and
- * %CAIRO_FORMAT_RGB24 as #cairo_format_t.
- *
- * Since: 1.8
- */
-
-/* Cairo stores the data in native byte order as ARGB but Cogl's pixel
- * formats specify the actual byte order. Therefore we need to use a
- * different format depending on the architecture
- */
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN
-#define CLUTTER_CAIRO_FORMAT_ARGB32     (COGL_PIXEL_FORMAT_BGRA_8888_PRE)
-#else
-#define CLUTTER_CAIRO_FORMAT_ARGB32     (COGL_PIXEL_FORMAT_ARGB_8888_PRE)
-#endif
-
 typedef struct _ClutterCairoTexture             ClutterCairoTexture;
 typedef struct _ClutterCairoTextureClass        ClutterCairoTextureClass;
 typedef struct _ClutterCairoTexturePrivate      ClutterCairoTexturePrivate;
diff --git a/clutter/clutter-canvas.c b/clutter/clutter-canvas.c
new file mode 100644
index 0000000..64c135d
--- /dev/null
+++ b/clutter/clutter-canvas.c
@@ -0,0 +1,507 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2012  Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Emmanuele Bassi <ebassi linux intel com>
+ */
+
+/**
+ * SECTION:clutter-canvas
+ * @Title: ClutterCanvas
+ * @Short_Description: Content for 2D painting
+ * @See_Also: #ClutterContent
+ *
+ * The #ClutterCanvas class is a #ClutterContent implementation that allows
+ * drawing using the Cairo API on a 2D surface.
+ *
+ * In order to draw on a #ClutterCanvas, you should connect a handler to the
+ * #ClutterCanvas::draw signal; the signal will receive a #cairo_t context
+ * that can be used to draw. #ClutterCanvas will emit the #ClutterCanvas::draw
+ * signal when invalidated using clutter_content_invalidate().
+ *
+ * <informalexample id="canvas-example">
+ *   <programlisting>
+ * <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"; parse="text" href="../../../../tests/interactive/test-canvas.c">
+ *   <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
+ * </xi:include>
+ *   </programlisting>
+ * </informalexample>
+ *
+ * #ClutterCanvas is available since Clutter 1.10.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <cogl/cogl.h>
+#include <cairo-gobject.h>
+
+#include "clutter-canvas.h"
+
+#define CLUTTER_ENABLE_EXPERIMENTAL_API
+
+#include "clutter-backend.h"
+#include "clutter-color.h"
+#include "clutter-content-private.h"
+#include "clutter-marshal.h"
+#include "clutter-paint-node.h"
+#include "clutter-paint-nodes.h"
+#include "clutter-private.h"
+
+struct _ClutterCanvasPrivate
+{
+  cairo_t *cr;
+
+  int width;
+  int height;
+
+  CoglBitmap *buffer;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_WIDTH,
+  PROP_HEIGHT,
+
+  LAST_PROP
+};
+
+static GParamSpec *obj_props[LAST_PROP] = { NULL, };
+
+enum
+{
+  DRAW,
+
+  LAST_SIGNAL
+};
+
+static guint canvas_signals[LAST_SIGNAL] = { 0, };
+
+static void clutter_content_iface_init (ClutterContentIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (ClutterCanvas, clutter_canvas, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT,
+                                                clutter_content_iface_init))
+
+static void
+clutter_cairo_context_draw_marshaller (GClosure     *closure,
+                                       GValue       *return_value,
+                                       guint         n_param_values,
+                                       const GValue *param_values,
+                                       gpointer      invocation_hint,
+                                       gpointer      marshal_data)
+{
+  cairo_t *cr = g_value_get_boxed (&param_values[1]);
+
+  cairo_save (cr);
+
+  _clutter_marshal_BOOLEAN__BOXED (closure,
+                                   return_value,
+                                   n_param_values,
+                                   param_values,
+                                   invocation_hint,
+                                   marshal_data);
+
+  cairo_restore (cr);
+}
+
+static void
+clutter_canvas_finalize (GObject *gobject)
+{
+  ClutterCanvasPrivate *priv = CLUTTER_CANVAS (gobject)->priv;
+
+  if (priv->buffer != NULL)
+    {
+      cogl_object_unref (priv->buffer);
+      priv->buffer = NULL;
+    }
+
+  G_OBJECT_CLASS (clutter_canvas_parent_class)->finalize (gobject);
+}
+
+static void
+clutter_canvas_set_property (GObject      *gobject,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  ClutterCanvasPrivate *priv = CLUTTER_CANVAS (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_WIDTH:
+      if (priv->width != g_value_get_int (value))
+        {
+          priv->width = g_value_get_int (value);
+          clutter_content_invalidate (CLUTTER_CONTENT (gobject));
+        }
+      break;
+
+    case PROP_HEIGHT:
+      if (priv->height != g_value_get_int (value))
+        {
+          priv->height = g_value_get_int (value);
+          clutter_content_invalidate (CLUTTER_CONTENT (gobject));
+        }
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+clutter_canvas_get_property (GObject    *gobject,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+  ClutterCanvasPrivate *priv = CLUTTER_CANVAS (gobject)->priv;
+
+  switch (prop_id)
+    {
+    case PROP_WIDTH:
+      g_value_set_int (value, priv->width);
+      break;
+
+    case PROP_HEIGHT:
+      g_value_set_int (value, priv->height);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+clutter_canvas_class_init (ClutterCanvasClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (ClutterCanvasPrivate));
+
+  /**
+   * ClutterCanvas:width:
+   *
+   * The width of the canvas.
+   *
+   * Since: 1.10
+   */
+  obj_props[PROP_WIDTH] =
+    g_param_spec_int ("width",
+                      P_("Width"),
+                      P_("The width of the canvas"),
+                      -1, G_MAXINT,
+                      -1,
+                      G_PARAM_READWRITE |
+                      G_PARAM_STATIC_STRINGS);
+
+  /**
+   * ClutterCanvas:height:
+   *
+   * The height of the canvas.
+   *
+   * Since: 1.10
+   */
+  obj_props[PROP_HEIGHT] =
+    g_param_spec_int ("height",
+                      P_("Height"),
+                      P_("The height of the canvas"),
+                      -1, G_MAXINT,
+                      -1,
+                      G_PARAM_READWRITE |
+                      G_PARAM_STATIC_STRINGS);
+
+  /**
+   * ClutterCanvas::draw:
+   * @canvas: the #ClutterCanvas that emitted the signal
+   * @cr: the Cairo context used to draw
+   *
+   * The #ClutterCanvas::draw signal is emitted each time a canvas is
+   * invalidated.
+   *
+   * It is safe to connect multiple handlers to this signal: each
+   * handler invocation will be automatically protected by cairo_save()
+   * and cairo_restore() pairs.
+   *
+   * Return value: %TRUE if the signal emission should stop, and
+   *   %FALSE otherwise
+   *
+   * Since: 1.10
+   */
+  canvas_signals[DRAW] =
+    g_signal_new (I_("draw"),
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
+                  G_STRUCT_OFFSET (ClutterCanvasClass, draw),
+                  _clutter_boolean_handled_accumulator, NULL,
+                  clutter_cairo_context_draw_marshaller,
+                  G_TYPE_BOOLEAN, 1,
+                  CAIRO_GOBJECT_TYPE_CONTEXT);
+
+  gobject_class->set_property = clutter_canvas_set_property;
+  gobject_class->get_property = clutter_canvas_get_property;
+  gobject_class->finalize = clutter_canvas_finalize;
+
+  g_object_class_install_properties (gobject_class, LAST_PROP, obj_props);
+}
+
+static void
+clutter_canvas_init (ClutterCanvas *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, CLUTTER_TYPE_CANVAS,
+                                            ClutterCanvasPrivate);
+
+  self->priv->width = -1;
+  self->priv->height = -1;
+}
+
+static void
+clutter_canvas_paint_content (ClutterContent   *content,
+                              ClutterActor     *actor,
+                              ClutterPaintNode *root)
+{
+  ClutterCanvas *self = CLUTTER_CANVAS (content);
+  ClutterPaintNode *node;
+  CoglTexture *texture;
+  ClutterActorBox box;
+  ClutterColor color;
+  guint8 paint_opacity;
+
+  if (self->priv->buffer == NULL)
+    return;
+
+  texture = cogl_texture_new_from_bitmap (self->priv->buffer,
+                                          COGL_TEXTURE_NO_SLICING,
+                                          CLUTTER_CAIRO_FORMAT_ARGB32);
+  if (texture == NULL)
+    return;
+
+  clutter_actor_get_content_box (actor, &box);
+  paint_opacity = clutter_actor_get_paint_opacity (actor);
+
+  color.red = paint_opacity;
+  color.green = paint_opacity;
+  color.blue = paint_opacity;
+  color.alpha = paint_opacity;
+
+  node = clutter_texture_node_new (texture, &color);
+  cogl_object_unref (texture);
+
+  clutter_paint_node_set_name (node, "Canvas");
+  clutter_paint_node_add_rectangle (node, &box);
+  clutter_paint_node_add_child (root, node);
+  clutter_paint_node_unref (node);
+}
+
+static void
+clutter_canvas_emit_draw (ClutterCanvas *self)
+{
+  ClutterCanvasPrivate *priv = self->priv;
+  cairo_surface_t *surface;
+  gboolean mapped_buffer;
+  unsigned char *data;
+  CoglBuffer *buffer;
+  gboolean res;
+  cairo_t *cr;
+
+  g_assert (priv->width >= 0 && priv->width >= 0);
+
+  if (priv->buffer == NULL)
+    {
+      CoglContext *ctx;
+
+      ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+      priv->buffer = cogl_bitmap_new_with_size (ctx,
+                                                priv->width,
+                                                priv->height,
+                                                CLUTTER_CAIRO_FORMAT_ARGB32);
+    }
+
+  buffer = COGL_BUFFER (cogl_bitmap_get_buffer (priv->buffer));
+  if (buffer == NULL)
+    return;
+
+  cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_DYNAMIC);
+
+  data = cogl_buffer_map (buffer,
+                          COGL_BUFFER_ACCESS_READ_WRITE,
+                          COGL_BUFFER_MAP_HINT_DISCARD);
+
+  if (data != NULL)
+    {
+      int bitmap_stride = cogl_bitmap_get_rowstride (priv->buffer);
+
+      surface = cairo_image_surface_create_for_data (data,
+                                                     CAIRO_FORMAT_ARGB32,
+                                                     priv->width,
+                                                     priv->height,
+                                                     bitmap_stride);
+      mapped_buffer = TRUE;
+    }
+  else
+    {
+      surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+                                            priv->width,
+                                            priv->height);
+
+      mapped_buffer = FALSE;
+    }
+
+  self->priv->cr = cr = cairo_create (surface);
+
+  g_signal_emit (self, canvas_signals[DRAW], 0, cr, &res);
+
+  self->priv->cr = NULL;
+  cairo_destroy (cr);
+
+  if (mapped_buffer)
+    cogl_buffer_unmap (buffer);
+  else
+    {
+      int size = cairo_image_surface_get_stride (surface) * priv->height;
+      cogl_buffer_set_data (buffer,
+                            0,
+                            cairo_image_surface_get_data (surface),
+                            size);
+    }
+
+  cairo_surface_destroy (surface);
+}
+
+static void
+clutter_canvas_invalidate (ClutterContent *content)
+{
+  ClutterCanvas *self = CLUTTER_CANVAS (content);
+  ClutterCanvasPrivate *priv = self->priv;
+
+  if (priv->buffer != NULL)
+    {
+      cogl_object_unref (priv->buffer);
+      priv->buffer = NULL;
+    }
+
+  if (priv->width < 0 || priv->height < 0)
+    return;
+
+  clutter_canvas_emit_draw (self);
+}
+
+static gboolean
+clutter_canvas_get_preferred_size (ClutterContent *content,
+                                   gfloat         *width,
+                                   gfloat         *height)
+{
+  ClutterCanvasPrivate *priv = CLUTTER_CANVAS (content)->priv;
+
+  if (priv->width < 0 || priv->height < 0)
+    return FALSE;
+
+  if (width != NULL)
+    *width = priv->width;
+
+  if (height != NULL)
+    *height = priv->height;
+
+  return TRUE;
+}
+
+static void
+clutter_content_iface_init (ClutterContentIface *iface)
+{
+  iface->invalidate = clutter_canvas_invalidate;
+  iface->paint_content = clutter_canvas_paint_content;
+  iface->get_preferred_size = clutter_canvas_get_preferred_size;
+}
+
+/**
+ * clutter_canvas_new:
+ *
+ * Creates a new instance of #ClutterCanvas.
+ *
+ * You should call clutter_canvas_set_size() to set the size of the canvas.
+ *
+ * You should call clutter_content_invalidate() every time you wish to
+ * draw the contents of the canvas.
+ *
+ * Return value: (transfer full): The newly allocated instance of
+ *   #ClutterCanvas. Use g_object_unref() when done.
+ *
+ * Since: 1.10
+ */
+ClutterContent *
+clutter_canvas_new (void)
+{
+  return g_object_new (CLUTTER_TYPE_CANVAS, NULL);
+}
+
+/**
+ * clutter_canvas_set_size:
+ * @canvas: a #ClutterCanvas
+ * @width: the width of the canvas, in pixels
+ * @height: the height of the canvas, in pixels
+ *
+ * Sets the size of the @canvas.
+ *
+ * This function will cause the @canvas to be invalidated.
+ *
+ * Since: 1.10
+ */
+void
+clutter_canvas_set_size (ClutterCanvas *canvas,
+                         int            width,
+                         int            height)
+{
+  GObject *obj;
+  gboolean width_changed = FALSE, height_changed = FALSE;
+
+  g_return_if_fail (CLUTTER_IS_CANVAS (canvas));
+  g_return_if_fail (width >= -1 && height >= -1);
+
+  obj = G_OBJECT (canvas);
+
+  g_object_freeze_notify (obj);
+
+  if (canvas->priv->width != width)
+    {
+      canvas->priv->width = width;
+      width_changed = TRUE;
+
+      g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]);
+    }
+
+  if (canvas->priv->height != height)
+    {
+      canvas->priv->height = height;
+      height_changed = TRUE;
+
+      g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]);
+    }
+
+  if (width_changed || height_changed)
+    clutter_content_invalidate (CLUTTER_CONTENT (canvas));
+
+  g_object_thaw_notify (obj);
+}
diff --git a/clutter/clutter-canvas.h b/clutter/clutter-canvas.h
new file mode 100644
index 0000000..02ed0d6
--- /dev/null
+++ b/clutter/clutter-canvas.h
@@ -0,0 +1,91 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2012  Intel Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author:
+ *   Emmanuele Bassi <ebassi linux intel com>
+ */
+
+#ifndef __CLUTTER_CANVAS_H__
+#define __CLUTTER_CANVAS_H__
+
+#include <clutter/clutter-types.h>
+
+G_BEGIN_DECLS
+
+#define CLUTTER_TYPE_CANVAS             (clutter_canvas_get_type ())
+#define CLUTTER_CANVAS(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_CANVAS, ClutterCanvas))
+#define CLUTTER_IS_CANVAS(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_CANVAS))
+#define CLUTTER_CANVAS_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_CANVAS, ClutterCanvasClass))
+#define CLUTTER_IS_CANVAS_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_CANVAS))
+#define CLUTTER_CANVAS_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_CANVAS, ClutterCanvasClass))
+
+typedef struct _ClutterCanvas           ClutterCanvas;
+typedef struct _ClutterCanvasPrivate    ClutterCanvasPrivate;
+typedef struct _ClutterCanvasClass      ClutterCanvasClass;
+
+/**
+ * ClutterCanvas:
+ *
+ * The <structname>ClutterCanvas</structname> structure contains
+ * private data and should only be accessed using the provided
+ * API.
+ *
+ * Since: 1.10
+ */
+struct _ClutterCanvas
+{
+  /*< private >*/
+  GObject parent_instance;
+
+  ClutterCanvasPrivate *priv;
+};
+
+/**
+ * ClutterCanvasClass:
+ * @draw: class handler for the #ClutterCanvas::draw signal
+ *
+ * The <structname>ClutterCanvasClass</structname> structure contains
+ * private data.
+ *
+ * Since: 1.10
+ */
+struct _ClutterCanvasClass
+{
+  /*< private >*/
+  GObjectClass parent_class;
+
+  /*< public >*/
+  gboolean (* draw) (ClutterCanvas *canvas,
+                     cairo_t       *cr);
+
+  /*< private >*/
+  gpointer _padding[16];
+};
+
+GType clutter_canvas_get_type (void) G_GNUC_CONST;
+
+ClutterContent *        clutter_canvas_new              (void);
+void                    clutter_canvas_set_size         (ClutterCanvas *canvas,
+                                                         int            width,
+                                                         int            height);
+
+G_END_DECLS
+
+#endif /* __CLUTTER_CANVAS_H__ */
diff --git a/clutter/clutter-macros.h b/clutter/clutter-macros.h
index 2e70a6b..50eb021 100644
--- a/clutter/clutter-macros.h
+++ b/clutter/clutter-macros.h
@@ -248,4 +248,24 @@
 # define CLUTTER_AVAILABLE_IN_1_10
 #endif
 
+/**
+ * CLUTTER_CAIRO_FORMAT_ARGB32:
+ *
+ * The #CoglPixelFormat to be used when uploading image data from
+ * and to a Cairo image surface using %CAIRO_FORMAT_ARGB32 and
+ * %CAIRO_FORMAT_RGB24 as #cairo_format_t.
+ *
+ * Since: 1.8
+ */
+
+/* Cairo stores the data in native byte order as ARGB but Cogl's pixel
+ * formats specify the actual byte order. Therefore we need to use a
+ * different format depending on the architecture
+ */
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define CLUTTER_CAIRO_FORMAT_ARGB32     (COGL_PIXEL_FORMAT_BGRA_8888_PRE)
+#else
+#define CLUTTER_CAIRO_FORMAT_ARGB32     (COGL_PIXEL_FORMAT_ARGB_8888_PRE)
+#endif
+
 #endif /* __CLUTTER_MACROS_H__ */
diff --git a/clutter/clutter.h b/clutter/clutter.h
index 042c6ab..40d4951 100644
--- a/clutter/clutter.h
+++ b/clutter/clutter.h
@@ -47,6 +47,7 @@
 #include "clutter-box-layout.h"
 #include "clutter-brightness-contrast-effect.h"
 #include "clutter-cairo-texture.h"
+#include "clutter-canvas.h"
 #include "clutter-child-meta.h"
 #include "clutter-click-action.h"
 #include "clutter-clone.h"
diff --git a/clutter/clutter.symbols b/clutter/clutter.symbols
index 43943cf..e3c9655 100644
--- a/clutter/clutter.symbols
+++ b/clutter/clutter.symbols
@@ -470,6 +470,9 @@ clutter_brightness_contrast_effect_set_brightness_full
 clutter_brightness_contrast_effect_set_brightness
 clutter_brightness_contrast_effect_set_contrast_full
 clutter_brightness_contrast_effect_set_contrast
+clutter_canvas_get_type
+clutter_canvas_new
+clutter_canvas_set_size
 clutter_cairo_set_source_color
 clutter_cairo_texture_clear
 clutter_cairo_texture_create
diff --git a/doc/reference/clutter/clutter-docs.xml.in b/doc/reference/clutter/clutter-docs.xml.in
index d237726..89cee95 100644
--- a/doc/reference/clutter/clutter-docs.xml.in
+++ b/doc/reference/clutter/clutter-docs.xml.in
@@ -122,6 +122,12 @@
     </chapter>
 
     <chapter>
+      <title>Content</title>
+
+      <xi:include href="xml/clutter-canvas.xml"/>
+    </chapter>
+
+    <chapter>
       <title>Paint Objects</title>
 
       <xi:include href="xml/clutter-paint-node.xml"/>
diff --git a/doc/reference/clutter/clutter-sections.txt b/doc/reference/clutter/clutter-sections.txt
index 613292a..9629afd 100644
--- a/doc/reference/clutter/clutter-sections.txt
+++ b/doc/reference/clutter/clutter-sections.txt
@@ -3069,3 +3069,20 @@ clutter_text_node_get_type
 clutter_clip_node_get_type
 clutter_layer_node_get_type
 </SECTION>
+
+<SECTION>
+<FILE>clutter-canvas</FILE>
+ClutterCanvas
+ClutterCanvasClass
+clutter_canvas_new
+clutter_canvas_set_size
+<SUBSECTION Standard>
+CLUTTER_TYPE_CANVAS
+CLUTTER_CANVAS
+CLUTTER_CANVAS_CLASS
+CLUTTER_IS_CANVAS
+CLUTTER_IS_CANVAS_CLASS
+<SUBSECTION Private>
+ClutterCanvasPrivate
+clutter_canvas_get_type
+</SECTION>
diff --git a/doc/reference/clutter/clutter.types b/doc/reference/clutter/clutter.types
index d3763de..dc3381d 100644
--- a/doc/reference/clutter/clutter.types
+++ b/doc/reference/clutter/clutter.types
@@ -22,6 +22,7 @@ clutter_blur_effect_get_type
 clutter_box_get_type
 clutter_box_layout_get_type
 clutter_brightness_contrast_effect_get_type
+clutter_canvas_get_type
 clutter_cairo_texture_get_type
 clutter_child_meta_get_type
 clutter_click_action_get_type
diff --git a/tests/interactive/Makefile.am b/tests/interactive/Makefile.am
index 90599d8..83a25e8 100644
--- a/tests/interactive/Makefile.am
+++ b/tests/interactive/Makefile.am
@@ -56,7 +56,8 @@ UNIT_TESTS = \
 	test-drop.c \
 	test-devices.c \
 	test-actor.c \
-	test-content.c
+	test-content.c \
+	test-canvas.c
 
 if X11_TESTS
 UNIT_TESTS += test-pixmap.c
diff --git a/tests/interactive/test-canvas.c b/tests/interactive/test-canvas.c
new file mode 100644
index 0000000..61038ef
--- /dev/null
+++ b/tests/interactive/test-canvas.c
@@ -0,0 +1,135 @@
+#include <stdlib.h>
+#include <math.h>
+#include <cairo.h>
+#include <clutter/clutter.h>
+
+static gboolean
+draw_clock (ClutterCanvas *canvas,
+            cairo_t       *cr)
+{
+  float width, height;
+  GDateTime *now;
+  float hours, minutes, seconds;
+  ClutterColor color;
+
+  /* get the current time and compute the angles */
+  now = g_date_time_new_now_local ();
+  seconds = g_date_time_get_second (now) * G_PI / 30;
+  minutes = g_date_time_get_minute (now) * G_PI / 30;
+  hours = g_date_time_get_hour (now) * G_PI / 6;
+
+  cairo_save (cr);
+
+  /* clear the contents of the canvas, to avoid painting
+   * over the previous frame
+   */
+  cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+  cairo_paint (cr);
+
+  cairo_restore (cr);
+
+  cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+  /* scale the modelview to the size of the surface */
+  clutter_content_get_preferred_size (CLUTTER_CONTENT (canvas),
+                                      &width,
+                                      &height);
+  cairo_scale (cr, width, height);
+
+  cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+  cairo_set_line_width (cr, 0.1);
+
+  /* the black rail that holds the seconds indicator */
+  clutter_cairo_set_source_color (cr, CLUTTER_COLOR_Black);
+  cairo_translate (cr, 0.5, 0.5);
+  cairo_arc (cr, 0, 0, 0.4, 0, G_PI * 2);
+  cairo_stroke (cr);
+
+  /* the seconds indicator */
+  color = *CLUTTER_COLOR_White;
+  color.alpha = 128;
+  clutter_cairo_set_source_color (cr, &color);
+  cairo_move_to (cr, 0, 0);
+  cairo_arc (cr, sinf (seconds) * 0.4, - cosf (seconds) * 0.4, 0.05, 0, G_PI * 2);
+  cairo_fill (cr);
+
+  /* the minutes hand */
+  color = *CLUTTER_COLOR_DarkChameleon;
+  color.alpha = 196;
+  clutter_cairo_set_source_color (cr, &color);
+  cairo_move_to (cr, 0, 0);
+  cairo_line_to (cr, sinf (minutes) * 0.4, -cosf (minutes) * 0.4);
+  cairo_stroke (cr);
+
+  /* the hours hand */
+  cairo_move_to (cr, 0, 0);
+  cairo_line_to (cr, sinf (hours) * 0.2, -cosf (hours) * 0.2);
+  cairo_stroke (cr);
+
+  g_date_time_unref (now);
+
+  /* we're done drawing */
+  return TRUE;
+}
+
+static gboolean
+invalidate_clock (gpointer data_)
+{
+  /* invalidate the contents of the canvas */
+  clutter_content_invalidate (data_);
+
+  /* keep the timeout source */
+  return TRUE;
+}
+
+G_MODULE_EXPORT int
+test_canvas_main (int argc, char *argv[])
+{
+  ClutterActor *stage, *actor;
+  ClutterContent *canvas;
+
+  /* initialize Clutter */
+  if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
+    return EXIT_FAILURE;
+
+  /* create a resizable stage */
+  stage = clutter_stage_new ();
+  clutter_stage_set_title (CLUTTER_STAGE (stage), "2D Clock");
+  clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), TRUE);
+  clutter_actor_set_background_color (stage, CLUTTER_COLOR_LightSkyBlue);
+  clutter_actor_set_size (stage, 300, 300);
+  clutter_actor_show (stage);
+
+  /* our 2D canvas, courtesy of Cairo */
+  canvas = clutter_canvas_new ();
+  clutter_canvas_set_size (CLUTTER_CANVAS (canvas), 300, 300);
+
+  actor = clutter_actor_new ();
+  clutter_actor_set_content (actor, canvas);
+  clutter_actor_add_child (stage, actor);
+
+  /* bind the size of the actor to that of the stage */
+  clutter_actor_add_constraint (actor, clutter_bind_constraint_new (stage, CLUTTER_BIND_SIZE, 0));
+
+  /* quit on destroy */
+  g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
+
+  /* connect our drawing code */
+  g_signal_connect (canvas, "draw", G_CALLBACK (draw_clock), NULL);
+
+  /* invalidate the canvas, so that we can draw before the main loop starts */
+  clutter_content_invalidate (canvas);
+
+  /* set up a timer that invalidates the canvas every second */
+  clutter_threads_add_timeout (1000, invalidate_clock, canvas);
+
+  clutter_main ();
+
+  return EXIT_SUCCESS;
+}
+
+G_MODULE_EXPORT const char *
+test_canvas_describe (void)
+{
+  return "Simple 2D clock using the Canvas content";
+}



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