2008-12-11 10:24:07 -05:00
|
|
|
/*
|
|
|
|
* Clutter
|
|
|
|
*
|
|
|
|
* An OpenGL based 'interactive canvas' library.
|
|
|
|
*
|
|
|
|
* Authored By: Emmanuele Bassi <ebassi@linux.intel.com>
|
|
|
|
* Matthew Allum <mallum@o-hand.com>
|
|
|
|
* Chris Lord <chris@o-hand.com>
|
|
|
|
* Iain Holmes <iain@o-hand.com>
|
|
|
|
* Neil Roberts <neil@linux.intel.com>
|
|
|
|
*
|
2011-07-26 07:40:52 -04:00
|
|
|
* Copyright (C) 2008, 2009, 2010, 2011 Intel Corporation.
|
2008-12-11 10:24:07 -05:00
|
|
|
*
|
|
|
|
* 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
|
2010-03-01 07:56:10 -05:00
|
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
2008-12-11 10:24:07 -05:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SECTION:clutter-cairo-texture
|
|
|
|
* @short_description: Texture with Cairo integration
|
|
|
|
*
|
|
|
|
* #ClutterCairoTexture is a #ClutterTexture that displays the contents
|
|
|
|
* of a Cairo context. The #ClutterCairoTexture actor will create a
|
|
|
|
* Cairo image surface which will then be uploaded to a GL texture when
|
|
|
|
* needed.
|
|
|
|
*
|
|
|
|
* <note><para>Since #ClutterCairoTexture uses a Cairo image surface
|
|
|
|
* internally all the drawing operations will be performed in
|
|
|
|
* software and not using hardware acceleration. This can lead to
|
|
|
|
* performance degradation if the contents of the texture change
|
|
|
|
* frequently.</para></note>
|
|
|
|
*
|
2011-07-26 07:40:52 -04:00
|
|
|
* In order to use a #ClutterCairoTexture you should connect to the
|
|
|
|
* #ClutterCairoTexture::draw signal; the signal is emitted each time
|
|
|
|
* the #ClutterCairoTexture has been told to invalidate its contents,
|
|
|
|
* by using clutter_cairo_texture_invalidate_rectangle() or its
|
|
|
|
* sister function, clutter_cairo_texture_invalidate().
|
|
|
|
*
|
|
|
|
* Each callback to the #ClutterCairoTexture::draw signal will receive
|
|
|
|
* a #cairo_t context which can be used for drawing; the Cairo context
|
|
|
|
* is owned by the #ClutterCairoTexture and should not be destroyed
|
|
|
|
* explicitly.
|
|
|
|
*
|
|
|
|
* <example id="cairo-texture-example">
|
|
|
|
* <title>A simple ClutterCairoTexture canvas</title>
|
|
|
|
* <programlisting>
|
|
|
|
* <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../tests/interactive/test-cairo-clock.c">
|
|
|
|
* <xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback>
|
|
|
|
* </xi:include>
|
|
|
|
* </programlisting>
|
|
|
|
* </example>
|
|
|
|
*
|
2008-12-11 10:24:07 -05:00
|
|
|
* #ClutterCairoTexture is available since Clutter 1.0.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2011-07-26 09:53:26 -04:00
|
|
|
#include <math.h>
|
2008-12-11 10:24:07 -05:00
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
#include <cairo-gobject.h>
|
|
|
|
|
2008-12-11 10:24:07 -05:00
|
|
|
#include "clutter-cairo-texture.h"
|
2010-10-15 12:35:41 -04:00
|
|
|
|
2010-10-21 07:16:05 -04:00
|
|
|
#include "clutter-actor-private.h"
|
2012-02-23 12:21:57 -05:00
|
|
|
#include "clutter-color.h"
|
2008-12-11 10:24:07 -05:00
|
|
|
#include "clutter-debug.h"
|
2010-10-15 12:35:41 -04:00
|
|
|
#include "clutter-marshal.h"
|
2008-12-11 10:24:07 -05:00
|
|
|
#include "clutter-private.h"
|
|
|
|
|
2012-03-07 07:34:06 -05:00
|
|
|
#include "deprecated/clutter-cairo-texture.h"
|
|
|
|
|
2008-12-11 10:24:07 -05:00
|
|
|
G_DEFINE_TYPE (ClutterCairoTexture,
|
|
|
|
clutter_cairo_texture,
|
|
|
|
CLUTTER_TYPE_TEXTURE);
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
PROP_0,
|
|
|
|
|
|
|
|
PROP_SURFACE_WIDTH,
|
2010-06-21 05:20:32 -04:00
|
|
|
PROP_SURFACE_HEIGHT,
|
2011-07-26 09:53:26 -04:00
|
|
|
PROP_AUTO_RESIZE,
|
2010-06-21 05:20:32 -04:00
|
|
|
|
|
|
|
PROP_LAST
|
2008-12-11 10:24:07 -05:00
|
|
|
};
|
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
enum
|
|
|
|
{
|
|
|
|
CREATE_SURFACE,
|
2011-07-26 07:40:52 -04:00
|
|
|
DRAW,
|
2010-10-15 12:35:41 -04:00
|
|
|
|
|
|
|
LAST_SIGNAL
|
|
|
|
};
|
|
|
|
|
|
|
|
static GParamSpec *obj_props[PROP_LAST] = { NULL, };
|
|
|
|
|
|
|
|
static guint cairo_signals[LAST_SIGNAL] = { 0, };
|
2010-06-21 05:20:32 -04:00
|
|
|
|
2008-12-11 10:48:43 -05:00
|
|
|
#ifdef CLUTTER_ENABLE_DEBUG
|
2008-12-12 06:42:16 -05:00
|
|
|
#define clutter_warn_if_paint_fail(obj) G_STMT_START { \
|
2010-07-21 11:10:46 -04:00
|
|
|
if (CLUTTER_ACTOR_IN_PAINT (obj)) { \
|
2008-12-11 10:48:43 -05:00
|
|
|
g_warning ("%s should not be called during the paint sequence " \
|
|
|
|
"of a ClutterCairoTexture as it will likely cause " \
|
|
|
|
"performance issues.", G_STRFUNC); \
|
|
|
|
} } G_STMT_END
|
|
|
|
#else
|
2008-12-12 06:42:16 -05:00
|
|
|
#define clutter_warn_if_paint_fail(obj) /* void */
|
2008-12-11 10:48:43 -05:00
|
|
|
#endif /* CLUTTER_ENABLE_DEBUG */
|
|
|
|
|
2008-12-11 10:24:07 -05:00
|
|
|
#define CLUTTER_CAIRO_TEXTURE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_CAIRO_TEXTURE, ClutterCairoTexturePrivate))
|
|
|
|
|
|
|
|
struct _ClutterCairoTexturePrivate
|
|
|
|
{
|
2010-05-10 10:33:05 -04:00
|
|
|
cairo_surface_t *cr_surface;
|
2008-12-11 10:24:07 -05:00
|
|
|
|
2011-07-26 07:40:52 -04:00
|
|
|
guint surface_width;
|
|
|
|
guint surface_height;
|
|
|
|
|
|
|
|
cairo_t *cr_context;
|
2011-07-26 09:53:26 -04:00
|
|
|
|
|
|
|
guint auto_resize : 1;
|
2010-10-15 12:35:41 -04:00
|
|
|
};
|
2008-12-11 10:24:07 -05:00
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
typedef struct {
|
2011-07-26 07:40:52 -04:00
|
|
|
ClutterCairoTexture *texture;
|
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
cairo_rectangle_int_t rect;
|
2011-07-26 07:40:52 -04:00
|
|
|
|
|
|
|
guint is_clipped : 1;
|
|
|
|
} DrawContext;
|
2008-12-11 10:24:07 -05:00
|
|
|
|
2010-05-10 10:33:05 -04:00
|
|
|
static const cairo_user_data_key_t clutter_cairo_texture_context_key;
|
|
|
|
|
2011-07-26 07:40:52 -04:00
|
|
|
static DrawContext *
|
|
|
|
draw_context_create (ClutterCairoTexture *texture)
|
|
|
|
{
|
|
|
|
DrawContext *context = g_slice_new0 (DrawContext);
|
|
|
|
|
|
|
|
context->texture = g_object_ref (texture);
|
|
|
|
|
|
|
|
return context;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
draw_context_destroy (gpointer data)
|
|
|
|
{
|
|
|
|
if (G_LIKELY (data != NULL))
|
|
|
|
{
|
|
|
|
DrawContext *context = data;
|
|
|
|
|
|
|
|
g_object_unref (context->texture);
|
|
|
|
|
|
|
|
g_slice_free (DrawContext, data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-11 10:24:07 -05:00
|
|
|
static void
|
|
|
|
clutter_cairo_texture_set_property (GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
const GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
ClutterCairoTexturePrivate *priv;
|
|
|
|
|
|
|
|
priv = CLUTTER_CAIRO_TEXTURE (object)->priv;
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_SURFACE_WIDTH:
|
2011-07-26 09:53:26 -04:00
|
|
|
/* we perform the resize on notify to coalesce separate
|
|
|
|
* surface-width/surface-height property set
|
|
|
|
*/
|
2011-07-26 07:40:52 -04:00
|
|
|
priv->surface_width = g_value_get_uint (value);
|
2008-12-11 10:24:07 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_SURFACE_HEIGHT:
|
2011-07-26 07:40:52 -04:00
|
|
|
priv->surface_height = g_value_get_uint (value);
|
2008-12-11 10:24:07 -05:00
|
|
|
break;
|
|
|
|
|
2011-07-26 09:53:26 -04:00
|
|
|
case PROP_AUTO_RESIZE:
|
|
|
|
clutter_cairo_texture_set_auto_resize (CLUTTER_CAIRO_TEXTURE (object),
|
|
|
|
g_value_get_boolean (value));
|
|
|
|
break;
|
|
|
|
|
2008-12-11 10:24:07 -05:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_cairo_texture_get_property (GObject *object,
|
|
|
|
guint prop_id,
|
|
|
|
GValue *value,
|
|
|
|
GParamSpec *pspec)
|
|
|
|
{
|
|
|
|
ClutterCairoTexturePrivate *priv;
|
|
|
|
|
|
|
|
priv = CLUTTER_CAIRO_TEXTURE (object)->priv;
|
|
|
|
|
|
|
|
switch (prop_id)
|
|
|
|
{
|
|
|
|
case PROP_SURFACE_WIDTH:
|
2011-07-26 07:40:52 -04:00
|
|
|
g_value_set_uint (value, priv->surface_width);
|
2008-12-11 10:24:07 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
case PROP_SURFACE_HEIGHT:
|
2011-07-26 07:40:52 -04:00
|
|
|
g_value_set_uint (value, priv->surface_height);
|
2008-12-11 10:24:07 -05:00
|
|
|
break;
|
|
|
|
|
2011-07-26 09:53:26 -04:00
|
|
|
case PROP_AUTO_RESIZE:
|
|
|
|
g_value_set_boolean (value, priv->auto_resize);
|
|
|
|
break;
|
|
|
|
|
2008-12-11 10:24:07 -05:00
|
|
|
default:
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_cairo_texture_finalize (GObject *object)
|
|
|
|
{
|
|
|
|
ClutterCairoTexturePrivate *priv = CLUTTER_CAIRO_TEXTURE (object)->priv;
|
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
if (priv->cr_surface != NULL)
|
2008-12-11 10:24:07 -05:00
|
|
|
{
|
2010-05-10 10:33:05 -04:00
|
|
|
cairo_surface_t *surface = priv->cr_surface;
|
|
|
|
|
|
|
|
priv->cr_surface = NULL;
|
2010-10-15 12:35:41 -04:00
|
|
|
|
|
|
|
cairo_surface_finish (surface);
|
|
|
|
cairo_surface_destroy (surface);
|
2010-05-10 10:33:05 -04:00
|
|
|
}
|
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
G_OBJECT_CLASS (clutter_cairo_texture_parent_class)->finalize (object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static cairo_surface_t *
|
|
|
|
get_surface (ClutterCairoTexture *self)
|
|
|
|
{
|
|
|
|
ClutterCairoTexturePrivate *priv = self->priv;
|
|
|
|
|
|
|
|
if (priv->cr_surface == NULL)
|
2010-05-10 10:33:05 -04:00
|
|
|
{
|
2010-10-15 12:35:41 -04:00
|
|
|
g_signal_emit (self, cairo_signals[CREATE_SURFACE], 0,
|
2011-07-26 07:40:52 -04:00
|
|
|
priv->surface_width,
|
|
|
|
priv->surface_height,
|
2010-10-15 12:35:41 -04:00
|
|
|
&priv->cr_surface);
|
2008-12-11 10:24:07 -05:00
|
|
|
}
|
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
return priv->cr_surface;
|
2008-12-11 10:24:07 -05:00
|
|
|
}
|
|
|
|
|
2011-07-26 07:40:52 -04:00
|
|
|
static void
|
|
|
|
clutter_cairo_texture_context_destroy (void *data)
|
|
|
|
{
|
|
|
|
DrawContext *ctxt = data;
|
|
|
|
ClutterCairoTexture *cairo = ctxt->texture;
|
|
|
|
ClutterCairoTexturePrivate *priv = cairo->priv;
|
|
|
|
guint8 *cairo_data;
|
|
|
|
gint cairo_width, cairo_height, cairo_stride;
|
|
|
|
gint surface_width, surface_height;
|
|
|
|
CoglHandle cogl_texture;
|
|
|
|
|
|
|
|
if (priv->cr_surface == NULL)
|
|
|
|
{
|
|
|
|
/* the surface went away before we could use it */
|
|
|
|
draw_context_destroy (ctxt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* for any other surface type, we presume that there exists a native
|
|
|
|
* communication between Cairo and GL that is triggered by cairo_destroy().
|
|
|
|
*
|
|
|
|
* for instance, cairo-drm will flush the outstanding modifications to the
|
|
|
|
* surface upon context destruction and so the texture is automatically
|
|
|
|
* updated.
|
|
|
|
*/
|
|
|
|
if (cairo_surface_get_type (priv->cr_surface) != CAIRO_SURFACE_TYPE_IMAGE)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
surface_width = cairo_image_surface_get_width (priv->cr_surface);
|
|
|
|
surface_height = cairo_image_surface_get_height (priv->cr_surface);
|
|
|
|
|
|
|
|
cairo_width = MIN (ctxt->rect.width, surface_width);
|
|
|
|
cairo_height = MIN (ctxt->rect.height, surface_height);
|
|
|
|
|
|
|
|
cogl_texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (cairo));
|
|
|
|
if (cairo_width == 0 ||
|
|
|
|
cairo_height == 0 ||
|
|
|
|
cogl_texture == COGL_INVALID_HANDLE)
|
|
|
|
{
|
|
|
|
draw_context_destroy (ctxt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cairo_stride = cairo_image_surface_get_stride (priv->cr_surface);
|
|
|
|
cairo_data = cairo_image_surface_get_data (priv->cr_surface);
|
|
|
|
cairo_data += cairo_stride * ctxt->rect.y;
|
|
|
|
cairo_data += 4 * ctxt->rect.x;
|
|
|
|
|
|
|
|
cogl_texture_set_region (cogl_texture,
|
|
|
|
0, 0,
|
|
|
|
ctxt->rect.x, ctxt->rect.y,
|
|
|
|
cairo_width, cairo_height,
|
|
|
|
cairo_width, cairo_height,
|
|
|
|
CLUTTER_CAIRO_FORMAT_ARGB32,
|
|
|
|
cairo_stride,
|
|
|
|
cairo_data);
|
|
|
|
|
|
|
|
out:
|
|
|
|
draw_context_destroy (ctxt);
|
|
|
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (cairo));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
|
|
|
clutter_cairo_texture_emit_draw (ClutterCairoTexture *self,
|
|
|
|
DrawContext *ctxt)
|
|
|
|
{
|
|
|
|
gboolean result;
|
|
|
|
cairo_t *cr;
|
|
|
|
|
2011-11-18 12:22:12 -05:00
|
|
|
/* 0x0 surfaces don't need a ::draw */
|
|
|
|
if (self->priv->surface_width == 0 ||
|
|
|
|
self->priv->surface_height == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* if the size is !0 then we must have a surface */
|
2011-07-26 07:40:52 -04:00
|
|
|
g_assert (self->priv->cr_surface != NULL);
|
|
|
|
|
|
|
|
cr = cairo_create (self->priv->cr_surface);
|
|
|
|
cairo_set_user_data (cr, &clutter_cairo_texture_context_key,
|
|
|
|
ctxt,
|
|
|
|
clutter_cairo_texture_context_destroy);
|
|
|
|
|
|
|
|
if (ctxt->is_clipped)
|
|
|
|
{
|
|
|
|
cairo_rectangle (cr,
|
|
|
|
ctxt->rect.x,
|
|
|
|
ctxt->rect.y,
|
|
|
|
ctxt->rect.width,
|
|
|
|
ctxt->rect.height);
|
|
|
|
cairo_clip (cr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* store the cairo_t as a guard */
|
|
|
|
self->priv->cr_context = cr;
|
|
|
|
|
|
|
|
g_signal_emit (self, cairo_signals[DRAW], 0, cr, &result);
|
|
|
|
|
|
|
|
self->priv->cr_context = NULL;
|
|
|
|
|
|
|
|
cairo_destroy (cr);
|
|
|
|
}
|
|
|
|
|
2008-12-11 10:24:07 -05:00
|
|
|
static inline void
|
|
|
|
clutter_cairo_texture_surface_resize_internal (ClutterCairoTexture *cairo)
|
|
|
|
{
|
|
|
|
ClutterCairoTexturePrivate *priv = cairo->priv;
|
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
if (priv->cr_surface != NULL)
|
2008-12-11 10:24:07 -05:00
|
|
|
{
|
2010-05-10 10:33:05 -04:00
|
|
|
cairo_surface_t *surface = priv->cr_surface;
|
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
/* if the surface is an image one, and the size is already the
|
|
|
|
* same, then we don't need to do anything
|
|
|
|
*/
|
|
|
|
if (cairo_surface_get_type (surface) != CAIRO_SURFACE_TYPE_IMAGE)
|
|
|
|
{
|
|
|
|
gint surface_width = cairo_image_surface_get_width (surface);
|
|
|
|
gint surface_height = cairo_image_surface_get_height (surface);
|
|
|
|
|
2011-07-26 07:40:52 -04:00
|
|
|
if (priv->surface_width == surface_width &&
|
|
|
|
priv->surface_height == surface_height)
|
2010-10-15 12:35:41 -04:00
|
|
|
return;
|
|
|
|
}
|
2008-12-19 08:15:26 -05:00
|
|
|
|
2010-05-10 10:33:05 -04:00
|
|
|
cairo_surface_finish (surface);
|
|
|
|
cairo_surface_destroy (surface);
|
|
|
|
priv->cr_surface = NULL;
|
|
|
|
}
|
|
|
|
|
2011-07-26 07:40:52 -04:00
|
|
|
if (priv->surface_width == 0 ||
|
|
|
|
priv->surface_height == 0)
|
2008-12-17 10:40:33 -05:00
|
|
|
return;
|
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
g_signal_emit (cairo, cairo_signals[CREATE_SURFACE], 0,
|
2011-07-26 07:40:52 -04:00
|
|
|
priv->surface_width,
|
|
|
|
priv->surface_height,
|
2010-10-15 12:35:41 -04:00
|
|
|
&priv->cr_surface);
|
2008-12-11 10:24:07 -05:00
|
|
|
}
|
|
|
|
|
2008-12-17 10:40:33 -05:00
|
|
|
static void
|
2008-12-19 08:15:26 -05:00
|
|
|
clutter_cairo_texture_notify (GObject *object,
|
|
|
|
GParamSpec *pspec)
|
2008-12-11 10:24:07 -05:00
|
|
|
{
|
2008-12-19 08:15:26 -05:00
|
|
|
/* When the surface width or height changes then resize the cairo
|
|
|
|
surface. This is done here instead of directly in set_property so
|
|
|
|
that if both the width and height properties are set using a
|
|
|
|
single call to g_object_set then the surface will only be resized
|
|
|
|
once because the notifications will be frozen in between */
|
2011-07-26 07:40:52 -04:00
|
|
|
|
|
|
|
if (obj_props[PROP_SURFACE_WIDTH]->name == pspec->name ||
|
|
|
|
obj_props[PROP_SURFACE_HEIGHT]->name == pspec->name)
|
2008-12-19 08:15:26 -05:00
|
|
|
{
|
|
|
|
ClutterCairoTexture *cairo = CLUTTER_CAIRO_TEXTURE (object);
|
2008-12-11 10:24:07 -05:00
|
|
|
|
2008-12-19 08:15:26 -05:00
|
|
|
clutter_cairo_texture_surface_resize_internal (cairo);
|
|
|
|
}
|
2008-12-11 10:24:07 -05:00
|
|
|
|
2008-12-19 08:15:26 -05:00
|
|
|
if (G_OBJECT_CLASS (clutter_cairo_texture_parent_class)->notify)
|
|
|
|
G_OBJECT_CLASS (clutter_cairo_texture_parent_class)->notify (object, pspec);
|
2008-12-11 10:24:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_cairo_texture_get_preferred_width (ClutterActor *actor,
|
Remove Units from the public API
With the recent change to internal floating point values, ClutterUnit
has become a redundant type, defined to be a float. All integer entry
points are being internally converted to floating point values to be
passed to the GL pipeline with the least amount of conversion.
ClutterUnit is thus exposed as just a "pixel with fractionary bits",
and not -- as users might think -- as generic, resolution and device
independent units. not that it was the case, but a definitive amount
of people was convinced it did provide this "feature", and was flummoxed
about the mere existence of this type.
So, having ClutterUnit exposed in the public API doubles the entry
points and has the following disadvantages:
- we have to maintain twice the amount of entry points in ClutterActor
- we still do an integer-to-float implicit conversion
- we introduce a weird impedance between pixels and "pixels with
fractionary bits"
- language bindings will have to choose what to bind, and resort
to manually overriding the API
+ *except* for language bindings based on GObject-Introspection, as
they cannot do manual overrides, thus will replicate the entire
set of entry points
For these reason, we should coalesces every Actor entry point for
pixels and for ClutterUnit into a single entry point taking a float,
like:
void clutter_actor_set_x (ClutterActor *self,
gfloat x);
void clutter_actor_get_size (ClutterActor *self,
gfloat *width,
gfloat *height);
gfloat clutter_actor_get_height (ClutterActor *self);
etc.
The issues I have identified are:
- we'll have a two cases of compiler warnings:
- printf() format of the return values from %d to %f
- clutter_actor_get_size() taking floats instead of unsigned ints
- we'll have a problem with varargs when passing an integer instead
of a floating point value, except on 64bit platforms where the
size of a float is the same as the size of an int
To be clear: the *intent* of the API should not change -- we still use
pixels everywhere -- but:
- we remove ambiguity in the API with regard to pixels and units
- we remove entry points we get to maintain for the whole 1.0
version of the API
- we make things simpler to bind for both manual language bindings
and automatic (gobject-introspection based) ones
- we have the simplest API possible while still exposing the
capabilities of the underlying GL implementation
2009-05-06 11:44:47 -04:00
|
|
|
gfloat for_height,
|
|
|
|
gfloat *min_width,
|
|
|
|
gfloat *natural_width)
|
2008-12-11 10:24:07 -05:00
|
|
|
{
|
|
|
|
ClutterCairoTexturePrivate *priv = CLUTTER_CAIRO_TEXTURE (actor)->priv;
|
|
|
|
|
|
|
|
if (min_width)
|
|
|
|
*min_width = 0;
|
|
|
|
|
|
|
|
if (natural_width)
|
2011-07-26 07:40:52 -04:00
|
|
|
*natural_width = (gfloat) priv->surface_width;
|
2008-12-11 10:24:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_cairo_texture_get_preferred_height (ClutterActor *actor,
|
Remove Units from the public API
With the recent change to internal floating point values, ClutterUnit
has become a redundant type, defined to be a float. All integer entry
points are being internally converted to floating point values to be
passed to the GL pipeline with the least amount of conversion.
ClutterUnit is thus exposed as just a "pixel with fractionary bits",
and not -- as users might think -- as generic, resolution and device
independent units. not that it was the case, but a definitive amount
of people was convinced it did provide this "feature", and was flummoxed
about the mere existence of this type.
So, having ClutterUnit exposed in the public API doubles the entry
points and has the following disadvantages:
- we have to maintain twice the amount of entry points in ClutterActor
- we still do an integer-to-float implicit conversion
- we introduce a weird impedance between pixels and "pixels with
fractionary bits"
- language bindings will have to choose what to bind, and resort
to manually overriding the API
+ *except* for language bindings based on GObject-Introspection, as
they cannot do manual overrides, thus will replicate the entire
set of entry points
For these reason, we should coalesces every Actor entry point for
pixels and for ClutterUnit into a single entry point taking a float,
like:
void clutter_actor_set_x (ClutterActor *self,
gfloat x);
void clutter_actor_get_size (ClutterActor *self,
gfloat *width,
gfloat *height);
gfloat clutter_actor_get_height (ClutterActor *self);
etc.
The issues I have identified are:
- we'll have a two cases of compiler warnings:
- printf() format of the return values from %d to %f
- clutter_actor_get_size() taking floats instead of unsigned ints
- we'll have a problem with varargs when passing an integer instead
of a floating point value, except on 64bit platforms where the
size of a float is the same as the size of an int
To be clear: the *intent* of the API should not change -- we still use
pixels everywhere -- but:
- we remove ambiguity in the API with regard to pixels and units
- we remove entry points we get to maintain for the whole 1.0
version of the API
- we make things simpler to bind for both manual language bindings
and automatic (gobject-introspection based) ones
- we have the simplest API possible while still exposing the
capabilities of the underlying GL implementation
2009-05-06 11:44:47 -04:00
|
|
|
gfloat for_width,
|
|
|
|
gfloat *min_height,
|
|
|
|
gfloat *natural_height)
|
2008-12-11 10:24:07 -05:00
|
|
|
{
|
|
|
|
ClutterCairoTexturePrivate *priv = CLUTTER_CAIRO_TEXTURE (actor)->priv;
|
|
|
|
|
|
|
|
if (min_height)
|
|
|
|
*min_height = 0;
|
|
|
|
|
|
|
|
if (natural_height)
|
2011-07-26 07:40:52 -04:00
|
|
|
*natural_height = (gfloat) priv->surface_height;
|
2008-12-11 10:24:07 -05:00
|
|
|
}
|
|
|
|
|
2011-07-26 09:53:26 -04:00
|
|
|
static void
|
|
|
|
clutter_cairo_texture_allocate (ClutterActor *self,
|
|
|
|
const ClutterActorBox *allocation,
|
|
|
|
ClutterAllocationFlags flags)
|
|
|
|
{
|
|
|
|
ClutterCairoTexturePrivate *priv = CLUTTER_CAIRO_TEXTURE (self)->priv;
|
|
|
|
ClutterActorClass *parent_class;
|
|
|
|
|
|
|
|
parent_class = CLUTTER_ACTOR_CLASS (clutter_cairo_texture_parent_class);
|
|
|
|
parent_class->allocate (self, allocation, flags);
|
|
|
|
|
|
|
|
if (priv->auto_resize)
|
|
|
|
{
|
|
|
|
ClutterCairoTexture *texture = CLUTTER_CAIRO_TEXTURE (self);
|
|
|
|
gfloat width, height;
|
|
|
|
|
|
|
|
clutter_actor_box_get_size (allocation, &width, &height);
|
|
|
|
|
2011-10-24 06:58:23 -04:00
|
|
|
priv->surface_width = ceilf (width);
|
|
|
|
priv->surface_height = ceilf (height);
|
2011-07-26 09:53:26 -04:00
|
|
|
|
|
|
|
clutter_cairo_texture_surface_resize_internal (texture);
|
|
|
|
clutter_cairo_texture_invalidate (texture);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-08 14:47:11 -04:00
|
|
|
static gboolean
|
2010-09-09 07:30:29 -04:00
|
|
|
clutter_cairo_texture_get_paint_volume (ClutterActor *self,
|
2010-09-08 14:47:11 -04:00
|
|
|
ClutterPaintVolume *volume)
|
|
|
|
{
|
2010-09-09 07:30:29 -04:00
|
|
|
return _clutter_actor_set_default_paint_volume (self,
|
|
|
|
CLUTTER_TYPE_CAIRO_TEXTURE,
|
|
|
|
volume);
|
2010-09-08 14:47:11 -04:00
|
|
|
}
|
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
static cairo_surface_t *
|
|
|
|
clutter_cairo_texture_create_surface (ClutterCairoTexture *self,
|
|
|
|
guint width,
|
|
|
|
guint height)
|
|
|
|
{
|
|
|
|
cairo_surface_t *surface;
|
|
|
|
guint cairo_stride;
|
|
|
|
guint8 *cairo_data;
|
|
|
|
CoglHandle cogl_texture;
|
|
|
|
|
|
|
|
surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
|
|
|
|
width,
|
|
|
|
height);
|
|
|
|
|
|
|
|
cairo_stride = cairo_image_surface_get_stride (surface);
|
|
|
|
cairo_data = cairo_image_surface_get_data (surface);
|
|
|
|
|
|
|
|
/* create a backing Cogl texture */
|
|
|
|
cogl_texture = cogl_texture_new_from_data (width, height,
|
|
|
|
COGL_TEXTURE_NONE,
|
2011-04-12 12:00:46 -04:00
|
|
|
CLUTTER_CAIRO_FORMAT_ARGB32,
|
2010-10-15 12:35:41 -04:00
|
|
|
COGL_PIXEL_FORMAT_ANY,
|
|
|
|
cairo_stride,
|
|
|
|
cairo_data);
|
|
|
|
clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (self), cogl_texture);
|
|
|
|
cogl_handle_unref (cogl_texture);
|
|
|
|
|
|
|
|
return surface;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
create_surface_accum (GSignalInvocationHint *ihint,
|
|
|
|
GValue *return_accu,
|
|
|
|
const GValue *handler_return,
|
|
|
|
gpointer data)
|
|
|
|
{
|
|
|
|
g_value_copy (handler_return, return_accu);
|
|
|
|
|
|
|
|
/* stop on the first non-NULL return value */
|
|
|
|
return g_value_get_boxed (handler_return) == NULL;
|
|
|
|
}
|
|
|
|
|
2011-07-26 07:40:52 -04:00
|
|
|
static void
|
|
|
|
clutter_cairo_texture_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 (¶m_values[1]);
|
|
|
|
|
|
|
|
cairo_save (cr);
|
|
|
|
|
|
|
|
_clutter_marshal_BOOLEAN__BOXED (closure,
|
|
|
|
return_value,
|
|
|
|
n_param_values,
|
|
|
|
param_values,
|
|
|
|
invocation_hint,
|
|
|
|
marshal_data);
|
|
|
|
|
|
|
|
cairo_restore (cr);
|
|
|
|
}
|
|
|
|
|
2008-12-11 10:24:07 -05:00
|
|
|
static void
|
|
|
|
clutter_cairo_texture_class_init (ClutterCairoTextureClass *klass)
|
|
|
|
{
|
|
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
|
|
|
|
|
|
|
gobject_class->finalize = clutter_cairo_texture_finalize;
|
|
|
|
gobject_class->set_property = clutter_cairo_texture_set_property;
|
|
|
|
gobject_class->get_property = clutter_cairo_texture_get_property;
|
2008-12-19 08:15:26 -05:00
|
|
|
gobject_class->notify = clutter_cairo_texture_notify;
|
2008-12-11 10:24:07 -05:00
|
|
|
|
2010-09-08 14:47:11 -04:00
|
|
|
actor_class->get_paint_volume =
|
|
|
|
clutter_cairo_texture_get_paint_volume;
|
2008-12-11 10:24:07 -05:00
|
|
|
actor_class->get_preferred_width =
|
|
|
|
clutter_cairo_texture_get_preferred_width;
|
|
|
|
actor_class->get_preferred_height =
|
|
|
|
clutter_cairo_texture_get_preferred_height;
|
2011-07-26 09:53:26 -04:00
|
|
|
actor_class->allocate =
|
|
|
|
clutter_cairo_texture_allocate;
|
2008-12-11 10:24:07 -05:00
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
klass->create_surface = clutter_cairo_texture_create_surface;
|
|
|
|
|
2008-12-11 10:24:07 -05:00
|
|
|
g_type_class_add_private (gobject_class, sizeof (ClutterCairoTexturePrivate));
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ClutterCairoTexture:surface-width:
|
|
|
|
*
|
|
|
|
* The width of the Cairo surface used by the #ClutterCairoTexture
|
|
|
|
* actor, in pixels.
|
|
|
|
*
|
|
|
|
* Since: 1.0
|
|
|
|
*/
|
2010-10-15 10:24:27 -04:00
|
|
|
obj_props[PROP_SURFACE_WIDTH] =
|
|
|
|
g_param_spec_uint ("surface-width",
|
|
|
|
P_("Surface Width"),
|
|
|
|
P_("The width of the Cairo surface"),
|
|
|
|
0, G_MAXUINT,
|
|
|
|
0,
|
|
|
|
CLUTTER_PARAM_READWRITE);
|
2008-12-11 10:24:07 -05:00
|
|
|
/**
|
|
|
|
* ClutterCairoTexture:surface-height:
|
|
|
|
*
|
|
|
|
* The height of the Cairo surface used by the #ClutterCairoTexture
|
|
|
|
* actor, in pixels.
|
|
|
|
*
|
|
|
|
* Since: 1.0
|
|
|
|
*/
|
2010-10-15 10:24:27 -04:00
|
|
|
obj_props[PROP_SURFACE_HEIGHT] =
|
|
|
|
g_param_spec_uint ("surface-height",
|
|
|
|
P_("Surface Height"),
|
|
|
|
P_("The height of the Cairo surface"),
|
|
|
|
0, G_MAXUINT,
|
|
|
|
0,
|
|
|
|
CLUTTER_PARAM_READWRITE);
|
|
|
|
|
2011-07-26 09:53:26 -04:00
|
|
|
/**
|
|
|
|
* ClutterCairoTexture:auto-resize:
|
|
|
|
*
|
|
|
|
* Controls whether the #ClutterCairoTexture should automatically
|
|
|
|
* resize the Cairo surface whenever the actor's allocation changes.
|
|
|
|
* If :auto-resize is set to %TRUE the surface contents will also
|
|
|
|
* be invalidated automatically.
|
|
|
|
*
|
|
|
|
* Since: 1.8
|
|
|
|
*/
|
|
|
|
obj_props[PROP_AUTO_RESIZE] =
|
|
|
|
g_param_spec_boolean ("auto-resize",
|
|
|
|
P_("Auto Resize"),
|
|
|
|
P_("Whether the surface should match the allocation"),
|
|
|
|
FALSE,
|
|
|
|
CLUTTER_PARAM_READWRITE);
|
|
|
|
|
|
|
|
g_object_class_install_properties (gobject_class, PROP_LAST, obj_props);
|
2010-10-15 12:35:41 -04:00
|
|
|
|
|
|
|
/**
|
2010-11-08 11:01:19 -05:00
|
|
|
* ClutterCairoTexture::create-surface:
|
2010-10-15 12:35:41 -04:00
|
|
|
* @texture: the #ClutterCairoTexture that emitted the signal
|
|
|
|
* @width: the width of the surface to create
|
|
|
|
* @height: the height of the surface to create
|
|
|
|
*
|
|
|
|
* The ::create-surface signal is emitted when a #ClutterCairoTexture
|
|
|
|
* news its surface (re)created, which happens either when the Cairo
|
|
|
|
* context is created with clutter_cairo_texture_create() or
|
|
|
|
* clutter_cairo_texture_create_region(), or when the surface is resized
|
|
|
|
* through clutter_cairo_texture_set_surface_size().
|
|
|
|
*
|
|
|
|
* The first signal handler that returns a non-%NULL, valid surface will
|
|
|
|
* stop any further signal emission, and the returned surface will be
|
|
|
|
* the one used.
|
|
|
|
*
|
|
|
|
* Return value: the newly created #cairo_surface_t for the texture
|
|
|
|
*
|
|
|
|
* Since: 1.6
|
|
|
|
*/
|
|
|
|
cairo_signals[CREATE_SURFACE] =
|
|
|
|
g_signal_new (I_("create-surface"),
|
|
|
|
G_TYPE_FROM_CLASS (klass),
|
2011-07-26 07:40:52 -04:00
|
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
|
2010-10-15 12:35:41 -04:00
|
|
|
G_STRUCT_OFFSET (ClutterCairoTextureClass, create_surface),
|
|
|
|
create_surface_accum, NULL,
|
|
|
|
_clutter_marshal_BOXED__UINT_UINT,
|
|
|
|
CAIRO_GOBJECT_TYPE_SURFACE, 2,
|
|
|
|
G_TYPE_UINT,
|
|
|
|
G_TYPE_UINT);
|
2011-07-26 07:40:52 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* ClutterCairoTexture::draw:
|
2011-07-26 08:43:37 -04:00
|
|
|
* @texture: the #ClutterCairoTexture that emitted the signal
|
2011-07-26 07:40:52 -04:00
|
|
|
* @cr: the Cairo context to use to draw
|
|
|
|
*
|
|
|
|
* The ::draw signal is emitted each time a #ClutterCairoTexture has
|
|
|
|
* been invalidated.
|
|
|
|
*
|
|
|
|
* The passed Cairo context passed will be clipped to the invalidated
|
|
|
|
* area.
|
|
|
|
*
|
|
|
|
* It is safe to connect multiple callbacks to this signals; the state
|
|
|
|
* of the Cairo context passed to each callback is automatically saved
|
|
|
|
* and restored, so it's not necessary to call cairo_save() and
|
|
|
|
* cairo_restore().
|
|
|
|
*
|
|
|
|
* Return value: %TRUE if the signal emission should stop, and %FALSE
|
|
|
|
* to continue
|
|
|
|
*
|
|
|
|
* Since: 1.8
|
|
|
|
*/
|
|
|
|
cairo_signals[DRAW] =
|
|
|
|
g_signal_new (I_("draw"),
|
|
|
|
G_TYPE_FROM_CLASS (klass),
|
|
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
|
|
|
|
G_STRUCT_OFFSET (ClutterCairoTextureClass, draw),
|
|
|
|
_clutter_boolean_handled_accumulator, NULL,
|
|
|
|
clutter_cairo_texture_draw_marshaller,
|
|
|
|
G_TYPE_BOOLEAN, 1,
|
|
|
|
CAIRO_GOBJECT_TYPE_CONTEXT);
|
2008-12-11 10:24:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
clutter_cairo_texture_init (ClutterCairoTexture *self)
|
|
|
|
{
|
|
|
|
ClutterCairoTexturePrivate *priv;
|
|
|
|
|
|
|
|
self->priv = priv = CLUTTER_CAIRO_TEXTURE_GET_PRIVATE (self);
|
|
|
|
|
|
|
|
/* FIXME - we are hardcoding the format; it would be good to have
|
|
|
|
* a :surface-format construct-only property for creating
|
2008-12-11 10:51:24 -05:00
|
|
|
* textures with a different format and have the cairo surface
|
|
|
|
* match that format
|
2010-10-15 12:35:41 -04:00
|
|
|
*
|
|
|
|
* priv->format = CAIRO_FORMAT_ARGB32;
|
2008-12-11 10:24:07 -05:00
|
|
|
*/
|
2010-06-16 08:58:41 -04:00
|
|
|
|
|
|
|
/* the Cairo surface is responsible for driving the size of
|
|
|
|
* the texture; if we let sync_size to its default of TRUE,
|
|
|
|
* the Texture will try to queue a relayout every time we
|
|
|
|
* change the size of the Cairo surface - which is not what
|
|
|
|
* we want
|
|
|
|
*/
|
|
|
|
clutter_texture_set_sync_size (CLUTTER_TEXTURE (self), FALSE);
|
2008-12-11 10:24:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_cairo_texture_new:
|
|
|
|
* @width: the width of the surface
|
|
|
|
* @height: the height of the surface
|
|
|
|
*
|
|
|
|
* Creates a new #ClutterCairoTexture actor, with a surface of @width by
|
|
|
|
* @height pixels.
|
|
|
|
*
|
|
|
|
* Return value: the newly created #ClutterCairoTexture actor
|
|
|
|
*
|
|
|
|
* Since: 1.0
|
|
|
|
*/
|
|
|
|
ClutterActor*
|
|
|
|
clutter_cairo_texture_new (guint width,
|
|
|
|
guint height)
|
|
|
|
{
|
|
|
|
return g_object_new (CLUTTER_TYPE_CAIRO_TEXTURE,
|
|
|
|
"surface-width", width,
|
|
|
|
"surface-height", height,
|
|
|
|
NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2010-10-15 12:35:41 -04:00
|
|
|
intersect_rectangles (cairo_rectangle_int_t *a,
|
|
|
|
cairo_rectangle_int_t *b,
|
|
|
|
cairo_rectangle_int_t *inter)
|
2008-12-11 10:24:07 -05:00
|
|
|
{
|
|
|
|
gint dest_x, dest_y;
|
|
|
|
gint dest_width, dest_height;
|
|
|
|
|
|
|
|
dest_x = MAX (a->x, b->x);
|
|
|
|
dest_y = MAX (a->y, b->y);
|
|
|
|
dest_width = MIN (a->x + a->width, b->x + b->width) - dest_x;
|
|
|
|
dest_height = MIN (a->y + a->height, b->y + b->height) - dest_y;
|
|
|
|
|
|
|
|
if (dest_width > 0 && dest_height > 0)
|
|
|
|
{
|
|
|
|
inter->x = dest_x;
|
|
|
|
inter->y = dest_y;
|
|
|
|
inter->width = dest_width;
|
|
|
|
inter->height = dest_height;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
inter->x = 0;
|
|
|
|
inter->y = 0;
|
|
|
|
inter->width = 0;
|
|
|
|
inter->height = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-02 08:48:31 -04:00
|
|
|
static cairo_t *
|
|
|
|
clutter_cairo_texture_create_region_internal (ClutterCairoTexture *self,
|
|
|
|
gint x_offset,
|
|
|
|
gint y_offset,
|
|
|
|
gint width,
|
|
|
|
gint height)
|
2008-12-11 10:24:07 -05:00
|
|
|
{
|
2011-11-02 08:48:31 -04:00
|
|
|
ClutterCairoTexturePrivate *priv = self->priv;
|
2010-10-15 12:35:41 -04:00
|
|
|
cairo_rectangle_int_t region, area, inter;
|
|
|
|
cairo_surface_t *surface;
|
2011-11-02 08:48:31 -04:00
|
|
|
DrawContext *ctxt;
|
2008-12-11 10:24:07 -05:00
|
|
|
cairo_t *cr;
|
|
|
|
|
|
|
|
if (width < 0)
|
2011-07-26 07:40:52 -04:00
|
|
|
width = priv->surface_width;
|
2008-12-11 10:24:07 -05:00
|
|
|
|
|
|
|
if (height < 0)
|
2011-07-26 07:40:52 -04:00
|
|
|
height = priv->surface_height;
|
2008-12-11 10:24:07 -05:00
|
|
|
|
2008-12-17 10:40:33 -05:00
|
|
|
if (width == 0 || height == 0)
|
|
|
|
{
|
|
|
|
g_warning ("Unable to create a context for an image surface of "
|
|
|
|
"width %d and height %d. Set the surface size to be "
|
|
|
|
"at least 1 pixel by 1 pixel.",
|
|
|
|
width, height);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
surface = get_surface (self);
|
2008-12-17 10:40:33 -05:00
|
|
|
|
2011-07-26 07:40:52 -04:00
|
|
|
ctxt = draw_context_create (self);
|
2008-12-11 10:24:07 -05:00
|
|
|
|
|
|
|
region.x = x_offset;
|
|
|
|
region.y = y_offset;
|
|
|
|
region.width = width;
|
|
|
|
region.height = height;
|
|
|
|
|
|
|
|
area.x = 0;
|
|
|
|
area.y = 0;
|
2011-07-26 07:40:52 -04:00
|
|
|
area.width = priv->surface_width;
|
|
|
|
area.height = priv->surface_height;
|
2008-12-11 10:24:07 -05:00
|
|
|
|
|
|
|
/* Limit the region to the visible rectangle */
|
|
|
|
intersect_rectangles (&area, ®ion, &inter);
|
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
ctxt->rect = inter;
|
2008-12-11 10:24:07 -05:00
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
cr = cairo_create (surface);
|
2010-05-10 10:33:05 -04:00
|
|
|
cairo_set_user_data (cr, &clutter_cairo_texture_context_key,
|
2010-10-15 12:35:41 -04:00
|
|
|
ctxt,
|
|
|
|
clutter_cairo_texture_context_destroy);
|
2008-12-11 10:24:07 -05:00
|
|
|
|
|
|
|
return cr;
|
|
|
|
}
|
|
|
|
|
2011-11-02 08:48:31 -04:00
|
|
|
/**
|
|
|
|
* clutter_cairo_texture_create_region:
|
|
|
|
* @self: a #ClutterCairoTexture
|
|
|
|
* @x_offset: offset of the region on the X axis
|
|
|
|
* @y_offset: offset of the region on the Y axis
|
|
|
|
* @width: width of the region, or -1 for the full surface width
|
|
|
|
* @height: height of the region, or -1 for the full surface height
|
|
|
|
*
|
|
|
|
* Creates a new Cairo context that will updat the region defined
|
|
|
|
* by @x_offset, @y_offset, @width and @height.
|
|
|
|
*
|
|
|
|
* <warning><para>Do not call this function within the paint virtual
|
|
|
|
* function or from a callback to the #ClutterActor::paint
|
|
|
|
* signal.</para></warning>
|
|
|
|
*
|
|
|
|
* Return value: a newly created Cairo context. Use cairo_destroy()
|
|
|
|
* to upload the contents of the context when done drawing
|
|
|
|
*
|
|
|
|
* Since: 1.0
|
|
|
|
*
|
|
|
|
* Deprecated: 1.8: Use the #ClutterCairoTexture::draw signal and
|
|
|
|
* clutter_cairo_texture_invalidate_rectangle() to obtain a
|
|
|
|
* clipped Cairo context for 2D drawing.
|
|
|
|
*/
|
|
|
|
cairo_t *
|
|
|
|
clutter_cairo_texture_create_region (ClutterCairoTexture *self,
|
|
|
|
gint x_offset,
|
|
|
|
gint y_offset,
|
|
|
|
gint width,
|
|
|
|
gint height)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self), NULL);
|
|
|
|
|
|
|
|
clutter_warn_if_paint_fail (self);
|
|
|
|
|
|
|
|
return clutter_cairo_texture_create_region_internal (self,
|
|
|
|
x_offset, y_offset,
|
|
|
|
width, height);
|
|
|
|
}
|
|
|
|
|
2011-07-26 07:40:52 -04:00
|
|
|
/**
|
|
|
|
* clutter_cairo_texture_invalidate_rectangle:
|
|
|
|
* @self: a #ClutterCairoTexture
|
|
|
|
* @rect: (allow-none): a rectangle with the area to invalida,
|
|
|
|
* or %NULL to perform an unbounded invalidation
|
|
|
|
*
|
|
|
|
* Invalidates a rectangular region of a #ClutterCairoTexture.
|
|
|
|
*
|
|
|
|
* The invalidation will cause the #ClutterCairoTexture::draw signal
|
|
|
|
* to be emitted.
|
|
|
|
*
|
|
|
|
* See also: clutter_cairo_texture_invalidate()
|
|
|
|
*
|
|
|
|
* Since: 1.8
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_cairo_texture_invalidate_rectangle (ClutterCairoTexture *self,
|
|
|
|
cairo_rectangle_int_t *rect)
|
|
|
|
{
|
|
|
|
DrawContext *ctxt = NULL;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self));
|
|
|
|
|
|
|
|
if (self->priv->cr_context != NULL)
|
|
|
|
{
|
|
|
|
g_warning ("It is not possible to invalidate a Cairo texture"
|
|
|
|
"while drawing into it.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ctxt = draw_context_create (self);
|
|
|
|
|
|
|
|
if (rect != NULL)
|
|
|
|
{
|
|
|
|
cairo_rectangle_int_t area, inter;
|
|
|
|
|
|
|
|
area.x = 0;
|
|
|
|
area.y = 0;
|
|
|
|
area.width = self->priv->surface_width;
|
|
|
|
area.height = self->priv->surface_height;
|
|
|
|
|
|
|
|
/* Limit the region to the visible rectangle */
|
|
|
|
intersect_rectangles (&area, rect, &inter);
|
|
|
|
|
|
|
|
ctxt->is_clipped = TRUE;
|
|
|
|
ctxt->rect = inter;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ctxt->is_clipped = FALSE;
|
|
|
|
ctxt->rect.x = ctxt->rect.y = 0;
|
|
|
|
ctxt->rect.width = self->priv->surface_width;
|
|
|
|
ctxt->rect.height = self->priv->surface_height;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX - it might be good to move the emission inside the paint cycle
|
|
|
|
* using a repaint function, to avoid blocking inside this function
|
|
|
|
*/
|
|
|
|
clutter_cairo_texture_emit_draw (self, ctxt);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_cairo_texture_invalidate:
|
|
|
|
* @self: a #ClutterCairoTexture
|
|
|
|
*
|
|
|
|
* Invalidates the whole surface of a #ClutterCairoTexture.
|
|
|
|
*
|
|
|
|
* This function will cause the #ClutterCairoTexture::draw signal
|
|
|
|
* to be emitted.
|
|
|
|
*
|
|
|
|
* See also: clutter_cairo_texture_invalidate_rectangle()
|
|
|
|
*
|
|
|
|
* Since: 1.8
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_cairo_texture_invalidate (ClutterCairoTexture *self)
|
|
|
|
{
|
|
|
|
g_return_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self));
|
|
|
|
|
|
|
|
clutter_cairo_texture_invalidate_rectangle (self, NULL);
|
|
|
|
}
|
|
|
|
|
2008-12-11 10:24:07 -05:00
|
|
|
/**
|
|
|
|
* clutter_cairo_texture_create:
|
2008-12-19 12:48:30 -05:00
|
|
|
* @self: a #ClutterCairoTexture
|
2008-12-11 10:24:07 -05:00
|
|
|
*
|
|
|
|
* Creates a new Cairo context for the @cairo texture. It is
|
|
|
|
* similar to using clutter_cairo_texture_create_region() with @x_offset
|
|
|
|
* and @y_offset of 0, @width equal to the @cairo texture surface width
|
|
|
|
* and @height equal to the @cairo texture surface height.
|
|
|
|
*
|
2008-12-19 12:48:30 -05:00
|
|
|
* <warning><para>Do not call this function within the paint virtual
|
|
|
|
* function or from a callback to the #ClutterActor::paint
|
|
|
|
* signal.</para></warning>
|
|
|
|
*
|
2008-12-11 10:24:07 -05:00
|
|
|
* Return value: a newly created Cairo context. Use cairo_destroy()
|
2008-12-19 12:48:30 -05:00
|
|
|
* to upload the contents of the context when done drawing
|
2008-12-11 10:24:07 -05:00
|
|
|
*
|
|
|
|
* Since: 1.0
|
2011-07-26 07:53:22 -04:00
|
|
|
*
|
|
|
|
* Deprecated: 1.8: Use the #ClutterCairoTexture::draw signal and
|
|
|
|
* the clutter_cairo_texture_invalidate() function to obtain a
|
|
|
|
* Cairo context for 2D drawing.
|
2008-12-11 10:24:07 -05:00
|
|
|
*/
|
|
|
|
cairo_t *
|
|
|
|
clutter_cairo_texture_create (ClutterCairoTexture *self)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self), NULL);
|
2008-12-12 06:42:16 -05:00
|
|
|
|
|
|
|
clutter_warn_if_paint_fail (self);
|
2008-12-11 10:24:07 -05:00
|
|
|
|
2011-11-02 08:48:31 -04:00
|
|
|
return clutter_cairo_texture_create_region_internal (self, 0, 0, -1, -1);
|
2008-12-11 10:24:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_cairo_set_source_color:
|
|
|
|
* @cr: a Cairo context
|
|
|
|
* @color: a #ClutterColor
|
|
|
|
*
|
|
|
|
* Utility function for setting the source color of @cr using
|
2011-07-26 07:40:52 -04:00
|
|
|
* a #ClutterColor. This function is the equivalent of:
|
|
|
|
*
|
|
|
|
* |[
|
|
|
|
* cairo_set_source_rgba (cr,
|
|
|
|
* color->red / 255.0,
|
|
|
|
* color->green / 255.0,
|
|
|
|
* color->blue / 255.0,
|
|
|
|
* color->alpha / 255.0);
|
|
|
|
* ]|
|
2008-12-11 10:24:07 -05:00
|
|
|
*
|
|
|
|
* Since: 1.0
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_cairo_set_source_color (cairo_t *cr,
|
|
|
|
const ClutterColor *color)
|
|
|
|
{
|
|
|
|
g_return_if_fail (cr != NULL);
|
|
|
|
g_return_if_fail (color != NULL);
|
|
|
|
|
|
|
|
if (color->alpha == 0xff)
|
|
|
|
cairo_set_source_rgb (cr,
|
|
|
|
color->red / 255.0,
|
|
|
|
color->green / 255.0,
|
|
|
|
color->blue / 255.0);
|
|
|
|
else
|
|
|
|
cairo_set_source_rgba (cr,
|
|
|
|
color->red / 255.0,
|
|
|
|
color->green / 255.0,
|
|
|
|
color->blue / 255.0,
|
|
|
|
color->alpha / 255.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-12-19 12:48:30 -05:00
|
|
|
* clutter_cairo_texture_set_surface_size:
|
2008-12-11 10:24:07 -05:00
|
|
|
* @self: a #ClutterCairoTexture
|
|
|
|
* @width: the new width of the surface
|
|
|
|
* @height: the new height of the surface
|
|
|
|
*
|
|
|
|
* Resizes the Cairo surface used by @self to @width and @height.
|
|
|
|
*
|
2011-07-26 07:40:52 -04:00
|
|
|
* This function will not invalidate the contents of the Cairo
|
|
|
|
* texture: you will have to explicitly call either
|
|
|
|
* clutter_cairo_texture_invalidate_rectangle() or
|
|
|
|
* clutter_cairo_texture_invalidate().
|
|
|
|
*
|
2008-12-11 10:24:07 -05:00
|
|
|
* Since: 1.0
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_cairo_texture_set_surface_size (ClutterCairoTexture *self,
|
|
|
|
guint width,
|
|
|
|
guint height)
|
|
|
|
{
|
|
|
|
ClutterCairoTexturePrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self));
|
|
|
|
|
|
|
|
priv = self->priv;
|
|
|
|
|
2011-07-26 07:40:52 -04:00
|
|
|
if (width == priv->surface_width &&
|
|
|
|
height == priv->surface_height)
|
2008-12-11 10:24:07 -05:00
|
|
|
return;
|
|
|
|
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
|
2011-07-26 07:40:52 -04:00
|
|
|
if (priv->surface_width != width)
|
2008-12-11 10:24:07 -05:00
|
|
|
{
|
2011-07-26 07:40:52 -04:00
|
|
|
priv->surface_width = width;
|
2011-03-03 06:35:46 -05:00
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SURFACE_WIDTH]);
|
2008-12-11 10:24:07 -05:00
|
|
|
}
|
|
|
|
|
2011-07-26 07:40:52 -04:00
|
|
|
if (priv->surface_height != height)
|
2008-12-11 10:24:07 -05:00
|
|
|
{
|
2011-07-26 07:40:52 -04:00
|
|
|
priv->surface_height = height;
|
2011-03-03 06:35:46 -05:00
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SURFACE_HEIGHT]);
|
2008-12-11 10:24:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
clutter_cairo_texture_surface_resize_internal (self);
|
|
|
|
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_cairo_texture_get_surface_size:
|
|
|
|
* @self: a #ClutterCairoTexture
|
2010-09-08 10:37:58 -04:00
|
|
|
* @width: (out): return location for the surface width, or %NULL
|
|
|
|
* @height: (out): return location for the surface height, or %NULL
|
2008-12-11 10:24:07 -05:00
|
|
|
*
|
|
|
|
* Retrieves the surface width and height for @self.
|
|
|
|
*
|
|
|
|
* Since: 1.0
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_cairo_texture_get_surface_size (ClutterCairoTexture *self,
|
|
|
|
guint *width,
|
|
|
|
guint *height)
|
|
|
|
{
|
|
|
|
g_return_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self));
|
|
|
|
|
|
|
|
if (width)
|
2011-07-26 07:40:52 -04:00
|
|
|
*width = self->priv->surface_width;
|
2008-12-11 10:24:07 -05:00
|
|
|
|
|
|
|
if (height)
|
2011-07-26 07:40:52 -04:00
|
|
|
*height = self->priv->surface_height;
|
2008-12-11 10:24:07 -05:00
|
|
|
}
|
2009-05-14 09:28:12 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_cairo_texture_clear:
|
|
|
|
* @self: a #ClutterCairoTexture
|
|
|
|
*
|
|
|
|
* Clears @self's internal drawing surface, so that the next upload
|
|
|
|
* will replace the previous contents of the #ClutterCairoTexture
|
|
|
|
* rather than adding to it.
|
|
|
|
*
|
2011-07-26 07:40:52 -04:00
|
|
|
* Calling this function from within a #ClutterCairoTexture::draw
|
|
|
|
* signal handler will clear the invalidated area.
|
|
|
|
*
|
2009-05-14 09:28:12 -04:00
|
|
|
* Since: 1.0
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_cairo_texture_clear (ClutterCairoTexture *self)
|
|
|
|
{
|
2011-07-26 07:40:52 -04:00
|
|
|
ClutterCairoTexturePrivate *priv;
|
2010-10-15 12:35:41 -04:00
|
|
|
cairo_t *cr;
|
2009-05-14 09:28:12 -04:00
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self));
|
|
|
|
|
2011-07-26 07:40:52 -04:00
|
|
|
priv = self->priv;
|
|
|
|
|
|
|
|
/* if we got called outside of a ::draw signal handler
|
|
|
|
* then we clear the whole surface by creating a temporary
|
|
|
|
* cairo_t; otherwise, we clear the current cairo_t, which
|
|
|
|
* will take into account the clip region.
|
|
|
|
*/
|
|
|
|
if (priv->cr_context == NULL)
|
|
|
|
{
|
|
|
|
cairo_surface_t *surface;
|
|
|
|
|
|
|
|
surface = get_surface (self);
|
|
|
|
|
|
|
|
cr = cairo_create (surface);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cr = priv->cr_context;
|
|
|
|
|
|
|
|
cairo_save (cr);
|
2009-05-14 09:28:12 -04:00
|
|
|
|
2010-10-15 12:35:41 -04:00
|
|
|
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
|
|
|
|
cairo_paint (cr);
|
2011-07-26 07:40:52 -04:00
|
|
|
|
|
|
|
cairo_restore (cr);
|
|
|
|
|
|
|
|
if (priv->cr_context == NULL)
|
|
|
|
cairo_destroy (cr);
|
2009-05-14 09:28:12 -04:00
|
|
|
}
|
2011-07-26 09:53:26 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_cairo_texture_set_auto_resize:
|
|
|
|
* @self: a #ClutterCairoTexture
|
|
|
|
* @value: %TRUE if the #ClutterCairoTexture should bind the surface
|
|
|
|
* size to the allocation
|
|
|
|
*
|
|
|
|
* Sets whether the #ClutterCairoTexture should ensure that the
|
|
|
|
* backing Cairo surface used matches the allocation assigned to
|
|
|
|
* the actor. If the allocation changes, the contents of the
|
|
|
|
* #ClutterCairoTexture will also be invalidated automatically.
|
|
|
|
*
|
|
|
|
* Since: 1.8
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
clutter_cairo_texture_set_auto_resize (ClutterCairoTexture *self,
|
|
|
|
gboolean value)
|
|
|
|
{
|
|
|
|
ClutterCairoTexturePrivate *priv;
|
|
|
|
|
|
|
|
g_return_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self));
|
|
|
|
|
|
|
|
value = !!value;
|
|
|
|
|
|
|
|
priv = self->priv;
|
|
|
|
|
|
|
|
if (priv->auto_resize == value)
|
|
|
|
return;
|
|
|
|
|
|
|
|
priv->auto_resize = value;
|
|
|
|
|
|
|
|
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
|
|
|
|
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_AUTO_RESIZE]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* clutter_cairo_texture_get_auto_resize:
|
|
|
|
* @self: a #ClutterCairoTexture
|
|
|
|
*
|
|
|
|
* Retrieves the value set using clutter_cairo_texture_set_auto_resize().
|
|
|
|
*
|
|
|
|
* Return value: %TRUE if the #ClutterCairoTexture should track the
|
|
|
|
* allocation, and %FALSE otherwise
|
|
|
|
*
|
|
|
|
* Since: 1.8
|
|
|
|
*/
|
|
|
|
gboolean
|
|
|
|
clutter_cairo_texture_get_auto_resize (ClutterCairoTexture *self)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self), FALSE);
|
|
|
|
|
|
|
|
return self->priv->auto_resize;
|
|
|
|
}
|