diff --git a/clutter/clutter-cairo-texture.c b/clutter/clutter-cairo-texture.c index e7ae1df84..88cf5d8c4 100644 --- a/clutter/clutter-cairo-texture.c +++ b/clutter/clutter-cairo-texture.c @@ -81,8 +81,6 @@ #include "clutter-cairo-texture.h" #include "clutter-debug.h" #include "clutter-private.h" -#include "cogl/cogl-pixel-buffer.h" -#include "cogl/cogl-buffer.h" G_DEFINE_TYPE (ClutterCairoTexture, clutter_cairo_texture, @@ -120,15 +118,14 @@ enum struct _ClutterCairoTexturePrivate { - cairo_format_t format; + cairo_format_t format; - CoglHandle buffer; - guint buffer_width; - guint buffer_height; + cairo_surface_t *cr_surface; + guchar *cr_surface_data; - guint width; - guint height; - guint rowstride; + guint width; + guint height; + guint rowstride; }; typedef struct @@ -146,6 +143,15 @@ typedef struct } 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, @@ -204,10 +210,23 @@ clutter_cairo_texture_finalize (GObject *object) { ClutterCairoTexturePrivate *priv = CLUTTER_CAIRO_TEXTURE (object)->priv; - if (priv->buffer) + if (priv->cr_surface) { - cogl_handle_unref (priv->buffer); - priv->buffer = 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; } G_OBJECT_CLASS (clutter_cairo_texture_parent_class)->finalize (object); @@ -217,30 +236,80 @@ static inline void clutter_cairo_texture_surface_resize_internal (ClutterCairoTexture *cairo) { ClutterCairoTexturePrivate *priv = cairo->priv; + CoglHandle cogl_texture; - if (priv->buffer) + if (priv->cr_surface) { - /* If the buffer is already the right size then don't bother + cairo_surface_t *surface = priv->cr_surface; + + /* If the surface is already the right size then don't bother doing anything */ - if (priv->buffer_width == priv->width && - priv->buffer_height == priv->height) + if (priv->width == cairo_image_surface_get_width (priv->cr_surface) + && priv->height == cairo_image_surface_get_height (priv->cr_surface)) return; - cogl_handle_unref (priv->buffer); - priv->buffer = COGL_INVALID_HANDLE; + 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; - priv->buffer = - cogl_pixel_buffer_new_for_size (priv->width, - priv->height, - CLUTTER_CAIRO_TEXTURE_PIXEL_FORMAT, - &priv->rowstride); +#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; - priv->buffer_width = priv->width; - priv->buffer_height = priv->height; + 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 @@ -252,8 +321,8 @@ clutter_cairo_texture_notify (GObject *object, 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) || - !strcmp ("surface-height", pspec->name)) + if (!strcmp ("surface-width", pspec->name) + || !strcmp ("surface-height", pspec->name)) { ClutterCairoTexture *cairo = CLUTTER_CAIRO_TEXTURE (object); @@ -384,31 +453,46 @@ clutter_cairo_texture_new (guint width, } static void -clutter_cairo_texture_surface_destroy (void *data) +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->buffer) + if (!priv->cr_surface) return; - cogl_buffer_unmap (priv->buffer); + surface_width = cairo_image_surface_get_width (priv->cr_surface); + surface_height = cairo_image_surface_get_height (priv->cr_surface); - cogl_texture = - cogl_texture_new_from_buffer (priv->buffer, - priv->buffer_width, - priv->buffer_height, - COGL_TEXTURE_NONE, - CLUTTER_CAIRO_TEXTURE_PIXEL_FORMAT, - COGL_PIXEL_FORMAT_ANY, - priv->rowstride, - 0); + cairo_width = MIN (ctxt->rect.width, surface_width); + cairo_height = MIN (ctxt->rect.height, surface_height); - clutter_texture_set_cogl_texture (CLUTTER_TEXTURE (cairo), cogl_texture); + cogl_texture = clutter_texture_get_cogl_texture (CLUTTER_TEXTURE (cairo)); - cogl_handle_unref (cogl_texture); + 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); @@ -475,8 +559,6 @@ clutter_cairo_texture_create_region (ClutterCairoTexture *self, ClutterCairoTextureContext *ctxt; ClutterCairoTextureRectangle region, area, inter; cairo_t *cr; - cairo_surface_t *surface; - CoglHandle material; g_return_val_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self), NULL); @@ -499,7 +581,7 @@ clutter_cairo_texture_create_region (ClutterCairoTexture *self, return NULL; } - if (!priv->buffer) + if (!priv->cr_surface) return NULL; ctxt = g_new0 (ClutterCairoTextureContext, 1); @@ -523,30 +605,9 @@ clutter_cairo_texture_create_region (ClutterCairoTexture *self, ctxt->rect.width = inter.width; ctxt->rect.height = inter.height; - /* Destroy the existing texture so that the GL driver won't have to - copy it when we map the PBO */ - if ((material = clutter_texture_get_cogl_material (CLUTTER_TEXTURE (self)))) - cogl_material_set_layer (material, 0, COGL_INVALID_HANDLE); - - /* Create a surface to render directly to the PBO */ - surface = - cairo_image_surface_create_for_data (cogl_buffer_map (priv->buffer, - COGL_BUFFER_ACCESS_READ | - COGL_BUFFER_ACCESS_WRITE), - priv->format, - priv->buffer_width, - priv->buffer_height, - priv->rowstride); - - /* Set a key so that we can recreate the texture when the surface is - destroyed */ - cairo_surface_set_user_data (surface, &clutter_cairo_texture_surface_key, - ctxt, clutter_cairo_texture_surface_destroy); - - cr = cairo_create (surface); - /* Remove the reference we have on the surface so that it will be - destroyed when the context is destroyed */ - cairo_surface_destroy (surface); + cr = cairo_create (priv->cr_surface); + cairo_set_user_data (cr, &clutter_cairo_texture_context_key, + ctxt, clutter_cairo_texture_context_destroy); return cr; } @@ -690,16 +751,13 @@ void clutter_cairo_texture_clear (ClutterCairoTexture *self) { ClutterCairoTexturePrivate *priv; - guint8 *data; g_return_if_fail (CLUTTER_IS_CAIRO_TEXTURE (self)); priv = self->priv; - if (!priv->buffer) + if (!priv->cr_surface_data) return; - data = cogl_buffer_map (priv->buffer, COGL_BUFFER_ACCESS_WRITE); - memset (data, 0, priv->buffer_height * priv->rowstride); - cogl_buffer_unmap (priv->buffer); + memset (priv->cr_surface_data, 0, priv->height * priv->rowstride); }