cairo-texture: Allow overriding the surface creation
By using a new signal, ::create-surface (width, height), it should be possible for third party code and sub-classes to override the default surface creation code in CairoSurface. This commit takes a bit of the patch from: http://bugzilla.clutter-project.org/show_bug.cgi?id=1878 which cleans up CairoTexture; the idea, mutuated from that bug, is that the CairoTexture actor checks whether the surface it has it's an image one, and in that case it uses a Cogl texture as the backing store. In case the surface is not an image one we assume that the surface itself has some way of updating the GL state and flush the surface.
This commit is contained in:
parent
63e314b07e
commit
0f613ea134
@ -78,8 +78,12 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <cairo-gobject.h>
|
||||
|
||||
#include "clutter-cairo-texture.h"
|
||||
|
||||
#include "clutter-debug.h"
|
||||
#include "clutter-marshal.h"
|
||||
#include "clutter-private.h"
|
||||
|
||||
G_DEFINE_TYPE (ClutterCairoTexture,
|
||||
@ -96,7 +100,16 @@ enum
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
static GParamSpec *obj_props[PROP_LAST];
|
||||
enum
|
||||
{
|
||||
CREATE_SURFACE,
|
||||
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static GParamSpec *obj_props[PROP_LAST] = { NULL, };
|
||||
|
||||
static guint cairo_signals[LAST_SIGNAL] = { 0, };
|
||||
|
||||
#ifdef CLUTTER_ENABLE_DEBUG
|
||||
#define clutter_warn_if_paint_fail(obj) G_STMT_START { \
|
||||
@ -122,41 +135,21 @@ static GParamSpec *obj_props[PROP_LAST];
|
||||
|
||||
struct _ClutterCairoTexturePrivate
|
||||
{
|
||||
cairo_format_t format;
|
||||
|
||||
cairo_surface_t *cr_surface;
|
||||
guchar *cr_surface_data;
|
||||
gint surface_width;
|
||||
gint surface_height;
|
||||
|
||||
guint width;
|
||||
guint height;
|
||||
guint rowstride;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
gint x;
|
||||
gint y;
|
||||
guint width;
|
||||
guint height;
|
||||
} ClutterCairoTextureRectangle;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
typedef struct {
|
||||
ClutterCairoTexture *cairo;
|
||||
ClutterCairoTextureRectangle rect;
|
||||
cairo_rectangle_int_t 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,
|
||||
@ -214,105 +207,69 @@ clutter_cairo_texture_finalize (GObject *object)
|
||||
{
|
||||
ClutterCairoTexturePrivate *priv = CLUTTER_CAIRO_TEXTURE (object)->priv;
|
||||
|
||||
if (priv->cr_surface)
|
||||
if (priv->cr_surface != NULL)
|
||||
{
|
||||
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;
|
||||
cairo_surface_finish (surface);
|
||||
cairo_surface_destroy (surface);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
g_signal_emit (self, cairo_signals[CREATE_SURFACE], 0,
|
||||
priv->width,
|
||||
priv->height,
|
||||
&priv->cr_surface);
|
||||
}
|
||||
|
||||
return priv->cr_surface;
|
||||
}
|
||||
|
||||
static inline void
|
||||
clutter_cairo_texture_surface_resize_internal (ClutterCairoTexture *cairo)
|
||||
{
|
||||
ClutterCairoTexturePrivate *priv = cairo->priv;
|
||||
CoglHandle cogl_texture;
|
||||
|
||||
if (priv->cr_surface)
|
||||
if (priv->cr_surface != NULL)
|
||||
{
|
||||
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;
|
||||
/* 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);
|
||||
|
||||
if (priv->width == surface_width &&
|
||||
priv->height == surface_height)
|
||||
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);
|
||||
g_signal_emit (cairo, cairo_signals[CREATE_SURFACE], 0,
|
||||
priv->width,
|
||||
priv->height,
|
||||
&priv->cr_surface);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -375,6 +332,51 @@ clutter_cairo_texture_get_paint_volume (ClutterActor *self,
|
||||
volume);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
self->priv->surface_width = width;
|
||||
self->priv->surface_height = height;
|
||||
|
||||
/* create a backing Cogl texture */
|
||||
cogl_texture = cogl_texture_new_from_data (width, height,
|
||||
COGL_TEXTURE_NONE,
|
||||
CLUTTER_CAIRO_TEXTURE_PIXEL_FORMAT,
|
||||
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;
|
||||
}
|
||||
|
||||
static void
|
||||
clutter_cairo_texture_class_init (ClutterCairoTextureClass *klass)
|
||||
{
|
||||
@ -388,12 +390,13 @@ clutter_cairo_texture_class_init (ClutterCairoTextureClass *klass)
|
||||
|
||||
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;
|
||||
|
||||
klass->create_surface = clutter_cairo_texture_create_surface;
|
||||
|
||||
g_type_class_add_private (gobject_class, sizeof (ClutterCairoTexturePrivate));
|
||||
|
||||
/**
|
||||
@ -430,6 +433,37 @@ clutter_cairo_texture_class_init (ClutterCairoTextureClass *klass)
|
||||
_clutter_object_class_install_properties (gobject_class,
|
||||
PROP_LAST,
|
||||
obj_props);
|
||||
|
||||
/**
|
||||
* ClutterCairoTexture::create_surface:
|
||||
* @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),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -443,8 +477,9 @@ clutter_cairo_texture_init (ClutterCairoTexture *self)
|
||||
* 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;
|
||||
*/
|
||||
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,
|
||||
@ -483,14 +518,24 @@ 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;
|
||||
guint8 *cairo_data;
|
||||
gint cairo_width, cairo_height, cairo_stride;
|
||||
gint surface_width, surface_height;
|
||||
CoglHandle cogl_texture;
|
||||
|
||||
if (!priv->cr_surface)
|
||||
if (priv->cr_surface == NULL)
|
||||
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);
|
||||
|
||||
@ -498,17 +543,16 @@ clutter_cairo_texture_context_destroy (void *data)
|
||||
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);
|
||||
|
||||
g_slice_free (ClutterCairoTextureContext, ctxt);
|
||||
return;
|
||||
}
|
||||
|
||||
cairo_data = (priv->cr_surface_data
|
||||
+ (ctxt->rect.y * priv->rowstride)
|
||||
+ (ctxt->rect.x * 4));
|
||||
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,
|
||||
@ -516,18 +560,18 @@ clutter_cairo_texture_context_destroy (void *data)
|
||||
cairo_width, cairo_height,
|
||||
cairo_width, cairo_height,
|
||||
CLUTTER_CAIRO_TEXTURE_PIXEL_FORMAT,
|
||||
priv->rowstride,
|
||||
cairo_stride,
|
||||
cairo_data);
|
||||
|
||||
g_free (ctxt);
|
||||
|
||||
out:
|
||||
g_slice_free (ClutterCairoTextureContext, ctxt);
|
||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (cairo));
|
||||
}
|
||||
|
||||
static void
|
||||
intersect_rectangles (ClutterCairoTextureRectangle *a,
|
||||
ClutterCairoTextureRectangle *b,
|
||||
ClutterCairoTextureRectangle *inter)
|
||||
intersect_rectangles (cairo_rectangle_int_t *a,
|
||||
cairo_rectangle_int_t *b,
|
||||
cairo_rectangle_int_t *inter)
|
||||
{
|
||||
gint dest_x, dest_y;
|
||||
gint dest_width, dest_height;
|
||||
@ -582,7 +626,8 @@ clutter_cairo_texture_create_region (ClutterCairoTexture *self,
|
||||
{
|
||||
ClutterCairoTexturePrivate *priv;
|
||||
ClutterCairoTextureContext *ctxt;
|
||||
ClutterCairoTextureRectangle region, area, inter;
|
||||
cairo_rectangle_int_t region, area, inter;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
|
||||
g_return_val_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self), NULL);
|
||||
@ -606,10 +651,9 @@ clutter_cairo_texture_create_region (ClutterCairoTexture *self,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!priv->cr_surface)
|
||||
return NULL;
|
||||
surface = get_surface (self);
|
||||
|
||||
ctxt = g_new0 (ClutterCairoTextureContext, 1);
|
||||
ctxt = g_slice_new0 (ClutterCairoTextureContext);
|
||||
ctxt->cairo = self;
|
||||
|
||||
region.x = x_offset;
|
||||
@ -625,14 +669,12 @@ clutter_cairo_texture_create_region (ClutterCairoTexture *self,
|
||||
/* 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;
|
||||
ctxt->rect = inter;
|
||||
|
||||
cr = cairo_create (priv->cr_surface);
|
||||
cr = cairo_create (surface);
|
||||
cairo_set_user_data (cr, &clutter_cairo_texture_context_key,
|
||||
ctxt, clutter_cairo_texture_context_destroy);
|
||||
ctxt,
|
||||
clutter_cairo_texture_context_destroy);
|
||||
|
||||
return cr;
|
||||
}
|
||||
@ -775,14 +817,15 @@ clutter_cairo_texture_get_surface_size (ClutterCairoTexture *self,
|
||||
void
|
||||
clutter_cairo_texture_clear (ClutterCairoTexture *self)
|
||||
{
|
||||
ClutterCairoTexturePrivate *priv;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
|
||||
g_return_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self));
|
||||
|
||||
priv = self->priv;
|
||||
surface = get_surface (self);
|
||||
|
||||
if (!priv->cr_surface_data)
|
||||
return;
|
||||
|
||||
memset (priv->cr_surface_data, 0, priv->height * priv->rowstride);
|
||||
cr = cairo_create (surface);
|
||||
cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint (cr);
|
||||
cairo_destroy (cr);
|
||||
}
|
||||
|
@ -75,7 +75,12 @@ struct _ClutterCairoTextureClass
|
||||
/*< private >*/
|
||||
ClutterTextureClass parent_class;
|
||||
|
||||
void (*_clutter_cairo_1) (void);
|
||||
/*< public >*/
|
||||
cairo_surface_t *(* create_surface) (ClutterCairoTexture *texture,
|
||||
guint width,
|
||||
guint height);
|
||||
|
||||
/*< private >*/
|
||||
void (*_clutter_cairo_2) (void);
|
||||
void (*_clutter_cairo_3) (void);
|
||||
void (*_clutter_cairo_4) (void);
|
||||
|
@ -1,5 +1,6 @@
|
||||
BOOLEAN:BOXED
|
||||
BOOLEAN:STRING,UINT,ENUM
|
||||
BOXED:UINT,UINT
|
||||
DOUBLE:VOID
|
||||
UINT:VOID
|
||||
VOID:BOXED
|
||||
|
@ -46,7 +46,7 @@ UNIT_TESTS = \
|
||||
test-binding-pool.c \
|
||||
test-text.c \
|
||||
test-text-field.c \
|
||||
test-clutter-cairo-flowers.c \
|
||||
test-cairo-flowers.c \
|
||||
test-cogl-vertex-buffer.c \
|
||||
test-bin-layout.c \
|
||||
test-flow-layout.c \
|
||||
|
@ -169,7 +169,7 @@ tick (ClutterTimeline *timeline,
|
||||
}
|
||||
|
||||
int
|
||||
test_clutter_cairo_flowers_main (int argc, char **argv)
|
||||
test_cairo_flowers_main (int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
ClutterActor *stage;
|
||||
@ -184,7 +184,6 @@ test_clutter_cairo_flowers_main (int argc, char **argv)
|
||||
stage = clutter_stage_get_default ();
|
||||
|
||||
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
|
||||
clutter_stage_set_fullscreen (CLUTTER_STAGE (stage), TRUE);
|
||||
|
||||
for (i=0; i< N_FLOWERS; i++)
|
||||
{
|
Loading…
Reference in New Issue
Block a user