mirror of
https://github.com/brl/mutter.git
synced 2024-11-27 02:20:43 -05:00
72eeb8e809
There are too many examples where the default assumption that an actor paints inside its allocation isn't true, so we now return FALSE in the base implementation instead. This means that by default we are saying "we don't know the paint volume of the actor", so developers need to implement the get_paint_volume virtual to take advantage of culling and clipped redraws with their actors. This patch provides very conservative get_paint_volume implementations for ClutterTexture, ClutterCairoTexture, ClutterRectangle and ClutterText which all explicitly check the actor's object type to avoid making any assumptions about subclasses.
808 lines
24 KiB
C
808 lines
24 KiB
C
/*
|
|
* 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>
|
|
*
|
|
* Copyright (C) 2008, 2009, 2010 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/>.
|
|
*/
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* #ClutterCairoTexture will provide a #cairo_t context by using the
|
|
* clutter_cairo_texture_create() and clutter_cairo_texture_create_region()
|
|
* functions; you can use the Cairo API to draw on the context and then
|
|
* call cairo_destroy() when done.
|
|
*
|
|
* As soon as the context is destroyed with cairo_destroy(), the contents
|
|
* of the surface will be uploaded into the #ClutterCairoTexture actor:
|
|
*
|
|
* |[
|
|
* cairo_t *cr;
|
|
*
|
|
* cr = clutter_cairo_texture_create (CLUTTER_CAIRO_TEXTURE (texture));
|
|
*
|
|
* /* draw on the context */
|
|
*
|
|
* cairo_destroy (cr);
|
|
* ]|
|
|
*
|
|
* Although a new #cairo_t is created each time you call
|
|
* clutter_cairo_texture_create() or
|
|
* clutter_cairo_texture_create_region(), it uses the same
|
|
* #cairo_surface_t each time. You can call
|
|
* clutter_cairo_texture_clear() to erase the contents between calls.
|
|
*
|
|
* <warning><para>Note that you should never use the code above inside the
|
|
* #ClutterActor::paint or #ClutterActor::pick virtual functions or
|
|
* signal handlers because it will lead to performance
|
|
* degradation.</para></warning>
|
|
*
|
|
* <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>
|
|
*
|
|
* #ClutterCairoTexture is available since Clutter 1.0.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <string.h>
|
|
|
|
#include "clutter-cairo-texture.h"
|
|
#include "clutter-debug.h"
|
|
#include "clutter-private.h"
|
|
|
|
G_DEFINE_TYPE (ClutterCairoTexture,
|
|
clutter_cairo_texture,
|
|
CLUTTER_TYPE_TEXTURE);
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
|
|
PROP_SURFACE_WIDTH,
|
|
PROP_SURFACE_HEIGHT,
|
|
|
|
PROP_LAST
|
|
};
|
|
|
|
static GParamSpec *obj_props[PROP_LAST];
|
|
|
|
#ifdef CLUTTER_ENABLE_DEBUG
|
|
#define clutter_warn_if_paint_fail(obj) G_STMT_START { \
|
|
if (CLUTTER_ACTOR_IN_PAINT (obj)) { \
|
|
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
|
|
#define clutter_warn_if_paint_fail(obj) /* void */
|
|
#endif /* CLUTTER_ENABLE_DEBUG */
|
|
|
|
#define CLUTTER_CAIRO_TEXTURE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CLUTTER_TYPE_CAIRO_TEXTURE, ClutterCairoTexturePrivate))
|
|
|
|
/* 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_TEXTURE_PIXEL_FORMAT COGL_PIXEL_FORMAT_BGRA_8888_PRE
|
|
#else
|
|
#define CLUTTER_CAIRO_TEXTURE_PIXEL_FORMAT COGL_PIXEL_FORMAT_ARGB_8888_PRE
|
|
#endif
|
|
|
|
struct _ClutterCairoTexturePrivate
|
|
{
|
|
cairo_format_t format;
|
|
|
|
cairo_surface_t *cr_surface;
|
|
guchar *cr_surface_data;
|
|
|
|
guint width;
|
|
guint height;
|
|
guint rowstride;
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
gint x;
|
|
gint y;
|
|
guint width;
|
|
guint height;
|
|
} ClutterCairoTextureRectangle;
|
|
|
|
typedef struct
|
|
{
|
|
ClutterCairoTexture *cairo;
|
|
ClutterCairoTextureRectangle rect;
|
|
} ClutterCairoTextureContext;
|
|
|
|
static const cairo_user_data_key_t clutter_cairo_texture_surface_key;
|
|
static const cairo_user_data_key_t clutter_cairo_texture_context_key;
|
|
|
|
static void
|
|
clutter_cairo_texture_surface_destroy (void *data)
|
|
{
|
|
ClutterCairoTexture *cairo = data;
|
|
|
|
cairo->priv->cr_surface = NULL;
|
|
}
|
|
|
|
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:
|
|
priv->width = g_value_get_uint (value);
|
|
break;
|
|
|
|
case PROP_SURFACE_HEIGHT:
|
|
priv->height = g_value_get_uint (value);
|
|
break;
|
|
|
|
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:
|
|
g_value_set_uint (value, priv->width);
|
|
break;
|
|
|
|
case PROP_SURFACE_HEIGHT:
|
|
g_value_set_uint (value, priv->height);
|
|
break;
|
|
|
|
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;
|
|
|
|
if (priv->cr_surface)
|
|
{
|
|
cairo_surface_t *surface = priv->cr_surface;
|
|
|
|
cairo_surface_finish (priv->cr_surface);
|
|
cairo_surface_set_user_data (priv->cr_surface,
|
|
&clutter_cairo_texture_surface_key,
|
|
NULL, NULL);
|
|
cairo_surface_destroy (surface);
|
|
|
|
priv->cr_surface = NULL;
|
|
}
|
|
|
|
if (priv->cr_surface_data)
|
|
{
|
|
g_free (priv->cr_surface_data);
|
|
priv->cr_surface_data = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (clutter_cairo_texture_parent_class)->finalize (object);
|
|
}
|
|
|
|
static inline void
|
|
clutter_cairo_texture_surface_resize_internal (ClutterCairoTexture *cairo)
|
|
{
|
|
ClutterCairoTexturePrivate *priv = cairo->priv;
|
|
CoglHandle cogl_texture;
|
|
|
|
if (priv->cr_surface)
|
|
{
|
|
cairo_surface_t *surface = priv->cr_surface;
|
|
|
|
/* If the surface is already the right size then don't bother
|
|
doing anything */
|
|
if (priv->width == cairo_image_surface_get_width (priv->cr_surface)
|
|
&& priv->height == cairo_image_surface_get_height (priv->cr_surface))
|
|
return;
|
|
|
|
cairo_surface_finish (surface);
|
|
cairo_surface_set_user_data (surface,
|
|
&clutter_cairo_texture_surface_key,
|
|
NULL, NULL);
|
|
cairo_surface_destroy (surface);
|
|
|
|
priv->cr_surface = NULL;
|
|
}
|
|
|
|
if (priv->cr_surface_data)
|
|
{
|
|
g_free (priv->cr_surface_data);
|
|
priv->cr_surface_data = NULL;
|
|
}
|
|
|
|
if (priv->width == 0 || priv->height == 0)
|
|
return;
|
|
|
|
#if CAIRO_VERSION > 106000
|
|
priv->rowstride = cairo_format_stride_for_width (priv->format, priv->width);
|
|
#else
|
|
/* poor man's version of cairo_format_stride_for_width() */
|
|
switch (priv->format)
|
|
{
|
|
case CAIRO_FORMAT_ARGB32:
|
|
case CAIRO_FORMAT_RGB24:
|
|
priv->rowstride = priv->width * 4;
|
|
break;
|
|
|
|
case CAIRO_FORMAT_A8:
|
|
case CAIRO_FORMAT_A1:
|
|
priv->rowstride = priv->width;
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
#endif /* CAIRO_VERSION > 106000 */
|
|
|
|
priv->cr_surface_data = g_malloc0 (priv->height * priv->rowstride);
|
|
priv->cr_surface =
|
|
cairo_image_surface_create_for_data (priv->cr_surface_data,
|
|
priv->format,
|
|
priv->width, priv->height,
|
|
priv->rowstride);
|
|
|
|
cairo_surface_set_user_data (priv->cr_surface,
|
|
&clutter_cairo_texture_surface_key,
|
|
cairo,
|
|
clutter_cairo_texture_surface_destroy);
|
|
|
|
/* Create a blank Cogl texture */
|
|
cogl_texture = cogl_texture_new_from_data (priv->width, priv->height,
|
|
COGL_TEXTURE_NONE,
|
|
CLUTTER_CAIRO_TEXTURE_PIXEL_FORMAT,
|
|
COGL_PIXEL_FORMAT_ANY,
|
|
priv->rowstride,
|
|
priv->cr_surface_data);
|
|
clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (cairo), cogl_texture);
|
|
cogl_handle_unref (cogl_texture);
|
|
}
|
|
|
|
static void
|
|
clutter_cairo_texture_notify (GObject *object,
|
|
GParamSpec *pspec)
|
|
{
|
|
/* 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 */
|
|
if (strcmp ("surface-width", pspec->name) == 0 ||
|
|
strcmp ("surface-height", pspec->name) == 0)
|
|
{
|
|
ClutterCairoTexture *cairo = CLUTTER_CAIRO_TEXTURE (object);
|
|
|
|
clutter_cairo_texture_surface_resize_internal (cairo);
|
|
}
|
|
|
|
if (G_OBJECT_CLASS (clutter_cairo_texture_parent_class)->notify)
|
|
G_OBJECT_CLASS (clutter_cairo_texture_parent_class)->notify (object, pspec);
|
|
}
|
|
|
|
static void
|
|
clutter_cairo_texture_get_preferred_width (ClutterActor *actor,
|
|
gfloat for_height,
|
|
gfloat *min_width,
|
|
gfloat *natural_width)
|
|
{
|
|
ClutterCairoTexturePrivate *priv = CLUTTER_CAIRO_TEXTURE (actor)->priv;
|
|
|
|
if (min_width)
|
|
*min_width = 0;
|
|
|
|
if (natural_width)
|
|
*natural_width = (gfloat) priv->width;
|
|
}
|
|
|
|
static void
|
|
clutter_cairo_texture_get_preferred_height (ClutterActor *actor,
|
|
gfloat for_width,
|
|
gfloat *min_height,
|
|
gfloat *natural_height)
|
|
{
|
|
ClutterCairoTexturePrivate *priv = CLUTTER_CAIRO_TEXTURE (actor)->priv;
|
|
|
|
if (min_height)
|
|
*min_height = 0;
|
|
|
|
if (natural_height)
|
|
*natural_height = (gfloat) priv->height;
|
|
}
|
|
|
|
static gboolean
|
|
clutter_cairo_texture_get_paint_volume (ClutterActor *self,
|
|
ClutterPaintVolume *volume)
|
|
{
|
|
ClutterGeometry allocation;
|
|
|
|
/* XXX: we are being conservative here and not making assumptions
|
|
* that sub-classes won't paint outside their allocation. */
|
|
if (G_OBJECT_TYPE (self) != CLUTTER_TYPE_CAIRO_TEXTURE)
|
|
return FALSE;
|
|
|
|
/* XXX: clutter_actor_get_allocation can potentially be very
|
|
* expensive to call if called while the actor doesn't have a valid
|
|
* allocation since it will trigger a synchronous relayout of the
|
|
* scenegraph. We explicitly check we have a valid allocation
|
|
* to avoid hitting that codepath. */
|
|
if (!clutter_actor_has_allocation (self))
|
|
return FALSE;
|
|
|
|
clutter_actor_get_allocation_geometry (self, &allocation);
|
|
clutter_paint_volume_set_width (volume, allocation.width);
|
|
clutter_paint_volume_set_height (volume, allocation.height);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
clutter_cairo_texture_class_init (ClutterCairoTextureClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
|
GParamSpec *pspec;
|
|
|
|
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;
|
|
gobject_class->notify = clutter_cairo_texture_notify;
|
|
|
|
actor_class->get_paint_volume =
|
|
clutter_cairo_texture_get_paint_volume;
|
|
|
|
actor_class->get_preferred_width =
|
|
clutter_cairo_texture_get_preferred_width;
|
|
actor_class->get_preferred_height =
|
|
clutter_cairo_texture_get_preferred_height;
|
|
|
|
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
|
|
*/
|
|
pspec = g_param_spec_uint ("surface-width",
|
|
P_("Surface Width"),
|
|
P_("The width of the Cairo surface"),
|
|
0, G_MAXUINT,
|
|
0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_SURFACE_WIDTH] = pspec;
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_SURFACE_WIDTH,
|
|
pspec);
|
|
/**
|
|
* ClutterCairoTexture:surface-height:
|
|
*
|
|
* The height of the Cairo surface used by the #ClutterCairoTexture
|
|
* actor, in pixels.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
pspec = g_param_spec_uint ("surface-height",
|
|
P_("Surface Height"),
|
|
P_("The height of the Cairo surface"),
|
|
0, G_MAXUINT,
|
|
0,
|
|
CLUTTER_PARAM_READWRITE);
|
|
obj_props[PROP_SURFACE_HEIGHT] = pspec;
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_SURFACE_HEIGHT,
|
|
pspec);
|
|
}
|
|
|
|
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
|
|
* textures with a different format and have the cairo surface
|
|
* match that format
|
|
*/
|
|
priv->format = CAIRO_FORMAT_ARGB32;
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
clutter_cairo_texture_context_destroy (void *data)
|
|
{
|
|
ClutterCairoTextureContext *ctxt = data;
|
|
ClutterCairoTexture *cairo = ctxt->cairo;
|
|
ClutterCairoTexturePrivate *priv = cairo->priv;
|
|
guchar *cairo_data;
|
|
gint cairo_width, cairo_height;
|
|
gint surface_width, surface_height;
|
|
CoglHandle cogl_texture;
|
|
|
|
if (!priv->cr_surface)
|
|
return;
|
|
|
|
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 || !cairo_height || cogl_texture == COGL_INVALID_HANDLE)
|
|
{
|
|
g_free (ctxt);
|
|
|
|
return;
|
|
}
|
|
|
|
cairo_data = (priv->cr_surface_data
|
|
+ (ctxt->rect.y * priv->rowstride)
|
|
+ (ctxt->rect.x * 4));
|
|
|
|
cogl_texture_set_region (cogl_texture,
|
|
0, 0,
|
|
ctxt->rect.x, ctxt->rect.y,
|
|
cairo_width, cairo_height,
|
|
cairo_width, cairo_height,
|
|
CLUTTER_CAIRO_TEXTURE_PIXEL_FORMAT,
|
|
priv->rowstride,
|
|
cairo_data);
|
|
|
|
g_free (ctxt);
|
|
|
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (cairo));
|
|
}
|
|
|
|
static void
|
|
intersect_rectangles (ClutterCairoTextureRectangle *a,
|
|
ClutterCairoTextureRectangle *b,
|
|
ClutterCairoTextureRectangle *inter)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
cairo_t *
|
|
clutter_cairo_texture_create_region (ClutterCairoTexture *self,
|
|
gint x_offset,
|
|
gint y_offset,
|
|
gint width,
|
|
gint height)
|
|
{
|
|
ClutterCairoTexturePrivate *priv;
|
|
ClutterCairoTextureContext *ctxt;
|
|
ClutterCairoTextureRectangle region, area, inter;
|
|
cairo_t *cr;
|
|
|
|
g_return_val_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self), NULL);
|
|
|
|
clutter_warn_if_paint_fail (self);
|
|
|
|
priv = self->priv;
|
|
|
|
if (width < 0)
|
|
width = priv->width;
|
|
|
|
if (height < 0)
|
|
height = priv->height;
|
|
|
|
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;
|
|
}
|
|
|
|
if (!priv->cr_surface)
|
|
return NULL;
|
|
|
|
ctxt = g_new0 (ClutterCairoTextureContext, 1);
|
|
ctxt->cairo = self;
|
|
|
|
region.x = x_offset;
|
|
region.y = y_offset;
|
|
region.width = width;
|
|
region.height = height;
|
|
|
|
area.x = 0;
|
|
area.y = 0;
|
|
area.width = priv->width;
|
|
area.height = priv->height;
|
|
|
|
/* Limit the region to the visible rectangle */
|
|
intersect_rectangles (&area, ®ion, &inter);
|
|
|
|
ctxt->rect.x = inter.x;
|
|
ctxt->rect.y = inter.y;
|
|
ctxt->rect.width = inter.width;
|
|
ctxt->rect.height = inter.height;
|
|
|
|
cr = cairo_create (priv->cr_surface);
|
|
cairo_set_user_data (cr, &clutter_cairo_texture_context_key,
|
|
ctxt, clutter_cairo_texture_context_destroy);
|
|
|
|
return cr;
|
|
}
|
|
|
|
/**
|
|
* clutter_cairo_texture_create:
|
|
* @self: a #ClutterCairoTexture
|
|
*
|
|
* 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.
|
|
*
|
|
* <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
|
|
*/
|
|
cairo_t *
|
|
clutter_cairo_texture_create (ClutterCairoTexture *self)
|
|
{
|
|
g_return_val_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self), NULL);
|
|
|
|
clutter_warn_if_paint_fail (self);
|
|
|
|
return clutter_cairo_texture_create_region (self, 0, 0, -1, -1);
|
|
}
|
|
|
|
/**
|
|
* clutter_cairo_set_source_color:
|
|
* @cr: a Cairo context
|
|
* @color: a #ClutterColor
|
|
*
|
|
* Utility function for setting the source color of @cr using
|
|
* a #ClutterColor.
|
|
*
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* clutter_cairo_texture_set_surface_size:
|
|
* @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.
|
|
*
|
|
* 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;
|
|
|
|
if (width == priv->width && height == priv->height)
|
|
return;
|
|
|
|
g_object_freeze_notify (G_OBJECT (self));
|
|
|
|
if (priv->width != width)
|
|
{
|
|
priv->width = width;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SURFACE_WIDTH]);
|
|
}
|
|
|
|
if (priv->height != height)
|
|
{
|
|
priv->height = height;
|
|
_clutter_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SURFACE_HEIGHT]);
|
|
}
|
|
|
|
clutter_cairo_texture_surface_resize_internal (self);
|
|
|
|
g_object_thaw_notify (G_OBJECT (self));
|
|
}
|
|
|
|
/**
|
|
* clutter_cairo_texture_get_surface_size:
|
|
* @self: a #ClutterCairoTexture
|
|
* @width: (out): return location for the surface width, or %NULL
|
|
* @height: (out): return location for the surface height, or %NULL
|
|
*
|
|
* 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)
|
|
*width = self->priv->width;
|
|
|
|
if (height)
|
|
*height = self->priv->height;
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* Since: 1.0
|
|
*/
|
|
void
|
|
clutter_cairo_texture_clear (ClutterCairoTexture *self)
|
|
{
|
|
ClutterCairoTexturePrivate *priv;
|
|
|
|
g_return_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self));
|
|
|
|
priv = self->priv;
|
|
|
|
if (!priv->cr_surface_data)
|
|
return;
|
|
|
|
memset (priv->cr_surface_data, 0, priv->height * priv->rowstride);
|
|
}
|