From 06fe42dde9f98e5156d44f76842005de4e2c625b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Kol=C3=A5s?= Date: Thu, 20 Dec 2007 14:26:07 +0000 Subject: [PATCH] * clutter/clutter-texture.[ch]: (bug #675) added clutter_texture_set_area_from_rgb_data, sharing some of the initialization infrastructure with clutter_texture_set_from_rgb_data. --- ChangeLog | 6 + clutter/clutter-texture.c | 376 +++++++++++++++++++++++++++++++++----- clutter/clutter-texture.h | 15 +- 3 files changed, 354 insertions(+), 43 deletions(-) diff --git a/ChangeLog b/ChangeLog index a8aba81b1..c964a7d2c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2007-12-19 Øyvind Kolås + + * clutter/clutter-texture.[ch]: (bug #675) added + clutter_texture_set_area_from_rgb_data, sharing some of the + initialization infrastructure with clutter_texture_set_from_rgb_data. + 2007-12-20 Emmanuele Bassi * clutter/clutter-container.c: diff --git a/clutter/clutter-texture.c b/clutter/clutter-texture.c index 5263612bf..09e9087fc 100644 --- a/clutter/clutter-texture.c +++ b/clutter/clutter-texture.c @@ -426,14 +426,14 @@ texture_free_gl_resources (ClutterTexture *texture) } } -static void +static void inline texture_upload_data (ClutterTexture *texture, - const guchar *data, - gboolean has_alpha, - gint width, - gint height, - gint rowstride, - gint bpp) + const guchar *data, + gboolean has_alpha, + gint width, + gint height, + gint rowstride, + gint bpp) { ClutterTexturePrivate *priv; gint x, y; @@ -1247,6 +1247,79 @@ clutter_texture_get_pixbuf (ClutterTexture* texture) #endif } +/* internal helper function for initialization of + * clutter_texture_set_from_rgb_data and clutter_texture_set_area_from_rgb_data. + */ +static inline gboolean +texture_prepare_upload (gboolean initialize, + ClutterTexture *texture, + const guchar *data, + gboolean has_alpha, + gint width, + gint height, + gint rowstride, + gint bpp, + ClutterTextureFlags flags, + guchar **copy_data, + gboolean *texture_dirty, + gboolean *size_change) +{ + ClutterTexturePrivate *priv; + COGLenum prev_format; + priv = texture->priv; + + g_return_val_if_fail (data != NULL, FALSE); + /* Needed for GL_RGBA (internal format) and gdk pixbuf usage */ + g_return_val_if_fail (bpp == 4, FALSE); + + if (initialize) + { + *texture_dirty = *size_change = + (width != priv->width || height != priv->height) ; + } + + prev_format = priv->pixel_format; + + if (has_alpha) + priv->pixel_format = CGL_RGBA; + else + priv->pixel_format = CGL_RGB; + + if (flags & CLUTTER_TEXTURE_RGB_FLAG_BGR) + { +#if HAVE_COGL_GL + if (has_alpha) + priv->pixel_format = CGL_BGRA; + else + priv->pixel_format = CGL_BGR; +#else + /* GLES has no BGR format*/ + *copy_data = rgb_to_bgr (data, has_alpha, width, height, rowstride); +#endif /* HAVE_COGL_GL */ + } + + if (flags & CLUTTER_TEXTURE_RGB_FLAG_PREMULT) + *copy_data = un_pre_multiply_alpha (data, width, height, rowstride); + + if (initialize) + { + if (prev_format != priv->pixel_format || priv->pixel_type != PIXEL_TYPE) + *texture_dirty = TRUE; + } + else + { + if (prev_format != priv->pixel_format || priv->pixel_type != PIXEL_TYPE) + { + g_warning ("%s: pixel format or type mismatch", G_STRFUNC); + return FALSE; + } + } + + priv->pixel_type = PIXEL_TYPE; + + return TRUE; +} + /** * clutter_texture_set_from_rgb_data: * @texture: A #ClutterTexture @@ -1277,47 +1350,16 @@ clutter_texture_set_from_rgb_data (ClutterTexture *texture, GError **error) { ClutterTexturePrivate *priv; - gboolean texture_dirty = TRUE, size_change = FALSE; - COGLenum prev_format; guchar *copy_data = NULL; + gboolean texture_dirty = TRUE, size_change = FALSE; priv = texture->priv; - - g_return_val_if_fail (data != NULL, FALSE); - - /* Needed for GL_RGBA (internal format) and gdk pixbuf usage */ - g_return_val_if_fail (bpp == 4, FALSE); - - texture_dirty = size_change = - (width != priv->width || height != priv->height) ; - - prev_format = priv->pixel_format; - - if (has_alpha) - priv->pixel_format = CGL_RGBA; - else - priv->pixel_format = CGL_RGB; - - if (flags & CLUTTER_TEXTURE_RGB_FLAG_BGR) + if (!texture_prepare_upload (TRUE, texture, data, has_alpha, width, height, rowstride, + bpp, flags, ©_data, &texture_dirty, &size_change)) { -#if HAVE_COGL_GL - if (has_alpha) - priv->pixel_format = CGL_BGRA; - else - priv->pixel_format = CGL_BGR; -#else - /* GLES has no BGR format*/ - copy_data = rgb_to_bgr (data, has_alpha, width, height, rowstride); -#endif /* HAVE_COGL_GL */ + return FALSE; } - if (flags & CLUTTER_TEXTURE_RGB_FLAG_PREMULT) - copy_data = un_pre_multiply_alpha (data, width, height, rowstride); - - if (prev_format != priv->pixel_format || priv->pixel_type != PIXEL_TYPE) - texture_dirty = TRUE; - - priv->pixel_type = PIXEL_TYPE; priv->width = width; priv->height = height; @@ -1781,3 +1823,253 @@ clutter_texture_is_tiled (ClutterTexture *texture) return texture->priv->is_tiled; } + + +static void inline +texture_update_data (ClutterTexture *texture, + const guchar *data, + gboolean has_alpha, + gint x0, + gint y0, + gint width, + gint height, + gint rowstride, + gint bpp) +{ + ClutterTexturePrivate *priv; + gint x, y; + gint i = 0; + gboolean create_textures = FALSE; + + priv = texture->priv; + + g_return_if_fail (data != NULL); + + CLUTTER_MARK(); + + if (!priv->is_tiled) + { + g_assert (priv->tiles); + + CLUTTER_NOTE (TEXTURE, "syncing for single tile"); + + cogl_texture_bind (priv->target_type, priv->tiles[0]); + cogl_texture_set_alignment (priv->target_type, 4, width); + + cogl_texture_set_filters + (priv->target_type, + priv->filter_quality ? CGL_LINEAR : CGL_NEAREST, + priv->filter_quality ? CGL_LINEAR : CGL_NEAREST); + + cogl_texture_set_wrap (priv->target_type, + priv->repeat_x ? CGL_REPEAT : CGL_CLAMP_TO_EDGE, + priv->repeat_y ? CGL_REPEAT : CGL_CLAMP_TO_EDGE); + + priv->filter_quality = 1; + + cogl_texture_sub_image_2d (priv->target_type, + x0, + y0, + width, + height, + priv->pixel_format, + priv->pixel_type, + data); + return; + } + + /* Multiple tiled texture */ + + CLUTTER_NOTE (TEXTURE, + "syncing for multiple tiles for %ix%i pixbuf", + priv->width, priv->height); + + g_return_if_fail (priv->x_tiles != NULL && priv->y_tiles != NULL); + + if (priv->tiles == NULL) + { + g_assert (0); + priv->tiles = g_new (COGLuint, priv->n_x_tiles * priv->n_y_tiles); + glGenTextures (priv->n_x_tiles * priv->n_y_tiles, priv->tiles); + create_textures = TRUE; + } + + for (x = 0; x < priv->n_x_tiles; x++) + for (y = 0; y < priv->n_y_tiles; y++) + { + gint master_offset_x; + gint effective_x; + gint effective_width; + + gint master_offset_y; + gint effective_y; + gint effective_height; + + +/* +-- first tile -- +|--------------------- priv->width ------------------------------| +| <- priv->x_tiles[x].pos +|-----------| <- priv->x_tiles[x].size +|-------| <- x0 + |------------| <- width +|--------------------| <- x0 + width +|-------| <- master_offset = -8 +|-------| <- effective_x = 8 + |---| <- effective_width + +-- second tile --- + +|--------------------- priv->width ------------------------------| +|-----------| <- priv->x_tiles[x].pos + |-----------| <- priv->x_tiles[x].size (src_w) +|-------| <- x0 + |------------| <- width +|--------------------| <- x0 + width + |---| <- master_offset = 4 + | <- effective_x (0 in between) + |--------| <- effective_width + + + XXXXXXXXXXXXXX <- master +|___________|___________|___________|___________|___________|_____%%%%%%| +*/ + + gint src_w, src_h; + + src_w = priv->x_tiles[x].size; + src_h = priv->y_tiles[y].size; + + /* skip tiles that do not intersect the updated region */ + if ((priv->x_tiles[x].pos + src_w < x0 || + priv->y_tiles[y].pos + src_h < y0 || + priv->x_tiles[x].pos >= x0 + width || + priv->y_tiles[y].pos >= y0 + height)) + { + i++; + continue; + } + + master_offset_x = priv->x_tiles[x].pos - x0; + + if (priv->x_tiles[x].pos > x0) + effective_x = 0; + else + effective_x = x0 - priv->x_tiles[x].pos; + + effective_width = (x0 + width) - priv->x_tiles[x].pos; + + if (effective_width > src_w - effective_x) + effective_width = src_w - effective_x; + + master_offset_y = priv->y_tiles[y].pos - y0; + + if (priv->y_tiles[y].pos > y0) + effective_y = 0; + else + effective_y = y0 - priv->y_tiles[y].pos; + + effective_height = (y0 + height) - priv->y_tiles[y].pos; + if (effective_height > src_h - effective_y) + effective_height = src_h - effective_y; + + if (master_offset_x < 0) + master_offset_x = 0; + if (master_offset_y < 0) + master_offset_y = 0; + + if (master_offset_x + effective_width > width) + effective_width = width - master_offset_x; + if (master_offset_y + effective_height > height) + effective_height = height - master_offset_y; + + cogl_texture_bind (priv->target_type, priv->tiles[i]); + + cogl_texture_set_alignment (priv->target_type, 4, rowstride/4); + + cogl_texture_set_filters + (priv->target_type, + priv->filter_quality ? CGL_LINEAR : CGL_NEAREST, + priv->filter_quality ? CGL_LINEAR : CGL_NEAREST); + + cogl_texture_set_wrap (priv->target_type, + priv->repeat_x ? CGL_REPEAT : CGL_CLAMP_TO_EDGE, + priv->repeat_y ? CGL_REPEAT : CGL_CLAMP_TO_EDGE); + cogl_texture_sub_image_2d (priv->target_type, + effective_x, + effective_y, + effective_width, + effective_height, + priv->pixel_format, + priv->pixel_type, + data + (master_offset_y * width + master_offset_x) * 4); + + i++; + } +} + +/** + * clutter_texture_set_area_from_rgb_data: + * @texture: A #ClutterTexture + * @data: Image data in RGB type colorspace. + * @has_alpha: Set to TRUE if image data has a alpha channel. + * @x: X coordinate of upper left corner of region to update. + * @y: Y coordinate of upper left corner of region to update. + * @width: Width in pixels of region to update. + * @height: Height in pixels of region to update. + * @rowstride: Distance in bytes between row starts. + * @bpp: bytes per pixel ( Currently only 4 supported ) + * @flags: #ClutterTextureFlags + * @error: FIXME. + * + * Updates a subregion of the pixel data in a #ClutterTexture. + * + * Return value: TRUE on success, FALSE on failure. + * + * Since 0.6. + **/ +gboolean +clutter_texture_set_area_from_rgb_data (ClutterTexture *texture, + const guchar *data, + gboolean has_alpha, + gint x, + gint y, + gint width, + gint height, + gint rowstride, + gint bpp, + ClutterTextureFlags flags, + GError **error) +{ + ClutterTexturePrivate *priv; + guchar *copy_data = NULL; + + priv = texture->priv; + + if (!texture_prepare_upload (FALSE, texture, data, has_alpha, width, height, rowstride, + bpp, flags, ©_data, NULL, NULL)) + { + return FALSE; + } + + texture_update_data (texture, + copy_data != NULL ? copy_data : data, + has_alpha, + x, y, + width, height, + rowstride, + bpp); + + CLUTTER_ACTOR_SET_FLAGS (CLUTTER_ACTOR (texture), CLUTTER_ACTOR_REALIZED); + + /* rename signal */ + g_signal_emit (texture, texture_signals[PIXBUF_CHANGE], 0); + + if (CLUTTER_ACTOR_IS_MAPPED (CLUTTER_ACTOR(texture))) + clutter_actor_queue_redraw (CLUTTER_ACTOR(texture)); + + if (copy_data != NULL) + g_free (copy_data); + + return TRUE; +} diff --git a/clutter/clutter-texture.h b/clutter/clutter-texture.h index b40305a3b..9ce9a0739 100644 --- a/clutter/clutter-texture.h +++ b/clutter/clutter-texture.h @@ -94,7 +94,7 @@ struct _ClutterTextureClass * @CLUTTER_TEXTURE_YUV_FLAG_YUV2: FIXME * * Flags for clutter_texture_set_from_rgb_data() and - * clutter_texture_set_from_rgb_data(). + * clutter_texture_set_from_yuv_data(). * * Since: 0.4 */ @@ -119,6 +119,19 @@ gboolean clutter_texture_set_from_rgb_data (ClutterTexture *texture, gint bpp, ClutterTextureFlags flags, GError **error); + +gboolean clutter_texture_set_area_from_rgb_data (ClutterTexture *texture, + const guchar *data, + gboolean has_alpha, + gint x, + gint y, + gint width, + gint height, + gint rowstride, + gint bpp, + ClutterTextureFlags flags, + GError **error); + gboolean clutter_texture_set_from_yuv_data (ClutterTexture *texture, const guchar *data, gint width,