diff --git a/cogl/Makefile.am b/cogl/Makefile.am index 59121ab09..0b2417641 100644 --- a/cogl/Makefile.am +++ b/cogl/Makefile.am @@ -112,11 +112,19 @@ cogl_sources_c = \ $(srcdir)/cogl-blend-string.c \ $(srcdir)/cogl-blend-string.h \ $(srcdir)/cogl-debug.c \ + $(srcdir)/cogl-sub-texture-private.h \ $(srcdir)/cogl-texture-private.h \ + $(srcdir)/cogl-texture-2d-private.h \ $(srcdir)/cogl-texture-2d-sliced-private.h \ $(srcdir)/cogl-texture-driver.h \ + $(srcdir)/cogl-sub-texture.c \ $(srcdir)/cogl-texture.c \ + $(srcdir)/cogl-texture-2d.c \ $(srcdir)/cogl-texture-2d-sliced.c \ + $(srcdir)/cogl-atlas.h \ + $(srcdir)/cogl-atlas.c \ + $(srcdir)/cogl-atlas-texture-private.h \ + $(srcdir)/cogl-atlas-texture.c \ $(srcdir)/cogl-spans.h \ $(srcdir)/cogl-spans.c \ $(srcdir)/cogl-journal-private.h \ diff --git a/cogl/cogl-atlas-texture-private.h b/cogl/cogl-atlas-texture-private.h new file mode 100644 index 000000000..de0fcdb7f --- /dev/null +++ b/cogl/cogl-atlas-texture-private.h @@ -0,0 +1,64 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_ATLAS_TEXTURE_H +#define __COGL_ATLAS_TEXTURE_H + +#include "cogl-handle.h" +#include "cogl-texture-private.h" +#include "cogl-atlas.h" + +#define COGL_ATLAS_TEXTURE(tex) ((CoglAtlasTexture *) tex) + +typedef struct _CoglAtlasTexture CoglAtlasTexture; + +struct _CoglAtlasTexture +{ + CoglTexture _parent; + + /* The format that the texture is in. This isn't necessarily the + same format as the atlas texture because we can store + pre-multiplied and non-pre-multiplied textures together */ + CoglPixelFormat format; + + /* The rectangle that was used to add this texture to the + atlas. This includes the 1-pixel border */ + CoglAtlasRectangle rectangle; + + /* The texture might need to be migrated out in which case this will + be set to TRUE and sub_texture will actually be a real texture */ + gboolean in_atlas; + + /* A CoglSubTexture representing the region for easy rendering */ + CoglHandle sub_texture; +}; + +GQuark +_cogl_handle_atlas_texture_get_type (void); + +CoglHandle +_cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, + CoglTextureFlags flags, + CoglPixelFormat internal_format); + +#endif /* __COGL_ATLAS_TEXTURE_H */ diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c new file mode 100644 index 000000000..f84594762 --- /dev/null +++ b/cogl/cogl-atlas-texture.c @@ -0,0 +1,1045 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-texture-private.h" +#include "cogl-atlas-texture-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-sub-texture-private.h" +#include "cogl-context.h" +#include "cogl-handle.h" +#include "cogl-texture-driver.h" +#include "cogl-atlas.h" + +#include + +#ifdef HAVE_COGL_GLES2 + +#include "../gles/cogl-gles2-wrapper.h" + +#else /* HAVE_COGL_GLES2 */ + +#define glGenFramebuffers ctx->drv.pf_glGenFramebuffers +#define glBindFramebuffer ctx->drv.pf_glBindFramebuffer +#define glFramebufferTexture2D ctx->drv.pf_glFramebufferTexture2D +#define glCheckFramebufferStatus ctx->drv.pf_glCheckFramebufferStatus +#define glDeleteFramebuffers ctx->drv.pf_glDeleteFramebuffers + +#endif /* HAVE_COGL_GLES2 */ + +#ifndef GL_FRAMEBUFFER +#define GL_FRAMEBUFFER 0x8D40 +#endif +#ifndef GL_FRAMEBUFFER_BINDING +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#endif +#ifndef GL_COLOR_ATTACHMENT0 +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#endif +#ifndef GL_FRAMEBUFFER_COMPLETE +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#endif + +static void _cogl_atlas_texture_free (CoglAtlasTexture *sub_tex); + +COGL_HANDLE_DEFINE (AtlasTexture, atlas_texture); + +static const CoglTextureVtable cogl_atlas_texture_vtable; + +/* If we want to do mulitple blits from a texture (such as when + reorganizing the atlas) then it's quicker to download all of the + data once and upload multiple times from that. This struct is used + to keep the image data for a series of blits */ + +typedef struct _CoglAtlasTextureBlitData +{ + CoglHandle src_tex, dst_tex; + + /* If we're using an FBO to blit, then FBO will be non-zero and + old_fbo will be the previous framebuffer binding */ + GLuint fbo, old_fbo; + + /* If we're not using an FBO then we g_malloc a buffer and copy the + complete texture data in */ + unsigned char *image_data; + CoglPixelFormat format; + gint bpp; + guint src_height, src_width; + + GLenum dst_gl_target; +} CoglAtlasTextureBlitData; + +static void +_cogl_atlas_texture_blit_begin (CoglAtlasTextureBlitData *data, + CoglHandle dst_tex, + CoglHandle src_tex) +{ + GLenum src_gl_target; + GLuint src_gl_texture; + GLuint dst_gl_texture; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + data->dst_tex = dst_tex; + data->src_tex = src_tex; + data->fbo = 0; + + /* If we can use an FBO then we don't need to download the data and + we can tell GL to blit directly between the textures */ + if (cogl_features_available (COGL_FEATURE_OFFSCREEN) && + !cogl_texture_is_sliced (dst_tex) && + cogl_texture_get_gl_texture (src_tex, &src_gl_texture, &src_gl_target) && + cogl_texture_get_gl_texture (dst_tex, &dst_gl_texture, + &data->dst_gl_target)) + { + /* Ideally we would use the cogl-offscreen API here, but I'd + rather avoid creating a stencil renderbuffer which you can't + currently do */ + /* Preserve the previous framebuffer binding so we don't trample + on cogl-offscreen */ + data->old_fbo = 0; + GE( glGetIntegerv (GL_FRAMEBUFFER_BINDING, (GLint *) &data->old_fbo) ); + + _cogl_texture_set_filters (src_tex, GL_NEAREST, GL_NEAREST); + + /* Create an FBO to read from the src texture */ + GE( glGenFramebuffers (1, &data->fbo) ); + GE( glBindFramebuffer (GL_FRAMEBUFFER, data->fbo) ); + GE( glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + src_gl_target, src_gl_texture, 0) ); + if (glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + { + /* The FBO failed for whatever reason so we'll fallback to + reading the texture data */ + GE( glBindFramebuffer (GL_FRAMEBUFFER, data->old_fbo) ); + GE( glDeleteFramebuffers (1, &data->fbo) ); + data->fbo = 0; + } + + GE( glBindTexture (data->dst_gl_target, dst_gl_texture) ); + } + + if (data->fbo) + COGL_NOTE (ATLAS, "Blit set up using an FBO"); + else + { + /* We need to retrieve the entire texture data (there is no + glGetTexSubImage2D) */ + + data->format = cogl_texture_get_format (src_tex); + data->bpp = _cogl_get_format_bpp (data->format); + data->src_width = cogl_texture_get_width (src_tex); + data->src_height = cogl_texture_get_height (src_tex); + + data->image_data = g_malloc (data->bpp * data->src_width * + data->src_height); + cogl_texture_get_data (src_tex, data->format, + data->src_width * data->bpp, data->image_data); + } +} + +static void +_cogl_atlas_texture_blit (CoglAtlasTextureBlitData *data, + guint src_x, + guint src_y, + guint dst_x, + guint dst_y, + guint width, + guint height) +{ + /* If we have an FBO then we can do a fast blit */ + if (data->fbo) + GE( glCopyTexSubImage2D (data->dst_gl_target, 0, dst_x, dst_y, src_x, src_y, + width, height) ); + else + cogl_texture_set_region (data->dst_tex, + src_x, src_y, + dst_x, dst_y, + width, height, + data->src_width, data->src_height, + data->format, + data->src_width * data->bpp, + data->image_data); +} + +static void +_cogl_atlas_texture_blit_end (CoglAtlasTextureBlitData *data) +{ + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + if (data->fbo) + { + GE( glBindFramebuffer (GL_FRAMEBUFFER, data->old_fbo) ); + GE( glDeleteFramebuffers (1, &data->fbo) ); + } + else + g_free (data->image_data); +} + +static void +_cogl_atlas_texture_foreach_sub_texture_in_region ( + CoglTexture *tex, + float virtual_tx_1, + float virtual_ty_1, + float virtual_tx_2, + float virtual_ty_2, + CoglTextureSliceCallback callback, + void *user_data) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + _cogl_texture_foreach_sub_texture_in_region (atlas_tex->sub_texture, + virtual_tx_1, + virtual_ty_1, + virtual_tx_2, + virtual_ty_2, + callback, + user_data); +} + +static void +_cogl_atlas_texture_set_wrap_mode_parameter (CoglTexture *tex, + GLenum wrap_mode) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + _cogl_texture_set_wrap_mode_parameter (atlas_tex->sub_texture, wrap_mode); +} + +static void +_cogl_atlas_texture_remove_from_atlas (CoglAtlasTexture *atlas_tex) +{ + if (atlas_tex->in_atlas) + { + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + _cogl_atlas_remove_rectangle (ctx->atlas, &atlas_tex->rectangle); + + COGL_NOTE (ATLAS, "Removed rectangle sized %ix%i", + atlas_tex->rectangle.width, + atlas_tex->rectangle.height); + COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", + _cogl_atlas_get_width (ctx->atlas), + _cogl_atlas_get_height (ctx->atlas), + _cogl_atlas_get_n_rectangles (ctx->atlas), + _cogl_atlas_get_remaining_space (ctx->atlas) * 100 / + (_cogl_atlas_get_width (ctx->atlas) * + _cogl_atlas_get_height (ctx->atlas))); + + atlas_tex->in_atlas = FALSE; + } +} + +static void +_cogl_atlas_texture_free (CoglAtlasTexture *atlas_tex) +{ + _cogl_atlas_texture_remove_from_atlas (atlas_tex); + + cogl_handle_unref (atlas_tex->sub_texture); +} + +static gint +_cogl_atlas_texture_get_max_waste (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return cogl_texture_get_max_waste (atlas_tex->sub_texture); +} + +static gboolean +_cogl_atlas_texture_is_sliced (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return cogl_texture_is_sliced (atlas_tex->sub_texture); +} + +static gboolean +_cogl_atlas_texture_can_hardware_repeat (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return _cogl_texture_can_hardware_repeat (atlas_tex->sub_texture); +} + +static void +_cogl_atlas_texture_transform_coords_to_gl (CoglTexture *tex, + float *s, + float *t) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + _cogl_texture_transform_coords_to_gl (atlas_tex->sub_texture, s, t); +} + +static gboolean +_cogl_atlas_texture_transform_quad_coords_to_gl (CoglTexture *tex, + float *coords) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return _cogl_texture_transform_quad_coords_to_gl (atlas_tex->sub_texture, + coords); +} + +static gboolean +_cogl_atlas_texture_get_gl_texture (CoglTexture *tex, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return cogl_texture_get_gl_texture (atlas_tex->sub_texture, + out_gl_handle, + out_gl_target); +} + +static void +_cogl_atlas_texture_set_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + _cogl_texture_set_filters (atlas_tex->sub_texture, min_filter, mag_filter); +} + +static void +_cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex) +{ + /* Make sure this texture is not in the atlas */ + if (atlas_tex->in_atlas) + { + CoglAtlasTextureBlitData blit_data; + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + COGL_NOTE (ATLAS, "Migrating texture out of the atlas"); + + cogl_handle_unref (atlas_tex->sub_texture); + + /* Create a new texture at the right size, not including the + border */ + atlas_tex->sub_texture = + cogl_texture_new_with_size (atlas_tex->rectangle.width - 2, + atlas_tex->rectangle.height - 2, + COGL_TEXTURE_NO_ATLAS, + atlas_tex->format); + + /* Blit the data out of the atlas to the new texture. If FBOs + aren't available this will end up having to copy the entire + atlas texture */ + _cogl_atlas_texture_blit_begin (&blit_data, atlas_tex->sub_texture, + ctx->atlas_texture); + _cogl_atlas_texture_blit (&blit_data, + atlas_tex->rectangle.x + 1, + atlas_tex->rectangle.y + 1, + 0, 0, + atlas_tex->rectangle.width - 2, + atlas_tex->rectangle.height - 2); + _cogl_atlas_texture_blit_end (&blit_data); + + _cogl_atlas_texture_remove_from_atlas (atlas_tex); + } +} + +static void +_cogl_atlas_texture_ensure_mipmaps (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Mipmaps do not work well with the current atlas so instead we'll + just migrate the texture out and use a regular texture */ + _cogl_atlas_texture_migrate_out_of_atlas (atlas_tex); + + /* Forward on to the sub texture */ + _cogl_texture_ensure_mipmaps (atlas_tex->sub_texture); +} + +static void +_cogl_atlas_texture_ensure_non_quad_rendering (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Sub textures can't support non-quad rendering so we'll just + migrate the texture out */ + _cogl_atlas_texture_migrate_out_of_atlas (atlas_tex); + + /* Forward on to the sub texture */ + _cogl_texture_ensure_non_quad_rendering (atlas_tex->sub_texture); +} + +static gboolean +_cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int dst_width, + unsigned int dst_height, + int width, + int height, + CoglPixelFormat format, + unsigned int rowstride, + const guint8 *data) +{ + _COGL_GET_CONTEXT (ctx, FALSE); + + /* Copy the central data */ + if (!cogl_texture_set_region (ctx->atlas_texture, + src_x, src_y, + dst_x + atlas_tex->rectangle.x + 1, + dst_y + atlas_tex->rectangle.y + 1, + dst_width, + dst_height, + width, height, + format, + rowstride, + data)) + return FALSE; + + /* Update the left edge pixels */ + if (dst_x == 0 && + !cogl_texture_set_region (ctx->atlas_texture, + src_x, src_y, + atlas_tex->rectangle.x, + dst_y + atlas_tex->rectangle.y + 1, + 1, dst_height, + width, height, + format, rowstride, + data)) + return FALSE; + /* Update the right edge pixels */ + if (dst_x + dst_width == atlas_tex->rectangle.width - 2 && + !cogl_texture_set_region (ctx->atlas_texture, + src_x + dst_width - 1, src_y, + atlas_tex->rectangle.x + + atlas_tex->rectangle.width - 1, + dst_y + atlas_tex->rectangle.y + 1, + 1, dst_height, + width, height, + format, rowstride, + data)) + return FALSE; + /* Update the top edge pixels */ + if (dst_y == 0 && + !cogl_texture_set_region (ctx->atlas_texture, + src_x, src_y, + dst_x + atlas_tex->rectangle.x + 1, + atlas_tex->rectangle.y, + dst_width, 1, + width, height, + format, rowstride, + data)) + return FALSE; + /* Update the bottom edge pixels */ + if (dst_y + dst_height == atlas_tex->rectangle.height - 2 && + !cogl_texture_set_region (ctx->atlas_texture, + src_x, src_y + dst_height - 1, + dst_x + atlas_tex->rectangle.x + 1, + atlas_tex->rectangle.y + + atlas_tex->rectangle.height - 1, + dst_width, 1, + width, height, + format, rowstride, + data)) + return FALSE; + + return TRUE; +} + +static gboolean +_cogl_atlas_texture_set_region (CoglTexture *tex, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int dst_width, + unsigned int dst_height, + int width, + int height, + CoglPixelFormat format, + unsigned int rowstride, + const guint8 *data) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* If the texture is in the atlas then we need to copy the edge + pixels to the border */ + if (atlas_tex->in_atlas) + { + gint bpp; + CoglBitmap source_bmp; + CoglBitmap tmp_bmp; + gboolean tmp_bmp_owner = FALSE; + gboolean success; + + /* Check for valid format */ + if (format == COGL_PIXEL_FORMAT_ANY) + return FALSE; + + /* Shortcut out early if the image is empty */ + if (width == 0 || height == 0) + return TRUE; + + /* Init source bitmap */ + source_bmp.width = width; + source_bmp.height = height; + source_bmp.format = format; + source_bmp.data = (guchar*) data; + + /* Rowstride from width if none specified */ + bpp = _cogl_get_format_bpp (format); + source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride; + + /* Prepare the bitmap so that it will do the premultiplication + conversion */ + _cogl_texture_prepare_for_upload (&source_bmp, + atlas_tex->format, + NULL, + &tmp_bmp, + &tmp_bmp_owner, + NULL, NULL, NULL); + + /* Upload the data ignoring the premult bit */ + success = + _cogl_atlas_texture_set_region_with_border (atlas_tex, + src_x, src_y, + dst_x, dst_y, + dst_width, dst_height, + tmp_bmp.width, + tmp_bmp.height, + tmp_bmp.format & + ~COGL_PREMULT_BIT, + tmp_bmp.rowstride, + tmp_bmp.data); + + /* Free data if owner */ + if (tmp_bmp_owner) + g_free (tmp_bmp.data); + + return success; + } + else + /* Otherwise we can just forward on to the sub texture */ + return cogl_texture_set_region (atlas_tex->sub_texture, + src_x, src_y, + dst_x, dst_y, + dst_width, dst_height, + width, height, + format, rowstride, + data); +} + +static int +_cogl_atlas_texture_get_data (CoglTexture *tex, + CoglPixelFormat format, + unsigned int rowstride, + guint8 *data) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return cogl_texture_get_data (atlas_tex->sub_texture, + format, + rowstride, + data); +} + +static CoglPixelFormat +_cogl_atlas_texture_get_format (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* We don't want to forward this on the sub-texture because it isn't + the necessarily the same format. This will happen if the texture + isn't pre-multiplied */ + return atlas_tex->format; +} + +static GLenum +_cogl_atlas_texture_get_gl_format (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return _cogl_texture_get_gl_format (atlas_tex->sub_texture); +} + +static gint +_cogl_atlas_texture_get_width (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return cogl_texture_get_width (atlas_tex->sub_texture); +} + +static gint +_cogl_atlas_texture_get_height (CoglTexture *tex) +{ + CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); + + /* Forward on to the sub texture */ + return cogl_texture_get_height (atlas_tex->sub_texture); +} + +static CoglHandle +_cogl_atlas_texture_create_sub_texture (CoglHandle full_texture, + const CoglAtlasRectangle *rectangle) +{ + /* Create a subtexture for the given rectangle not including the + 1-pixel border */ + return _cogl_sub_texture_new (full_texture, + rectangle->x + 1, + rectangle->y + 1, + rectangle->width - 2, + rectangle->height - 2); +} + +typedef struct _CoglAtlasTextureRepositionData +{ + /* The current texture which already has a position */ + CoglAtlasTexture *texture; + /* The new position of the texture */ + CoglAtlasRectangle new_position; +} CoglAtlasTextureRepositionData; + +static void +_cogl_atlas_texture_migrate (guint n_textures, + CoglAtlasTextureRepositionData *textures, + CoglHandle old_texture, + CoglHandle new_texture, + CoglAtlasTexture *skip_texture) +{ + guint i; + CoglAtlasTextureBlitData blit_data; + + _cogl_atlas_texture_blit_begin (&blit_data, new_texture, old_texture); + + for (i = 0; i < n_textures; i++) + { + /* Skip the texture that is being added because it doesn't contain + any data yet */ + if (textures[i].texture != skip_texture) + { + _cogl_atlas_texture_blit (&blit_data, + textures[i].texture->rectangle.x, + textures[i].texture->rectangle.y, + textures[i].new_position.x, + textures[i].new_position.y, + textures[i].new_position.width, + textures[i].new_position.height); + /* Update the sub texture */ + cogl_handle_unref (textures[i].texture->sub_texture); + textures[i].texture->sub_texture = + _cogl_atlas_texture_create_sub_texture (new_texture, + &textures[i].new_position); + } + + /* Update the texture position */ + textures[i].texture->rectangle = textures[i].new_position; + } + + _cogl_atlas_texture_blit_end (&blit_data); +} + +typedef struct _CoglAtlasTextureGetRectanglesData +{ + CoglAtlasTextureRepositionData *textures; + /* Number of textures found so far */ + guint n_textures; +} CoglAtlasTextureGetRectanglesData; + +static void +_cogl_atlas_texture_get_rectangles_cb (const CoglAtlasRectangle *rectangle, + gpointer rectangle_data, + gpointer user_data) +{ + CoglAtlasTextureGetRectanglesData *data = user_data; + + data->textures[data->n_textures++].texture = rectangle_data; +} + +static void +_cogl_atlas_texture_get_next_size (guint *atlas_width, + guint *atlas_height) +{ + /* Double the size of the texture by increasing whichever dimension + is smaller */ + if (*atlas_width < *atlas_height) + *atlas_width <<= 1; + else + *atlas_height <<= 1; +} + +static CoglAtlas * +_cogl_atlas_texture_create_atlas (guint atlas_width, + guint atlas_height, + guint n_textures, + CoglAtlasTextureRepositionData *textures) +{ + GLint max_texture_size = 1024; + + GE( glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_texture_size) ); + + /* Sanity check that we're not going to get stuck in an infinite + loop if the maximum texture size has the high bit set */ + if ((max_texture_size & (1 << (sizeof (GLint) * 8 - 2)))) + max_texture_size >>= 1; + + /* Keep trying increasingly larger atlases until we can fit all of + the textures */ + while (atlas_width < max_texture_size && atlas_height < max_texture_size) + { + CoglAtlas *new_atlas = _cogl_atlas_new (atlas_width, atlas_height, NULL); + guint i; + + /* Add all of the textures and keep track of the new position */ + for (i = 0; i < n_textures; i++) + if (!_cogl_atlas_add_rectangle (new_atlas, + textures[i].texture->rectangle.width, + textures[i].texture->rectangle.height, + textures[i].texture, + &textures[i].new_position)) + break; + + /* If the atlas can contain all of the textures then we have a + winner */ + if (i >= n_textures) + return new_atlas; + + _cogl_atlas_free (new_atlas); + _cogl_atlas_texture_get_next_size (&atlas_width, &atlas_height); + } + + /* If we get here then there's no atlas that can accommodate all of + the rectangles */ + + return NULL; +} + +static int +_cogl_atlas_texture_compare_size_cb (const void *a, + const void *b) +{ + const CoglAtlasTextureRepositionData *ta = a; + const CoglAtlasTextureRepositionData *tb = b; + guint a_size, b_size; + + a_size = ta->texture->rectangle.width * ta->texture->rectangle.height; + b_size = tb->texture->rectangle.width * tb->texture->rectangle.height; + + return a_size < b_size ? 1 : a_size > b_size ? -1 : 0; +} + +static gboolean +_cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex, + guint width, + guint height) +{ + CoglAtlasTextureGetRectanglesData data; + CoglAtlas *new_atlas; + CoglHandle new_tex; + guint atlas_width, atlas_height; + gboolean ret; + + _COGL_GET_CONTEXT (ctx, FALSE); + + /* Check if we can fit the rectangle into the existing atlas */ + if (ctx->atlas && _cogl_atlas_add_rectangle (ctx->atlas, width, height, + new_sub_tex, + &new_sub_tex->rectangle)) + { + COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", + _cogl_atlas_get_width (ctx->atlas), + _cogl_atlas_get_height (ctx->atlas), + _cogl_atlas_get_n_rectangles (ctx->atlas), + _cogl_atlas_get_remaining_space (ctx->atlas) * 100 / + (_cogl_atlas_get_width (ctx->atlas) * + _cogl_atlas_get_height (ctx->atlas))); + + return TRUE; + } + + /* We need to reorganise the atlas so we'll get an array of all the + textures currently in the atlas. */ + data.n_textures = 0; + if (ctx->atlas == NULL) + data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData)); + else + { + data.textures = + g_malloc (sizeof (CoglAtlasTextureRepositionData) * + (_cogl_atlas_get_n_rectangles (ctx->atlas) + 1)); + _cogl_atlas_foreach (ctx->atlas, _cogl_atlas_texture_get_rectangles_cb, + &data); + } + + /* Add the new rectangle as a dummy texture so that it can be + positioned with the rest */ + data.textures[data.n_textures++].texture = new_sub_tex; + + /* The atlasing algorithm works a lot better if the rectangles are + added in decreasing order of size so we'll first sort the + array */ + qsort (data.textures, data.n_textures, + sizeof (CoglAtlasTextureRepositionData), + _cogl_atlas_texture_compare_size_cb); + + /* Try to create a new atlas that can contain all of the textures */ + if (ctx->atlas) + { + atlas_width = _cogl_atlas_get_width (ctx->atlas); + atlas_height = _cogl_atlas_get_height (ctx->atlas); + + /* If there is enough space in the existing for the new + rectangle in the existing atlas we'll start with the same + size, otherwise we'll immediately double it */ + if (_cogl_atlas_get_remaining_space (ctx->atlas) < width * height) + _cogl_atlas_texture_get_next_size (&atlas_width, &atlas_height); + } + else + { + /* Start with an initial size of 256x256 */ + atlas_width = 256; + atlas_height = 256; + } + + new_atlas = _cogl_atlas_texture_create_atlas (atlas_width, atlas_height, + data.n_textures, data.textures); + + /* If we can't create an atlas with the texture then give up */ + if (new_atlas == NULL) + { + COGL_NOTE (ATLAS, "Could not fit texture in the atlas"); + ret = FALSE; + } + else + { + /* We need to migrate the existing textures into a new texture */ + new_tex = + _cogl_texture_2d_new_with_size (_cogl_atlas_get_width (new_atlas), + _cogl_atlas_get_height (new_atlas), + COGL_TEXTURE_NONE, + COGL_PIXEL_FORMAT_RGBA_8888); + + COGL_NOTE (ATLAS, + "Atlas %s with size %ix%i", + ctx->atlas == NULL || + _cogl_atlas_get_width (ctx->atlas) != + _cogl_atlas_get_width (new_atlas) || + _cogl_atlas_get_height (ctx->atlas) != + _cogl_atlas_get_height (new_atlas) ? + "resized" : "reorganized", + _cogl_atlas_get_width (new_atlas), + _cogl_atlas_get_height (new_atlas)); + + if (ctx->atlas) + { + /* Move all the textures to the right position in the new + texture. This will also update the texture's rectangle */ + _cogl_atlas_texture_migrate (data.n_textures, + data.textures, + ctx->atlas_texture, + new_tex, + new_sub_tex); + _cogl_atlas_free (ctx->atlas); + cogl_handle_unref (ctx->atlas_texture); + } + else + /* We know there's only one texture so we can just directly + update the rectangle from its new position */ + data.textures[0].texture->rectangle = data.textures[0].new_position; + + ctx->atlas = new_atlas; + ctx->atlas_texture = new_tex; + + COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", + _cogl_atlas_get_width (ctx->atlas), + _cogl_atlas_get_height (ctx->atlas), + _cogl_atlas_get_n_rectangles (ctx->atlas), + _cogl_atlas_get_remaining_space (ctx->atlas) * 100 / + (_cogl_atlas_get_width (ctx->atlas) * + _cogl_atlas_get_height (ctx->atlas))); + + ret = TRUE; + } + + g_free (data.textures); + + return ret; +} + +CoglHandle +_cogl_atlas_texture_new_from_bitmap (CoglHandle bmp_handle, + CoglTextureFlags flags, + CoglPixelFormat internal_format) +{ + CoglAtlasTexture *atlas_tex; + CoglBitmap *bmp = (CoglBitmap *) bmp_handle; + CoglBitmap dst_bmp; + gboolean dst_bmp_owner; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + + _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE); + + g_return_val_if_fail (bmp_handle != COGL_INVALID_HANDLE, COGL_INVALID_HANDLE); + + /* Don't put textures in the atlas if the user has explicitly + requested to disable it */ + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_ATLAS)) + return COGL_INVALID_HANDLE; + + /* We can't put the texture in the atlas if there are any special + flags. This precludes textures with COGL_TEXTURE_NO_ATLAS and + COGL_TEXTURE_NO_SLICING from being atlased */ + if (flags) + return COGL_INVALID_HANDLE; + + /* We can't atlas zero-sized textures because it breaks the atlas + data structure */ + if (bmp->width < 1 || bmp->height < 1) + return COGL_INVALID_HANDLE; + + /* If we can't read back texture data then it will be too slow to + migrate textures and we shouldn't use the atlas */ + if (!cogl_features_available (COGL_FEATURE_TEXTURE_READ_PIXELS)) + return COGL_INVALID_HANDLE; + + COGL_NOTE (ATLAS, "Adding texture of size %ix%i", bmp->width, bmp->height); + + internal_format = _cogl_texture_determine_internal_format (bmp->format, + internal_format); + + /* If the texture is in a strange format then we can't use it */ + if (internal_format != COGL_PIXEL_FORMAT_RGB_888 && + (internal_format & ~COGL_PREMULT_BIT) != COGL_PIXEL_FORMAT_RGBA_8888) + { + COGL_NOTE (ATLAS, "Texture can not be added because the " + "format is unsupported"); + + return COGL_INVALID_HANDLE; + } + + /* We need to allocate the texture now because we need the pointer + to set as the data for the rectangle in the atlas */ + atlas_tex = g_new (CoglAtlasTexture, 1); + /* We need to fill in the texture size now because it is used in the + reserve_space function below. We add two pixels for the border */ + atlas_tex->rectangle.width = bmp->width + 2; + atlas_tex->rectangle.height = bmp->height + 2; + + /* Try to make some space in the atlas for the texture */ + if (!_cogl_atlas_texture_reserve_space (atlas_tex, + atlas_tex->rectangle.width, + atlas_tex->rectangle.height)) + { + g_free (atlas_tex); + return COGL_INVALID_HANDLE; + } + + if (!_cogl_texture_prepare_for_upload (bmp, + internal_format, + &internal_format, + &dst_bmp, + &dst_bmp_owner, + &gl_intformat, + &gl_format, + &gl_type)) + { + _cogl_atlas_remove_rectangle (ctx->atlas, &atlas_tex->rectangle); + g_free (atlas_tex); + return COGL_INVALID_HANDLE; + } + + atlas_tex->_parent.vtable = &cogl_atlas_texture_vtable; + atlas_tex->format = internal_format; + atlas_tex->in_atlas = TRUE; + atlas_tex->sub_texture = + _cogl_atlas_texture_create_sub_texture (ctx->atlas_texture, + &atlas_tex->rectangle); + + /* Defer to set_region so that we can share the code for copying the + edge pixels to the border. We don't want to pass the actual + format of the converted texture because otherwise it will get + unpremultiplied. */ + _cogl_atlas_texture_set_region_with_border (atlas_tex, + 0, 0, + 0, 0, + dst_bmp.width, + dst_bmp.height, + dst_bmp.width, + dst_bmp.height, + dst_bmp.format & + ~COGL_PREMULT_BIT, + dst_bmp.rowstride, + dst_bmp.data); + + if (dst_bmp_owner) + g_free (dst_bmp.data); + + return _cogl_atlas_texture_handle_new (atlas_tex); +} + +static const CoglTextureVtable +cogl_atlas_texture_vtable = + { + _cogl_atlas_texture_set_region, + _cogl_atlas_texture_get_data, + _cogl_atlas_texture_foreach_sub_texture_in_region, + _cogl_atlas_texture_get_max_waste, + _cogl_atlas_texture_is_sliced, + _cogl_atlas_texture_can_hardware_repeat, + _cogl_atlas_texture_transform_coords_to_gl, + _cogl_atlas_texture_transform_quad_coords_to_gl, + _cogl_atlas_texture_get_gl_texture, + _cogl_atlas_texture_set_filters, + _cogl_atlas_texture_ensure_mipmaps, + _cogl_atlas_texture_ensure_non_quad_rendering, + _cogl_atlas_texture_set_wrap_mode_parameter, + _cogl_atlas_texture_get_format, + _cogl_atlas_texture_get_gl_format, + _cogl_atlas_texture_get_width, + _cogl_atlas_texture_get_height + }; diff --git a/cogl/cogl-atlas.c b/cogl/cogl-atlas.c new file mode 100644 index 000000000..47c16598c --- /dev/null +++ b/cogl/cogl-atlas.c @@ -0,0 +1,596 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "cogl-atlas.h" +#include "cogl-debug.h" + +/* Implements a data structure which keeps track of unused + sub-rectangles within a larger rectangle using a binary tree + structure. The algorithm for this is based on the description here: + + http://www.blackpawn.com/texts/lightmaps/default.html +*/ + +#ifdef COGL_ENABLE_DEBUG + +/* The cairo header is only used for debugging to generate an image of + the atlas */ +#include + +static void _cogl_atlas_dump_image (CoglAtlas *atlas); + +#endif /* COGL_ENABLE_DEBUG */ + +typedef struct _CoglAtlasNode CoglAtlasNode; +typedef struct _CoglAtlasStackEntry CoglAtlasStackEntry; + +typedef void (* CoglAtlasInternalForeachCb) (CoglAtlasNode *node, + gpointer data); + +typedef enum +{ + COGL_ATLAS_BRANCH, + COGL_ATLAS_FILLED_LEAF, + COGL_ATLAS_EMPTY_LEAF +} CoglAtlasNodeType; + +struct _CoglAtlas +{ + CoglAtlasNode *root; + + guint space_remaining; + guint n_rectangles; + + GDestroyNotify value_destroy_func; +}; + +struct _CoglAtlasNode +{ + CoglAtlasNodeType type; + + CoglAtlasRectangle rectangle; + + CoglAtlasNode *parent; + + union + { + /* Fields used when this is a branch */ + struct + { + CoglAtlasNode *left; + CoglAtlasNode *right; + } branch; + + /* Field used when this is a filled leaf */ + gpointer data; + } d; +}; + +struct _CoglAtlasStackEntry +{ + /* The node to search */ + CoglAtlasNode *node; + /* Index of next branch of this node to explore. Basically either 0 + to go left or 1 to go right */ + gboolean next_index; + /* Next entry in the stack */ + CoglAtlasStackEntry *next; +}; + +static CoglAtlasNode * +_cogl_atlas_node_new (void) +{ + return g_slice_new (CoglAtlasNode); +} + +static void +_cogl_atlas_node_free (CoglAtlasNode *node) +{ + g_slice_free (CoglAtlasNode, node); +} + +CoglAtlas * +_cogl_atlas_new (guint width, guint height, + GDestroyNotify value_destroy_func) +{ + CoglAtlas *atlas = g_new (CoglAtlas, 1); + CoglAtlasNode *root = _cogl_atlas_node_new (); + + root->type = COGL_ATLAS_EMPTY_LEAF; + root->parent = NULL; + root->rectangle.x = 0; + root->rectangle.y = 0; + root->rectangle.width = width; + root->rectangle.height = height; + + atlas->root = root; + atlas->space_remaining = width * height; + atlas->n_rectangles = 0; + atlas->value_destroy_func = value_destroy_func; + + return atlas; +} + +static CoglAtlasStackEntry * +_cogl_atlas_stack_push (CoglAtlasStackEntry *stack, + CoglAtlasNode *node, + gboolean next_index) +{ + CoglAtlasStackEntry *new_entry = g_slice_new (CoglAtlasStackEntry); + + new_entry->node = node; + new_entry->next_index = next_index; + new_entry->next = stack; + + return new_entry; +} + +static CoglAtlasStackEntry * +_cogl_atlas_stack_pop (CoglAtlasStackEntry *stack) +{ + CoglAtlasStackEntry *next = stack->next; + + g_slice_free (CoglAtlasStackEntry, stack); + + return next; +} + +static CoglAtlasNode * +_cogl_atlas_node_split_horizontally (CoglAtlasNode *node, + guint left_width) +{ + /* Splits the node horizontally (according to emacs' definition, not + vim) by converting it to a branch and adding two new leaf + nodes. The leftmost branch will have the width left_width and + will be returned. If the node is already just the right size it + won't do anything */ + + CoglAtlasNode *left_node, *right_node; + + if (node->rectangle.width == left_width) + return node; + + left_node = _cogl_atlas_node_new (); + left_node->type = COGL_ATLAS_EMPTY_LEAF; + left_node->parent = node; + left_node->rectangle.x = node->rectangle.x; + left_node->rectangle.y = node->rectangle.y; + left_node->rectangle.width = left_width; + left_node->rectangle.height = node->rectangle.height; + node->d.branch.left = left_node; + + right_node = _cogl_atlas_node_new (); + right_node->type = COGL_ATLAS_EMPTY_LEAF; + right_node->parent = node; + right_node->rectangle.x = node->rectangle.x + left_width; + right_node->rectangle.y = node->rectangle.y; + right_node->rectangle.width = node->rectangle.width - left_width; + right_node->rectangle.height = node->rectangle.height; + node->d.branch.right = right_node; + + node->type = COGL_ATLAS_BRANCH; + + return left_node; +} + +static CoglAtlasNode * +_cogl_atlas_node_split_vertically (CoglAtlasNode *node, + guint top_height) +{ + /* Splits the node vertically (according to emacs' definition, not + vim) by converting it to a branch and adding two new leaf + nodes. The topmost branch will have the height top_height and + will be returned. If the node is already just the right size it + won't do anything */ + + CoglAtlasNode *top_node, *bottom_node; + + if (node->rectangle.height == top_height) + return node; + + top_node = _cogl_atlas_node_new (); + top_node->type = COGL_ATLAS_EMPTY_LEAF; + top_node->parent = node; + top_node->rectangle.x = node->rectangle.x; + top_node->rectangle.y = node->rectangle.y; + top_node->rectangle.width = node->rectangle.width; + top_node->rectangle.height = top_height; + node->d.branch.left = top_node; + + bottom_node = _cogl_atlas_node_new (); + bottom_node->type = COGL_ATLAS_EMPTY_LEAF; + bottom_node->parent = node; + bottom_node->rectangle.x = node->rectangle.x; + bottom_node->rectangle.y = node->rectangle.y + top_height; + bottom_node->rectangle.width = node->rectangle.width; + bottom_node->rectangle.height = node->rectangle.height - top_height; + node->d.branch.right = bottom_node; + + node->type = COGL_ATLAS_BRANCH; + + return top_node; +} + +gboolean +_cogl_atlas_add_rectangle (CoglAtlas *atlas, + guint width, guint height, + gpointer data, + CoglAtlasRectangle *rectangle) +{ + /* Stack of nodes to search in */ + CoglAtlasStackEntry *node_stack; + CoglAtlasNode *found_node = NULL; + + /* Zero-sized rectangles break the algorithm for removing rectangles + so we'll disallow them */ + g_return_val_if_fail (width > 0 && height > 0, FALSE); + + /* Start with the root node */ + node_stack = _cogl_atlas_stack_push (NULL, atlas->root, FALSE); + + /* Depth-first search for an empty node that is big enough */ + while (node_stack) + { + /* Pop an entry off the stack */ + CoglAtlasNode *node = node_stack->node; + int next_index = node_stack->next_index; + node_stack = _cogl_atlas_stack_pop (node_stack); + + /* Regardless of the type of the node, there's no point + descending any further if the new rectangle won't fit within + it */ + if (node->rectangle.width >= width && + node->rectangle.height >= height) + { + if (node->type == COGL_ATLAS_EMPTY_LEAF) + { + /* We've found a node we can use */ + found_node = node; + break; + } + else if (node->type == COGL_ATLAS_BRANCH) + { + if (next_index) + /* Try the right branch */ + node_stack = _cogl_atlas_stack_push (node_stack, + node->d.branch.right, + 0); + else + { + /* Make sure we remember to try the right branch once + we've finished descending the left branch */ + node_stack = _cogl_atlas_stack_push (node_stack, + node, + 1); + /* Try the left branch */ + node_stack = _cogl_atlas_stack_push (node_stack, + node->d.branch.left, + 0); + } + } + } + } + + /* Free the stack */ + while (node_stack) + node_stack = _cogl_atlas_stack_pop (node_stack); + + if (found_node) + { + /* Split according to whichever axis will leave us with the + largest space */ + if (found_node->rectangle.width - width > + found_node->rectangle.height - height) + { + found_node = _cogl_atlas_node_split_horizontally (found_node, width); + found_node = _cogl_atlas_node_split_vertically (found_node, height); + } + else + { + found_node = _cogl_atlas_node_split_vertically (found_node, height); + found_node = _cogl_atlas_node_split_horizontally (found_node, width); + } + + found_node->type = COGL_ATLAS_FILLED_LEAF; + found_node->d.data = data; + if (rectangle) + *rectangle = found_node->rectangle; + + /* Record how much empty space is remaining after this rectangle + is added */ + g_assert (width * height <= atlas->space_remaining); + atlas->space_remaining -= width * height; + atlas->n_rectangles++; + +#ifdef COGL_ENABLE_DEBUG + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE)) + _cogl_atlas_dump_image (atlas); +#endif + + return TRUE; + } + else + return FALSE; +} + +void +_cogl_atlas_remove_rectangle (CoglAtlas *atlas, + const CoglAtlasRectangle *rectangle) +{ + CoglAtlasNode *node = atlas->root; + + /* We can do a binary-chop down the search tree to find the rectangle */ + while (node->type == COGL_ATLAS_BRANCH) + { + CoglAtlasNode *left_node = node->d.branch.left; + + /* If and only if the rectangle is in the left node then the x,y + position of the rectangle will be within the node's + rectangle */ + if (rectangle->x < left_node->rectangle.x + left_node->rectangle.width && + rectangle->y < left_node->rectangle.y + left_node->rectangle.height) + /* Go left */ + node = left_node; + else + /* Go right */ + node = node->d.branch.right; + } + + /* Make sure we found the right node */ + if (node->type != COGL_ATLAS_FILLED_LEAF || + node->rectangle.x != rectangle->x || + node->rectangle.y != rectangle->y || + node->rectangle.width != rectangle->width || + node->rectangle.height != rectangle->height) + /* This should only happen if someone tried to remove a rectangle + that was not in the atlas so something has gone wrong */ + g_return_if_reached (); + else + { + /* Convert the node back to an empty node */ + if (atlas->value_destroy_func) + atlas->value_destroy_func (node->d.data); + node->type = COGL_ATLAS_EMPTY_LEAF; + + /* Walk back up the tree combining branch nodes that have two + empty leaves back into a single empty leaf */ + for (node = node->parent; node; node = node->parent) + { + /* This node is a parent so it should always be a branch */ + g_assert (node->type == COGL_ATLAS_BRANCH); + + if (node->d.branch.left->type == COGL_ATLAS_EMPTY_LEAF && + node->d.branch.right->type == COGL_ATLAS_EMPTY_LEAF) + { + _cogl_atlas_node_free (node->d.branch.left); + _cogl_atlas_node_free (node->d.branch.right); + node->type = COGL_ATLAS_EMPTY_LEAF; + } + else + break; + } + + /* There is now more free space and one less rectangle */ + atlas->space_remaining += rectangle->width * rectangle->height; + g_assert (atlas->n_rectangles > 0); + atlas->n_rectangles--; + } + +#ifdef COGL_ENABLE_DEBUG + if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE)) + _cogl_atlas_dump_image (atlas); +#endif +} + +guint +_cogl_atlas_get_width (CoglAtlas *atlas) +{ + return atlas->root->rectangle.width; +} + +guint +_cogl_atlas_get_height (CoglAtlas *atlas) +{ + return atlas->root->rectangle.height; +} + +guint +_cogl_atlas_get_remaining_space (CoglAtlas *atlas) +{ + return atlas->space_remaining; +} + +guint +_cogl_atlas_get_n_rectangles (CoglAtlas *atlas) +{ + return atlas->n_rectangles; +} + +static void +_cogl_atlas_internal_foreach (CoglAtlas *atlas, + CoglAtlasInternalForeachCb callback, + gpointer data) +{ + /* Stack of nodes to search in */ + CoglAtlasStackEntry *node_stack; + + /* Start with the root node */ + node_stack = _cogl_atlas_stack_push (NULL, atlas->root, 0); + + /* Iterate all nodes depth-first */ + while (node_stack) + { + CoglAtlasNode *node = node_stack->node; + + switch (node->type) + { + case COGL_ATLAS_BRANCH: + if (node_stack->next_index == 0) + { + /* Next time we come back to this node, go to the right */ + node_stack->next_index = 1; + + /* Explore the left branch next */ + node_stack = _cogl_atlas_stack_push (node_stack, + node->d.branch.left, + 0); + } + else if (node_stack->next_index == 1) + { + /* Next time we come back to this node, stop processing it */ + node_stack->next_index = 2; + + /* Explore the right branch next */ + node_stack = _cogl_atlas_stack_push (node_stack, + node->d.branch.right, + 0); + } + else + { + /* We're finished with this node so we can call the callback */ + callback (node, data); + node_stack = _cogl_atlas_stack_pop (node_stack); + } + break; + + default: + /* Some sort of leaf node, just call the callback */ + callback (node, data); + node_stack = _cogl_atlas_stack_pop (node_stack); + break; + } + } + + /* The stack should now be empty */ + g_assert (node_stack == NULL); +} + +typedef struct _CoglAtlasForeachClosure +{ + CoglAtlasCallback callback; + gpointer data; +} CoglAtlasForeachClosure; + +static void +_cogl_atlas_foreach_cb (CoglAtlasNode *node, gpointer data) +{ + CoglAtlasForeachClosure *closure = data; + + if (node->type == COGL_ATLAS_FILLED_LEAF) + closure->callback (&node->rectangle, node->d.data, closure->data); +} + +void +_cogl_atlas_foreach (CoglAtlas *atlas, + CoglAtlasCallback callback, + gpointer data) +{ + CoglAtlasForeachClosure closure; + + closure.callback = callback; + closure.data = data; + + _cogl_atlas_internal_foreach (atlas, _cogl_atlas_foreach_cb, &closure); +} + +static void +_cogl_atlas_free_cb (CoglAtlasNode *node, gpointer data) +{ + CoglAtlas *atlas = data; + + if (node->type == COGL_ATLAS_FILLED_LEAF && atlas->value_destroy_func) + atlas->value_destroy_func (node->d.data); + + _cogl_atlas_node_free (node); +} + +void +_cogl_atlas_free (CoglAtlas *atlas) +{ + _cogl_atlas_internal_foreach (atlas, _cogl_atlas_free_cb, atlas); + g_free (atlas); +} + +#ifdef COGL_ENABLE_DEBUG + +static void +_cogl_atlas_dump_image_cb (CoglAtlasNode *node, gpointer data) +{ + cairo_t *cr = data; + + if (node->type == COGL_ATLAS_FILLED_LEAF || + node->type == COGL_ATLAS_EMPTY_LEAF) + { + /* Fill the rectangle using a different colour depending on + whether the rectangle is used */ + if (node->type == COGL_ATLAS_FILLED_LEAF) + cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); + else + cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); + + cairo_rectangle (cr, + node->rectangle.x, + node->rectangle.y, + node->rectangle.width, + node->rectangle.height); + + cairo_fill_preserve (cr); + + /* Draw a white outline around the rectangle */ + cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); + cairo_stroke (cr); + } +} + +static void +_cogl_atlas_dump_image (CoglAtlas *atlas) +{ + /* This dumps a png to help visualize the atlas. Each leaf rectangle + is drawn with a white outline. Unused leaves are filled in black + and used leaves are blue */ + + cairo_surface_t *surface = + cairo_image_surface_create (CAIRO_FORMAT_RGB24, + _cogl_atlas_get_width (atlas), + _cogl_atlas_get_height (atlas)); + cairo_t *cr = cairo_create (surface); + + _cogl_atlas_internal_foreach (atlas, _cogl_atlas_dump_image_cb, cr); + + cairo_destroy (cr); + + cairo_surface_write_to_png (surface, "cogl-atlas-dump.png"); + + cairo_surface_destroy (surface); +} + +#endif /* COGL_ENABLE_DEBUG */ diff --git a/cogl/cogl-atlas.h b/cogl/cogl-atlas.h new file mode 100644 index 000000000..00d4a086d --- /dev/null +++ b/cogl/cogl-atlas.h @@ -0,0 +1,76 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_ATLAS_H +#define __COGL_ATLAS_H + +#include + +typedef struct _CoglAtlas CoglAtlas; +typedef struct _CoglAtlasRectangle CoglAtlasRectangle; + +typedef void (* CoglAtlasCallback) (const CoglAtlasRectangle *rectangle, + gpointer rectangle_data, + gpointer user_data); + +struct _CoglAtlasRectangle +{ + guint x, y; + guint width, height; +}; + +CoglAtlas * +_cogl_atlas_new (guint width, guint height, + GDestroyNotify value_destroy_func); + +gboolean +_cogl_atlas_add_rectangle (CoglAtlas *atlas, + guint width, guint height, + gpointer data, + CoglAtlasRectangle *rectangle); + +void +_cogl_atlas_remove_rectangle (CoglAtlas *atlas, + const CoglAtlasRectangle *rectangle); + +guint +_cogl_atlas_get_width (CoglAtlas *atlas); + +guint +_cogl_atlas_get_height (CoglAtlas *atlas); + +guint +_cogl_atlas_get_remaining_space (CoglAtlas *atlas); + +guint +_cogl_atlas_get_n_rectangles (CoglAtlas *atlas); + +void +_cogl_atlas_foreach (CoglAtlas *atlas, + CoglAtlasCallback callback, + gpointer data); + +void +_cogl_atlas_free (CoglAtlas *atlas); + +#endif /* __COGL_ATLAS_H */ diff --git a/cogl/cogl-bitmap-fallback.c b/cogl/cogl-bitmap-fallback.c index ddff6ea40..a02d85611 100644 --- a/cogl/cogl-bitmap-fallback.c +++ b/cogl/cogl-bitmap-fallback.c @@ -150,7 +150,7 @@ _cogl_rgba_to_abgr (const guchar *src, guchar *dst) /* (Un)Premultiplication */ inline static void -_cogl_unpremult_alpha_0 (const guchar *src, guchar *dst) +_cogl_unpremult_alpha_0 (guchar *dst) { dst[0] = 0; dst[1] = 0; @@ -159,58 +159,58 @@ _cogl_unpremult_alpha_0 (const guchar *src, guchar *dst) } inline static void -_cogl_unpremult_alpha_last (const guchar *src, guchar *dst) +_cogl_unpremult_alpha_last (guchar *dst) { - guchar alpha = src[3]; + guchar alpha = dst[3]; - dst[0] = (src[0] * 255) / alpha; - dst[1] = (src[1] * 255) / alpha; - dst[2] = (src[2] * 255) / alpha; - dst[3] = alpha; + dst[0] = (dst[0] * 255) / alpha; + dst[1] = (dst[1] * 255) / alpha; + dst[2] = (dst[2] * 255) / alpha; } inline static void -_cogl_unpremult_alpha_first (const guchar *src, guchar *dst) +_cogl_unpremult_alpha_first (guchar *dst) { - guchar alpha = src[0]; + guchar alpha = dst[0]; - dst[0] = alpha; - dst[1] = (src[1] * 255) / alpha; - dst[2] = (src[2] * 255) / alpha; - dst[3] = (src[3] * 255) / alpha; + dst[1] = (dst[1] * 255) / alpha; + dst[2] = (dst[2] * 255) / alpha; + dst[3] = (dst[3] * 255) / alpha; } /* No division form of floor((c*a + 128)/255) (I first encountered * this in the RENDER implementation in the X server.) Being exact * is important for a == 255 - we want to get exactly c. */ -#define MULT(d,c,a,t) G_STMT_START { t = c * a + 128; d = ((t >> 8) + t) >> 8; } G_STMT_END +#define MULT(d,a,t) \ + G_STMT_START { \ + t = d * a + 128; \ + d = ((t >> 8) + t) >> 8; \ + } G_STMT_END inline static void -_cogl_premult_alpha_last (const guchar *src, guchar *dst) +_cogl_premult_alpha_last (guchar *dst) { - guchar alpha = src[3]; + guchar alpha = dst[3]; /* Using a separate temporary per component has given slightly better * code generation with GCC in the past; it shouldn't do any worse in * any case. */ guint t1, t2, t3; - MULT(dst[0], src[0], alpha, t1); - MULT(dst[1], src[1], alpha, t2); - MULT(dst[2], src[2], alpha, t3); - dst[3] = alpha; + MULT(dst[0], alpha, t1); + MULT(dst[1], alpha, t2); + MULT(dst[2], alpha, t3); } inline static void -_cogl_premult_alpha_first (const guchar *src, guchar *dst) +_cogl_premult_alpha_first (guchar *dst) { - guchar alpha = src[0]; + guchar alpha = dst[0]; guint t1, t2, t3; - dst[0] = alpha; - MULT(dst[1], src[1], alpha, t1); - MULT(dst[2], src[2], alpha, t2); - MULT(dst[3], src[3], alpha, t3); + MULT(dst[1], alpha, t1); + MULT(dst[2], alpha, t2); + MULT(dst[3], alpha, t3); } #undef MULT @@ -342,112 +342,82 @@ _cogl_bitmap_fallback_convert (const CoglBitmap *bmp, } gboolean -_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp) +_cogl_bitmap_fallback_unpremult (CoglBitmap *bmp) { - guchar *src; - guchar *dst; - gint bpp; + guchar *p; gint x,y; /* Make sure format supported for un-premultiplication */ if (!_cogl_bitmap_fallback_can_unpremult (bmp->format)) return FALSE; - bpp = _cogl_get_format_bpp (bmp->format); - - /* Initialize destination bitmap */ - *dst_bmp = *bmp; - dst_bmp->format = (bmp->format & COGL_UNPREMULT_MASK); - - /* Allocate a new buffer to hold converted data */ - dst_bmp->data = g_malloc (sizeof(guchar) - * dst_bmp->height - * dst_bmp->rowstride); - for (y = 0; y < bmp->height; y++) { - src = (guchar*)bmp->data + y * bmp->rowstride; - dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride; + p = (guchar*) bmp->data + y * bmp->rowstride; if (bmp->format & COGL_AFIRST_BIT) { for (x = 0; x < bmp->width; x++) { - if (src[0] == 0) - _cogl_unpremult_alpha_0 (src, dst); + if (p[0] == 0) + _cogl_unpremult_alpha_0 (p); else - _cogl_unpremult_alpha_first (src, dst); - src += bpp; - dst += bpp; + _cogl_unpremult_alpha_first (p); + p += 4; } } else { for (x = 0; x < bmp->width; x++) { - if (src[0] == 0) - _cogl_unpremult_alpha_0 (src, dst); + if (p[3] == 0) + _cogl_unpremult_alpha_0 (p); else - _cogl_unpremult_alpha_last (src, dst); - src += bpp; - dst += bpp; + _cogl_unpremult_alpha_last (p); + p += 4; } } } + bmp->format &= ~COGL_PREMULT_BIT; + return TRUE; } gboolean -_cogl_bitmap_fallback_premult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp) +_cogl_bitmap_fallback_premult (CoglBitmap *bmp) { - guchar *src; - guchar *dst; - gint bpp; + guchar *p; gint x,y; /* Make sure format supported for un-premultiplication */ if (!_cogl_bitmap_fallback_can_premult (bmp->format)) return FALSE; - bpp = _cogl_get_format_bpp (bmp->format); - - /* Initialize destination bitmap */ - *dst_bmp = *bmp; - dst_bmp->format |= COGL_PREMULT_BIT; - - /* Allocate a new buffer to hold converted data */ - dst_bmp->data = g_malloc (sizeof(guchar) - * dst_bmp->height - * dst_bmp->rowstride); - for (y = 0; y < bmp->height; y++) { - src = (guchar*)bmp->data + y * bmp->rowstride; - dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride; + p = (guchar*) bmp->data + y * bmp->rowstride; if (bmp->format & COGL_AFIRST_BIT) - { - for (x = 0; x < bmp->width; x++) - { - _cogl_premult_alpha_first (src, dst); - src += bpp; - dst += bpp; - } - } + { + for (x = 0; x < bmp->width; x++) + { + _cogl_premult_alpha_first (p); + p += 4; + } + } else - { - for (x = 0; x < bmp->width; x++) - { - _cogl_premult_alpha_last (src, dst); - src += bpp; - dst += bpp; - } - } + { + for (x = 0; x < bmp->width; x++) + { + _cogl_premult_alpha_last (p); + p += 4; + } + } } + bmp->format |= COGL_PREMULT_BIT; + return TRUE; } diff --git a/cogl/cogl-bitmap-pixbuf.c b/cogl/cogl-bitmap-pixbuf.c index 79ed62a7f..b1a756a24 100644 --- a/cogl/cogl-bitmap-pixbuf.c +++ b/cogl/cogl-bitmap-pixbuf.c @@ -64,15 +64,13 @@ _cogl_bitmap_convert (const CoglBitmap *bmp, } gboolean -_cogl_bitmap_unpremult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp) +_cogl_bitmap_unpremult (CoglBitmap *dst_bmp) { return FALSE; } gboolean -_cogl_bitmap_premult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp) +_cogl_bitmap_premult (CoglBitmap *dst_bmp) { return FALSE; } diff --git a/cogl/cogl-bitmap-private.h b/cogl/cogl-bitmap-private.h index b6e425733..803e10bb0 100644 --- a/cogl/cogl-bitmap-private.h +++ b/cogl/cogl-bitmap-private.h @@ -68,20 +68,16 @@ _cogl_bitmap_fallback_convert (const CoglBitmap *bmp, CoglPixelFormat dst_format); gboolean -_cogl_bitmap_unpremult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp); +_cogl_bitmap_unpremult (CoglBitmap *dst_bmp); gboolean -_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp); +_cogl_bitmap_fallback_unpremult (CoglBitmap *dst_bmp); gboolean -_cogl_bitmap_premult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp); +_cogl_bitmap_premult (CoglBitmap *dst_bmp); gboolean -_cogl_bitmap_fallback_premult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp); +_cogl_bitmap_fallback_premult (CoglBitmap *dst_bmp); gboolean _cogl_bitmap_from_file (CoglBitmap *bmp, @@ -93,9 +89,13 @@ _cogl_bitmap_fallback_from_file (CoglBitmap *bmp, const gchar *filename); gboolean -_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp, - CoglPixelFormat dst_format); +_cogl_bitmap_convert_premult_status (CoglBitmap *bmp, + CoglPixelFormat dst_format); + +gboolean +_cogl_bitmap_convert_format_and_premult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format); void _cogl_bitmap_copy_subregion (CoglBitmap *src, diff --git a/cogl/cogl-bitmap.c b/cogl/cogl-bitmap.c index 3dc7fd534..fe3cbdb72 100644 --- a/cogl/cogl-bitmap.c +++ b/cogl/cogl-bitmap.c @@ -61,84 +61,58 @@ _cogl_get_format_bpp (CoglPixelFormat format) } gboolean -_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp, - CoglBitmap *dst_bmp, - CoglPixelFormat dst_format) +_cogl_bitmap_convert_premult_status (CoglBitmap *bmp, + CoglPixelFormat dst_format) { - CoglBitmap tmp_bmp = *bmp; - CoglBitmap new_bmp = *bmp; - gboolean new_bmp_owner = FALSE; + /* Do we need to unpremultiply? */ + if ((bmp->format & COGL_PREMULT_BIT) > 0 && + (dst_format & COGL_PREMULT_BIT) == 0) + /* Try unpremultiplying using imaging library */ + return (_cogl_bitmap_unpremult (bmp) + /* ... or try fallback */ + || _cogl_bitmap_fallback_unpremult (bmp)); + /* Do we need to premultiply? */ + if ((bmp->format & COGL_PREMULT_BIT) == 0 && + (dst_format & COGL_PREMULT_BIT) > 0) + /* Try premultiplying using imaging library */ + return (_cogl_bitmap_premult (bmp) + /* ... or try fallback */ + || _cogl_bitmap_fallback_premult (bmp)); + + return TRUE; +} + +gboolean +_cogl_bitmap_convert_format_and_premult (const CoglBitmap *bmp, + CoglBitmap *dst_bmp, + CoglPixelFormat dst_format) +{ /* Is base format different (not considering premult status)? */ if ((bmp->format & COGL_UNPREMULT_MASK) != (dst_format & COGL_UNPREMULT_MASK)) { /* Try converting using imaging library */ - if (!_cogl_bitmap_convert (&new_bmp, &tmp_bmp, dst_format)) - { - /* ... or try fallback */ - if (!_cogl_bitmap_fallback_convert (&new_bmp, &tmp_bmp, dst_format)) - return FALSE; - } - - /* Update bitmap with new data */ - new_bmp = tmp_bmp; - new_bmp_owner = TRUE; + if (!_cogl_bitmap_convert (bmp, dst_bmp, dst_format)) + { + /* ... or try fallback */ + if (!_cogl_bitmap_fallback_convert (bmp, dst_bmp, dst_format)) + return FALSE; + } } - - /* Do we need to unpremultiply */ - if ((bmp->format & COGL_PREMULT_BIT) > 0 && - (dst_format & COGL_PREMULT_BIT) == 0) + else { - /* Try unpremultiplying using imaging library */ - if (!_cogl_bitmap_unpremult (&new_bmp, &tmp_bmp)) - { - /* ... or try fallback */ - if (!_cogl_bitmap_fallback_unpremult (&new_bmp, &tmp_bmp)) - { - if (new_bmp_owner) - g_free (new_bmp.data); - - return FALSE; - } - } - - /* Update bitmap with new data */ - if (new_bmp_owner) - g_free (new_bmp.data); - - new_bmp = tmp_bmp; - new_bmp_owner = TRUE; + /* Copy the bitmap so that we can premultiply in-place */ + *dst_bmp = *bmp; + dst_bmp->data = g_memdup (bmp->data, bmp->rowstride * bmp->height); } - /* Do we need to premultiply */ - if ((bmp->format & COGL_PREMULT_BIT) == 0 && - (dst_format & COGL_PREMULT_BIT) > 0) + if (!_cogl_bitmap_convert_premult_status (dst_bmp, dst_format)) { - /* Try premultiplying using imaging library */ - if (!_cogl_bitmap_premult (&new_bmp, &tmp_bmp)) - { - /* ... or try fallback */ - if (!_cogl_bitmap_fallback_premult (&new_bmp, &tmp_bmp)) - { - if (new_bmp_owner) - g_free (new_bmp.data); - - return FALSE; - } - } - - /* Update bitmap with new data */ - if (new_bmp_owner) - g_free (new_bmp.data); - - new_bmp = tmp_bmp; - new_bmp_owner = TRUE; + g_free (dst_bmp->data); + return FALSE; } - /* Output new bitmap info */ - *dst_bmp = new_bmp; - return TRUE; } diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c index c78b2821c..3fc854288 100644 --- a/cogl/cogl-context.c +++ b/cogl/cogl-context.c @@ -147,6 +147,9 @@ cogl_create_context (void) cogl_enable (enable_flags); _cogl_flush_face_winding (); + _context->atlas = NULL; + _context->atlas_texture = COGL_INVALID_HANDLE; + return TRUE; } @@ -188,6 +191,11 @@ _cogl_destroy_context () if (_context->default_material) cogl_handle_unref (_context->default_material); + if (_context->atlas) + _cogl_atlas_free (_context->atlas); + if (_context->atlas_texture) + cogl_handle_unref (_context->atlas_texture); + g_free (_context); } diff --git a/cogl/cogl-context.h b/cogl/cogl-context.h index 53c233c4b..dcbba2c70 100644 --- a/cogl/cogl-context.h +++ b/cogl/cogl-context.h @@ -30,6 +30,7 @@ #include "cogl-clip-stack.h" #include "cogl-matrix-stack.h" #include "cogl-material-private.h" +#include "cogl-atlas.h" typedef struct { @@ -111,6 +112,9 @@ typedef struct CoglHandle texture_download_material; + CoglAtlas *atlas; + CoglHandle atlas_texture; + /* This debugging variable is used to pick a colour for visually displaying the quad batches. It needs to be global so that it can be reset by cogl_clear. It needs to be reset to increase the diff --git a/cogl/cogl-debug.c b/cogl/cogl-debug.c index 1265595f9..1ee180569 100644 --- a/cogl/cogl-debug.c +++ b/cogl/cogl-debug.c @@ -47,7 +47,10 @@ static const GDebugKey cogl_debug_keys[] = { { "batching", COGL_DEBUG_BATCHING }, { "disable-software-transform", COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM }, { "matrices", COGL_DEBUG_MATRICES }, - { "force-scanline-paths", COGL_DEBUG_FORCE_SCANLINE_PATHS } + { "force-scanline-paths", COGL_DEBUG_FORCE_SCANLINE_PATHS }, + { "atlas", COGL_DEBUG_ATLAS }, + { "dump-atlas-image", COGL_DEBUG_DUMP_ATLAS_IMAGE }, + { "disable-atlas", COGL_DEBUG_DISABLE_ATLAS } }; static const gint n_cogl_debug_keys = G_N_ELEMENTS (cogl_debug_keys); diff --git a/cogl/cogl-debug.h b/cogl/cogl-debug.h index bc450aa99..0c6fb4de7 100644 --- a/cogl/cogl-debug.h +++ b/cogl/cogl-debug.h @@ -45,7 +45,10 @@ typedef enum { COGL_DEBUG_BATCHING = 1 << 13, COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM = 1 << 14, COGL_DEBUG_MATRICES = 1 << 15, - COGL_DEBUG_FORCE_SCANLINE_PATHS = 1 << 16 + COGL_DEBUG_FORCE_SCANLINE_PATHS = 1 << 16, + COGL_DEBUG_ATLAS = 1 << 17, + COGL_DEBUG_DUMP_ATLAS_IMAGE = 1 << 18, + COGL_DEBUG_DISABLE_ATLAS = 1 << 19 } CoglDebugFlags; #ifdef COGL_ENABLE_DEBUG diff --git a/cogl/cogl-journal-private.h b/cogl/cogl-journal-private.h index dc008f67a..a410c8ff0 100644 --- a/cogl/cogl-journal-private.h +++ b/cogl/cogl-journal-private.h @@ -51,7 +51,7 @@ _cogl_journal_log_quad (float x_1, int n_layers, guint32 fallback_layers, GLuint layer0_override_texture, - float *tex_coords, + const float *tex_coords, unsigned int tex_coords_len); #endif /* __COGL_JOURNAL_PRIVATE_H */ diff --git a/cogl/cogl-journal.c b/cogl/cogl-journal.c index 0d8b09173..a684a526f 100644 --- a/cogl/cogl-journal.c +++ b/cogl/cogl-journal.c @@ -665,7 +665,7 @@ _cogl_journal_log_quad (float x_1, int n_layers, guint32 fallback_layers, GLuint layer0_override_texture, - float *tex_coords, + const float *tex_coords, unsigned int tex_coords_len) { size_t stride; diff --git a/cogl/cogl-material-private.h b/cogl/cogl-material-private.h index c70ea304f..a9cf77a17 100644 --- a/cogl/cogl-material-private.h +++ b/cogl/cogl-material-private.h @@ -204,6 +204,12 @@ typedef enum _CoglMaterialLayerFlags */ gulong _cogl_material_layer_get_flags (CoglHandle layer_handle); +/* + * Ensures the mipmaps are available for the texture in the layer if + * the filter settings would require it + */ +void _cogl_material_layer_ensure_mipmaps (CoglHandle layer_handler); + /* * CoglMaterialFlushFlag: * @COGL_MATERIAL_FLUSH_FALLBACK_MASK: The fallback_layers member is set to diff --git a/cogl/cogl-material.c b/cogl/cogl-material.c index 65b7258d0..8f12fc81b 100644 --- a/cogl/cogl-material.c +++ b/cogl/cogl-material.c @@ -1320,6 +1320,19 @@ _cogl_material_layer_flush_gl_sampler_state (CoglMaterialLayer *layer, _cogl_matrix_stack_flush_to_gl (unit->matrix_stack, COGL_MATRIX_TEXTURE); } +void +_cogl_material_layer_ensure_mipmaps (CoglHandle layer_handle) +{ + CoglMaterialLayer *layer; + + layer = _cogl_material_layer_pointer_from_handle (layer_handle); + + if (layer->texture && + (is_mipmap_filter (layer->min_filter) || + is_mipmap_filter (layer->mag_filter))) + _cogl_texture_ensure_mipmaps (layer->texture); +} + /* * _cogl_material_flush_layers_gl_state: * @fallback_mask: is a bitmask of the material layers that need to be @@ -1391,6 +1404,8 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material, #endif CoglTextureUnit *unit; + _cogl_material_layer_ensure_mipmaps (layer_handle); + new_gl_layer_info.layer0_overridden = layer0_override_texture ? TRUE : FALSE; new_gl_layer_info.fallback = @@ -1400,7 +1415,13 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material, tex_handle = layer->texture; if (tex_handle != COGL_INVALID_HANDLE) - cogl_texture_get_gl_texture (tex_handle, &gl_texture, &gl_target); + { + _cogl_texture_set_filters (tex_handle, + layer->min_filter, + layer->mag_filter); + + cogl_texture_get_gl_texture (tex_handle, &gl_texture, &gl_target); + } else { new_gl_layer_info.fallback = TRUE; @@ -1431,13 +1452,6 @@ _cogl_material_flush_layers_gl_state (CoglMaterial *material, GE (glActiveTexture (GL_TEXTURE0 + i)); unit = _cogl_get_texture_unit (i); - _cogl_texture_set_filters (layer->texture, - layer->min_filter, - layer->mag_filter); - if (is_mipmap_filter (layer->min_filter) - || is_mipmap_filter (layer->mag_filter)) - _cogl_texture_ensure_mipmaps (layer->texture); - /* FIXME: We could be more clever here and only bind the texture if it is different from gl_layer_info->gl_texture to avoid redundant GL calls. However a few other places in Cogl and @@ -1692,13 +1706,36 @@ _cogl_material_flush_gl_state (CoglHandle handle, 0, sizeof (CoglMaterialFlushOptions)); } +static gboolean +_cogl_material_texture_equal (CoglHandle texture0, CoglHandle texture1) +{ + GLenum gl_handle0, gl_handle1, gl_target0, gl_target1; + + /* If the texture handles are the same then the textures are + definitely equal */ + if (texture0 == texture1) + return TRUE; + + /* If neither texture is sliced then they could still be the same if + the are referring to the same GL texture */ + if (cogl_texture_is_sliced (texture0) || + cogl_texture_is_sliced (texture1)) + return FALSE; + + cogl_texture_get_gl_texture (texture0, &gl_handle0, &gl_target0); + cogl_texture_get_gl_texture (texture1, &gl_handle1, &gl_target1); + + return gl_handle0 == gl_handle1 && gl_target0 == gl_target1; +} + static gboolean _cogl_material_layer_equal (CoglMaterialLayer *material0_layer, CoglHandle material0_layer_texture, CoglMaterialLayer *material1_layer, CoglHandle material1_layer_texture) { - if (material0_layer_texture != material1_layer_texture) + if (!_cogl_material_texture_equal (material0_layer_texture, + material1_layer_texture)) return FALSE; if ((material0_layer->flags & COGL_MATERIAL_LAYER_FLAG_DEFAULT_COMBINE) != diff --git a/cogl/cogl-primitives.c b/cogl/cogl-primitives.c index 60a452dea..726cebaf4 100644 --- a/cogl/cogl-primitives.c +++ b/cogl/cogl-primitives.c @@ -71,8 +71,8 @@ static void log_quad_sub_textures_cb (CoglHandle texture_handle, GLuint gl_handle, GLenum gl_target, - float *subtexture_coords, - float *virtual_coords, + const float *subtexture_coords, + const float *virtual_coords, void *user_data) { TextureSlicedQuadState *state = user_data; @@ -249,6 +249,9 @@ _cogl_multitexture_quad_single_primitive (float x_1, const float *in_tex_coords; float *out_tex_coords; float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0}; + gboolean need_repeat = FALSE; + gint coord_num; + GLenum wrap_mode; tex_handle = cogl_material_layer_get_texture (layer); @@ -257,27 +260,41 @@ _cogl_multitexture_quad_single_primitive (float x_1, if (tex_handle == COGL_INVALID_HANDLE) continue; - in_tex_coords = &user_tex_coords[i * 4]; + /* If the user didn't supply texture coordinates for this layer + then use the default coords */ + if (i >= user_tex_coords_len / 4) + in_tex_coords = default_tex_coords; + else + in_tex_coords = &user_tex_coords[i * 4]; + out_tex_coords = &final_tex_coords[i * 4]; + memcpy (out_tex_coords, in_tex_coords, sizeof (GLfloat) * 4); + + /* Convert the texture coordinates to GL. We also work out + whether any of the texture coordinates are outside the range + [0.0,1.0]. We need to do this after calling + transform_coords_to_gl in case the texture backend is munging + the coordinates (such as in the sub texture backend). This + should be safe to call because we know that the texture only + has one slice. */ + if (!_cogl_texture_transform_quad_coords_to_gl (tex_handle, + out_tex_coords)) + /* If the backend can't support these coordinates then bail out */ + return FALSE; + for (coord_num = 0; coord_num < 4; coord_num++) + if (out_tex_coords[coord_num] < 0.0f || + out_tex_coords[coord_num] > 1.0f) + need_repeat = TRUE; /* If the texture has waste or we are using GL_TEXTURE_RECT we - * can't handle texture repeating so we check that the texture - * coords lie in the range [0,1]. - * - * NB: We already know that the texture isn't sliced so we can assume - * that the default coords (0,0) and (1,1) would only reference a single - * GL texture. + * can't handle texture repeating so we can't use the layer if + * repeating is required. * * NB: We already know that no texture matrix is being used if the * texture doesn't support hardware repeat. */ - if (!_cogl_texture_can_hardware_repeat (tex_handle) - && i < user_tex_coords_len / 4 - && (in_tex_coords[0] < 0 || in_tex_coords[0] > 1.0 - || in_tex_coords[1] < 0 || in_tex_coords[1] > 1.0 - || in_tex_coords[2] < 0 || in_tex_coords[2] > 1.0 - || in_tex_coords[3] < 0 || in_tex_coords[3] > 1.0)) + if (!_cogl_texture_can_hardware_repeat (tex_handle) && need_repeat) { if (i == 0) { @@ -315,45 +332,15 @@ _cogl_multitexture_quad_single_primitive (float x_1, } } - - /* - * Setup the texture unit... - */ - - /* NB: The user might not have supplied texture coordinates for all - * layers... */ - if (i < (user_tex_coords_len / 4)) - { - GLenum wrap_mode; - - /* If the texture coords are all in the range [0,1] then we want to - clamp the coords to the edge otherwise it can pull in edge pixels - from the wrong side when scaled */ - if (in_tex_coords[0] >= 0 && in_tex_coords[0] <= 1.0 - && in_tex_coords[1] >= 0 && in_tex_coords[1] <= 1.0 - && in_tex_coords[2] >= 0 && in_tex_coords[2] <= 1.0 - && in_tex_coords[3] >= 0 && in_tex_coords[3] <= 1.0) - wrap_mode = GL_CLAMP_TO_EDGE; - else - wrap_mode = GL_REPEAT; - - memcpy (out_tex_coords, in_tex_coords, sizeof (GLfloat) * 4); - - _cogl_texture_set_wrap_mode_parameter (tex_handle, wrap_mode); - } + /* If we're not repeating then we want to clamp the coords + to the edge otherwise it can pull in edge pixels from the + wrong side when scaled */ + if (need_repeat) + wrap_mode = GL_REPEAT; else - { - memcpy (out_tex_coords, default_tex_coords, sizeof (GLfloat) * 4); + wrap_mode = GL_CLAMP_TO_EDGE; - _cogl_texture_set_wrap_mode_parameter (tex_handle, GL_CLAMP_TO_EDGE); - } - - _cogl_texture_transform_coords_to_gl (tex_handle, - &out_tex_coords[0], - &out_tex_coords[1]); - _cogl_texture_transform_coords_to_gl (tex_handle, - &out_tex_coords[2], - &out_tex_coords[3]); + _cogl_texture_set_wrap_mode_parameter (tex_handle, wrap_mode); } _cogl_journal_log_quad (x_1, @@ -414,6 +401,12 @@ _cogl_rectangles_with_multitexture_coords ( != COGL_MATERIAL_LAYER_TYPE_TEXTURE) continue; + /* We need to ensure the mipmaps are ready before deciding + anything else about the texture because it could become + something completely different if it needs to be migrated out + of the atlas */ + _cogl_material_layer_ensure_mipmaps (layer); + tex_handle = cogl_material_layer_get_texture (layer); /* COGL_INVALID_HANDLE textures are handled by @@ -651,11 +644,11 @@ cogl_rectangle (float x_1, void draw_polygon_sub_texture_cb (CoglHandle tex_handle, - GLuint gl_handle, - GLenum gl_target, - float *subtexture_coords, - float *virtual_coords, - void *user_data) + GLuint gl_handle, + GLenum gl_target, + const float *subtexture_coords, + const float *virtual_coords, + void *user_data) { TextureSlicedPolygonState *state = user_data; GLfloat *v; @@ -880,6 +873,11 @@ cogl_polygon (const CoglTextureVertex *vertices, if (tex_handle == COGL_INVALID_HANDLE) continue; + /* Give the texture a chance to know that we're rendering + non-quad shaped primitives. If the texture is in an atlas it + will be migrated */ + _cogl_texture_ensure_non_quad_rendering (tex_handle); + if (i == 0 && cogl_texture_is_sliced (tex_handle)) { #if defined (HAVE_COGL_GLES) || defined (HAVE_COGL_GLES2) diff --git a/cogl/cogl-sub-texture-private.h b/cogl/cogl-sub-texture-private.h new file mode 100644 index 000000000..c8fa4bbc9 --- /dev/null +++ b/cogl/cogl-sub-texture-private.h @@ -0,0 +1,55 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_SUB_TEXTURE_H +#define __COGL_SUB_TEXTURE_H + +#include "cogl-handle.h" +#include "cogl-texture-private.h" + +#define COGL_SUB_TEXTURE(tex) ((CoglSubTexture *) tex) + +typedef struct _CoglSubTexture CoglSubTexture; + +struct _CoglSubTexture +{ + CoglTexture _parent; + + CoglHandle full_texture; + + /* The region represented by this sub-texture */ + gint sub_x; + gint sub_y; + gint sub_width; + gint sub_height; +}; + +GQuark +_cogl_handle_sub_texture_get_type (void); + +CoglHandle +_cogl_sub_texture_new (CoglHandle full_texture, + gint sub_x, gint sub_y, + gint sub_width, gint sub_height); + +#endif /* __COGL_SUB_TEXTURE_H */ diff --git a/cogl/cogl-sub-texture.c b/cogl/cogl-sub-texture.c new file mode 100644 index 000000000..53b4d58cc --- /dev/null +++ b/cogl/cogl-sub-texture.c @@ -0,0 +1,539 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-texture-private.h" +#include "cogl-sub-texture-private.h" +#include "cogl-context.h" +#include "cogl-handle.h" +#include "cogl-texture-driver.h" + +#include +#include + +static void _cogl_sub_texture_free (CoglSubTexture *sub_tex); + +COGL_HANDLE_DEFINE (SubTexture, sub_texture); + +static const CoglTextureVtable cogl_sub_texture_vtable; + +static void +_cogl_sub_texture_map_range (gfloat *t1, gfloat *t2, + gint sub_offset, + gint sub_size, + gint full_size) +{ + gfloat t1_frac, t1_int, t2_frac, t2_int; + + t1_frac = modff (*t1, &t1_int); + t2_frac = modff (*t2, &t2_int); + + if (t1_frac < 0.0f) + { + t1_frac += 1.0f; + t1_int -= 1.0f; + } + if (t2_frac < 0.0f) + { + t2_frac += 1.0f; + t2_int -= 1.0f; + } + + /* If one of the coordinates is zero we need to make sure it is + still greater than the other coordinate if it was originally so + we'll flip it to the other side */ + if (*t1 < *t2) + { + if (t2_frac == 0.0f) + { + t2_frac = 1.0f; + t2_int -= 1.0f; + } + } + else + { + if (t1_frac == 0.0f) + { + t1_frac = 1.0f; + t1_int -= 1.0f; + } + } + + /* Convert the fractional part leaving the integer part intact */ + t1_frac = (sub_offset + t1_frac * sub_size) / full_size; + *t1 = t1_frac + t1_int; + + t2_frac = (sub_offset + t2_frac * sub_size) / full_size; + *t2 = t2_frac + t2_int; +} + +static void +_cogl_sub_texture_map_quad (CoglSubTexture *sub_tex, + gfloat *coords) +{ + guint full_width = cogl_texture_get_width (sub_tex->full_texture); + guint full_height = cogl_texture_get_height (sub_tex->full_texture); + + _cogl_sub_texture_map_range (coords + 0, coords + 2, + sub_tex->sub_x, sub_tex->sub_width, + full_width); + _cogl_sub_texture_map_range (coords + 1, coords + 3, + sub_tex->sub_y, sub_tex->sub_height, + full_height); +} + +/* Maps from the texture coordinates of the full texture to the + texture coordinates of the sub texture */ +static gfloat +_cogl_sub_texture_unmap_coord (gfloat t, + gint sub_offset, + gint sub_size, + gint full_size) +{ + gfloat frac_part, int_part; + + /* Convert the fractional part leaving the integer part in tact */ + frac_part = modff (t, &int_part); + + if (signbit (frac_part)) + frac_part = ((1.0f + frac_part) * full_size - + sub_offset - sub_size) / sub_size; + else + frac_part = (frac_part * full_size - sub_offset) / sub_size; + + return frac_part + int_part; +} + +static void +_cogl_sub_texture_unmap_coords (CoglSubTexture *sub_tex, + gfloat *s, + gfloat *t) +{ + guint full_width = cogl_texture_get_width (sub_tex->full_texture); + guint full_height = cogl_texture_get_height (sub_tex->full_texture); + + *s = _cogl_sub_texture_unmap_coord (*s, sub_tex->sub_x, sub_tex->sub_width, + full_width); + *t = _cogl_sub_texture_unmap_coord (*t, sub_tex->sub_y, sub_tex->sub_height, + full_height); +} + +typedef struct _CoglSubTextureForeachData +{ + CoglSubTexture *sub_tex; + CoglTextureSliceCallback callback; + void *user_data; +} CoglSubTextureForeachData; + +static void +_cogl_sub_texture_foreach_cb (CoglHandle handle, + GLuint gl_handle, + GLenum gl_target, + const float *slice_coords, + const float *full_virtual_coords, + void *user_data) +{ + CoglSubTextureForeachData *data = user_data; + float virtual_coords[4]; + + memcpy (virtual_coords, full_virtual_coords, sizeof (virtual_coords)); + /* Convert the virtual coords from the full-texture space to the sub + texture space */ + _cogl_sub_texture_unmap_coords (data->sub_tex, + &virtual_coords[0], + &virtual_coords[1]); + _cogl_sub_texture_unmap_coords (data->sub_tex, + &virtual_coords[2], + &virtual_coords[3]); + + data->callback (handle, gl_handle, gl_target, + slice_coords, virtual_coords, + data->user_data); +} + +static void +_cogl_sub_texture_manual_repeat_cb (const float *coords, + void *user_data) +{ + CoglSubTextureForeachData *data = user_data; + float mapped_coords[4]; + + memcpy (mapped_coords, coords, sizeof (mapped_coords)); + + _cogl_sub_texture_map_quad (data->sub_tex, mapped_coords); + + _cogl_texture_foreach_sub_texture_in_region (data->sub_tex->full_texture, + mapped_coords[0], + mapped_coords[1], + mapped_coords[2], + mapped_coords[3], + _cogl_sub_texture_foreach_cb, + user_data); +} + +static void +_cogl_sub_texture_foreach_sub_texture_in_region ( + CoglTexture *tex, + float virtual_tx_1, + float virtual_ty_1, + float virtual_tx_2, + float virtual_ty_2, + CoglTextureSliceCallback callback, + void *user_data) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + CoglSubTextureForeachData data; + + data.sub_tex = sub_tex; + data.callback = callback; + data.user_data = user_data; + + _cogl_texture_iterate_manual_repeats (_cogl_sub_texture_manual_repeat_cb, + virtual_tx_1, virtual_ty_1, + virtual_tx_2, virtual_ty_2, + &data); +} + +static void +_cogl_sub_texture_set_wrap_mode_parameter (CoglTexture *tex, + GLenum wrap_mode) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + _cogl_texture_set_wrap_mode_parameter (sub_tex->full_texture, wrap_mode); +} + +static void +_cogl_sub_texture_free (CoglSubTexture *sub_tex) +{ + cogl_handle_unref (sub_tex->full_texture); + + g_free (sub_tex); +} + +CoglHandle +_cogl_sub_texture_new (CoglHandle full_texture, + gint sub_x, gint sub_y, + gint sub_width, gint sub_height) +{ + CoglSubTexture *sub_tex; + CoglTexture *tex; + guint full_width, full_height; + + full_width = cogl_texture_get_width (full_texture); + full_height = cogl_texture_get_height (full_texture); + + /* The region must specify a non-zero subset of the full texture */ + g_return_val_if_fail (sub_x >= 0 && sub_y >= 0, COGL_INVALID_HANDLE); + g_return_val_if_fail (sub_width > 0 && sub_height > 0, COGL_INVALID_HANDLE); + g_return_val_if_fail (sub_x + sub_width <= full_width, COGL_INVALID_HANDLE); + g_return_val_if_fail (sub_y + sub_height <= full_height, COGL_INVALID_HANDLE); + + sub_tex = g_new (CoglSubTexture, 1); + + tex = COGL_TEXTURE (sub_tex); + tex->vtable = &cogl_sub_texture_vtable; + + sub_tex->full_texture = cogl_handle_ref (full_texture); + + sub_tex->sub_x = sub_x; + sub_tex->sub_y = sub_y; + sub_tex->sub_width = sub_width; + sub_tex->sub_height = sub_height; + + return _cogl_sub_texture_handle_new (sub_tex); +} + +static gint +_cogl_sub_texture_get_max_waste (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return cogl_texture_get_max_waste (sub_tex->full_texture); +} + +static gboolean +_cogl_sub_texture_is_sliced (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return cogl_texture_is_sliced (sub_tex->full_texture); +} + +static gboolean +_cogl_sub_texture_can_hardware_repeat (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + /* We can hardware repeat if the subtexture actually represents all of the + of the full texture */ + return (sub_tex->sub_width == + cogl_texture_get_width (sub_tex->full_texture) && + sub_tex->sub_height == + cogl_texture_get_height (sub_tex->full_texture) && + _cogl_texture_can_hardware_repeat (sub_tex->full_texture)); +} + +static void +_cogl_sub_texture_transform_coords_to_gl (CoglTexture *tex, + float *s, + float *t) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + /* This won't work if the sub texture is not the size of the full + texture and the coordinates are outside the range [0,1] */ + *s = ((*s * sub_tex->sub_width + sub_tex->sub_x) / + cogl_texture_get_width (sub_tex->full_texture)); + *t = ((*t * sub_tex->sub_height + sub_tex->sub_y) / + cogl_texture_get_height (sub_tex->full_texture)); + + return _cogl_texture_transform_coords_to_gl (sub_tex->full_texture, s, t); +} + +static gboolean +_cogl_sub_texture_transform_quad_coords_to_gl (CoglTexture *tex, + float *coords) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + int i; + + /* We can't support repeating with this method. In this case + cogl-primitives will resort to manual repeating */ + for (i = 0; i < 4; i++) + if (coords[i] < 0.0f || coords[i] > 1.0f) + return FALSE; + + _cogl_sub_texture_map_quad (sub_tex, coords); + + _cogl_texture_transform_quad_coords_to_gl (sub_tex->full_texture, coords); + + return TRUE; +} + +static gboolean +_cogl_sub_texture_get_gl_texture (CoglTexture *tex, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return cogl_texture_get_gl_texture (sub_tex->full_texture, + out_gl_handle, + out_gl_target); +} + +static void +_cogl_sub_texture_set_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + _cogl_texture_set_filters (sub_tex->full_texture, min_filter, mag_filter); +} + +static void +_cogl_sub_texture_ensure_mipmaps (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + _cogl_texture_ensure_mipmaps (sub_tex->full_texture); +} + +static void +_cogl_sub_texture_ensure_non_quad_rendering (CoglTexture *tex) +{ +} + +static gboolean +_cogl_sub_texture_set_region (CoglTexture *tex, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int dst_width, + unsigned int dst_height, + int width, + int height, + CoglPixelFormat format, + unsigned int rowstride, + const guint8 *data) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return cogl_texture_set_region (sub_tex->full_texture, + src_x, src_y, + dst_x + sub_tex->sub_x, + dst_y + sub_tex->sub_y, + dst_width, dst_height, + width, height, + format, + rowstride, + data); +} + +static void +_cogl_sub_texture_copy_region (guchar *dst, + const guchar *src, + gint dst_x, gint dst_y, + gint src_x, gint src_y, + gint width, gint height, + gint dst_rowstride, + gint src_rowstride, + gint bpp) +{ + int y; + + dst += dst_x * bpp + dst_y * dst_rowstride; + src += src_x * bpp + src_y * src_rowstride; + + for (y = 0; y < height; y++) + { + memcpy (dst, src, bpp * width); + dst += dst_rowstride; + src += src_rowstride; + } +} + +static int +_cogl_sub_texture_get_data (CoglTexture *tex, + CoglPixelFormat format, + unsigned int rowstride, + guint8 *data) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + unsigned int full_rowstride; + guint8 *full_data; + int byte_size, full_size; + gint bpp; + gint full_tex_width, full_tex_height; + + /* FIXME: This gets the full data from the full texture and then + copies a subregion of that. It would be better if there was a + texture_get_sub_data virtual and it can just munge the texture + coordinates */ + + /* Default to internal format if none specified */ + if (format == COGL_PIXEL_FORMAT_ANY) + format = cogl_texture_get_format (sub_tex->full_texture); + + full_tex_width = cogl_texture_get_width (sub_tex->full_texture); + full_tex_height = cogl_texture_get_height (sub_tex->full_texture); + + /* Rowstride from texture width if none specified */ + bpp = _cogl_get_format_bpp (format); + if (rowstride == 0) + rowstride = sub_tex->sub_width * bpp; + + /* Return byte size if only that requested */ + byte_size = sub_tex->sub_height * rowstride; + if (data == NULL) + return byte_size; + + full_rowstride = _cogl_get_format_bpp (format) * full_tex_width; + full_data = g_malloc (full_rowstride * full_tex_height); + + full_size = cogl_texture_get_data (sub_tex->full_texture, format, + full_rowstride, full_data); + + if (full_size) + _cogl_sub_texture_copy_region (data, full_data, + 0, 0, + sub_tex->sub_x, + sub_tex->sub_y, + sub_tex->sub_width, + sub_tex->sub_height, + rowstride, + full_rowstride, + bpp); + else + byte_size = 0; + + g_free (full_data); + + return byte_size; +} + +static CoglPixelFormat +_cogl_sub_texture_get_format (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return cogl_texture_get_format (sub_tex->full_texture); +} + +static GLenum +_cogl_sub_texture_get_gl_format (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return _cogl_texture_get_gl_format (sub_tex->full_texture); +} + +static gint +_cogl_sub_texture_get_width (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return sub_tex->sub_width; +} + +static gint +_cogl_sub_texture_get_height (CoglTexture *tex) +{ + CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); + + return sub_tex->sub_height; +} + +static const CoglTextureVtable +cogl_sub_texture_vtable = + { + _cogl_sub_texture_set_region, + _cogl_sub_texture_get_data, + _cogl_sub_texture_foreach_sub_texture_in_region, + _cogl_sub_texture_get_max_waste, + _cogl_sub_texture_is_sliced, + _cogl_sub_texture_can_hardware_repeat, + _cogl_sub_texture_transform_coords_to_gl, + _cogl_sub_texture_transform_quad_coords_to_gl, + _cogl_sub_texture_get_gl_texture, + _cogl_sub_texture_set_filters, + _cogl_sub_texture_ensure_mipmaps, + _cogl_sub_texture_ensure_non_quad_rendering, + _cogl_sub_texture_set_wrap_mode_parameter, + _cogl_sub_texture_get_format, + _cogl_sub_texture_get_gl_format, + _cogl_sub_texture_get_width, + _cogl_sub_texture_get_height + }; diff --git a/cogl/cogl-texture-2d-private.h b/cogl/cogl-texture-2d-private.h new file mode 100644 index 000000000..69910db66 --- /dev/null +++ b/cogl/cogl-texture-2d-private.h @@ -0,0 +1,69 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __COGL_TEXTURE_2D_H +#define __COGL_TEXTURE_2D_H + +#include "cogl-handle.h" +#include "cogl-material-private.h" +#include "cogl-texture-private.h" + +#define COGL_TEXTURE_2D(tex) ((CoglTexture2D *) tex) + +typedef struct _CoglTexture2D CoglTexture2D; + +struct _CoglTexture2D +{ + CoglTexture _parent; + + /* The internal format of the GL texture represented as a + CoglPixelFormat */ + CoglPixelFormat format; + /* The internal format of the GL texture represented as a GL enum */ + GLenum gl_format; + /* The texture object number */ + GLuint gl_texture; + gint width; + gint height; + GLenum min_filter; + GLenum mag_filter; + GLint wrap_mode; + gboolean auto_mipmap; + gboolean mipmaps_dirty; +}; + +GQuark +_cogl_handle_texture_2d_get_type (void); + +CoglHandle +_cogl_texture_2d_new_with_size (unsigned int width, + unsigned int height, + CoglTextureFlags flags, + CoglPixelFormat internal_format); + +CoglHandle +_cogl_texture_2d_new_from_bitmap (CoglHandle bmp_handle, + CoglTextureFlags flags, + CoglPixelFormat internal_format); + +#endif /* __COGL_TEXTURE_2D_H */ diff --git a/cogl/cogl-texture-2d-sliced-private.h b/cogl/cogl-texture-2d-sliced-private.h index 99f0e722a..dfde144ac 100644 --- a/cogl/cogl-texture-2d-sliced-private.h +++ b/cogl/cogl-texture-2d-sliced-private.h @@ -49,15 +49,30 @@ struct _CoglTexturePixel struct _CoglTexture2DSliced { CoglTexture _parent; - GArray *slice_x_spans; - GArray *slice_y_spans; - GArray *slice_gl_handles; - gint max_waste; + GArray *slice_x_spans; + GArray *slice_y_spans; + GArray *slice_gl_handles; + gint max_waste; + + /* The internal format of the GL texture represented as a + CoglPixelFormat */ + CoglPixelFormat format; + /* The internal format of the GL texture represented as a GL enum */ + GLenum gl_format; + GLenum gl_target; + gint width; + gint height; + GLenum min_filter; + GLenum mag_filter; + gboolean is_foreign; + GLint wrap_mode; + gboolean auto_mipmap; + gboolean mipmaps_dirty; /* This holds a copy of the first pixel in each slice. It is only used to force an automatic update of the mipmaps when glGenerateMipmap is not available. */ - CoglTexturePixel *first_pixels; + CoglTexturePixel *first_pixels; }; GQuark @@ -69,12 +84,6 @@ _cogl_texture_2d_sliced_new_with_size (unsigned int width, CoglTextureFlags flags, CoglPixelFormat internal_format); -CoglHandle -_cogl_texture_2d_sliced_new_from_file (const gchar *filename, - CoglTextureFlags flags, - CoglPixelFormat internal_format, - GError **error); - CoglHandle _cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle, GLenum gl_target, @@ -84,16 +93,6 @@ _cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle, GLuint y_pot_waste, CoglPixelFormat format); -CoglHandle -_cogl_texture_2d_sliced_new_from_data (unsigned int width, - unsigned int height, - CoglTextureFlags flags, - CoglPixelFormat format, - CoglPixelFormat internal_format, - unsigned int rowstride, - const guint8 *data); - - CoglHandle _cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle, CoglTextureFlags flags, diff --git a/cogl/cogl-texture-2d-sliced.c b/cogl/cogl-texture-2d-sliced.c index 56a4fdbea..0cfef1f58 100644 --- a/cogl/cogl-texture-2d-sliced.c +++ b/cogl/cogl-texture-2d-sliced.c @@ -71,12 +71,12 @@ _cogl_texture_2d_sliced_foreach_sub_texture_in_region ( void *user_data) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); - float width = tex->bitmap.width; - float height = tex->bitmap.height; + float width = tex_2ds->width; + float height = tex_2ds->height; CoglSpanIter iter_x; CoglSpanIter iter_y; - g_assert (tex->gl_target == GL_TEXTURE_2D); + g_assert (tex_2ds->gl_target == GL_TEXTURE_2D); /* Slice spans are stored in denormalized coordinates, and this is what * the _cogl_span_iter_* funcs expect to be given, so we scale the given @@ -173,7 +173,7 @@ _cogl_texture_2d_sliced_foreach_sub_texture_in_region ( callback (tex, gl_handle, - tex->gl_target, + tex_2ds->gl_target, slice_coords, virtual_coords, user_data); @@ -182,12 +182,12 @@ _cogl_texture_2d_sliced_foreach_sub_texture_in_region ( } static guchar * -_cogl_texture_2d_sliced_allocate_waste_buffer (CoglTexture2DSliced *tex_2ds) +_cogl_texture_2d_sliced_allocate_waste_buffer (CoglTexture2DSliced *tex_2ds, + CoglPixelFormat format) { CoglSpan *last_x_span; CoglSpan *last_y_span; guchar *waste_buf = NULL; - CoglTexture *tex = COGL_TEXTURE (tex_2ds); /* If the texture has any waste then allocate a buffer big enough to fill the gaps */ @@ -197,7 +197,7 @@ _cogl_texture_2d_sliced_allocate_waste_buffer (CoglTexture2DSliced *tex_2ds) tex_2ds->slice_y_spans->len - 1); if (last_x_span->waste > 0 || last_y_span->waste > 0) { - gint bpp = _cogl_get_format_bpp (tex->bitmap.format); + gint bpp = _cogl_get_format_bpp (format); CoglSpan *first_x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0); CoglSpan *first_y_span @@ -212,7 +212,11 @@ _cogl_texture_2d_sliced_allocate_waste_buffer (CoglTexture2DSliced *tex_2ds) } static gboolean -_cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds) +_cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds, + CoglBitmap *bmp, + GLenum gl_intformat, + GLenum gl_format, + GLenum gl_type) { CoglSpan *x_span; CoglSpan *y_span; @@ -220,11 +224,11 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds) gint bpp; gint x,y; guchar *waste_buf; - CoglTexture *tex = COGL_TEXTURE (tex_2ds); - bpp = _cogl_get_format_bpp (tex->bitmap.format); + bpp = _cogl_get_format_bpp (bmp->format); - waste_buf = _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds); + waste_buf = _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds, + bmp->format); /* Iterate vertical slices */ for (y = 0; y < tex_2ds->slice_y_spans->len; ++y) @@ -242,34 +246,34 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds) gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, slice_num); _cogl_texture_driver_upload_subregion_to_gl ( - tex, + tex_2ds->gl_target, + gl_handle, x_span->start, /* src x */ y_span->start, /* src y */ 0, /* dst x */ 0, /* dst y */ x_span->size - x_span->waste, /* width */ y_span->size - y_span->waste, /* height */ - &tex->bitmap, - tex->gl_format, - tex->gl_type, - gl_handle); + bmp, + gl_format, + gl_type); /* Keep a copy of the first pixel if needed */ if (tex_2ds->first_pixels) { memcpy (tex_2ds->first_pixels[slice_num].data, - tex->bitmap.data + x_span->start * bpp - + y_span->start * tex->bitmap.rowstride, + bmp->data + x_span->start * bpp + + y_span->start * bmp->rowstride, bpp); - tex_2ds->first_pixels[slice_num].gl_format = tex->gl_format; - tex_2ds->first_pixels[slice_num].gl_type = tex->gl_type; + tex_2ds->first_pixels[slice_num].gl_format = gl_format; + tex_2ds->first_pixels[slice_num].gl_type = gl_type; } /* Fill the waste with a copies of the rightmost pixels */ if (x_span->waste > 0) { - const guchar *src = tex->bitmap.data - + y_span->start * tex->bitmap.rowstride + const guchar *src = bmp->data + + y_span->start * bmp->rowstride + (x_span->start + x_span->size - x_span->waste - 1) * bpp; guchar *dst = waste_buf; guint wx, wy; @@ -281,27 +285,27 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds) memcpy (dst, src, bpp); dst += bpp; } - src += tex->bitmap.rowstride; + src += bmp->rowstride; } _cogl_texture_driver_prep_gl_for_pixels_upload ( x_span->waste * bpp, bpp); - GE( glTexSubImage2D (tex->gl_target, 0, + GE( glTexSubImage2D (tex_2ds->gl_target, 0, x_span->size - x_span->waste, 0, x_span->waste, y_span->size - y_span->waste, - tex->gl_format, tex->gl_type, + gl_format, gl_type, waste_buf) ); } if (y_span->waste > 0) { - const guchar *src = tex->bitmap.data + const guchar *src = bmp->data + ((y_span->start + y_span->size - y_span->waste - 1) - * tex->bitmap.rowstride) + * bmp->rowstride) + x_span->start * bpp; guchar *dst = waste_buf; guint wy, wx; @@ -322,12 +326,12 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds) x_span->size * bpp, bpp); - GE( glTexSubImage2D (tex->gl_target, 0, + GE( glTexSubImage2D (tex_2ds->gl_target, 0, 0, y_span->size - y_span->waste, x_span->size, y_span->waste, - tex->gl_format, tex->gl_type, + gl_format, gl_type, waste_buf) ); } } @@ -336,7 +340,7 @@ _cogl_texture_2d_sliced_upload_to_gl (CoglTexture2DSliced *tex_2ds) if (waste_buf) g_free (waste_buf); - tex->mipmaps_dirty = TRUE; + tex_2ds->mipmaps_dirty = TRUE; return TRUE; } @@ -353,7 +357,6 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, GLuint source_gl_format, GLuint source_gl_type) { - CoglTexture *tex = COGL_TEXTURE (tex_2ds); CoglSpan *x_span; CoglSpan *y_span; gint bpp; @@ -367,13 +370,14 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, bpp = _cogl_get_format_bpp (source_bmp->format); - waste_buf = _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds); + waste_buf = + _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds, source_bmp->format); /* Iterate vertical spans */ for (source_y = src_y, _cogl_span_iter_begin (&y_iter, tex_2ds->slice_y_spans, - tex->bitmap.height, + tex_2ds->height, dst_y, dst_y + height); @@ -396,7 +400,7 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, for (source_x = src_x, _cogl_span_iter_begin (&x_iter, tex_2ds->slice_x_spans, - tex->bitmap.width, + tex_2ds->width, dst_x, dst_x + width); @@ -430,7 +434,8 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, /* Pick slice GL handle */ gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, slice_num); - _cogl_texture_driver_upload_subregion_to_gl (tex, + _cogl_texture_driver_upload_subregion_to_gl (tex_2ds->gl_target, + gl_handle, source_x, source_y, local_x, /* dst x */ @@ -439,8 +444,7 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, inter_h, /* height */ source_bmp, source_gl_format, - source_gl_type, - gl_handle); + source_gl_type); /* Keep a copy of the first pixel if needed */ if (tex_2ds->first_pixels && local_x == 0 && local_y == 0) @@ -488,7 +492,7 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, x_span->waste * bpp, bpp); - GE( glTexSubImage2D (tex->gl_target, 0, + GE( glTexSubImage2D (tex_2ds->gl_target, 0, x_span->size - x_span->waste, local_y, x_span->waste, @@ -539,7 +543,7 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, copy_width * bpp, bpp); - GE( glTexSubImage2D (tex->gl_target, 0, + GE( glTexSubImage2D (tex_2ds->gl_target, 0, local_x, y_span->size - y_span->waste, copy_width, @@ -554,7 +558,7 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds, if (waste_buf) g_free (waste_buf); - tex->mipmaps_dirty = TRUE; + tex_2ds->mipmaps_dirty = TRUE; return TRUE; } @@ -659,7 +663,7 @@ _cogl_texture_2d_sliced_set_wrap_mode_parameter (CoglTexture *tex, /* Only set the wrap mode if it's different from the current value to avoid too many GL calls */ - if (tex->wrap_mode != wrap_mode) + if (tex_2ds->wrap_mode != wrap_mode) { int i; @@ -671,20 +675,22 @@ _cogl_texture_2d_sliced_set_wrap_mode_parameter (CoglTexture *tex, { GLuint texnum = g_array_index (tex_2ds->slice_gl_handles, GLuint, i); - GE( glBindTexture (tex->gl_target, texnum) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_S, wrap_mode) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_WRAP_T, wrap_mode) ); + GE( glBindTexture (tex_2ds->gl_target, texnum) ); + GE( glTexParameteri (tex_2ds->gl_target, GL_TEXTURE_WRAP_S, wrap_mode) ); + GE( glTexParameteri (tex_2ds->gl_target, GL_TEXTURE_WRAP_T, wrap_mode) ); } - tex->wrap_mode = wrap_mode; + tex_2ds->wrap_mode = wrap_mode; } } static gboolean -_cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) +_cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds, + gint width, gint height, + GLenum gl_intformat, + GLenum gl_format, + GLenum gl_type) { - CoglTexture *tex = COGL_TEXTURE (tex_2ds); - gint bpp; gint max_width; gint max_height; GLuint *gl_handles; @@ -698,21 +704,19 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) gint (*slices_for_size) (gint, gint, gint, GArray*); - bpp = _cogl_get_format_bpp (tex->bitmap.format); - /* Initialize size of largest slice according to supported features */ if (cogl_features_available (COGL_FEATURE_TEXTURE_NPOT)) { - max_width = tex->bitmap.width; - max_height = tex->bitmap.height; - tex->gl_target = GL_TEXTURE_2D; + max_width = width; + max_height = height; + tex_2ds->gl_target = GL_TEXTURE_2D; slices_for_size = _cogl_rect_slices_for_size; } else { - max_width = cogl_util_next_p2 (tex->bitmap.width); - max_height = cogl_util_next_p2 (tex->bitmap.height); - tex->gl_target = GL_TEXTURE_2D; + max_width = cogl_util_next_p2 (width); + max_height = cogl_util_next_p2 (height); + tex_2ds->gl_target = GL_TEXTURE_2D; slices_for_size = _cogl_pot_slices_for_size; } @@ -722,9 +726,9 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) CoglSpan span; /* Check if size supported else bail out */ - if (!_cogl_texture_driver_size_supported (tex->gl_target, - tex->gl_format, - tex->gl_type, + if (!_cogl_texture_driver_size_supported (tex_2ds->gl_target, + gl_intformat, + gl_type, max_width, max_height)) { @@ -746,19 +750,19 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) /* Add a single span for width and height */ span.start = 0; span.size = max_width; - span.waste = max_width - tex->bitmap.width; + span.waste = max_width - width; g_array_append_val (tex_2ds->slice_x_spans, span); span.size = max_height; - span.waste = max_height - tex->bitmap.height; + span.waste = max_height - height; g_array_append_val (tex_2ds->slice_y_spans, span); } else { /* Decrease the size of largest slice until supported by GL */ - while (!_cogl_texture_driver_size_supported (tex->gl_target, - tex->gl_format, - tex->gl_type, + while (!_cogl_texture_driver_size_supported (tex_2ds->gl_target, + gl_intformat, + gl_type, max_width, max_height)) { @@ -773,11 +777,11 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) } /* Determine the slices required to cover the bitmap area */ - n_x_slices = slices_for_size (tex->bitmap.width, + n_x_slices = slices_for_size (width, max_width, tex_2ds->max_waste, NULL); - n_y_slices = slices_for_size (tex->bitmap.height, + n_y_slices = slices_for_size (height, max_height, tex_2ds->max_waste, NULL); @@ -791,11 +795,11 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) n_y_slices); /* Fill span arrays with info */ - slices_for_size (tex->bitmap.width, + slices_for_size (width, max_width, tex_2ds->max_waste, tex_2ds->slice_x_spans); - slices_for_size (tex->bitmap.height, + slices_for_size (height, max_height, tex_2ds->max_waste, tex_2ds->slice_y_spans); } @@ -818,7 +822,7 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) tex_2ds->first_pixels = g_new (CoglTexturePixel, n_slices); /* Wrap mode not yet set */ - tex->wrap_mode = GL_FALSE; + tex_2ds->wrap_mode = GL_FALSE; /* Generate a "working set" of GL texture objects * (some implementations might supported faster @@ -843,17 +847,17 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) y_span->size - y_span->waste); /* Setup texture parameters */ - GE( _cogl_texture_driver_bind (tex->gl_target, + GE( _cogl_texture_driver_bind (tex_2ds->gl_target, gl_handles[y * n_x_slices + x], - tex->gl_intformat) ); + gl_intformat) ); - _cogl_texture_driver_try_setting_gl_border_color (tex->gl_target, + _cogl_texture_driver_try_setting_gl_border_color (tex_2ds->gl_target, transparent_color); /* Pass NULL data to init size and internal format */ - GE( glTexImage2D (tex->gl_target, 0, tex->gl_intformat, + GE( glTexImage2D (tex_2ds->gl_target, 0, gl_intformat, x_span->size, y_span->size, 0, - tex->gl_format, tex->gl_type, 0) ); + gl_format, gl_type, 0) ); } } @@ -863,8 +867,6 @@ _cogl_texture_2d_sliced_slices_create (CoglTexture2DSliced *tex_2ds) static void _cogl_texture_2d_sliced_slices_free (CoglTexture2DSliced *tex_2ds) { - CoglTexture *tex = COGL_TEXTURE (tex_2ds); - if (tex_2ds->slice_x_spans != NULL) g_array_free (tex_2ds->slice_x_spans, TRUE); @@ -873,7 +875,7 @@ _cogl_texture_2d_sliced_slices_free (CoglTexture2DSliced *tex_2ds) if (tex_2ds->slice_gl_handles != NULL) { - if (tex->is_foreign == FALSE) + if (tex_2ds->is_foreign == FALSE) { GE( glDeleteTextures (tex_2ds->slice_gl_handles->len, (GLuint*) tex_2ds->slice_gl_handles->data) ); @@ -889,158 +891,142 @@ _cogl_texture_2d_sliced_slices_free (CoglTexture2DSliced *tex_2ds) static void _cogl_texture_2d_sliced_free (CoglTexture2DSliced *tex_2ds) { - /* Chain up to parent */ - _cogl_texture_free (COGL_TEXTURE (tex_2ds)); - _cogl_texture_2d_sliced_slices_free (tex_2ds); g_free (tex_2ds); } +static gboolean +_cogl_texture_2d_sliced_upload_from_data + (CoglTexture2DSliced *tex_2ds, + CoglBitmap *bmp, + CoglPixelFormat internal_format) +{ + CoglTexture *tex = COGL_TEXTURE (tex_2ds); + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + + tex->vtable = &cogl_texture_2d_sliced_vtable; + + tex_2ds->is_foreign = FALSE; + tex_2ds->auto_mipmap = FALSE; + tex_2ds->mipmaps_dirty = TRUE; + tex_2ds->first_pixels = NULL; + + tex_2ds->slice_x_spans = NULL; + tex_2ds->slice_y_spans = NULL; + tex_2ds->slice_gl_handles = NULL; + + /* Unknown filter */ + tex_2ds->min_filter = GL_FALSE; + tex_2ds->mag_filter = GL_FALSE; + + if (bmp->data) + { + CoglBitmap dst_bmp; + gboolean dst_bmp_owner; + + if (!_cogl_texture_prepare_for_upload (bmp, + internal_format, + &internal_format, + &dst_bmp, + &dst_bmp_owner, + &gl_intformat, + &gl_format, + &gl_type)) + return FALSE; + + /* Create slices for the given format and size */ + if (!_cogl_texture_2d_sliced_slices_create (tex_2ds, + bmp->width, + bmp->height, + gl_intformat, + gl_format, + gl_type)) + { + if (dst_bmp_owner) + g_free (dst_bmp.data); + + return FALSE; + } + + if (!_cogl_texture_2d_sliced_upload_to_gl (tex_2ds, + bmp, + gl_intformat, + gl_format, + gl_type)) + { + if (dst_bmp_owner) + g_free (dst_bmp.data); + + return FALSE; + } + + if (dst_bmp_owner) + g_free (dst_bmp.data); + } + else + { + /* Find closest GL format match */ + _cogl_pixel_format_to_gl (internal_format, + &gl_intformat, + &gl_format, + &gl_type); + + /* Create slices for the given format and size */ + if (!_cogl_texture_2d_sliced_slices_create (tex_2ds, + bmp->width, + bmp->height, + gl_intformat, + gl_format, + gl_type)) + return FALSE; + } + + tex_2ds->gl_format = gl_intformat; + tex_2ds->width = bmp->width; + tex_2ds->height = bmp->height; + tex_2ds->format = bmp->format; + + return TRUE; +} + CoglHandle _cogl_texture_2d_sliced_new_with_size (unsigned int width, unsigned int height, CoglTextureFlags flags, CoglPixelFormat internal_format) { - CoglTexture2DSliced *tex_2ds; - CoglTexture *tex; - gint bpp; - gint rowstride; + CoglTexture2DSliced *tex_2ds; + CoglTexture *tex; + CoglBitmap bmp; /* Since no data, we need some internal format */ if (internal_format == COGL_PIXEL_FORMAT_ANY) internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; - /* Rowstride from width */ - bpp = _cogl_get_format_bpp (internal_format); - rowstride = width * bpp; - /* Init texture with empty bitmap */ tex_2ds = g_new (CoglTexture2DSliced, 1); tex = COGL_TEXTURE (tex_2ds); - tex->vtable = &cogl_texture_2d_sliced_vtable; - tex->is_foreign = FALSE; - tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; - tex->mipmaps_dirty = TRUE; - tex_2ds->first_pixels = NULL; + bmp.width = width; + bmp.height = height; + bmp.data = NULL; - tex->bitmap.width = width; - tex->bitmap.height = height; - tex->bitmap.format = internal_format; - tex->bitmap.rowstride = rowstride; - tex->bitmap.data = NULL; - tex->bitmap_owner = FALSE; - - tex_2ds->slice_x_spans = NULL; - tex_2ds->slice_y_spans = NULL; - tex_2ds->slice_gl_handles = NULL; - - if (flags & COGL_TEXTURE_NO_SLICING) + if ((flags & COGL_TEXTURE_NO_SLICING)) tex_2ds->max_waste = -1; else tex_2ds->max_waste = COGL_TEXTURE_MAX_WASTE; - /* Unknown filter */ - tex->min_filter = GL_FALSE; - tex->mag_filter = GL_FALSE; - - /* Find closest GL format match */ - tex->bitmap.format = - _cogl_pixel_format_to_gl (internal_format, - &tex->gl_intformat, - &tex->gl_format, - &tex->gl_type); - - /* Create slices for the given format and size */ - if (!_cogl_texture_2d_sliced_slices_create (tex_2ds)) + if (!_cogl_texture_2d_sliced_upload_from_data (tex_2ds, &bmp, + internal_format)) { _cogl_texture_2d_sliced_free (tex_2ds); return COGL_INVALID_HANDLE; } - return _cogl_texture_2d_sliced_handle_new (tex_2ds); -} - -CoglHandle -_cogl_texture_2d_sliced_new_from_data (unsigned int width, - unsigned int height, - CoglTextureFlags flags, - CoglPixelFormat format, - CoglPixelFormat internal_format, - unsigned int rowstride, - const guint8 *data) -{ - CoglTexture2DSliced *tex_2ds; - CoglTexture *tex; - int bpp; - - if (format == COGL_PIXEL_FORMAT_ANY) - return COGL_INVALID_HANDLE; - - if (data == NULL) - return COGL_INVALID_HANDLE; - - /* Rowstride from width if not given */ - bpp = _cogl_get_format_bpp (format); - if (rowstride == 0) rowstride = width * bpp; - - /* Create new texture and fill with given data */ - tex_2ds = g_new0 (CoglTexture2DSliced, 1); - - tex = COGL_TEXTURE (tex_2ds); - tex->vtable = &cogl_texture_2d_sliced_vtable; - - tex->is_foreign = FALSE; - tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; - tex->mipmaps_dirty = TRUE; - tex_2ds->first_pixels = NULL; - - tex->bitmap.width = width; - tex->bitmap.height = height; - tex->bitmap.data = (guchar*)data; - tex->bitmap.format = format; - tex->bitmap.rowstride = rowstride; - tex->bitmap_owner = FALSE; - - tex_2ds->slice_x_spans = NULL; - tex_2ds->slice_y_spans = NULL; - tex_2ds->slice_gl_handles = NULL; - - if (flags & COGL_TEXTURE_NO_SLICING) - tex_2ds->max_waste = -1; - else - tex_2ds->max_waste = COGL_TEXTURE_MAX_WASTE; - - /* Unknown filter */ - tex->min_filter = GL_FALSE; - tex->mag_filter = GL_FALSE; - - /* FIXME: If upload fails we should set some kind of - * error flag but still return texture handle (this - * is to keep the behavior equal to _new_from_file; - * see below) */ - - if (!_cogl_texture_bitmap_prepare (tex, internal_format)) - { - _cogl_texture_2d_sliced_free (tex_2ds); - return COGL_INVALID_HANDLE; - } - - if (!_cogl_texture_2d_sliced_slices_create (tex_2ds)) - { - _cogl_texture_2d_sliced_free (tex_2ds); - return COGL_INVALID_HANDLE; - } - - if (!_cogl_texture_2d_sliced_upload_to_gl (tex_2ds)) - { - _cogl_texture_2d_sliced_free (tex_2ds); - return COGL_INVALID_HANDLE; - } - - _cogl_texture_bitmap_free (COGL_TEXTURE (tex_2ds)); + tex_2ds->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; return _cogl_texture_2d_sliced_handle_new (tex_2ds); } @@ -1050,9 +1036,9 @@ _cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle, CoglTextureFlags flags, CoglPixelFormat internal_format) { - CoglTexture2DSliced *tex_2ds; - CoglTexture *tex; - CoglBitmap *bmp = (CoglBitmap *)bmp_handle; + CoglTexture2DSliced *tex_2ds; + CoglTexture *tex; + CoglBitmap *bmp = (CoglBitmap *)bmp_handle; g_return_val_if_fail (bmp_handle != COGL_INVALID_HANDLE, COGL_INVALID_HANDLE); @@ -1060,29 +1046,12 @@ _cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle, tex_2ds = g_new0 (CoglTexture2DSliced, 1); tex = COGL_TEXTURE (tex_2ds); - tex->vtable = &cogl_texture_2d_sliced_vtable; - - tex->is_foreign = FALSE; - tex->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; - tex->mipmaps_dirty = TRUE; - tex_2ds->first_pixels = NULL; - - tex->bitmap = *bmp; - tex->bitmap_owner = FALSE; - - tex_2ds->slice_x_spans = NULL; - tex_2ds->slice_y_spans = NULL; - tex_2ds->slice_gl_handles = NULL; if (flags & COGL_TEXTURE_NO_SLICING) tex_2ds->max_waste = -1; else tex_2ds->max_waste = COGL_TEXTURE_MAX_WASTE; - /* Unknown filter */ - tex->min_filter = GL_FALSE; - tex->mag_filter = GL_FALSE; - /* FIXME: If upload fails we should set some kind of * error flag but still return texture handle if the * user decides to destroy another texture and upload @@ -1091,52 +1060,18 @@ _cogl_texture_2d_sliced_new_from_bitmap (CoglHandle bmp_handle, * CoglHandle is returned, it should also be destroyed * with cogl_handle_unref at some point! */ - if (!_cogl_texture_bitmap_prepare (tex, internal_format)) + if (!_cogl_texture_2d_sliced_upload_from_data (tex_2ds, bmp, + internal_format)) { _cogl_texture_2d_sliced_free (tex_2ds); return COGL_INVALID_HANDLE; } - if (!_cogl_texture_2d_sliced_slices_create (tex_2ds)) - { - _cogl_texture_2d_sliced_free (tex_2ds); - return COGL_INVALID_HANDLE; - } - - if (!_cogl_texture_2d_sliced_upload_to_gl (tex_2ds)) - { - _cogl_texture_2d_sliced_free (tex_2ds); - return COGL_INVALID_HANDLE; - } - - _cogl_texture_bitmap_free (tex); + tex_2ds->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; return _cogl_texture_2d_sliced_handle_new (tex_2ds); } -CoglHandle -_cogl_texture_2d_sliced_new_from_file ( - const char *filename, - CoglTextureFlags flags, - CoglPixelFormat internal_format, - GError **error) -{ - CoglHandle bmp; - CoglHandle handle; - - g_return_val_if_fail (error == NULL || *error == NULL, COGL_INVALID_HANDLE); - - bmp = cogl_bitmap_new_from_file (filename, error); - if (bmp == COGL_INVALID_HANDLE) - return COGL_INVALID_HANDLE; - - handle = - _cogl_texture_2d_sliced_new_from_bitmap (bmp, flags, internal_format); - cogl_handle_unref (bmp); - - return handle; -} - CoglHandle _cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle, GLenum gl_target, @@ -1160,7 +1095,6 @@ _cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle, GLint gl_width = 0; GLint gl_height = 0; GLint gl_gen_mipmap; - guint bpp; CoglTexture2DSliced *tex_2ds; CoglTexture *tex; CoglSpan x_span; @@ -1245,30 +1179,24 @@ _cogl_texture_2d_sliced_new_from_foreign (GLuint gl_handle, tex->vtable = &cogl_texture_2d_sliced_vtable; /* Setup bitmap info */ - tex->is_foreign = TRUE; - tex->auto_mipmap = (gl_gen_mipmap == GL_TRUE) ? TRUE : FALSE; - tex->mipmaps_dirty = TRUE; + tex_2ds->is_foreign = TRUE; + tex_2ds->auto_mipmap = (gl_gen_mipmap == GL_TRUE) ? TRUE : FALSE; + tex_2ds->mipmaps_dirty = TRUE; tex_2ds->first_pixels = NULL; - bpp = _cogl_get_format_bpp (format); - tex->bitmap.format = format; - tex->bitmap.width = gl_width - x_pot_waste; - tex->bitmap.height = gl_height - y_pot_waste; - tex->bitmap.rowstride = tex->bitmap.width * bpp; - tex->bitmap_owner = FALSE; - - tex->gl_target = gl_target; - tex->gl_intformat = gl_int_format; - tex->gl_format = gl_int_format; - tex->gl_type = GL_UNSIGNED_BYTE; + tex_2ds->format = format; + tex_2ds->width = gl_width - x_pot_waste; + tex_2ds->height = gl_height - y_pot_waste; + tex_2ds->gl_target = gl_target; + tex_2ds->gl_format = gl_int_format; /* Unknown filter */ - tex->min_filter = GL_FALSE; - tex->mag_filter = GL_FALSE; + tex_2ds->min_filter = GL_FALSE; + tex_2ds->mag_filter = GL_FALSE; tex_2ds->max_waste = 0; /* Wrap mode not yet set */ - tex->wrap_mode = GL_FALSE; + tex_2ds->wrap_mode = GL_FALSE; /* Create slice arrays */ tex_2ds->slice_x_spans = @@ -1333,6 +1261,12 @@ _cogl_texture_2d_sliced_can_hardware_repeat (CoglTexture *tex) x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0); y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, 0); +#if HAVE_COGL_GL + /* TODO: COGL_TEXTURE_TYPE_2D_RECTANGLE */ + if (tex_2ds->gl_target == GL_TEXTURE_RECTANGLE_ARB) + return FALSE; +#endif + return (x_span->waste || y_span->waste) ? FALSE : TRUE; } @@ -1351,12 +1285,12 @@ _cogl_texture_2d_sliced_transform_coords_to_gl (CoglTexture *tex, x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0); y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, 0); - *s *= tex->bitmap.width / (float)x_span->size; - *t *= tex->bitmap.height / (float)y_span->size; + *s *= tex_2ds->width / (float)x_span->size; + *t *= tex_2ds->height / (float)y_span->size; #if HAVE_COGL_GL /* Denormalize texture coordinates for rectangle textures */ - if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) + if (tex_2ds->gl_target == GL_TEXTURE_RECTANGLE_ARB) { *s *= x_span->size; *t *= y_span->size; @@ -1364,6 +1298,19 @@ _cogl_texture_2d_sliced_transform_coords_to_gl (CoglTexture *tex, #endif } +static gboolean +_cogl_texture_2d_sliced_transform_quad_coords_to_gl (CoglTexture *tex, + float *coords) +{ + if (_cogl_texture_2d_sliced_is_sliced (tex)) + return FALSE; + + _cogl_texture_2d_sliced_transform_coords_to_gl (tex, coords + 0, coords + 1); + _cogl_texture_2d_sliced_transform_coords_to_gl (tex, coords + 2, coords + 3); + + return TRUE; +} + static gboolean _cogl_texture_2d_sliced_get_gl_texture (CoglTexture *tex, GLuint *out_gl_handle, @@ -1381,7 +1328,7 @@ _cogl_texture_2d_sliced_get_gl_texture (CoglTexture *tex, *out_gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, 0); if (out_gl_target != NULL) - *out_gl_target = tex->gl_target; + *out_gl_target = tex_2ds->gl_target; return TRUE; } @@ -1399,23 +1346,23 @@ _cogl_texture_2d_sliced_set_filters (CoglTexture *tex, if (tex_2ds->slice_gl_handles == NULL) return; - if (min_filter == tex->min_filter - && mag_filter == tex->mag_filter) + if (min_filter == tex_2ds->min_filter + && mag_filter == tex_2ds->mag_filter) return; /* Store new values */ - tex->min_filter = min_filter; - tex->mag_filter = mag_filter; + tex_2ds->min_filter = min_filter; + tex_2ds->mag_filter = mag_filter; /* Apply new filters to every slice */ for (i=0; islice_gl_handles->len; ++i) { gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, i); - GE( glBindTexture (tex->gl_target, gl_handle) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MAG_FILTER, - tex->mag_filter) ); - GE( glTexParameteri (tex->gl_target, GL_TEXTURE_MIN_FILTER, - tex->min_filter) ); + GE( glBindTexture (tex_2ds->gl_target, gl_handle) ); + GE( glTexParameteri (tex_2ds->gl_target, GL_TEXTURE_MAG_FILTER, + tex_2ds->mag_filter) ); + GE( glTexParameteri (tex_2ds->gl_target, GL_TEXTURE_MIN_FILTER, + tex_2ds->min_filter) ); } } @@ -1428,7 +1375,7 @@ _cogl_texture_2d_sliced_ensure_mipmaps (CoglTexture *tex) _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* Only update if the mipmaps are dirty */ - if (!tex->auto_mipmap || !tex->mipmaps_dirty) + if (!tex_2ds->auto_mipmap || !tex_2ds->mipmaps_dirty) return; /* Make sure slices were created */ @@ -1439,25 +1386,31 @@ _cogl_texture_2d_sliced_ensure_mipmaps (CoglTexture *tex) for (i = 0; i < tex_2ds->slice_gl_handles->len; i++) { GLuint gl_handle = g_array_index (tex_2ds->slice_gl_handles, GLuint, i); - GE( glBindTexture (tex->gl_target, gl_handle) ); + GE( glBindTexture (tex_2ds->gl_target, gl_handle) ); /* glGenerateMipmap is defined in the FBO extension */ if (cogl_features_available (COGL_FEATURE_OFFSCREEN)) - _cogl_texture_driver_gl_generate_mipmaps (tex->gl_target); + _cogl_texture_driver_gl_generate_mipmaps (tex_2ds->gl_target); else if (tex_2ds->first_pixels) { CoglTexturePixel *pixel = tex_2ds->first_pixels + i; /* Temporarily enable automatic mipmap generation and re-upload the first pixel to cause a regeneration */ - GE( glTexParameteri (tex->gl_target, GL_GENERATE_MIPMAP, GL_TRUE) ); - GE( glTexSubImage2D (tex->gl_target, 0, 0, 0, 1, 1, + GE( glTexParameteri (tex_2ds->gl_target, GL_GENERATE_MIPMAP, GL_TRUE) ); + GE( glTexSubImage2D (tex_2ds->gl_target, 0, 0, 0, 1, 1, pixel->gl_format, pixel->gl_type, pixel->data) ); - GE( glTexParameteri (tex->gl_target, GL_GENERATE_MIPMAP, GL_FALSE) ); + GE( glTexParameteri (tex_2ds->gl_target, GL_GENERATE_MIPMAP, GL_FALSE) ); } } - tex->mipmaps_dirty = FALSE; + tex_2ds->mipmaps_dirty = FALSE; +} + +static void +_cogl_texture_2d_sliced_ensure_non_quad_rendering (CoglTexture *tex) +{ + /* Nothing needs to be done */ } static gboolean @@ -1477,12 +1430,10 @@ _cogl_texture_2d_sliced_set_region (CoglTexture *tex, CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); gint bpp; CoglBitmap source_bmp; - CoglBitmap temp_bmp; - gboolean source_bmp_owner = FALSE; - CoglPixelFormat closest_format; + CoglBitmap tmp_bmp; + gboolean tmp_bmp_owner = FALSE; GLenum closest_gl_format; GLenum closest_gl_type; - gboolean success; /* Check for valid format */ if (format == COGL_PIXEL_FORMAT_ANY) @@ -1502,38 +1453,30 @@ _cogl_texture_2d_sliced_set_region (CoglTexture *tex, bpp = _cogl_get_format_bpp (format); source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride; - /* Find closest format to internal that's supported by GL */ - closest_format = _cogl_pixel_format_to_gl (tex->bitmap.format, - NULL, /* don't need */ - &closest_gl_format, - &closest_gl_type); + /* Prepare the bitmap so that it will do the premultiplication + conversion */ + _cogl_texture_prepare_for_upload (&source_bmp, + tex_2ds->format, + NULL, + &tmp_bmp, + &tmp_bmp_owner, + NULL, + &closest_gl_format, + &closest_gl_type); - /* If no direct match, convert */ - if (closest_format != format) - { - /* Convert to required format */ - success = _cogl_bitmap_convert_and_premult (&source_bmp, - &temp_bmp, - closest_format); - - /* Swap bitmaps if succeeded */ - if (!success) return FALSE; - source_bmp = temp_bmp; - source_bmp_owner = TRUE; - } /* Send data to GL */ _cogl_texture_2d_sliced_upload_subregion_to_gl (tex_2ds, src_x, src_y, dst_x, dst_y, dst_width, dst_height, - &source_bmp, + &tmp_bmp, closest_gl_format, closest_gl_type); /* Free data if owner */ - if (source_bmp_owner) - g_free (source_bmp.data); + if (tmp_bmp_owner) + g_free (tmp_bmp.data); return TRUE; } @@ -1545,7 +1488,6 @@ _cogl_texture_2d_sliced_download_from_gl ( GLuint target_gl_format, GLuint target_gl_type) { - CoglTexture *tex = COGL_TEXTURE (tex_2ds); CoglSpan *x_span; CoglSpan *y_span; GLuint gl_handle; @@ -1589,9 +1531,9 @@ _cogl_texture_2d_sliced_download_from_gl ( bpp); /* Download slice image data into temp bmp */ - GE( glBindTexture (tex->gl_target, gl_handle) ); + GE( glBindTexture (tex_2ds->gl_target, gl_handle) ); - if (!_cogl_texture_driver_gl_get_tex_image (tex->gl_target, + if (!_cogl_texture_driver_gl_get_tex_image (tex_2ds->gl_target, target_gl_format, target_gl_type, slice_bmp.data)) @@ -1623,9 +1565,9 @@ _cogl_texture_2d_sliced_download_from_gl ( bpp); /* Download slice image data */ - GE( glBindTexture (tex->gl_target, gl_handle) ); + GE( glBindTexture (tex_2ds->gl_target, gl_handle) ); - if (!_cogl_texture_driver_gl_get_tex_image (tex->gl_target, + if (!_cogl_texture_driver_gl_get_tex_image (tex_2ds->gl_target, target_gl_format, target_gl_type, dst)) @@ -1661,14 +1603,14 @@ _cogl_texture_2d_sliced_get_data (CoglTexture *tex, /* Default to internal format if none specified */ if (format == COGL_PIXEL_FORMAT_ANY) - format = tex->bitmap.format; + format = tex_2ds->format; /* Rowstride from texture width if none specified */ bpp = _cogl_get_format_bpp (format); - if (rowstride == 0) rowstride = tex->bitmap.width * bpp; + if (rowstride == 0) rowstride = tex_2ds->width * bpp; /* Return byte size if only that requested */ - byte_size = tex->bitmap.height * rowstride; + byte_size = tex_2ds->height * rowstride; if (data == NULL) return byte_size; closest_format = @@ -1677,11 +1619,13 @@ _cogl_texture_2d_sliced_get_data (CoglTexture *tex, &closest_gl_type); closest_bpp = _cogl_get_format_bpp (closest_format); + target_bmp.width = tex_2ds->width; + target_bmp.height = tex_2ds->height; + /* Is the requested format supported? */ if (closest_format == format) { /* Target user data directly */ - target_bmp = tex->bitmap; target_bmp.format = format; target_bmp.rowstride = rowstride; target_bmp.data = data; @@ -1689,7 +1633,6 @@ _cogl_texture_2d_sliced_get_data (CoglTexture *tex, else { /* Target intermediate buffer */ - target_bmp = tex->bitmap; target_bmp.format = closest_format; target_bmp.rowstride = target_bmp.width * closest_bpp; target_bmp.data = (guchar*) g_malloc (target_bmp.height @@ -1714,9 +1657,9 @@ _cogl_texture_2d_sliced_get_data (CoglTexture *tex, if (closest_format != format) { /* Convert to requested format */ - success = _cogl_bitmap_convert_and_premult (&target_bmp, - &new_bmp, - format); + success = _cogl_bitmap_convert_format_and_premult (&target_bmp, + &new_bmp, + format); /* Free intermediate data and return if failed */ g_free (target_bmp.data); @@ -1737,6 +1680,30 @@ _cogl_texture_2d_sliced_get_data (CoglTexture *tex, return byte_size; } +static CoglPixelFormat +_cogl_texture_2d_sliced_get_format (CoglTexture *tex) +{ + return COGL_TEXTURE_2D_SLICED (tex)->format; +} + +static GLenum +_cogl_texture_2d_sliced_get_gl_format (CoglTexture *tex) +{ + return COGL_TEXTURE_2D_SLICED (tex)->gl_format; +} + +static gint +_cogl_texture_2d_sliced_get_width (CoglTexture *tex) +{ + return COGL_TEXTURE_2D_SLICED (tex)->width; +} + +static gint +_cogl_texture_2d_sliced_get_height (CoglTexture *tex) +{ + return COGL_TEXTURE_2D_SLICED (tex)->height; +} + static const CoglTextureVtable cogl_texture_2d_sliced_vtable = { @@ -1747,8 +1714,14 @@ cogl_texture_2d_sliced_vtable = _cogl_texture_2d_sliced_is_sliced, _cogl_texture_2d_sliced_can_hardware_repeat, _cogl_texture_2d_sliced_transform_coords_to_gl, + _cogl_texture_2d_sliced_transform_quad_coords_to_gl, _cogl_texture_2d_sliced_get_gl_texture, _cogl_texture_2d_sliced_set_filters, _cogl_texture_2d_sliced_ensure_mipmaps, - _cogl_texture_2d_sliced_set_wrap_mode_parameter + _cogl_texture_2d_sliced_ensure_non_quad_rendering, + _cogl_texture_2d_sliced_set_wrap_mode_parameter, + _cogl_texture_2d_sliced_get_format, + _cogl_texture_2d_sliced_get_gl_format, + _cogl_texture_2d_sliced_get_width, + _cogl_texture_2d_sliced_get_height }; diff --git a/cogl/cogl-texture-2d.c b/cogl/cogl-texture-2d.c new file mode 100644 index 000000000..8d32e310e --- /dev/null +++ b/cogl/cogl-texture-2d.c @@ -0,0 +1,630 @@ +/* + * Cogl + * + * An object oriented GL/GLES Abstraction/Utility Layer + * + * Copyright (C) 2009 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, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Authors: + * Neil Roberts + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cogl.h" +#include "cogl-internal.h" +#include "cogl-util.h" +#include "cogl-texture-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-texture-driver.h" +#include "cogl-context.h" +#include "cogl-handle.h" + +#include +#include + +static void _cogl_texture_2d_free (CoglTexture2D *tex_2d); + +COGL_HANDLE_DEFINE (Texture2D, texture_2d); + +static const CoglTextureVtable cogl_texture_2d_vtable; + +typedef struct _CoglTexture2DManualRepeatData +{ + CoglTexture2D *tex_2d; + CoglTextureSliceCallback callback; + void *user_data; +} CoglTexture2DManualRepeatData; + +static void +_cogl_texture_2d_wrap_coords (float t_1, float t_2, + float *out_t_1, float *out_t_2) +{ + float int_part; + + /* Wrap t_1 and t_2 to the range [0,1] */ + + modff (t_1 < t_2 ? t_1 : t_2, &int_part); + t_1 -= int_part; + t_2 -= int_part; + if (signbit (int_part)) + { + *out_t_1 = 1.0f + t_1; + *out_t_2 = 1.0f + t_2; + } + else + { + *out_t_1 = t_1; + *out_t_2 = t_2; + } +} + +static void +_cogl_texture_2d_manual_repeat_cb (const float *coords, + void *user_data) +{ + CoglTexture2DManualRepeatData *data = user_data; + float slice_coords[4]; + + _cogl_texture_2d_wrap_coords (coords[0], coords[2], + slice_coords + 0, slice_coords + 2); + _cogl_texture_2d_wrap_coords (coords[1], coords[3], + slice_coords + 1, slice_coords + 3); + + data->callback (COGL_TEXTURE (data->tex_2d), + data->tex_2d->gl_texture, + GL_TEXTURE_2D, + slice_coords, + coords, + data->user_data); +} + +static void +_cogl_texture_2d_foreach_sub_texture_in_region ( + CoglTexture *tex, + float virtual_tx_1, + float virtual_ty_1, + float virtual_tx_2, + float virtual_ty_2, + CoglTextureSliceCallback callback, + void *user_data) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + CoglTexture2DManualRepeatData data; + + data.tex_2d = tex_2d; + data.callback = callback; + data.user_data = user_data; + + /* We need to implement manual repeating because if Cogl is calling + this function then it will set the wrap mode to GL_CLAMP_TO_EDGE + and hardware repeating can't be done */ + _cogl_texture_iterate_manual_repeats (_cogl_texture_2d_manual_repeat_cb, + virtual_tx_1, virtual_ty_1, + virtual_tx_2, virtual_ty_2, + &data); +} + +static void +_cogl_texture_2d_set_wrap_mode_parameter (CoglTexture *tex, + GLenum wrap_mode) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + + /* Only set the wrap mode if it's different from the current + value to avoid too many GL calls */ + if (tex_2d->wrap_mode != wrap_mode) + { + /* Any queued texture rectangles may be depending on the + * previous wrap mode... */ + _cogl_journal_flush (); + + GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) ); + GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode) ); + GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode) ); + + tex_2d->wrap_mode = wrap_mode; + } +} + +static void +_cogl_texture_2d_free (CoglTexture2D *tex_2d) +{ + GE( glDeleteTextures (1, &tex_2d->gl_texture) ); + g_free (tex_2d); +} + +static gboolean +_cogl_texture_2d_is_pot (unsigned int num) +{ + gboolean have_bit = FALSE; + + /* Make sure there is only one bit set */ + while (num) + { + if (num & 1) + { + if (have_bit) + return FALSE; + have_bit = TRUE; + } + num >>= 1; + } + + return TRUE; +} + +static gboolean +_cogl_texture_2d_can_create (unsigned int width, + unsigned int height, + CoglPixelFormat internal_format) +{ + GLenum gl_intformat; + GLenum gl_type; + + /* If the driver doesn't support glGenerateMipmap then we need to + store a copy of the first pixels to cause an update. Instead of + duplicating the code here we'll just make it fallback to + CoglTexture2DSliced */ + if (!cogl_features_available (COGL_FEATURE_OFFSCREEN)) + return FALSE; + + /* If NPOT textures aren't supported then the size must be a power + of two */ + if (!cogl_features_available (COGL_FEATURE_TEXTURE_NPOT) && + (!_cogl_texture_2d_is_pot (width) || + !_cogl_texture_2d_is_pot (height))) + return FALSE; + + _cogl_pixel_format_to_gl (internal_format, + &gl_intformat, + NULL, + &gl_type); + + /* Check that the driver can create a texture with that size */ + if (!_cogl_texture_driver_size_supported (GL_TEXTURE_2D, + gl_intformat, + gl_type, + width, + height)) + return FALSE; + + return TRUE; +} + +static CoglTexture2D * +_cogl_texture_2d_create_base (unsigned int width, + unsigned int height, + CoglTextureFlags flags, + CoglPixelFormat internal_format) +{ + CoglTexture2D *tex_2d = g_new (CoglTexture2D, 1); + CoglTexture *tex = COGL_TEXTURE (tex_2d); + + tex->vtable = &cogl_texture_2d_vtable; + + tex_2d->width = width; + tex_2d->height = height; + tex_2d->mipmaps_dirty = TRUE; + tex_2d->auto_mipmap = (flags & COGL_TEXTURE_NO_AUTO_MIPMAP) == 0; + + /* Unknown filter */ + tex_2d->min_filter = GL_FALSE; + tex_2d->mag_filter = GL_FALSE; + + /* Wrap mode not yet set */ + tex_2d->wrap_mode = GL_FALSE; + + tex_2d->format = internal_format; + + return tex_2d; +} + +CoglHandle +_cogl_texture_2d_new_with_size (unsigned int width, + unsigned int height, + CoglTextureFlags flags, + CoglPixelFormat internal_format) +{ + CoglTexture2D *tex_2d; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + + /* Since no data, we need some internal format */ + if (internal_format == COGL_PIXEL_FORMAT_ANY) + internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; + + if (!_cogl_texture_2d_can_create (width, height, internal_format)) + return COGL_INVALID_HANDLE; + + internal_format = _cogl_pixel_format_to_gl (internal_format, + &gl_intformat, + &gl_format, + &gl_type); + + tex_2d = _cogl_texture_2d_create_base (width, height, flags, internal_format); + + GE( glGenTextures (1, &tex_2d->gl_texture) ); + GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) ); + GE( glTexImage2D (GL_TEXTURE_2D, 0, gl_intformat, + width, height, 0, gl_format, gl_type, NULL) ); + + return _cogl_texture_2d_handle_new (tex_2d); +} + +CoglHandle +_cogl_texture_2d_new_from_bitmap (CoglHandle bmp_handle, + CoglTextureFlags flags, + CoglPixelFormat internal_format) +{ + CoglTexture2D *tex_2d; + CoglBitmap *bmp = (CoglBitmap *)bmp_handle; + CoglBitmap dst_bmp; + gboolean dst_bmp_owner; + GLenum gl_intformat; + GLenum gl_format; + GLenum gl_type; + + g_return_val_if_fail (bmp_handle != COGL_INVALID_HANDLE, COGL_INVALID_HANDLE); + + if (!_cogl_texture_prepare_for_upload (bmp, + internal_format, + &internal_format, + &dst_bmp, + &dst_bmp_owner, + &gl_intformat, + &gl_format, + &gl_type)) + return COGL_INVALID_HANDLE; + + tex_2d = _cogl_texture_2d_create_base (bmp->width, + bmp->height, + flags, + internal_format); + + GE( glGenTextures (1, &tex_2d->gl_texture) ); + _cogl_texture_driver_upload_to_gl (GL_TEXTURE_2D, + tex_2d->gl_texture, + &dst_bmp, + gl_intformat, + gl_format, + gl_type); + + tex_2d->gl_format = gl_intformat; + + if (dst_bmp_owner) + g_free (dst_bmp.data); + + return _cogl_texture_2d_handle_new (tex_2d); +} + +static gint +_cogl_texture_2d_get_max_waste (CoglTexture *tex) +{ + return -1; +} + +static gboolean +_cogl_texture_2d_is_sliced (CoglTexture *tex) +{ + return FALSE; +} + +static gboolean +_cogl_texture_2d_can_hardware_repeat (CoglTexture *tex) +{ + return TRUE; +} + +static void +_cogl_texture_2d_transform_coords_to_gl (CoglTexture *tex, + float *s, + float *t) +{ + /* The texture coordinates map directly so we don't need to do + anything */ +} + +static gboolean +_cogl_texture_2d_transform_quad_coords_to_gl (CoglTexture *tex, + float *coords) +{ + /* The texture coordinates map directly so we don't need to do + anything */ + return TRUE; +} + +static gboolean +_cogl_texture_2d_get_gl_texture (CoglTexture *tex, + GLuint *out_gl_handle, + GLenum *out_gl_target) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + + if (out_gl_handle) + *out_gl_handle = tex_2d->gl_texture; + + if (out_gl_target) + *out_gl_target = GL_TEXTURE_2D; + + return TRUE; +} + +static void +_cogl_texture_2d_set_filters (CoglTexture *tex, + GLenum min_filter, + GLenum mag_filter) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + + if (min_filter == tex_2d->min_filter + && mag_filter == tex_2d->mag_filter) + return; + + /* Store new values */ + tex_2d->min_filter = min_filter; + tex_2d->mag_filter = mag_filter; + + /* Apply new filters to the texture */ + GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) ); + GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter) ); + GE( glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter) ); +} + +static void +_cogl_texture_2d_ensure_mipmaps (CoglTexture *tex) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + + _COGL_GET_CONTEXT (ctx, NO_RETVAL); + + /* Only update if the mipmaps are dirty */ + if (!tex_2d->auto_mipmap || !tex_2d->mipmaps_dirty) + return; + + GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) ); + /* glGenerateMipmap is defined in the FBO extension. We only allow + CoglTexture2D instances to be created if this feature is + available so we don't need to check for the extension */ + _cogl_texture_driver_gl_generate_mipmaps (GL_TEXTURE_2D); + + tex_2d->mipmaps_dirty = FALSE; +} + +static void +_cogl_texture_2d_ensure_non_quad_rendering (CoglTexture *tex) +{ + /* Nothing needs to be done */ +} + +static gboolean +_cogl_texture_2d_set_region (CoglTexture *tex, + int src_x, + int src_y, + int dst_x, + int dst_y, + unsigned int dst_width, + unsigned int dst_height, + int width, + int height, + CoglPixelFormat format, + unsigned int rowstride, + const guint8 *data) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + gint bpp; + CoglBitmap source_bmp; + CoglBitmap tmp_bmp; + gboolean tmp_bmp_owner = FALSE; + GLenum closest_gl_format; + GLenum closest_gl_type; + + /* Check for valid format */ + if (format == COGL_PIXEL_FORMAT_ANY) + return FALSE; + + /* Shortcut out early if the image is empty */ + if (width == 0 || height == 0) + return TRUE; + + /* Init source bitmap */ + source_bmp.width = width; + source_bmp.height = height; + source_bmp.format = format; + source_bmp.data = (guchar*) data; + + /* Rowstride from width if none specified */ + bpp = _cogl_get_format_bpp (format); + source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride; + + /* Prepare the bitmap so that it will do the premultiplication + conversion */ + _cogl_texture_prepare_for_upload (&source_bmp, + tex_2d->format, + NULL, + &tmp_bmp, + &tmp_bmp_owner, + NULL, + &closest_gl_format, + &closest_gl_type); + + /* Send data to GL */ + _cogl_texture_driver_upload_subregion_to_gl (GL_TEXTURE_2D, + tex_2d->gl_texture, + src_x, src_y, + dst_x, dst_y, + dst_width, dst_height, + &tmp_bmp, + closest_gl_format, + closest_gl_type); + + /* Free data if owner */ + if (tmp_bmp_owner) + g_free (tmp_bmp.data); + + return TRUE; +} + +static int +_cogl_texture_2d_get_data (CoglTexture *tex, + CoglPixelFormat format, + unsigned int rowstride, + guint8 *data) +{ + CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); + gint bpp; + gint byte_size; + CoglPixelFormat closest_format; + gint closest_bpp; + GLenum closest_gl_format; + GLenum closest_gl_type; + CoglBitmap target_bmp; + CoglBitmap new_bmp; + gboolean success; + guchar *src; + guchar *dst; + gint y; + + /* Default to internal format if none specified */ + if (format == COGL_PIXEL_FORMAT_ANY) + format = tex_2d->format; + + /* Rowstride from texture width if none specified */ + bpp = _cogl_get_format_bpp (format); + if (rowstride == 0) rowstride = tex_2d->width * bpp; + + /* Return byte size if only that requested */ + byte_size = tex_2d->height * rowstride; + if (data == NULL) return byte_size; + + closest_format = + _cogl_texture_driver_find_best_gl_get_data_format (format, + &closest_gl_format, + &closest_gl_type); + closest_bpp = _cogl_get_format_bpp (closest_format); + + target_bmp.width = tex_2d->width; + target_bmp.height = tex_2d->height; + + /* Is the requested format supported? */ + if (closest_format == format) + { + /* Target user data directly */ + target_bmp.format = format; + target_bmp.rowstride = rowstride; + target_bmp.data = data; + } + else + { + /* Target intermediate buffer */ + target_bmp.format = closest_format; + target_bmp.rowstride = target_bmp.width * closest_bpp; + target_bmp.data = (guchar*) g_malloc (target_bmp.height + * target_bmp.rowstride); + } + + GE( glBindTexture (GL_TEXTURE_2D, tex_2d->gl_texture) ); + if (!_cogl_texture_driver_gl_get_tex_image (GL_TEXTURE_2D, + closest_gl_format, + closest_gl_type, + target_bmp.data)) + { + /* XXX: In some cases _cogl_texture_2d_download_from_gl may + * fail to read back the texture data; such as for GLES which doesn't + * support glGetTexImage, so here we fallback to drawing the texture + * and reading the pixels from the framebuffer. */ + _cogl_texture_draw_and_read (tex, &target_bmp, + closest_gl_format, + closest_gl_type); + } + + /* Was intermediate used? */ + if (closest_format != format) + { + /* Convert to requested format */ + success = _cogl_bitmap_convert_format_and_premult (&target_bmp, + &new_bmp, + format); + + /* Free intermediate data and return if failed */ + g_free (target_bmp.data); + if (!success) return 0; + + /* Copy to user buffer */ + for (y = 0; y < new_bmp.height; ++y) + { + src = new_bmp.data + y * new_bmp.rowstride; + dst = data + y * rowstride; + memcpy (dst, src, new_bmp.width); + } + + /* Free converted data */ + g_free (new_bmp.data); + } + + return byte_size; +} + +static CoglPixelFormat +_cogl_texture_2d_get_format (CoglTexture *tex) +{ + return COGL_TEXTURE_2D (tex)->format; +} + +static GLenum +_cogl_texture_2d_get_gl_format (CoglTexture *tex) +{ + return COGL_TEXTURE_2D (tex)->gl_format; +} + +static gint +_cogl_texture_2d_get_width (CoglTexture *tex) +{ + return COGL_TEXTURE_2D (tex)->width; +} + +static gint +_cogl_texture_2d_get_height (CoglTexture *tex) +{ + return COGL_TEXTURE_2D (tex)->height; +} + +static const CoglTextureVtable +cogl_texture_2d_vtable = + { + _cogl_texture_2d_set_region, + _cogl_texture_2d_get_data, + _cogl_texture_2d_foreach_sub_texture_in_region, + _cogl_texture_2d_get_max_waste, + _cogl_texture_2d_is_sliced, + _cogl_texture_2d_can_hardware_repeat, + _cogl_texture_2d_transform_coords_to_gl, + _cogl_texture_2d_transform_quad_coords_to_gl, + _cogl_texture_2d_get_gl_texture, + _cogl_texture_2d_set_filters, + _cogl_texture_2d_ensure_mipmaps, + _cogl_texture_2d_ensure_non_quad_rendering, + _cogl_texture_2d_set_wrap_mode_parameter, + _cogl_texture_2d_get_format, + _cogl_texture_2d_get_gl_format, + _cogl_texture_2d_get_width, + _cogl_texture_2d_get_height + }; diff --git a/cogl/cogl-texture-driver.h b/cogl/cogl-texture-driver.h index 6d6b560f2..6961659d8 100644 --- a/cogl/cogl-texture-driver.h +++ b/cogl/cogl-texture-driver.h @@ -57,7 +57,8 @@ _cogl_texture_driver_prep_gl_for_pixels_upload (int pixels_rowstride, * XXX: sorry for the ridiculous number of arguments :-( */ void -_cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, +_cogl_texture_driver_upload_subregion_to_gl (GLenum gl_target, + GLuint gl_handle, int src_x, int src_y, int dst_x, @@ -66,8 +67,21 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, int height, CoglBitmap *source_bmp, GLuint source_gl_format, - GLuint source_gl_type, - GLuint gl_handle); + GLuint source_gl_type); + +/* + * Replaces the contents of the GL texture with the entire bitmap. On + * GL this just directly calls glTexImage2D, but under GLES it needs + * to copy the bitmap if the rowstride is not a multiple of a possible + * alignment value because there is no GL_UNPACK_ROW_LENGTH + */ +void +_cogl_texture_driver_upload_to_gl (GLenum gl_target, + GLuint gl_handle, + CoglBitmap *source_bmp, + GLint internal_gl_format, + GLuint source_gl_format, + GLuint source_gl_type); /* * This sets up the glPixelStore state for an download to a destination with diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h index 8c06af3da..920618529 100644 --- a/cogl/cogl-texture-private.h +++ b/cogl/cogl-texture-private.h @@ -30,16 +30,19 @@ #define COGL_TEXTURE(tex) ((CoglTexture *)(tex)) -typedef struct _CoglTexture CoglTexture; -typedef struct _CoglTextureVtable CoglTextureVtable; +typedef struct _CoglTexture CoglTexture; +typedef struct _CoglTextureVtable CoglTextureVtable; typedef void (*CoglTextureSliceCallback) (CoglHandle handle, GLuint gl_handle, GLenum gl_target, - float *slice_coords, - float *virtual_coords, + const float *slice_coords, + const float *virtual_coords, void *user_data); +typedef void (* CoglTextureManualRepeatCallback) (const float *coords, + void *user_data); + struct _CoglTextureVtable { /* Virtual functions that must be implemented for a texture @@ -80,6 +83,8 @@ struct _CoglTextureVtable void (* transform_coords_to_gl) (CoglTexture *tex, float *s, float *t); + gboolean (* transform_quad_coords_to_gl) (CoglTexture *tex, + float *coords); gboolean (* get_gl_texture) (CoglTexture *tex, GLuint *out_gl_handle, @@ -90,27 +95,21 @@ struct _CoglTextureVtable GLenum mag_filter); void (* ensure_mipmaps) (CoglTexture *tex); + void (* ensure_non_quad_rendering) (CoglTexture *tex); void (* set_wrap_mode_parameter) (CoglTexture *tex, GLenum wrap_mode); + + CoglPixelFormat (* get_format) (CoglTexture *tex); + GLenum (* get_gl_format) (CoglTexture *tex); + gint (* get_width) (CoglTexture *tex); + gint (* get_height) (CoglTexture *tex); }; struct _CoglTexture { CoglHandleObject _parent; const CoglTextureVtable *vtable; - CoglBitmap bitmap; - gboolean bitmap_owner; - GLenum gl_target; - GLenum gl_intformat; - GLenum gl_format; - GLenum gl_type; - GLenum min_filter; - GLenum mag_filter; - gboolean is_foreign; - GLint wrap_mode; - gboolean auto_mipmap; - gboolean mipmaps_dirty; }; void @@ -129,8 +128,12 @@ void _cogl_texture_transform_coords_to_gl (CoglHandle handle, float *s, float *t); +gboolean +_cogl_texture_transform_quad_coords_to_gl (CoglHandle handle, + float *coords); + GLenum -_cogl_texture_get_internal_gl_format (CoglHandle handle); +_cogl_texture_get_gl_format (CoglHandle handle); void _cogl_texture_set_wrap_mode_parameter (CoglHandle handle, @@ -144,23 +147,30 @@ _cogl_texture_set_filters (CoglHandle handle, void _cogl_texture_ensure_mipmaps (CoglHandle handle); - -/* Functions currently only used by CoglTexture implementations or - * drivers... */ - void -_cogl_texture_free (CoglTexture *tex); +_cogl_texture_ensure_non_quad_rendering (CoglHandle handle); -void -_cogl_texture_bitmap_free (CoglTexture *tex); +/* Utility function to determine which pixel format to use when + dst_format is COGL_PIXEL_FORMAT_ANY. If dst_format is not ANY then + it will just be returned directly */ +CoglPixelFormat +_cogl_texture_determine_internal_format (CoglPixelFormat src_format, + CoglPixelFormat dst_format); -void -_cogl_texture_bitmap_swap (CoglTexture *tex, - CoglBitmap *new_bitmap); +/* Utility function to help uploading a bitmap. If the bitmap needs + premult conversion then it will be copied and *copied_bitmap will + be set to TRUE. Otherwise dst_bmp will be set to a shallow copy of + src_bmp. The GLenums needed for uploading are returned */ gboolean -_cogl_texture_bitmap_prepare (CoglTexture *tex, - CoglPixelFormat internal_format); +_cogl_texture_prepare_for_upload (CoglBitmap *src_bmp, + CoglPixelFormat dst_format, + CoglPixelFormat *dst_format_out, + CoglBitmap *dst_bmp, + gboolean *copied_bitmap, + GLenum *out_glintformat, + GLenum *out_glformat, + GLenum *out_gltype); void _cogl_texture_prep_gl_alignment_for_pixels_upload (int pixels_rowstride); @@ -168,8 +178,22 @@ _cogl_texture_prep_gl_alignment_for_pixels_upload (int pixels_rowstride); void _cogl_texture_prep_gl_alignment_for_pixels_download (int pixels_rowstride); +/* Utility function for implementing manual repeating. Even texture + backends that always support hardware repeating need this because + when foreach_sub_texture_in_region is invoked Cogl will set the + wrap mode to GL_CLAMP_TO_EDGE so hardware repeating can't be + done */ +void +_cogl_texture_iterate_manual_repeats (CoglTextureManualRepeatCallback callback, + float tx_1, float ty_1, + float tx_2, float ty_2, + void *user_data); + +/* Utility function to use as a fallback for getting the data of any + texture via the framebuffer */ + gboolean -_cogl_texture_draw_and_read (CoglTexture *tex, +_cogl_texture_draw_and_read (CoglHandle handle, CoglBitmap *target_bmp, GLuint target_gl_format, GLuint target_gl_type); diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c index 085a23f5b..ec69aca6f 100644 --- a/cogl/cogl-texture.c +++ b/cogl/cogl-texture.c @@ -38,6 +38,9 @@ #include "cogl-texture-private.h" #include "cogl-texture-driver.h" #include "cogl-texture-2d-sliced-private.h" +#include "cogl-texture-2d-private.h" +#include "cogl-sub-texture-private.h" +#include "cogl-atlas-texture-private.h" #include "cogl-material.h" #include "cogl-context.h" #include "cogl-handle.h" @@ -62,8 +65,10 @@ cogl_is_texture (CoglHandle handle) if (handle == COGL_INVALID_HANDLE) return FALSE; - return obj->klass->type == _cogl_handle_texture_2d_sliced_get_type (); - //|| obj->klass->type == _cogl_handle_texture_3d_get_type (); + return (obj->klass->type == _cogl_handle_texture_2d_get_type () || + obj->klass->type == _cogl_handle_atlas_texture_get_type () || + obj->klass->type == _cogl_handle_texture_2d_sliced_get_type () || + obj->klass->type == _cogl_handle_sub_texture_get_type ()); } CoglHandle @@ -95,25 +100,86 @@ cogl_texture_unref (CoglHandle handle) cogl_handle_unref (handle); } -void -_cogl_texture_bitmap_free (CoglTexture *tex) +static gboolean +_cogl_texture_needs_premult_conversion (CoglPixelFormat src_format, + CoglPixelFormat dst_format) { - if (tex->bitmap.data != NULL && tex->bitmap_owner) - g_free (tex->bitmap.data); - - tex->bitmap.data = NULL; - tex->bitmap_owner = FALSE; + return ((src_format & COGL_A_BIT) && + src_format != COGL_PIXEL_FORMAT_A_8 && + (src_format & COGL_PREMULT_BIT) != + (dst_format & COGL_PREMULT_BIT)); } -void -_cogl_texture_bitmap_swap (CoglTexture *tex, - CoglBitmap *new_bitmap) +CoglPixelFormat +_cogl_texture_determine_internal_format (CoglPixelFormat src_format, + CoglPixelFormat dst_format) { - if (tex->bitmap.data != NULL && tex->bitmap_owner) - g_free (tex->bitmap.data); + /* If the application hasn't specified a specific format then we'll + * pick the most appropriate. By default Cogl will use a + * premultiplied internal format. Later we will add control over + * this. */ + if (dst_format == COGL_PIXEL_FORMAT_ANY) + { + if ((src_format & COGL_A_BIT) && + src_format != COGL_PIXEL_FORMAT_A_8) + return src_format | COGL_PREMULT_BIT; + else + return src_format; + } + else + return dst_format; +} - tex->bitmap = *new_bitmap; - tex->bitmap_owner = TRUE; +gboolean +_cogl_texture_prepare_for_upload (CoglBitmap *src_bmp, + CoglPixelFormat dst_format, + CoglPixelFormat *dst_format_out, + CoglBitmap *dst_bmp, + gboolean *copied_bitmap, + GLenum *out_glintformat, + GLenum *out_glformat, + GLenum *out_gltype) +{ + dst_format = _cogl_texture_determine_internal_format (src_bmp->format, + dst_format); + + *copied_bitmap = FALSE; + *dst_bmp = *src_bmp; + + /* If the source format does not have the same premult flag as the + dst format then we need to copy and convert it */ + if (_cogl_texture_needs_premult_conversion (src_bmp->format, + dst_format)) + { + dst_bmp->data = g_memdup (dst_bmp->data, + dst_bmp->height * dst_bmp->rowstride); + *copied_bitmap = TRUE; + + if (!_cogl_bitmap_convert_premult_status (dst_bmp, + src_bmp->format ^ + COGL_PREMULT_BIT)) + { + g_free (dst_bmp->data); + return FALSE; + } + } + + /* Use the source format from the src bitmap type and the internal + format from the dst format type so that GL can do the + conversion */ + _cogl_pixel_format_to_gl (src_bmp->format, + NULL, /* internal format */ + out_glformat, + out_gltype); + _cogl_pixel_format_to_gl (dst_format, + out_glintformat, + NULL, + NULL); + + if (dst_format_out) + *dst_format_out = dst_format; + + return TRUE; } void @@ -152,53 +218,99 @@ _cogl_texture_set_wrap_mode_parameter (CoglHandle handle, tex->vtable->set_wrap_mode_parameter (tex, wrap_mode); } -gboolean -_cogl_texture_bitmap_prepare (CoglTexture *tex, - CoglPixelFormat internal_format) +/* This is like CoglSpanIter except it deals with floats and it + effectively assumes there is only one span from 0.0 to 1.0 */ +typedef struct _CoglTextureIter { - CoglBitmap new_bitmap; - CoglPixelFormat new_data_format; - gboolean success; + gfloat pos, end, next_pos; + gboolean flipped; + gfloat t_1, t_2; +} CoglTextureIter; - /* Was there any internal conversion requested? - * By default Cogl will use a premultiplied internal format. Later we will - * add control over this. */ - if (internal_format == COGL_PIXEL_FORMAT_ANY) +static void +_cogl_texture_iter_update (CoglTextureIter *iter) +{ + gfloat t_2; + float frac_part; + + frac_part = modff (iter->pos, &iter->next_pos); + + /* modff rounds the int part towards zero so we need to add one if + we're meant to be heading away from zero */ + if (iter->pos >= 0.0f || frac_part == 0.0f) + iter->next_pos += 1.0f; + + if (iter->next_pos > iter->end) + t_2 = iter->end; + else + t_2 = iter->next_pos; + + if (iter->flipped) { - if ((tex->bitmap.format & COGL_A_BIT) && - tex->bitmap.format != COGL_PIXEL_FORMAT_A_8) - internal_format = tex->bitmap.format | COGL_PREMULT_BIT; - else - internal_format = tex->bitmap.format; + iter->t_1 = t_2; + iter->t_2 = iter->pos; } - - /* Find closest format accepted by GL */ - new_data_format = _cogl_pixel_format_to_gl (internal_format, - &tex->gl_intformat, - &tex->gl_format, - &tex->gl_type); - - /* Convert to internal format */ - if (new_data_format != tex->bitmap.format) + else { - success = _cogl_bitmap_convert_and_premult (&tex->bitmap, - &new_bitmap, - new_data_format); - - if (!success) - return FALSE; - - /* Update texture with new data */ - _cogl_texture_bitmap_swap (tex, &new_bitmap); + iter->t_1 = iter->pos; + iter->t_2 = t_2; } - - return TRUE; } -void -_cogl_texture_free (CoglTexture *tex) +static void +_cogl_texture_iter_begin (CoglTextureIter *iter, + gfloat t_1, gfloat t_2) { - _cogl_texture_bitmap_free (tex); + if (t_1 <= t_2) + { + iter->pos = t_1; + iter->end = t_2; + iter->flipped = FALSE; + } + else + { + iter->pos = t_2; + iter->end = t_1; + iter->flipped = TRUE; + } + + _cogl_texture_iter_update (iter); +} + +static void +_cogl_texture_iter_next (CoglTextureIter *iter) +{ + iter->pos = iter->next_pos; + _cogl_texture_iter_update (iter); +} + +static gboolean +_cogl_texture_iter_end (CoglTextureIter *iter) +{ + return iter->pos >= iter->end; +} + +/* This invokes the callback with enough quads to cover the manually + repeated range specified by the virtual texture coordinates without + emitting coordinates outside the range [0,1] */ +void +_cogl_texture_iterate_manual_repeats (CoglTextureManualRepeatCallback callback, + float tx_1, float ty_1, + float tx_2, float ty_2, + void *user_data) +{ + CoglTextureIter x_iter, y_iter; + + for (_cogl_texture_iter_begin (&y_iter, ty_1, ty_2); + !_cogl_texture_iter_end (&y_iter); + _cogl_texture_iter_next (&y_iter)) + for (_cogl_texture_iter_begin (&x_iter, tx_1, tx_2); + !_cogl_texture_iter_end (&x_iter); + _cogl_texture_iter_next (&x_iter)) + { + float coords[4] = { x_iter.t_1, y_iter.t_1, x_iter.t_2, y_iter.t_2 }; + callback (coords, user_data); + } } CoglHandle @@ -207,10 +319,19 @@ cogl_texture_new_with_size (guint width, CoglTextureFlags flags, CoglPixelFormat internal_format) { - return _cogl_texture_2d_sliced_new_with_size (width, - height, - flags, - internal_format); + CoglHandle tex; + + /* First try creating a fast-path non-sliced texture */ + tex = _cogl_texture_2d_new_with_size (width, height, flags, internal_format); + + /* If it fails resort to sliced textures */ + if (tex == COGL_INVALID_HANDLE) + tex = _cogl_texture_2d_sliced_new_with_size (width, + height, + flags, + internal_format); + + return tex; } CoglHandle @@ -222,13 +343,26 @@ cogl_texture_new_from_data (guint width, guint rowstride, const guchar *data) { - return _cogl_texture_2d_sliced_new_from_data (width, - height, - flags, - format, - internal_format, - rowstride, - data); + CoglBitmap bitmap; + + if (format == COGL_PIXEL_FORMAT_ANY) + return COGL_INVALID_HANDLE; + + if (data == NULL) + return COGL_INVALID_HANDLE; + + /* Rowstride from width if not given */ + if (rowstride == 0) + rowstride = width * _cogl_get_format_bpp (format); + + /* Wrap the data into a bitmap */ + bitmap.width = width; + bitmap.height = height; + bitmap.data = (guchar *) data; + bitmap.format = format; + bitmap.rowstride = rowstride; + + return cogl_texture_new_from_bitmap (&bitmap, flags, internal_format); } CoglHandle @@ -236,6 +370,21 @@ cogl_texture_new_from_bitmap (CoglHandle bmp_handle, CoglTextureFlags flags, CoglPixelFormat internal_format) { + CoglHandle tex; + + /* First try putting the texture in the atlas */ + if ((tex = _cogl_atlas_texture_new_from_bitmap (bmp_handle, + flags, + internal_format))) + return tex; + + /* If that doesn't work try a fast path 2D texture */ + if ((tex = _cogl_texture_2d_new_from_bitmap (bmp_handle, + flags, + internal_format))) + return tex; + + /* Otherwise create a sliced texture */ return _cogl_texture_2d_sliced_new_from_bitmap (bmp_handle, flags, internal_format); @@ -247,10 +396,31 @@ cogl_texture_new_from_file (const gchar *filename, CoglPixelFormat internal_format, GError **error) { - return _cogl_texture_2d_sliced_new_from_file (filename, - flags, - internal_format, - error); + CoglHandle bmp_handle; + CoglBitmap *bmp; + CoglHandle handle = COGL_INVALID_HANDLE; + + g_return_val_if_fail (error == NULL || *error == NULL, COGL_INVALID_HANDLE); + + bmp_handle = cogl_bitmap_new_from_file (filename, error); + if (bmp_handle == COGL_INVALID_HANDLE) + return COGL_INVALID_HANDLE; + + bmp = (CoglBitmap *) bmp_handle; + + /* We know that the bitmap data is solely owned by this function so + we can do the premult conversion in place. This avoids having to + copy the bitmap which will otherwise happen in + _cogl_texture_prepare_for_upload */ + internal_format = _cogl_texture_determine_internal_format (bmp->format, + internal_format); + if (!_cogl_texture_needs_premult_conversion (bmp->format, internal_format) || + _cogl_bitmap_convert_premult_status (bmp, bmp->format ^ COGL_PREMULT_BIT)) + handle = cogl_texture_new_from_bitmap (bmp, flags, internal_format); + + cogl_handle_unref (bmp); + + return handle; } CoglHandle @@ -271,6 +441,17 @@ cogl_texture_new_from_foreign (GLuint gl_handle, format); } +CoglHandle +cogl_texture_new_from_sub_texture (CoglHandle full_texture, + gint sub_x, + gint sub_y, + gint sub_width, + gint sub_height) +{ + return _cogl_sub_texture_new (full_texture, sub_x, sub_y, + sub_width, sub_height); +} + guint cogl_texture_get_width (CoglHandle handle) { @@ -281,7 +462,7 @@ cogl_texture_get_width (CoglHandle handle) tex = COGL_TEXTURE (handle); - return tex->bitmap.width; + return tex->vtable->get_width (tex); } guint @@ -294,7 +475,7 @@ cogl_texture_get_height (CoglHandle handle) tex = COGL_TEXTURE (handle); - return tex->bitmap.height; + return tex->vtable->get_height (tex); } CoglPixelFormat @@ -307,7 +488,7 @@ cogl_texture_get_format (CoglHandle handle) tex = COGL_TEXTURE (handle); - return tex->bitmap.format; + return tex->vtable->get_format (tex); } guint @@ -318,9 +499,15 @@ cogl_texture_get_rowstride (CoglHandle handle) if (!cogl_is_texture (handle)) return 0; + /* FIXME: This function should go away. It previously just returned + the rowstride that was used to upload the data as far as I can + tell. This is not helpful */ + tex = COGL_TEXTURE (handle); - return tex->bitmap.rowstride; + /* Just guess at a suitable rowstride */ + return (_cogl_get_format_bpp (cogl_texture_get_format (tex)) + * cogl_texture_get_width (tex)); } gint @@ -387,12 +574,6 @@ _cogl_texture_can_hardware_repeat (CoglHandle handle) { CoglTexture *tex = (CoglTexture *)handle; -#if HAVE_COGL_GL - /* TODO: COGL_TEXTURE_TYPE_2D_RECTANGLE */ - if (tex->gl_target == GL_TEXTURE_RECTANGLE_ARB) - return FALSE; -#endif - return tex->vtable->can_hardware_repeat (tex); } @@ -409,12 +590,21 @@ _cogl_texture_transform_coords_to_gl (CoglHandle handle, tex->vtable->transform_coords_to_gl (tex, s, t); } -GLenum -_cogl_texture_get_internal_gl_format (CoglHandle handle) +gboolean +_cogl_texture_transform_quad_coords_to_gl (CoglHandle handle, + float *coords) { CoglTexture *tex = COGL_TEXTURE (handle); - return tex->gl_intformat; + return tex->vtable->transform_quad_coords_to_gl (tex, coords); +} + +GLenum +_cogl_texture_get_gl_format (CoglHandle handle) +{ + CoglTexture *tex = COGL_TEXTURE (handle); + + return tex->vtable->get_gl_format (tex); } gboolean @@ -460,6 +650,19 @@ _cogl_texture_ensure_mipmaps (CoglHandle handle) tex->vtable->ensure_mipmaps (tex); } +void +_cogl_texture_ensure_non_quad_rendering (CoglHandle handle) +{ + CoglTexture *tex; + + if (!cogl_is_texture (handle)) + return; + + tex = COGL_TEXTURE (handle); + + return tex->vtable->ensure_non_quad_rendering (tex); +} + gboolean cogl_texture_set_region (CoglHandle handle, gint src_x, @@ -505,7 +708,7 @@ cogl_texture_set_region (CoglHandle handle, * glGetTexImage, but may be used as a fallback in some circumstances. */ static void -do_texture_draw_and_read (CoglTexture *tex, +do_texture_draw_and_read (CoglHandle handle, CoglBitmap *target_bmp, GLint *viewport) { @@ -516,16 +719,18 @@ do_texture_draw_and_read (CoglTexture *tex, float tx2, ty2; int bw, bh; CoglBitmap rect_bmp; - CoglHandle handle; + guint tex_width, tex_height; - handle = (CoglHandle) tex; bpp = _cogl_get_format_bpp (COGL_PIXEL_FORMAT_RGBA_8888); + tex_width = cogl_texture_get_width (handle); + tex_height = cogl_texture_get_height (handle); + ry1 = 0; ry2 = 0; ty1 = 0; ty2 = 0; /* Walk Y axis until whole bitmap height consumed */ - for (bh = tex->bitmap.height; bh > 0; bh -= viewport[3]) + for (bh = tex_height; bh > 0; bh -= viewport[3]) { /* Rectangle Y coords */ ry1 = ry2; @@ -533,13 +738,13 @@ do_texture_draw_and_read (CoglTexture *tex, /* Normalized texture Y coords */ ty1 = ty2; - ty2 = (ry2 / (float)tex->bitmap.height); + ty2 = (ry2 / (float) tex_height); rx1 = 0; rx2 = 0; tx1 = 0; tx2 = 0; /* Walk X axis until whole bitmap width consumed */ - for (bw = tex->bitmap.width; bw > 0; bw-=viewport[2]) + for (bw = tex_width; bw > 0; bw-=viewport[2]) { /* Rectangle X coords */ rx1 = rx2; @@ -547,7 +752,7 @@ do_texture_draw_and_read (CoglTexture *tex, /* Normalized texture X coords */ tx1 = tx2; - tx2 = (rx2 / (float)tex->bitmap.width); + tx2 = (rx2 / (float) tex_width); /* Draw a portion of texture */ cogl_rectangle_with_texture_coords (0, 0, @@ -593,7 +798,7 @@ do_texture_draw_and_read (CoglTexture *tex, * glGetTexImage, but may be used as a fallback in some circumstances. */ gboolean -_cogl_texture_draw_and_read (CoglTexture *tex, +_cogl_texture_draw_and_read (CoglHandle handle, CoglBitmap *target_bmp, GLuint target_gl_format, GLuint target_gl_type) @@ -648,14 +853,14 @@ _cogl_texture_draw_and_read (CoglTexture *tex, prev_source = cogl_handle_ref (ctx->source_material); cogl_set_source (ctx->texture_download_material); - cogl_material_set_layer (ctx->texture_download_material, 0, tex); + cogl_material_set_layer (ctx->texture_download_material, 0, handle); cogl_material_set_layer_combine (ctx->texture_download_material, 0, /* layer */ "RGBA = REPLACE (TEXTURE)", NULL); - do_texture_draw_and_read (tex, target_bmp, viewport); + do_texture_draw_and_read (handle, target_bmp, viewport); /* Check whether texture has alpha and framebuffer not */ /* FIXME: For some reason even if ALPHA_BITS is 8, the framebuffer @@ -670,7 +875,7 @@ _cogl_texture_draw_and_read (CoglTexture *tex, printf ("G bits: %d\n", g_bits); printf ("B bits: %d\n", b_bits); printf ("A bits: %d\n", a_bits); */ - if ((tex->bitmap.format & COGL_A_BIT)/* && a_bits == 0*/) + if ((cogl_texture_get_format (handle) & COGL_A_BIT)/* && a_bits == 0*/) { guchar *srcdata; guchar *dstdata; @@ -692,7 +897,7 @@ _cogl_texture_draw_and_read (CoglTexture *tex, "RGBA = REPLACE (TEXTURE[A])", NULL); - do_texture_draw_and_read (tex, &alpha_bmp, viewport); + do_texture_draw_and_read (handle, &alpha_bmp, viewport); /* Copy temp R to target A */ srcdata = alpha_bmp.data; diff --git a/cogl/cogl-texture.h b/cogl/cogl-texture.h index 93ead5436..c0becf944 100644 --- a/cogl/cogl-texture.h +++ b/cogl/cogl-texture.h @@ -317,6 +317,33 @@ gboolean cogl_texture_set_region (CoglHandle handle, guint rowstride, const guchar *data); +/** + * cogl_texture_new_from_sub_texture: + * @full_texture: a #CoglHandle to an existing texture + * @sub_x: X coordinate of the top-left of the subregion + * @sub_y: Y coordinate of the top-left of the subregion + * @sub_width: Width in pixels of the subregion + * @sub_height: Height in pixels of the subregion + * + * Creates a new texture which represents a subregion of another + * texture. The GL resources will be shared so that no new texture + * data is actually allocated. + * + * Sub textures have undefined behaviour texture coordinates outside + * of the range [0,1] are used. They also do not work with + * CoglVertexBuffers. + * + * Return value: a #CoglHandle to the new texture. + * + * Since: 1.2 + */ +CoglHandle cogl_texture_new_from_sub_texture + (CoglHandle full_texture, + gint sub_x, + gint sub_y, + gint sub_width, + gint sub_height); + #ifndef COGL_DISABLE_DEPRECATED /** diff --git a/cogl/cogl-types.h b/cogl/cogl-types.h index 826b26055..4ce1502f3 100644 --- a/cogl/cogl-types.h +++ b/cogl/cogl-types.h @@ -291,7 +291,8 @@ struct _CoglTextureVertex typedef enum { COGL_TEXTURE_NONE = 0, COGL_TEXTURE_NO_AUTO_MIPMAP = 1 << 0, - COGL_TEXTURE_NO_SLICING = 1 << 1 + COGL_TEXTURE_NO_SLICING = 1 << 1, + COGL_TEXTURE_NO_ATLAS = 1 << 2 } CoglTextureFlags; /** diff --git a/cogl/cogl-vertex-buffer.c b/cogl/cogl-vertex-buffer.c index ff2d2841e..806991046 100644 --- a/cogl/cogl-vertex-buffer.c +++ b/cogl/cogl-vertex-buffer.c @@ -1637,6 +1637,11 @@ enable_state_for_drawing_buffer (CoglVertexBuffer *buffer) if (tex_handle == COGL_INVALID_HANDLE) continue; + /* Give the texture a chance to know that we're rendering + non-quad shaped primitives. If the texture is in an atlas it + will be migrated */ + _cogl_texture_ensure_non_quad_rendering (tex_handle); + if (!_cogl_texture_can_hardware_repeat (tex_handle)) { g_warning ("Disabling layer %d of the current source material, " diff --git a/cogl/driver/gl/cogl-texture-driver.c b/cogl/driver/gl/cogl-texture-driver.c index 4e54babd2..f1fe59a2f 100644 --- a/cogl/driver/gl/cogl-texture-driver.c +++ b/cogl/driver/gl/cogl-texture-driver.c @@ -102,7 +102,8 @@ _cogl_texture_driver_prep_gl_for_pixels_download (int pixels_rowstride, } void -_cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, +_cogl_texture_driver_upload_subregion_to_gl (GLenum gl_target, + GLuint gl_handle, int src_x, int src_y, int dst_x, @@ -111,8 +112,7 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, int height, CoglBitmap *source_bmp, GLuint source_gl_format, - GLuint source_gl_type, - GLuint gl_handle) + GLuint source_gl_type) { int bpp = _cogl_get_format_bpp (source_bmp->format); @@ -122,11 +122,11 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, src_y, bpp); - /* Upload new image data */ - GE( _cogl_texture_driver_bind (tex->gl_target, - gl_handle, tex->gl_intformat) ); + /* We don't need to use _cogl_texture_driver_bind here because we're + not using the bound texture to render yet */ + GE( glBindTexture (gl_target, gl_handle) ); - GE( glTexSubImage2D (tex->gl_target, 0, + GE( glTexSubImage2D (gl_target, 0, dst_x, dst_y, width, height, source_gl_format, @@ -134,6 +134,32 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, source_bmp->data) ); } +void +_cogl_texture_driver_upload_to_gl (GLenum gl_target, + GLuint gl_handle, + CoglBitmap *source_bmp, + GLint internal_gl_format, + GLuint source_gl_format, + GLuint source_gl_type) +{ + int bpp = _cogl_get_format_bpp (source_bmp->format); + + /* Setup gl alignment to match rowstride and top-left corner */ + prep_gl_for_pixels_upload_full (source_bmp->rowstride, 0, 0, bpp); + + /* We don't need to use _cogl_texture_driver_bind here because we're + not using the bound texture to render yet */ + GE( glBindTexture (gl_target, gl_handle) ); + + GE( glTexImage2D (gl_target, 0, + internal_gl_format, + source_bmp->width, source_bmp->height, + 0, + source_gl_format, + source_gl_type, + source_bmp->data) ); +} + gboolean _cogl_texture_driver_gl_get_tex_image (GLenum gl_target, GLenum dest_gl_format, diff --git a/cogl/driver/gles/cogl-texture-driver.c b/cogl/driver/gles/cogl-texture-driver.c index 0461ae8da..62324f58c 100644 --- a/cogl/driver/gles/cogl-texture-driver.c +++ b/cogl/driver/gles/cogl-texture-driver.c @@ -71,7 +71,8 @@ _cogl_texture_driver_prep_gl_for_pixels_download (int pixels_rowstride, } void -_cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, +_cogl_texture_driver_upload_subregion_to_gl (GLenum gl_target, + GLuint gl_handle, int src_x, int src_y, int dst_x, @@ -80,8 +81,7 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, int height, CoglBitmap *source_bmp, GLuint source_gl_format, - GLuint source_gl_type, - GLuint gl_handle) + GLuint source_gl_type) { int bpp = _cogl_get_format_bpp (source_bmp->format); CoglBitmap slice_bmp; @@ -94,7 +94,7 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, * rowstride = bpp * width and the texture image is not sliced */ /* Setup temp bitmap for slice subregion */ - slice_bmp.format = tex->bitmap.format; + slice_bmp.format = source_bmp->format; slice_bmp.width = width; slice_bmp.height = height; slice_bmp.rowstride = bpp * slice_bmp.width; @@ -113,11 +113,11 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, slice_bmp.width, slice_bmp.height); - /* Upload new image data */ - GE( _cogl_texture_driver_bind (tex->gl_target, - gl_handle, tex->gl_intformat) ); + /* We don't need to use _cogl_texture_driver_bind here because we're + not using the bound texture to render yet */ + GE( glBindTexture (gl_target, gl_handle) ); - GE( glTexSubImage2D (tex->gl_target, 0, + GE( glTexSubImage2D (gl_target, 0, dst_x, dst_y, width, height, source_gl_format, @@ -128,6 +128,53 @@ _cogl_texture_driver_upload_subregion_to_gl (CoglTexture *tex, g_free (slice_bmp.data); } +void +_cogl_texture_driver_upload_to_gl (GLenum gl_target, + GLuint gl_handle, + CoglBitmap *source_bmp, + GLint internal_gl_format, + GLuint source_gl_format, + GLuint source_gl_type) +{ + int bpp = _cogl_get_format_bpp (source_bmp->format); + CoglBitmap bmp = *source_bmp; + gboolean bmp_owner = FALSE; + + /* If the rowstride can't be specified with just GL_ALIGNMENT alone + then we need to copy the bitmap because there is no GL_ROW_LENGTH */ + if (source_bmp->rowstride / bpp != source_bmp->width) + { + bmp.rowstride = bpp * bmp.width; + bmp.data = g_malloc (bmp.rowstride * bmp.height); + bmp_owner = TRUE; + + _cogl_bitmap_copy_subregion (source_bmp, + &bmp, + 0, 0, 0, 0, + bmp.width, + bmp.height); + } + + /* Setup gl alignment to match rowstride and top-left corner */ + _cogl_texture_driver_prep_gl_for_pixels_upload (bmp.rowstride, + bpp); + + /* We don't need to use _cogl_texture_driver_bind here because we're + not using the bound texture to render yet */ + GE( glBindTexture (gl_target, gl_handle) ); + + GE( glTexImage2D (gl_target, 0, + internal_gl_format, + bmp.width, bmp.height, + 0, + source_gl_format, + source_gl_type, + bmp.data) ); + + if (bmp_owner) + g_free (bmp.data); +} + /* NB: GLES doesn't support glGetTexImage2D, so cogl-texture will instead * fallback to a generic render + readpixels approach to downloading * texture data. (See _cogl_texture_draw_and_read() ) */ diff --git a/doc/reference/cogl/cogl-sections.txt b/doc/reference/cogl/cogl-sections.txt index 336f9ad03..eda10b462 100644 --- a/doc/reference/cogl/cogl-sections.txt +++ b/doc/reference/cogl/cogl-sections.txt @@ -192,6 +192,7 @@ cogl_texture_new_from_file cogl_texture_new_from_data cogl_texture_new_from_foreign cogl_texture_new_from_bitmap +cogl_texture_new_from_sub_texture cogl_is_texture cogl_texture_ref cogl_texture_unref