mirror of
https://github.com/brl/mutter.git
synced 2024-11-23 00:20:42 -05:00
cogl-atlas-texture: Split out the atlas data structure
Instead of storing a pointer to the CoglRectangleMap and a handle to the atlas texture in the context, there is a now a separate data structure called a CoglAtlas to manage these two. The context just contains a pointer to this. The code to reorganise the atlas has been moved from cogl-atlas-texture.c to cogl-atlas.c
This commit is contained in:
parent
0c8582aad8
commit
9b2f8179f0
@ -146,6 +146,8 @@ cogl_sources_c = \
|
|||||||
$(srcdir)/cogl-texture-rectangle.c \
|
$(srcdir)/cogl-texture-rectangle.c \
|
||||||
$(srcdir)/cogl-rectangle-map.h \
|
$(srcdir)/cogl-rectangle-map.h \
|
||||||
$(srcdir)/cogl-rectangle-map.c \
|
$(srcdir)/cogl-rectangle-map.c \
|
||||||
|
$(srcdir)/cogl-atlas.h \
|
||||||
|
$(srcdir)/cogl-atlas.c \
|
||||||
$(srcdir)/cogl-atlas-texture-private.h \
|
$(srcdir)/cogl-atlas-texture-private.h \
|
||||||
$(srcdir)/cogl-atlas-texture.c \
|
$(srcdir)/cogl-atlas-texture.c \
|
||||||
$(srcdir)/cogl-spans.h \
|
$(srcdir)/cogl-spans.h \
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "cogl-handle.h"
|
#include "cogl-handle.h"
|
||||||
#include "cogl-texture-private.h"
|
#include "cogl-texture-private.h"
|
||||||
#include "cogl-rectangle-map.h"
|
#include "cogl-rectangle-map.h"
|
||||||
|
#include "cogl-atlas.h"
|
||||||
|
|
||||||
#define COGL_ATLAS_TEXTURE(tex) ((CoglAtlasTexture *) tex)
|
#define COGL_ATLAS_TEXTURE(tex) ((CoglAtlasTexture *) tex)
|
||||||
|
|
||||||
@ -61,4 +62,7 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp,
|
|||||||
CoglTextureFlags flags,
|
CoglTextureFlags flags,
|
||||||
CoglPixelFormat internal_format);
|
CoglPixelFormat internal_format);
|
||||||
|
|
||||||
|
CoglAtlas *
|
||||||
|
_cogl_atlas_texture_get_atlas (void);
|
||||||
|
|
||||||
#endif /* __COGL_ATLAS_TEXTURE_H */
|
#endif /* __COGL_ATLAS_TEXTURE_H */
|
||||||
|
@ -42,172 +42,93 @@
|
|||||||
#include "cogl-rectangle-map.h"
|
#include "cogl-rectangle-map.h"
|
||||||
#include "cogl-journal-private.h"
|
#include "cogl-journal-private.h"
|
||||||
#include "cogl-material-opengl-private.h"
|
#include "cogl-material-opengl-private.h"
|
||||||
|
#include "cogl-atlas.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#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);
|
static void _cogl_atlas_texture_free (CoglAtlasTexture *sub_tex);
|
||||||
|
|
||||||
COGL_TEXTURE_INTERNAL_DEFINE (AtlasTexture, atlas_texture);
|
COGL_TEXTURE_INTERNAL_DEFINE (AtlasTexture, atlas_texture);
|
||||||
|
|
||||||
static const CoglTextureVtable cogl_atlas_texture_vtable;
|
static const CoglTextureVtable cogl_atlas_texture_vtable;
|
||||||
|
|
||||||
/* If we want to do mulitple blits from a texture (such as when
|
static CoglHandle
|
||||||
reorganizing the atlas) then it's quicker to download all of the
|
_cogl_atlas_texture_create_sub_texture (CoglHandle full_texture,
|
||||||
data once and upload multiple times from that. This struct is used
|
const CoglRectangleMapEntry *rectangle)
|
||||||
to keep the image data for a series of blits */
|
|
||||||
|
|
||||||
typedef struct _CoglAtlasTextureBlitData
|
|
||||||
{
|
{
|
||||||
CoglHandle src_tex, dst_tex;
|
/* Create a subtexture for the given rectangle not including the
|
||||||
|
1-pixel border */
|
||||||
/* If we're using an FBO to blit, then FBO will be non-zero and
|
return _cogl_sub_texture_new (full_texture,
|
||||||
old_fbo will be the previous framebuffer binding */
|
rectangle->x + 1,
|
||||||
GLuint fbo, old_fbo;
|
rectangle->y + 1,
|
||||||
|
rectangle->width - 2,
|
||||||
/* If we're not using an FBO then we g_malloc a buffer and copy the
|
rectangle->height - 2);
|
||||||
complete texture data in */
|
|
||||||
unsigned char *image_data;
|
|
||||||
CoglPixelFormat format;
|
|
||||||
int bpp;
|
|
||||||
unsigned int 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cogl_bind_gl_texture_transient (data->dst_gl_target,
|
|
||||||
dst_gl_texture,
|
|
||||||
FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
static void
|
||||||
_cogl_atlas_texture_blit (CoglAtlasTextureBlitData *data,
|
_cogl_atlas_texture_update_position_cb (gpointer user_data,
|
||||||
unsigned int src_x,
|
CoglHandle new_texture,
|
||||||
unsigned int src_y,
|
const CoglRectangleMapEntry *rectangle)
|
||||||
unsigned int dst_x,
|
|
||||||
unsigned int dst_y,
|
|
||||||
unsigned int width,
|
|
||||||
unsigned int height)
|
|
||||||
{
|
{
|
||||||
/* If we have an FBO then we can do a fast blit */
|
CoglAtlasTexture *atlas_tex = user_data;
|
||||||
if (data->fbo)
|
|
||||||
GE( glCopyTexSubImage2D (data->dst_gl_target, 0, dst_x, dst_y, src_x, src_y,
|
/* Update the sub texture */
|
||||||
width, height) );
|
if (atlas_tex->sub_texture)
|
||||||
else
|
cogl_handle_unref (atlas_tex->sub_texture);
|
||||||
cogl_texture_set_region (data->dst_tex,
|
atlas_tex->sub_texture =
|
||||||
src_x, src_y,
|
_cogl_atlas_texture_create_sub_texture (new_texture, rectangle);
|
||||||
dst_x, dst_y,
|
|
||||||
width, height,
|
/* Update the position */
|
||||||
data->src_width, data->src_height,
|
atlas_tex->rectangle = *rectangle;
|
||||||
data->format,
|
|
||||||
data->src_width * data->bpp,
|
|
||||||
data->image_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_cogl_atlas_texture_blit_end (CoglAtlasTextureBlitData *data)
|
_cogl_atlas_texture_reorganize_foreach_cb (const CoglRectangleMapEntry *entry,
|
||||||
|
void *rectangle_data,
|
||||||
|
void *user_data)
|
||||||
{
|
{
|
||||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
/* Notify cogl-material.c that the texture's underlying GL texture
|
||||||
|
* storage is changing so it knows it may need to bind a new texture
|
||||||
|
* if the CoglTexture is reused with the same texture unit. */
|
||||||
|
_cogl_material_texture_storage_change_notify (rectangle_data);
|
||||||
|
}
|
||||||
|
|
||||||
if (data->fbo)
|
static void
|
||||||
|
_cogl_atlas_texture_reorganize_cb (void *data)
|
||||||
|
{
|
||||||
|
CoglAtlas *atlas;
|
||||||
|
/* We don't know if any materials may currently be referenced in
|
||||||
|
* the journal that depend on the current underlying GL texture
|
||||||
|
* storage so we flush the journal before migrating.
|
||||||
|
*
|
||||||
|
* We are assuming that texture atlas migration never happens
|
||||||
|
* during a flush so we don't have to consider recursion here.
|
||||||
|
*/
|
||||||
|
_cogl_journal_flush ();
|
||||||
|
|
||||||
|
atlas = _cogl_atlas_texture_get_atlas ();
|
||||||
|
|
||||||
|
if (atlas->map)
|
||||||
|
_cogl_rectangle_map_foreach (atlas->map,
|
||||||
|
_cogl_atlas_texture_reorganize_foreach_cb,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
CoglAtlas *
|
||||||
|
_cogl_atlas_texture_get_atlas (void)
|
||||||
|
{
|
||||||
|
_COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
|
||||||
|
|
||||||
|
if (ctx->atlas == COGL_INVALID_HANDLE)
|
||||||
{
|
{
|
||||||
GE( glBindFramebuffer (GL_FRAMEBUFFER, data->old_fbo) );
|
ctx->atlas = _cogl_atlas_new (_cogl_atlas_texture_update_position_cb);
|
||||||
GE( glDeleteFramebuffers (1, &data->fbo) );
|
|
||||||
|
_cogl_atlas_add_reorganize_callback (ctx->atlas,
|
||||||
|
_cogl_atlas_texture_reorganize_cb,
|
||||||
|
NULL);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
g_free (data->image_data);
|
return ctx->atlas;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -252,20 +173,8 @@ _cogl_atlas_texture_remove_from_atlas (CoglAtlasTexture *atlas_tex)
|
|||||||
{
|
{
|
||||||
if (atlas_tex->in_atlas)
|
if (atlas_tex->in_atlas)
|
||||||
{
|
{
|
||||||
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
|
_cogl_atlas_remove (_cogl_atlas_texture_get_atlas (),
|
||||||
|
&atlas_tex->rectangle);
|
||||||
_cogl_rectangle_map_remove (ctx->rectangle_map, &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_rectangle_map_get_width (ctx->rectangle_map),
|
|
||||||
_cogl_rectangle_map_get_height (ctx->rectangle_map),
|
|
||||||
_cogl_rectangle_map_get_n_rectangles (ctx->rectangle_map),
|
|
||||||
_cogl_rectangle_map_get_remaining_space (ctx->rectangle_map) *
|
|
||||||
100 / (_cogl_rectangle_map_get_width (ctx->rectangle_map) *
|
|
||||||
_cogl_rectangle_map_get_height (ctx->rectangle_map)));
|
|
||||||
|
|
||||||
atlas_tex->in_atlas = FALSE;
|
atlas_tex->in_atlas = FALSE;
|
||||||
}
|
}
|
||||||
@ -361,10 +270,6 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex)
|
|||||||
/* Make sure this texture is not in the atlas */
|
/* Make sure this texture is not in the atlas */
|
||||||
if (atlas_tex->in_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_NOTE (ATLAS, "Migrating texture out of the atlas");
|
||||||
|
|
||||||
/* We don't know if any materials may currently be referenced in
|
/* We don't know if any materials may currently be referenced in
|
||||||
@ -383,27 +288,15 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex)
|
|||||||
|
|
||||||
cogl_handle_unref (atlas_tex->sub_texture);
|
cogl_handle_unref (atlas_tex->sub_texture);
|
||||||
|
|
||||||
/* Create a new texture at the right size, not including the
|
|
||||||
border */
|
|
||||||
atlas_tex->sub_texture =
|
atlas_tex->sub_texture =
|
||||||
cogl_texture_new_with_size (atlas_tex->rectangle.width - 2,
|
_cogl_atlas_copy_rectangle (_cogl_atlas_texture_get_atlas (),
|
||||||
|
atlas_tex->rectangle.x + 1,
|
||||||
|
atlas_tex->rectangle.y + 1,
|
||||||
|
atlas_tex->rectangle.width - 2,
|
||||||
atlas_tex->rectangle.height - 2,
|
atlas_tex->rectangle.height - 2,
|
||||||
COGL_TEXTURE_NO_ATLAS,
|
COGL_TEXTURE_NO_ATLAS,
|
||||||
atlas_tex->format);
|
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);
|
_cogl_atlas_texture_remove_from_atlas (atlas_tex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -445,10 +338,10 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex,
|
|||||||
unsigned int dst_height,
|
unsigned int dst_height,
|
||||||
CoglBitmap *bmp)
|
CoglBitmap *bmp)
|
||||||
{
|
{
|
||||||
_COGL_GET_CONTEXT (ctx, FALSE);
|
CoglAtlas *atlas = _cogl_atlas_texture_get_atlas ();
|
||||||
|
|
||||||
/* Copy the central data */
|
/* Copy the central data */
|
||||||
if (!_cogl_texture_set_region_from_bitmap (ctx->atlas_texture,
|
if (!_cogl_texture_set_region_from_bitmap (atlas->texture,
|
||||||
src_x, src_y,
|
src_x, src_y,
|
||||||
dst_x + atlas_tex->rectangle.x + 1,
|
dst_x + atlas_tex->rectangle.x + 1,
|
||||||
dst_y + atlas_tex->rectangle.y + 1,
|
dst_y + atlas_tex->rectangle.y + 1,
|
||||||
@ -459,7 +352,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex,
|
|||||||
|
|
||||||
/* Update the left edge pixels */
|
/* Update the left edge pixels */
|
||||||
if (dst_x == 0 &&
|
if (dst_x == 0 &&
|
||||||
!_cogl_texture_set_region_from_bitmap (ctx->atlas_texture,
|
!_cogl_texture_set_region_from_bitmap (atlas->texture,
|
||||||
src_x, src_y,
|
src_x, src_y,
|
||||||
atlas_tex->rectangle.x,
|
atlas_tex->rectangle.x,
|
||||||
dst_y + atlas_tex->rectangle.y + 1,
|
dst_y + atlas_tex->rectangle.y + 1,
|
||||||
@ -468,7 +361,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
/* Update the right edge pixels */
|
/* Update the right edge pixels */
|
||||||
if (dst_x + dst_width == atlas_tex->rectangle.width - 2 &&
|
if (dst_x + dst_width == atlas_tex->rectangle.width - 2 &&
|
||||||
!_cogl_texture_set_region_from_bitmap (ctx->atlas_texture,
|
!_cogl_texture_set_region_from_bitmap (atlas->texture,
|
||||||
src_x + dst_width - 1, src_y,
|
src_x + dst_width - 1, src_y,
|
||||||
atlas_tex->rectangle.x +
|
atlas_tex->rectangle.x +
|
||||||
atlas_tex->rectangle.width - 1,
|
atlas_tex->rectangle.width - 1,
|
||||||
@ -478,7 +371,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
/* Update the top edge pixels */
|
/* Update the top edge pixels */
|
||||||
if (dst_y == 0 &&
|
if (dst_y == 0 &&
|
||||||
!_cogl_texture_set_region_from_bitmap (ctx->atlas_texture,
|
!_cogl_texture_set_region_from_bitmap (atlas->texture,
|
||||||
src_x, src_y,
|
src_x, src_y,
|
||||||
dst_x + atlas_tex->rectangle.x + 1,
|
dst_x + atlas_tex->rectangle.x + 1,
|
||||||
atlas_tex->rectangle.y,
|
atlas_tex->rectangle.y,
|
||||||
@ -487,7 +380,7 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
/* Update the bottom edge pixels */
|
/* Update the bottom edge pixels */
|
||||||
if (dst_y + dst_height == atlas_tex->rectangle.height - 2 &&
|
if (dst_y + dst_height == atlas_tex->rectangle.height - 2 &&
|
||||||
!_cogl_texture_set_region_from_bitmap (ctx->atlas_texture,
|
!_cogl_texture_set_region_from_bitmap (atlas->texture,
|
||||||
src_x, src_y + dst_height - 1,
|
src_x, src_y + dst_height - 1,
|
||||||
dst_x + atlas_tex->rectangle.x + 1,
|
dst_x + atlas_tex->rectangle.x + 1,
|
||||||
atlas_tex->rectangle.y +
|
atlas_tex->rectangle.y +
|
||||||
@ -597,319 +490,6 @@ _cogl_atlas_texture_get_height (CoglTexture *tex)
|
|||||||
return cogl_texture_get_height (atlas_tex->sub_texture);
|
return cogl_texture_get_height (atlas_tex->sub_texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
static CoglHandle
|
|
||||||
_cogl_atlas_texture_create_sub_texture (CoglHandle full_texture,
|
|
||||||
const CoglRectangleMapEntry *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 */
|
|
||||||
CoglRectangleMapEntry new_position;
|
|
||||||
} CoglAtlasTextureRepositionData;
|
|
||||||
|
|
||||||
static void
|
|
||||||
_cogl_atlas_texture_migrate (unsigned int n_textures,
|
|
||||||
CoglAtlasTextureRepositionData *textures,
|
|
||||||
CoglHandle old_texture,
|
|
||||||
CoglHandle new_texture,
|
|
||||||
CoglAtlasTexture *skip_texture)
|
|
||||||
{
|
|
||||||
unsigned int i;
|
|
||||||
CoglAtlasTextureBlitData blit_data;
|
|
||||||
|
|
||||||
/* We don't know if any materials may currently be referenced in the
|
|
||||||
* journal that depend on the current underlying GL texture storage
|
|
||||||
* so we flush the journal before migrating.
|
|
||||||
*
|
|
||||||
* We are assuming that texture atlas migration never happens during
|
|
||||||
* a flush so we don't have to consider recursion here.
|
|
||||||
*/
|
|
||||||
_cogl_journal_flush ();
|
|
||||||
|
|
||||||
_cogl_atlas_texture_blit_begin (&blit_data, new_texture, old_texture);
|
|
||||||
|
|
||||||
for (i = 0; i < n_textures; i++)
|
|
||||||
{
|
|
||||||
/* Notify cogl-material.c that the texture's underlying GL texture
|
|
||||||
* storage is changing so it knows it may need to bind a new texture
|
|
||||||
* if the CoglTexture is reused with the same texture unit. */
|
|
||||||
_cogl_material_texture_storage_change_notify (textures[i].texture);
|
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
unsigned int n_textures;
|
|
||||||
} CoglAtlasTextureGetRectanglesData;
|
|
||||||
|
|
||||||
static void
|
|
||||||
_cogl_atlas_texture_get_rectangles_cb (const CoglRectangleMapEntry *rectangle,
|
|
||||||
gpointer rect_data,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
CoglAtlasTextureGetRectanglesData *data = user_data;
|
|
||||||
|
|
||||||
data->textures[data->n_textures++].texture = rect_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
_cogl_atlas_texture_get_next_size (unsigned int *map_width,
|
|
||||||
unsigned int *map_height)
|
|
||||||
{
|
|
||||||
/* Double the size of the texture by increasing whichever dimension
|
|
||||||
is smaller */
|
|
||||||
if (*map_width < *map_height)
|
|
||||||
*map_width <<= 1;
|
|
||||||
else
|
|
||||||
*map_height <<= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static CoglRectangleMap *
|
|
||||||
_cogl_atlas_texture_create_map (unsigned int map_width,
|
|
||||||
unsigned int map_height,
|
|
||||||
unsigned int 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 (map_width < max_texture_size && map_height < max_texture_size)
|
|
||||||
{
|
|
||||||
CoglRectangleMap *new_atlas = _cogl_rectangle_map_new (map_width,
|
|
||||||
map_height,
|
|
||||||
NULL);
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
/* Add all of the textures and keep track of the new position */
|
|
||||||
for (i = 0; i < n_textures; i++)
|
|
||||||
if (!_cogl_rectangle_map_add (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_rectangle_map_free (new_atlas);
|
|
||||||
_cogl_atlas_texture_get_next_size (&map_width, &map_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;
|
|
||||||
unsigned int 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,
|
|
||||||
unsigned int width,
|
|
||||||
unsigned int height)
|
|
||||||
{
|
|
||||||
CoglAtlasTextureGetRectanglesData data;
|
|
||||||
CoglRectangleMap *new_map;
|
|
||||||
CoglHandle new_tex;
|
|
||||||
unsigned int map_width, map_height;
|
|
||||||
gboolean ret;
|
|
||||||
|
|
||||||
_COGL_GET_CONTEXT (ctx, FALSE);
|
|
||||||
|
|
||||||
/* Check if we can fit the rectangle into the existing atlas */
|
|
||||||
if (ctx->rectangle_map &&
|
|
||||||
_cogl_rectangle_map_add (ctx->rectangle_map, width, height,
|
|
||||||
new_sub_tex,
|
|
||||||
&new_sub_tex->rectangle))
|
|
||||||
{
|
|
||||||
COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste",
|
|
||||||
_cogl_rectangle_map_get_width (ctx->rectangle_map),
|
|
||||||
_cogl_rectangle_map_get_height (ctx->rectangle_map),
|
|
||||||
_cogl_rectangle_map_get_n_rectangles (ctx->rectangle_map),
|
|
||||||
_cogl_rectangle_map_get_remaining_space (ctx->rectangle_map) *
|
|
||||||
100 / (_cogl_rectangle_map_get_width (ctx->rectangle_map) *
|
|
||||||
_cogl_rectangle_map_get_height (ctx->rectangle_map)));
|
|
||||||
|
|
||||||
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->rectangle_map == NULL)
|
|
||||||
data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
unsigned int n_rectangles =
|
|
||||||
_cogl_rectangle_map_get_n_rectangles (ctx->rectangle_map);
|
|
||||||
data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData) *
|
|
||||||
(n_rectangles + 1));
|
|
||||||
_cogl_rectangle_map_foreach (ctx->rectangle_map,
|
|
||||||
_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->rectangle_map)
|
|
||||||
{
|
|
||||||
map_width = _cogl_rectangle_map_get_width (ctx->rectangle_map);
|
|
||||||
map_height = _cogl_rectangle_map_get_height (ctx->rectangle_map);
|
|
||||||
|
|
||||||
/* 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_rectangle_map_get_remaining_space (ctx->rectangle_map) <
|
|
||||||
width * height)
|
|
||||||
_cogl_atlas_texture_get_next_size (&map_width, &map_height);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Start with an initial size of 256x256 */
|
|
||||||
map_width = 256;
|
|
||||||
map_height = 256;
|
|
||||||
}
|
|
||||||
|
|
||||||
new_map = _cogl_atlas_texture_create_map (map_width, map_height,
|
|
||||||
data.n_textures, data.textures);
|
|
||||||
map_width = _cogl_rectangle_map_get_width (new_map);
|
|
||||||
map_height = _cogl_rectangle_map_get_height (new_map);
|
|
||||||
|
|
||||||
/* If we can't create a map with the texture then give up */
|
|
||||||
if (new_map == NULL)
|
|
||||||
{
|
|
||||||
COGL_NOTE (ATLAS, "Could not fit texture in the atlas");
|
|
||||||
ret = FALSE;
|
|
||||||
}
|
|
||||||
/* We need to migrate the existing textures into a new texture */
|
|
||||||
else if ((new_tex =
|
|
||||||
_cogl_texture_2d_new_with_size (map_width, map_height,
|
|
||||||
COGL_TEXTURE_NONE,
|
|
||||||
COGL_PIXEL_FORMAT_RGBA_8888)) ==
|
|
||||||
COGL_INVALID_HANDLE)
|
|
||||||
{
|
|
||||||
COGL_NOTE (ATLAS, "Could not create a CoglTexture2D");
|
|
||||||
_cogl_rectangle_map_free (new_map);
|
|
||||||
ret = FALSE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
COGL_NOTE (ATLAS,
|
|
||||||
"Atlas %s with size %ix%i",
|
|
||||||
ctx->rectangle_map == NULL ||
|
|
||||||
_cogl_rectangle_map_get_width (ctx->rectangle_map) !=
|
|
||||||
_cogl_rectangle_map_get_width (new_map) ||
|
|
||||||
_cogl_rectangle_map_get_height (ctx->rectangle_map) !=
|
|
||||||
_cogl_rectangle_map_get_height (new_map) ?
|
|
||||||
"resized" : "reorganized",
|
|
||||||
_cogl_rectangle_map_get_width (new_map),
|
|
||||||
_cogl_rectangle_map_get_height (new_map));
|
|
||||||
|
|
||||||
if (ctx->rectangle_map)
|
|
||||||
{
|
|
||||||
/* 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_rectangle_map_free (ctx->rectangle_map);
|
|
||||||
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->rectangle_map = new_map;
|
|
||||||
ctx->atlas_texture = new_tex;
|
|
||||||
|
|
||||||
COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste",
|
|
||||||
_cogl_rectangle_map_get_width (ctx->rectangle_map),
|
|
||||||
_cogl_rectangle_map_get_height (ctx->rectangle_map),
|
|
||||||
_cogl_rectangle_map_get_n_rectangles (ctx->rectangle_map),
|
|
||||||
_cogl_rectangle_map_get_remaining_space (ctx->rectangle_map) *
|
|
||||||
100 / (_cogl_rectangle_map_get_width (ctx->rectangle_map) *
|
|
||||||
_cogl_rectangle_map_get_height (ctx->rectangle_map)));
|
|
||||||
|
|
||||||
ret = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_free (data.textures);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
_cogl_atlas_texture_can_use_format (CoglPixelFormat format)
|
_cogl_atlas_texture_can_use_format (CoglPixelFormat format)
|
||||||
{
|
{
|
||||||
@ -987,15 +567,14 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp,
|
|||||||
/* We need to allocate the texture now because we need the pointer
|
/* We need to allocate the texture now because we need the pointer
|
||||||
to set as the data for the rectangle in the atlas */
|
to set as the data for the rectangle in the atlas */
|
||||||
atlas_tex = g_new (CoglAtlasTexture, 1);
|
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->sub_texture = COGL_INVALID_HANDLE;
|
||||||
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 */
|
/* Try to make some space in the atlas for the texture */
|
||||||
if (!_cogl_atlas_texture_reserve_space (atlas_tex,
|
if (!_cogl_atlas_reserve_space (_cogl_atlas_texture_get_atlas (),
|
||||||
atlas_tex->rectangle.width,
|
/* Add two pixels for the border */
|
||||||
atlas_tex->rectangle.height))
|
bmp_width + 2, bmp_height + 2,
|
||||||
|
atlas_tex))
|
||||||
{
|
{
|
||||||
g_free (atlas_tex);
|
g_free (atlas_tex);
|
||||||
return COGL_INVALID_HANDLE;
|
return COGL_INVALID_HANDLE;
|
||||||
@ -1010,7 +589,8 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp,
|
|||||||
|
|
||||||
if (dst_bmp == NULL)
|
if (dst_bmp == NULL)
|
||||||
{
|
{
|
||||||
_cogl_rectangle_map_remove (ctx->rectangle_map, &atlas_tex->rectangle);
|
_cogl_atlas_remove (_cogl_atlas_texture_get_atlas (),
|
||||||
|
&atlas_tex->rectangle);
|
||||||
g_free (atlas_tex);
|
g_free (atlas_tex);
|
||||||
return COGL_INVALID_HANDLE;
|
return COGL_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
@ -1018,9 +598,6 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp,
|
|||||||
atlas_tex->_parent.vtable = &cogl_atlas_texture_vtable;
|
atlas_tex->_parent.vtable = &cogl_atlas_texture_vtable;
|
||||||
atlas_tex->format = internal_format;
|
atlas_tex->format = internal_format;
|
||||||
atlas_tex->in_atlas = TRUE;
|
atlas_tex->in_atlas = TRUE;
|
||||||
atlas_tex->sub_texture =
|
|
||||||
_cogl_atlas_texture_create_sub_texture (ctx->atlas_texture,
|
|
||||||
&atlas_tex->rectangle);
|
|
||||||
|
|
||||||
/* Make another bitmap so that we can override the format */
|
/* Make another bitmap so that we can override the format */
|
||||||
override_bmp = _cogl_bitmap_new_shared (dst_bmp,
|
override_bmp = _cogl_bitmap_new_shared (dst_bmp,
|
||||||
|
596
clutter/cogl/cogl/cogl-atlas.c
Normal file
596
clutter/cogl/cogl/cogl-atlas.c
Normal file
@ -0,0 +1,596 @@
|
|||||||
|
/*
|
||||||
|
* Cogl
|
||||||
|
*
|
||||||
|
* An object oriented GL/GLES Abstraction/Utility Layer
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Intel Corporation.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Neil Roberts <neil@linux.intel.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "cogl.h"
|
||||||
|
#include "cogl-atlas.h"
|
||||||
|
#include "cogl-rectangle-map.h"
|
||||||
|
#include "cogl-context.h"
|
||||||
|
#include "cogl-texture-private.h"
|
||||||
|
#include "cogl-texture-2d-private.h"
|
||||||
|
#include "cogl-material-opengl-private.h"
|
||||||
|
#include "cogl-debug.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
/* 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 _CoglAtlasBlitData
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
int bpp;
|
||||||
|
unsigned int src_height, src_width;
|
||||||
|
|
||||||
|
GLenum dst_gl_target;
|
||||||
|
} CoglAtlasBlitData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_cogl_atlas_blit_begin (CoglAtlasBlitData *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;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cogl_bind_gl_texture_transient (data->dst_gl_target,
|
||||||
|
dst_gl_texture,
|
||||||
|
FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_blit (CoglAtlasBlitData *data,
|
||||||
|
unsigned int src_x,
|
||||||
|
unsigned int src_y,
|
||||||
|
unsigned int dst_x,
|
||||||
|
unsigned int dst_y,
|
||||||
|
unsigned int width,
|
||||||
|
unsigned int 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_blit_end (CoglAtlasBlitData *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);
|
||||||
|
}
|
||||||
|
|
||||||
|
CoglAtlas *
|
||||||
|
_cogl_atlas_new (CoglAtlasUpdatePositionCallback update_position_cb)
|
||||||
|
{
|
||||||
|
CoglAtlas *atlas = g_new (CoglAtlas, 1);
|
||||||
|
|
||||||
|
atlas->update_position_cb = update_position_cb;
|
||||||
|
atlas->map = NULL;
|
||||||
|
atlas->texture = NULL;
|
||||||
|
_cogl_callback_list_init (&atlas->reorganize_callbacks);
|
||||||
|
|
||||||
|
return atlas;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_cogl_atlas_free (CoglAtlas *atlas)
|
||||||
|
{
|
||||||
|
if (atlas->texture)
|
||||||
|
cogl_handle_unref (atlas->texture);
|
||||||
|
if (atlas->map)
|
||||||
|
_cogl_rectangle_map_free (atlas->map);
|
||||||
|
|
||||||
|
_cogl_callback_list_destroy (&atlas->reorganize_callbacks);
|
||||||
|
|
||||||
|
g_free (atlas);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _CoglAtlasRepositionData
|
||||||
|
{
|
||||||
|
/* The current user data for this texture */
|
||||||
|
void *user_data;
|
||||||
|
/* The old and new positions of the texture */
|
||||||
|
CoglRectangleMapEntry old_position;
|
||||||
|
CoglRectangleMapEntry new_position;
|
||||||
|
} CoglAtlasRepositionData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_cogl_atlas_migrate (CoglAtlas *atlas,
|
||||||
|
unsigned int n_textures,
|
||||||
|
CoglAtlasRepositionData *textures,
|
||||||
|
CoglHandle old_texture,
|
||||||
|
CoglHandle new_texture,
|
||||||
|
void *skip_user_data)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
CoglAtlasBlitData blit_data;
|
||||||
|
|
||||||
|
_cogl_atlas_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].user_data != skip_user_data)
|
||||||
|
{
|
||||||
|
_cogl_atlas_blit (&blit_data,
|
||||||
|
textures[i].old_position.x,
|
||||||
|
textures[i].old_position.y,
|
||||||
|
textures[i].new_position.x,
|
||||||
|
textures[i].new_position.y,
|
||||||
|
textures[i].new_position.width,
|
||||||
|
textures[i].new_position.height);
|
||||||
|
}
|
||||||
|
/* Update the texture position */
|
||||||
|
atlas->update_position_cb (textures[i].user_data,
|
||||||
|
new_texture,
|
||||||
|
&textures[i].new_position);
|
||||||
|
}
|
||||||
|
|
||||||
|
_cogl_atlas_blit_end (&blit_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _CoglAtlasGetRectanglesData
|
||||||
|
{
|
||||||
|
CoglAtlasRepositionData *textures;
|
||||||
|
/* Number of textures found so far */
|
||||||
|
unsigned int n_textures;
|
||||||
|
} CoglAtlasGetRectanglesData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
_cogl_atlas_get_rectangles_cb (const CoglRectangleMapEntry *rectangle,
|
||||||
|
void *rect_data,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
CoglAtlasGetRectanglesData *data = user_data;
|
||||||
|
|
||||||
|
data->textures[data->n_textures].old_position = *rectangle;
|
||||||
|
data->textures[data->n_textures++].user_data = rect_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_cogl_atlas_get_next_size (unsigned int *map_width,
|
||||||
|
unsigned int *map_height)
|
||||||
|
{
|
||||||
|
/* Double the size of the texture by increasing whichever dimension
|
||||||
|
is smaller */
|
||||||
|
if (*map_width < *map_height)
|
||||||
|
*map_width <<= 1;
|
||||||
|
else
|
||||||
|
*map_height <<= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CoglRectangleMap *
|
||||||
|
_cogl_atlas_create_map (unsigned int map_width,
|
||||||
|
unsigned int map_height,
|
||||||
|
unsigned int n_textures,
|
||||||
|
CoglAtlasRepositionData *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 (map_width < max_texture_size && map_height < max_texture_size)
|
||||||
|
{
|
||||||
|
CoglRectangleMap *new_atlas = _cogl_rectangle_map_new (map_width,
|
||||||
|
map_height,
|
||||||
|
NULL);
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
/* Add all of the textures and keep track of the new position */
|
||||||
|
for (i = 0; i < n_textures; i++)
|
||||||
|
if (!_cogl_rectangle_map_add (new_atlas,
|
||||||
|
textures[i].old_position.width,
|
||||||
|
textures[i].old_position.height,
|
||||||
|
textures[i].user_data,
|
||||||
|
&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_rectangle_map_free (new_atlas);
|
||||||
|
_cogl_atlas_get_next_size (&map_width, &map_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we get here then there's no atlas that can accommodate all of
|
||||||
|
the rectangles */
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
_cogl_atlas_compare_size_cb (const void *a,
|
||||||
|
const void *b)
|
||||||
|
{
|
||||||
|
const CoglAtlasRepositionData *ta = a;
|
||||||
|
const CoglAtlasRepositionData *tb = b;
|
||||||
|
unsigned int a_size, b_size;
|
||||||
|
|
||||||
|
a_size = ta->old_position.width * ta->old_position.height;
|
||||||
|
b_size = tb->old_position.width * tb->old_position.height;
|
||||||
|
|
||||||
|
return a_size < b_size ? 1 : a_size > b_size ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_cogl_atlas_notify_reorganize (CoglAtlas *atlas)
|
||||||
|
{
|
||||||
|
_cogl_callback_list_invoke (&atlas->reorganize_callbacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
_cogl_atlas_reserve_space (CoglAtlas *atlas,
|
||||||
|
unsigned int width,
|
||||||
|
unsigned int height,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
CoglAtlasGetRectanglesData data;
|
||||||
|
CoglRectangleMap *new_map;
|
||||||
|
CoglHandle new_tex;
|
||||||
|
unsigned int map_width, map_height;
|
||||||
|
gboolean ret;
|
||||||
|
CoglRectangleMapEntry new_position;
|
||||||
|
|
||||||
|
/* Check if we can fit the rectangle into the existing map */
|
||||||
|
if (atlas->map &&
|
||||||
|
_cogl_rectangle_map_add (atlas->map, width, height,
|
||||||
|
user_data,
|
||||||
|
&new_position))
|
||||||
|
{
|
||||||
|
int waste =
|
||||||
|
_cogl_rectangle_map_get_remaining_space (atlas->map) *
|
||||||
|
100 / (_cogl_rectangle_map_get_width (atlas->map) *
|
||||||
|
_cogl_rectangle_map_get_height (atlas->map));
|
||||||
|
COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste",
|
||||||
|
_cogl_rectangle_map_get_width (atlas->map),
|
||||||
|
_cogl_rectangle_map_get_height (atlas->map),
|
||||||
|
_cogl_rectangle_map_get_n_rectangles (atlas->map),
|
||||||
|
waste);
|
||||||
|
|
||||||
|
atlas->update_position_cb (user_data,
|
||||||
|
atlas->texture,
|
||||||
|
&new_position);
|
||||||
|
|
||||||
|
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 (atlas->map == NULL)
|
||||||
|
data.textures = g_malloc (sizeof (CoglAtlasRepositionData));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned int n_rectangles =
|
||||||
|
_cogl_rectangle_map_get_n_rectangles (atlas->map);
|
||||||
|
data.textures = g_malloc (sizeof (CoglAtlasRepositionData) *
|
||||||
|
(n_rectangles + 1));
|
||||||
|
_cogl_rectangle_map_foreach (atlas->map,
|
||||||
|
_cogl_atlas_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].old_position.x = 0;
|
||||||
|
data.textures[data.n_textures].old_position.y = 0;
|
||||||
|
data.textures[data.n_textures].old_position.width = width;
|
||||||
|
data.textures[data.n_textures].old_position.height = height;
|
||||||
|
data.textures[data.n_textures++].user_data = user_data;
|
||||||
|
|
||||||
|
/* 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 (CoglAtlasRepositionData),
|
||||||
|
_cogl_atlas_compare_size_cb);
|
||||||
|
|
||||||
|
/* Try to create a new atlas that can contain all of the textures */
|
||||||
|
if (atlas->map)
|
||||||
|
{
|
||||||
|
map_width = _cogl_rectangle_map_get_width (atlas->map);
|
||||||
|
map_height = _cogl_rectangle_map_get_height (atlas->map);
|
||||||
|
|
||||||
|
/* 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_rectangle_map_get_remaining_space (atlas->map) <
|
||||||
|
width * height)
|
||||||
|
_cogl_atlas_get_next_size (&map_width, &map_height);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Start with an initial size of 256x256 */
|
||||||
|
map_width = 256;
|
||||||
|
map_height = 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_map = _cogl_atlas_create_map (map_width, map_height,
|
||||||
|
data.n_textures, data.textures);
|
||||||
|
|
||||||
|
/* If we can't create a map with the texture then give up */
|
||||||
|
if (new_map == NULL)
|
||||||
|
{
|
||||||
|
COGL_NOTE (ATLAS, "Could not fit texture in the atlas");
|
||||||
|
ret = FALSE;
|
||||||
|
}
|
||||||
|
/* We need to migrate the existing textures into a new texture */
|
||||||
|
else if ((new_tex = _cogl_texture_2d_new_with_size
|
||||||
|
(_cogl_rectangle_map_get_width (new_map),
|
||||||
|
_cogl_rectangle_map_get_height (new_map),
|
||||||
|
COGL_TEXTURE_NONE,
|
||||||
|
COGL_PIXEL_FORMAT_RGBA_8888)) == COGL_INVALID_HANDLE)
|
||||||
|
{
|
||||||
|
COGL_NOTE (ATLAS, "Could not create a CoglTexture2D");
|
||||||
|
_cogl_rectangle_map_free (new_map);
|
||||||
|
ret = FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int waste;
|
||||||
|
|
||||||
|
_cogl_atlas_notify_reorganize (atlas);
|
||||||
|
|
||||||
|
COGL_NOTE (ATLAS,
|
||||||
|
"Atlas %s with size %ix%i",
|
||||||
|
atlas->map == NULL ||
|
||||||
|
_cogl_rectangle_map_get_width (atlas->map) !=
|
||||||
|
_cogl_rectangle_map_get_width (new_map) ||
|
||||||
|
_cogl_rectangle_map_get_height (atlas->map) !=
|
||||||
|
_cogl_rectangle_map_get_height (new_map) ?
|
||||||
|
"resized" : "reorganized",
|
||||||
|
_cogl_rectangle_map_get_width (new_map),
|
||||||
|
_cogl_rectangle_map_get_height (new_map));
|
||||||
|
|
||||||
|
if (atlas->map)
|
||||||
|
{
|
||||||
|
/* Move all the textures to the right position in the new
|
||||||
|
texture. This will also update the texture's rectangle */
|
||||||
|
_cogl_atlas_migrate (atlas,
|
||||||
|
data.n_textures,
|
||||||
|
data.textures,
|
||||||
|
atlas->texture,
|
||||||
|
new_tex,
|
||||||
|
user_data);
|
||||||
|
_cogl_rectangle_map_free (atlas->map);
|
||||||
|
cogl_handle_unref (atlas->texture);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
/* We know there's only one texture so we can just directly
|
||||||
|
update the rectangle from its new position */
|
||||||
|
atlas->update_position_cb (data.textures[0].user_data,
|
||||||
|
new_tex,
|
||||||
|
&data.textures[0].new_position);
|
||||||
|
|
||||||
|
atlas->map = new_map;
|
||||||
|
atlas->texture = new_tex;
|
||||||
|
|
||||||
|
waste = (_cogl_rectangle_map_get_remaining_space (atlas->map) *
|
||||||
|
100 / (_cogl_rectangle_map_get_width (atlas->map) *
|
||||||
|
_cogl_rectangle_map_get_height (atlas->map)));
|
||||||
|
|
||||||
|
COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste",
|
||||||
|
_cogl_rectangle_map_get_width (atlas->map),
|
||||||
|
_cogl_rectangle_map_get_height (atlas->map),
|
||||||
|
_cogl_rectangle_map_get_n_rectangles (atlas->map),
|
||||||
|
waste);
|
||||||
|
|
||||||
|
ret = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free (data.textures);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_cogl_atlas_remove (CoglAtlas *atlas,
|
||||||
|
const CoglRectangleMapEntry *rectangle)
|
||||||
|
{
|
||||||
|
_cogl_rectangle_map_remove (atlas->map, rectangle);
|
||||||
|
|
||||||
|
COGL_NOTE (ATLAS, "Removed rectangle sized %ix%i",
|
||||||
|
rectangle->width,
|
||||||
|
rectangle->height);
|
||||||
|
COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste",
|
||||||
|
_cogl_rectangle_map_get_width (atlas->map),
|
||||||
|
_cogl_rectangle_map_get_height (atlas->map),
|
||||||
|
_cogl_rectangle_map_get_n_rectangles (atlas->map),
|
||||||
|
_cogl_rectangle_map_get_remaining_space (atlas->map) *
|
||||||
|
100 / (_cogl_rectangle_map_get_width (atlas->map) *
|
||||||
|
_cogl_rectangle_map_get_height (atlas->map)));
|
||||||
|
};
|
||||||
|
|
||||||
|
CoglHandle
|
||||||
|
_cogl_atlas_copy_rectangle (CoglAtlas *atlas,
|
||||||
|
unsigned int x,
|
||||||
|
unsigned int y,
|
||||||
|
unsigned int width,
|
||||||
|
unsigned int height,
|
||||||
|
CoglTextureFlags flags,
|
||||||
|
CoglPixelFormat format)
|
||||||
|
{
|
||||||
|
CoglHandle tex;
|
||||||
|
CoglAtlasBlitData blit_data;
|
||||||
|
|
||||||
|
/* Create a new texture at the right size */
|
||||||
|
tex = cogl_texture_new_with_size (width, height, flags, 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_blit_begin (&blit_data, tex, atlas->texture);
|
||||||
|
_cogl_atlas_blit (&blit_data,
|
||||||
|
x, y,
|
||||||
|
0, 0,
|
||||||
|
width, height);
|
||||||
|
_cogl_atlas_blit_end (&blit_data);
|
||||||
|
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_cogl_atlas_add_reorganize_callback (CoglAtlas *atlas,
|
||||||
|
CoglCallbackListFunc callback,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
_cogl_callback_list_add (&atlas->reorganize_callbacks, callback, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_cogl_atlas_remove_reorganize_callback (CoglAtlas *atlas,
|
||||||
|
CoglCallbackListFunc callback,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
_cogl_callback_list_remove (&atlas->reorganize_callbacks,
|
||||||
|
callback, user_data);
|
||||||
|
}
|
86
clutter/cogl/cogl/cogl-atlas.h
Normal file
86
clutter/cogl/cogl/cogl-atlas.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Cogl
|
||||||
|
*
|
||||||
|
* An object oriented GL/GLES Abstraction/Utility Layer
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Intel Corporation.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library; if not, 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 "cogl-rectangle-map.h"
|
||||||
|
#include "cogl-callback-list.h"
|
||||||
|
|
||||||
|
typedef void
|
||||||
|
(* CoglAtlasUpdatePositionCallback) (void *user_data,
|
||||||
|
CoglHandle new_texture,
|
||||||
|
const CoglRectangleMapEntry *rect);
|
||||||
|
|
||||||
|
typedef struct _CoglAtlas CoglAtlas;
|
||||||
|
|
||||||
|
struct _CoglAtlas
|
||||||
|
{
|
||||||
|
CoglRectangleMap *map;
|
||||||
|
|
||||||
|
CoglHandle texture;
|
||||||
|
|
||||||
|
CoglAtlasUpdatePositionCallback update_position_cb;
|
||||||
|
|
||||||
|
CoglCallbackList reorganize_callbacks;
|
||||||
|
};
|
||||||
|
|
||||||
|
CoglAtlas *
|
||||||
|
_cogl_atlas_new (CoglAtlasUpdatePositionCallback update_position_cb);
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
_cogl_atlas_reserve_space (CoglAtlas *atlas,
|
||||||
|
unsigned int width,
|
||||||
|
unsigned int height,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
void
|
||||||
|
_cogl_atlas_remove (CoglAtlas *atlas,
|
||||||
|
const CoglRectangleMapEntry *rectangle);
|
||||||
|
|
||||||
|
void
|
||||||
|
_cogl_atlas_free (CoglAtlas *atlas);
|
||||||
|
|
||||||
|
CoglHandle
|
||||||
|
_cogl_atlas_copy_rectangle (CoglAtlas *atlas,
|
||||||
|
unsigned int x,
|
||||||
|
unsigned int y,
|
||||||
|
unsigned int width,
|
||||||
|
unsigned int height,
|
||||||
|
CoglTextureFlags flags,
|
||||||
|
CoglPixelFormat format);
|
||||||
|
|
||||||
|
CoglAtlas *
|
||||||
|
_cogl_atlas_get_default (void);
|
||||||
|
|
||||||
|
void
|
||||||
|
_cogl_atlas_add_reorganize_callback (CoglAtlas *atlas,
|
||||||
|
CoglCallbackListFunc callback,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
void
|
||||||
|
_cogl_atlas_remove_reorganize_callback (CoglAtlas *atlas,
|
||||||
|
CoglCallbackListFunc callback,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
#endif /* __COGL_ATLAS_H */
|
@ -214,8 +214,7 @@ cogl_create_context (void)
|
|||||||
_cogl_enable (enable_flags);
|
_cogl_enable (enable_flags);
|
||||||
_cogl_flush_face_winding ();
|
_cogl_flush_face_winding ();
|
||||||
|
|
||||||
_context->rectangle_map = NULL;
|
_context->atlas = NULL;
|
||||||
_context->atlas_texture = COGL_INVALID_HANDLE;
|
|
||||||
|
|
||||||
/* As far as I can tell, GL_POINT_SPRITE doesn't have any effect
|
/* As far as I can tell, GL_POINT_SPRITE doesn't have any effect
|
||||||
unless GL_COORD_REPLACE is enabled for an individual
|
unless GL_COORD_REPLACE is enabled for an individual
|
||||||
@ -273,11 +272,8 @@ _cogl_destroy_context (void)
|
|||||||
if (_context->default_layer_0)
|
if (_context->default_layer_0)
|
||||||
cogl_handle_unref (_context->default_layer_0);
|
cogl_handle_unref (_context->default_layer_0);
|
||||||
|
|
||||||
if (_context->rectangle_map)
|
if (_context->atlas)
|
||||||
_cogl_rectangle_map_free (_context->rectangle_map);
|
_cogl_atlas_free (_context->atlas);
|
||||||
|
|
||||||
if (_context->atlas_texture)
|
|
||||||
cogl_handle_unref (_context->atlas_texture);
|
|
||||||
|
|
||||||
_cogl_bitmask_destroy (&_context->texcoord_arrays_enabled);
|
_cogl_bitmask_destroy (&_context->texcoord_arrays_enabled);
|
||||||
_cogl_bitmask_destroy (&_context->temp_bitmask);
|
_cogl_bitmask_destroy (&_context->temp_bitmask);
|
||||||
|
@ -31,9 +31,9 @@
|
|||||||
#include "cogl-clip-stack.h"
|
#include "cogl-clip-stack.h"
|
||||||
#include "cogl-matrix-stack.h"
|
#include "cogl-matrix-stack.h"
|
||||||
#include "cogl-material-private.h"
|
#include "cogl-material-private.h"
|
||||||
#include "cogl-rectangle-map.h"
|
|
||||||
#include "cogl-buffer-private.h"
|
#include "cogl-buffer-private.h"
|
||||||
#include "cogl-bitmask.h"
|
#include "cogl-bitmask.h"
|
||||||
|
#include "cogl-atlas.h"
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -143,8 +143,7 @@ typedef struct
|
|||||||
|
|
||||||
CoglMaterial *texture_download_material;
|
CoglMaterial *texture_download_material;
|
||||||
|
|
||||||
CoglRectangleMap *rectangle_map;
|
CoglAtlas *atlas;
|
||||||
CoglHandle atlas_texture;
|
|
||||||
|
|
||||||
/* This debugging variable is used to pick a colour for visually
|
/* This debugging variable is used to pick a colour for visually
|
||||||
displaying the quad batches. It needs to be global so that it can
|
displaying the quad batches. It needs to be global so that it can
|
||||||
|
Loading…
Reference in New Issue
Block a user