cogl-atlas: Rename to CoglRectangleMap

This simply renames CoglAtlas to CoglRectangleMap without making any
functional changes. The old 'CoglAtlas' is just a data structure for
managing unused areas of a rectangle and it doesn't neccessarily have
to be used for an atlas so it wasn't a very good name.
This commit is contained in:
Neil Roberts 2010-08-02 15:24:03 +01:00
parent bb17fd70e2
commit bc20010582
8 changed files with 366 additions and 344 deletions

View File

@ -144,8 +144,8 @@ cogl_sources_c = \
$(srcdir)/cogl-texture-3d.c \ $(srcdir)/cogl-texture-3d.c \
$(srcdir)/cogl-texture-rectangle-private.h \ $(srcdir)/cogl-texture-rectangle-private.h \
$(srcdir)/cogl-texture-rectangle.c \ $(srcdir)/cogl-texture-rectangle.c \
$(srcdir)/cogl-atlas.h \ $(srcdir)/cogl-rectangle-map.h \
$(srcdir)/cogl-atlas.c \ $(srcdir)/cogl-rectangle-map.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 \

View File

@ -26,7 +26,7 @@
#include "cogl-handle.h" #include "cogl-handle.h"
#include "cogl-texture-private.h" #include "cogl-texture-private.h"
#include "cogl-atlas.h" #include "cogl-rectangle-map.h"
#define COGL_ATLAS_TEXTURE(tex) ((CoglAtlasTexture *) tex) #define COGL_ATLAS_TEXTURE(tex) ((CoglAtlasTexture *) tex)
@ -34,23 +34,23 @@ typedef struct _CoglAtlasTexture CoglAtlasTexture;
struct _CoglAtlasTexture struct _CoglAtlasTexture
{ {
CoglTexture _parent; CoglTexture _parent;
/* The format that the texture is in. This isn't necessarily the /* The format that the texture is in. This isn't necessarily the
same format as the atlas texture because we can store same format as the atlas texture because we can store
pre-multiplied and non-pre-multiplied textures together */ pre-multiplied and non-pre-multiplied textures together */
CoglPixelFormat format; CoglPixelFormat format;
/* The rectangle that was used to add this texture to the /* The rectangle that was used to add this texture to the
atlas. This includes the 1-pixel border */ atlas. This includes the 1-pixel border */
CoglAtlasRectangle rectangle; CoglRectangleMapEntry rectangle;
/* The texture might need to be migrated out in which case this will /* 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 */ be set to TRUE and sub_texture will actually be a real texture */
gboolean in_atlas; gboolean in_atlas;
/* A CoglSubTexture representing the region for easy rendering */ /* A CoglSubTexture representing the region for easy rendering */
CoglHandle sub_texture; CoglHandle sub_texture;
}; };
GQuark GQuark

View File

@ -39,7 +39,7 @@
#include "cogl-context.h" #include "cogl-context.h"
#include "cogl-handle.h" #include "cogl-handle.h"
#include "cogl-texture-driver.h" #include "cogl-texture-driver.h"
#include "cogl-atlas.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"
@ -254,18 +254,18 @@ _cogl_atlas_texture_remove_from_atlas (CoglAtlasTexture *atlas_tex)
{ {
_COGL_GET_CONTEXT (ctx, NO_RETVAL); _COGL_GET_CONTEXT (ctx, NO_RETVAL);
_cogl_atlas_remove_rectangle (ctx->atlas, &atlas_tex->rectangle); _cogl_rectangle_map_remove (ctx->rectangle_map, &atlas_tex->rectangle);
COGL_NOTE (ATLAS, "Removed rectangle sized %ix%i", COGL_NOTE (ATLAS, "Removed rectangle sized %ix%i",
atlas_tex->rectangle.width, atlas_tex->rectangle.width,
atlas_tex->rectangle.height); atlas_tex->rectangle.height);
COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste",
_cogl_atlas_get_width (ctx->atlas), _cogl_rectangle_map_get_width (ctx->rectangle_map),
_cogl_atlas_get_height (ctx->atlas), _cogl_rectangle_map_get_height (ctx->rectangle_map),
_cogl_atlas_get_n_rectangles (ctx->atlas), _cogl_rectangle_map_get_n_rectangles (ctx->rectangle_map),
_cogl_atlas_get_remaining_space (ctx->atlas) * 100 / _cogl_rectangle_map_get_remaining_space (ctx->rectangle_map) *
(_cogl_atlas_get_width (ctx->atlas) * 100 / (_cogl_rectangle_map_get_width (ctx->rectangle_map) *
_cogl_atlas_get_height (ctx->atlas))); _cogl_rectangle_map_get_height (ctx->rectangle_map)));
atlas_tex->in_atlas = FALSE; atlas_tex->in_atlas = FALSE;
} }
@ -599,7 +599,7 @@ _cogl_atlas_texture_get_height (CoglTexture *tex)
static CoglHandle static CoglHandle
_cogl_atlas_texture_create_sub_texture (CoglHandle full_texture, _cogl_atlas_texture_create_sub_texture (CoglHandle full_texture,
const CoglAtlasRectangle *rectangle) const CoglRectangleMapEntry *rectangle)
{ {
/* Create a subtexture for the given rectangle not including the /* Create a subtexture for the given rectangle not including the
1-pixel border */ 1-pixel border */
@ -615,7 +615,7 @@ typedef struct _CoglAtlasTextureRepositionData
/* The current texture which already has a position */ /* The current texture which already has a position */
CoglAtlasTexture *texture; CoglAtlasTexture *texture;
/* The new position of the texture */ /* The new position of the texture */
CoglAtlasRectangle new_position; CoglRectangleMapEntry new_position;
} CoglAtlasTextureRepositionData; } CoglAtlasTextureRepositionData;
static void static void
@ -679,32 +679,32 @@ typedef struct _CoglAtlasTextureGetRectanglesData
} CoglAtlasTextureGetRectanglesData; } CoglAtlasTextureGetRectanglesData;
static void static void
_cogl_atlas_texture_get_rectangles_cb (const CoglAtlasRectangle *rectangle, _cogl_atlas_texture_get_rectangles_cb (const CoglRectangleMapEntry *rectangle,
gpointer rectangle_data, gpointer rect_data,
gpointer user_data) gpointer user_data)
{ {
CoglAtlasTextureGetRectanglesData *data = user_data; CoglAtlasTextureGetRectanglesData *data = user_data;
data->textures[data->n_textures++].texture = rectangle_data; data->textures[data->n_textures++].texture = rect_data;
} }
static void static void
_cogl_atlas_texture_get_next_size (unsigned int *atlas_width, _cogl_atlas_texture_get_next_size (unsigned int *map_width,
unsigned int *atlas_height) unsigned int *map_height)
{ {
/* Double the size of the texture by increasing whichever dimension /* Double the size of the texture by increasing whichever dimension
is smaller */ is smaller */
if (*atlas_width < *atlas_height) if (*map_width < *map_height)
*atlas_width <<= 1; *map_width <<= 1;
else else
*atlas_height <<= 1; *map_height <<= 1;
} }
static CoglAtlas * static CoglRectangleMap *
_cogl_atlas_texture_create_atlas (unsigned int atlas_width, _cogl_atlas_texture_create_map (unsigned int map_width,
unsigned int atlas_height, unsigned int map_height,
unsigned int n_textures, unsigned int n_textures,
CoglAtlasTextureRepositionData *textures) CoglAtlasTextureRepositionData *textures)
{ {
GLint max_texture_size = 1024; GLint max_texture_size = 1024;
@ -717,18 +717,20 @@ _cogl_atlas_texture_create_atlas (unsigned int atlas_width,
/* Keep trying increasingly larger atlases until we can fit all of /* Keep trying increasingly larger atlases until we can fit all of
the textures */ the textures */
while (atlas_width < max_texture_size && atlas_height < max_texture_size) while (map_width < max_texture_size && map_height < max_texture_size)
{ {
CoglAtlas *new_atlas = _cogl_atlas_new (atlas_width, atlas_height, NULL); CoglRectangleMap *new_atlas = _cogl_rectangle_map_new (map_width,
map_height,
NULL);
unsigned int i; unsigned int i;
/* Add all of the textures and keep track of the new position */ /* Add all of the textures and keep track of the new position */
for (i = 0; i < n_textures; i++) for (i = 0; i < n_textures; i++)
if (!_cogl_atlas_add_rectangle (new_atlas, if (!_cogl_rectangle_map_add (new_atlas,
textures[i].texture->rectangle.width, textures[i].texture->rectangle.width,
textures[i].texture->rectangle.height, textures[i].texture->rectangle.height,
textures[i].texture, textures[i].texture,
&textures[i].new_position)) &textures[i].new_position))
break; break;
/* If the atlas can contain all of the textures then we have a /* If the atlas can contain all of the textures then we have a
@ -736,8 +738,8 @@ _cogl_atlas_texture_create_atlas (unsigned int atlas_width,
if (i >= n_textures) if (i >= n_textures)
return new_atlas; return new_atlas;
_cogl_atlas_free (new_atlas); _cogl_rectangle_map_free (new_atlas);
_cogl_atlas_texture_get_next_size (&atlas_width, &atlas_height); _cogl_atlas_texture_get_next_size (&map_width, &map_height);
} }
/* If we get here then there's no atlas that can accommodate all of /* If we get here then there's no atlas that can accommodate all of
@ -766,25 +768,26 @@ _cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex,
unsigned int height) unsigned int height)
{ {
CoglAtlasTextureGetRectanglesData data; CoglAtlasTextureGetRectanglesData data;
CoglAtlas *new_atlas; CoglRectangleMap *new_map;
CoglHandle new_tex; CoglHandle new_tex;
unsigned int atlas_width, atlas_height; unsigned int map_width, map_height;
gboolean ret; gboolean ret;
_COGL_GET_CONTEXT (ctx, FALSE); _COGL_GET_CONTEXT (ctx, FALSE);
/* Check if we can fit the rectangle into the existing atlas */ /* Check if we can fit the rectangle into the existing atlas */
if (ctx->atlas && _cogl_atlas_add_rectangle (ctx->atlas, width, height, if (ctx->rectangle_map &&
new_sub_tex, _cogl_rectangle_map_add (ctx->rectangle_map, width, height,
&new_sub_tex->rectangle)) new_sub_tex,
&new_sub_tex->rectangle))
{ {
COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste",
_cogl_atlas_get_width (ctx->atlas), _cogl_rectangle_map_get_width (ctx->rectangle_map),
_cogl_atlas_get_height (ctx->atlas), _cogl_rectangle_map_get_height (ctx->rectangle_map),
_cogl_atlas_get_n_rectangles (ctx->atlas), _cogl_rectangle_map_get_n_rectangles (ctx->rectangle_map),
_cogl_atlas_get_remaining_space (ctx->atlas) * 100 / _cogl_rectangle_map_get_remaining_space (ctx->rectangle_map) *
(_cogl_atlas_get_width (ctx->atlas) * 100 / (_cogl_rectangle_map_get_width (ctx->rectangle_map) *
_cogl_atlas_get_height (ctx->atlas))); _cogl_rectangle_map_get_height (ctx->rectangle_map)));
return TRUE; return TRUE;
} }
@ -792,15 +795,17 @@ _cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex,
/* We need to reorganise the atlas so we'll get an array of all the /* We need to reorganise the atlas so we'll get an array of all the
textures currently in the atlas. */ textures currently in the atlas. */
data.n_textures = 0; data.n_textures = 0;
if (ctx->atlas == NULL) if (ctx->rectangle_map == NULL)
data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData)); data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData));
else else
{ {
data.textures = unsigned int n_rectangles =
g_malloc (sizeof (CoglAtlasTextureRepositionData) * _cogl_rectangle_map_get_n_rectangles (ctx->rectangle_map);
(_cogl_atlas_get_n_rectangles (ctx->atlas) + 1)); data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData) *
_cogl_atlas_foreach (ctx->atlas, _cogl_atlas_texture_get_rectangles_cb, (n_rectangles + 1));
&data); _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 /* Add the new rectangle as a dummy texture so that it can be
@ -815,59 +820,61 @@ _cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex,
_cogl_atlas_texture_compare_size_cb); _cogl_atlas_texture_compare_size_cb);
/* Try to create a new atlas that can contain all of the textures */ /* Try to create a new atlas that can contain all of the textures */
if (ctx->atlas) if (ctx->rectangle_map)
{ {
atlas_width = _cogl_atlas_get_width (ctx->atlas); map_width = _cogl_rectangle_map_get_width (ctx->rectangle_map);
atlas_height = _cogl_atlas_get_height (ctx->atlas); map_height = _cogl_rectangle_map_get_height (ctx->rectangle_map);
/* If there is enough space in the existing for the new /* If there is enough space in the existing for the new
rectangle in the existing atlas we'll start with the same rectangle in the existing atlas we'll start with the same
size, otherwise we'll immediately double it */ size, otherwise we'll immediately double it */
if (_cogl_atlas_get_remaining_space (ctx->atlas) < width * height) if (_cogl_rectangle_map_get_remaining_space (ctx->rectangle_map) <
_cogl_atlas_texture_get_next_size (&atlas_width, &atlas_height); width * height)
_cogl_atlas_texture_get_next_size (&map_width, &map_height);
} }
else else
{ {
/* Start with an initial size of 256x256 */ /* Start with an initial size of 256x256 */
atlas_width = 256; map_width = 256;
atlas_height = 256; map_height = 256;
} }
new_atlas = _cogl_atlas_texture_create_atlas (atlas_width, atlas_height, new_map = _cogl_atlas_texture_create_map (map_width, map_height,
data.n_textures, data.textures); 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 an atlas with the texture then give up */ /* If we can't create a map with the texture then give up */
if (new_atlas == NULL) if (new_map == NULL)
{ {
COGL_NOTE (ATLAS, "Could not fit texture in the atlas"); COGL_NOTE (ATLAS, "Could not fit texture in the atlas");
ret = FALSE; ret = FALSE;
} }
/* We need to migrate the existing textures into a new texture */ /* We need to migrate the existing textures into a new texture */
else if ((new_tex = else if ((new_tex =
_cogl_texture_2d_new_with_size (_cogl_atlas_get_width (new_atlas), _cogl_texture_2d_new_with_size (map_width, map_height,
_cogl_atlas_get_height (new_atlas),
COGL_TEXTURE_NONE, COGL_TEXTURE_NONE,
COGL_PIXEL_FORMAT_RGBA_8888)) == COGL_PIXEL_FORMAT_RGBA_8888)) ==
COGL_INVALID_HANDLE) COGL_INVALID_HANDLE)
{ {
COGL_NOTE (ATLAS, "Could not create a CoglTexture2D"); COGL_NOTE (ATLAS, "Could not create a CoglTexture2D");
_cogl_atlas_free (new_atlas); _cogl_rectangle_map_free (new_map);
ret = FALSE; ret = FALSE;
} }
else else
{ {
COGL_NOTE (ATLAS, COGL_NOTE (ATLAS,
"Atlas %s with size %ix%i", "Atlas %s with size %ix%i",
ctx->atlas == NULL || ctx->rectangle_map == NULL ||
_cogl_atlas_get_width (ctx->atlas) != _cogl_rectangle_map_get_width (ctx->rectangle_map) !=
_cogl_atlas_get_width (new_atlas) || _cogl_rectangle_map_get_width (new_map) ||
_cogl_atlas_get_height (ctx->atlas) != _cogl_rectangle_map_get_height (ctx->rectangle_map) !=
_cogl_atlas_get_height (new_atlas) ? _cogl_rectangle_map_get_height (new_map) ?
"resized" : "reorganized", "resized" : "reorganized",
_cogl_atlas_get_width (new_atlas), _cogl_rectangle_map_get_width (new_map),
_cogl_atlas_get_height (new_atlas)); _cogl_rectangle_map_get_height (new_map));
if (ctx->atlas) if (ctx->rectangle_map)
{ {
/* Move all the textures to the right position in the new /* Move all the textures to the right position in the new
texture. This will also update the texture's rectangle */ texture. This will also update the texture's rectangle */
@ -876,7 +883,7 @@ _cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex,
ctx->atlas_texture, ctx->atlas_texture,
new_tex, new_tex,
new_sub_tex); new_sub_tex);
_cogl_atlas_free (ctx->atlas); _cogl_rectangle_map_free (ctx->rectangle_map);
cogl_handle_unref (ctx->atlas_texture); cogl_handle_unref (ctx->atlas_texture);
} }
else else
@ -884,16 +891,16 @@ _cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex,
update the rectangle from its new position */ update the rectangle from its new position */
data.textures[0].texture->rectangle = data.textures[0].new_position; data.textures[0].texture->rectangle = data.textures[0].new_position;
ctx->atlas = new_atlas; ctx->rectangle_map = new_map;
ctx->atlas_texture = new_tex; ctx->atlas_texture = new_tex;
COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste", COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste",
_cogl_atlas_get_width (ctx->atlas), _cogl_rectangle_map_get_width (ctx->rectangle_map),
_cogl_atlas_get_height (ctx->atlas), _cogl_rectangle_map_get_height (ctx->rectangle_map),
_cogl_atlas_get_n_rectangles (ctx->atlas), _cogl_rectangle_map_get_n_rectangles (ctx->rectangle_map),
_cogl_atlas_get_remaining_space (ctx->atlas) * 100 / _cogl_rectangle_map_get_remaining_space (ctx->rectangle_map) *
(_cogl_atlas_get_width (ctx->atlas) * 100 / (_cogl_rectangle_map_get_width (ctx->rectangle_map) *
_cogl_atlas_get_height (ctx->atlas))); _cogl_rectangle_map_get_height (ctx->rectangle_map)));
ret = TRUE; ret = TRUE;
} }
@ -1003,7 +1010,7 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp,
if (dst_bmp == NULL) if (dst_bmp == NULL)
{ {
_cogl_atlas_remove_rectangle (ctx->atlas, &atlas_tex->rectangle); _cogl_rectangle_map_remove (ctx->rectangle_map, &atlas_tex->rectangle);
g_free (atlas_tex); g_free (atlas_tex);
return COGL_INVALID_HANDLE; return COGL_INVALID_HANDLE;
} }

View File

@ -1,78 +0,0 @@
/*
* 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, see <http://www.gnu.org/licenses/>.
*
*
*/
#ifndef __COGL_ATLAS_H
#define __COGL_ATLAS_H
#include <glib.h>
typedef struct _CoglAtlas CoglAtlas;
typedef struct _CoglAtlasRectangle CoglAtlasRectangle;
typedef void (* CoglAtlasCallback) (const CoglAtlasRectangle *rectangle,
gpointer rectangle_data,
gpointer user_data);
struct _CoglAtlasRectangle
{
unsigned int x, y;
unsigned int width, height;
};
CoglAtlas *
_cogl_atlas_new (unsigned int width,
unsigned int height,
GDestroyNotify value_destroy_func);
gboolean
_cogl_atlas_add_rectangle (CoglAtlas *atlas,
unsigned int width,
unsigned int height,
gpointer data,
CoglAtlasRectangle *rectangle);
void
_cogl_atlas_remove_rectangle (CoglAtlas *atlas,
const CoglAtlasRectangle *rectangle);
unsigned int
_cogl_atlas_get_width (CoglAtlas *atlas);
unsigned int
_cogl_atlas_get_height (CoglAtlas *atlas);
unsigned int
_cogl_atlas_get_remaining_space (CoglAtlas *atlas);
unsigned int
_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 */

View File

@ -214,7 +214,7 @@ cogl_create_context (void)
_cogl_enable (enable_flags); _cogl_enable (enable_flags);
_cogl_flush_face_winding (); _cogl_flush_face_winding ();
_context->atlas = NULL; _context->rectangle_map = NULL;
_context->atlas_texture = COGL_INVALID_HANDLE; _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
@ -273,8 +273,9 @@ _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->atlas) if (_context->rectangle_map)
_cogl_atlas_free (_context->atlas); _cogl_rectangle_map_free (_context->rectangle_map);
if (_context->atlas_texture) if (_context->atlas_texture)
cogl_handle_unref (_context->atlas_texture); cogl_handle_unref (_context->atlas_texture);

View File

@ -31,7 +31,7 @@
#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-atlas.h" #include "cogl-rectangle-map.h"
#include "cogl-buffer-private.h" #include "cogl-buffer-private.h"
#include "cogl-bitmask.h" #include "cogl-bitmask.h"
@ -143,7 +143,7 @@ typedef struct
CoglMaterial *texture_download_material; CoglMaterial *texture_download_material;
CoglAtlas *atlas; CoglRectangleMap *rectangle_map;
CoglHandle atlas_texture; 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

View File

@ -30,7 +30,7 @@
#include <glib.h> #include <glib.h>
#include "cogl-atlas.h" #include "cogl-rectangle-map.h"
#include "cogl-debug.h" #include "cogl-debug.h"
/* Implements a data structure which keeps track of unused /* Implements a data structure which keeps track of unused
@ -46,26 +46,26 @@
the atlas */ the atlas */
#include <cairo.h> #include <cairo.h>
static void _cogl_atlas_dump_image (CoglAtlas *atlas); static void _cogl_rectangle_map_dump_image (CoglRectangleMap *map);
#endif /* COGL_ENABLE_DEBUG */ #endif /* COGL_ENABLE_DEBUG */
typedef struct _CoglAtlasNode CoglAtlasNode; typedef struct _CoglRectangleMapNode CoglRectangleMapNode;
typedef struct _CoglAtlasStackEntry CoglAtlasStackEntry; typedef struct _CoglRectangleMapStackEntry CoglRectangleMapStackEntry;
typedef void (* CoglAtlasInternalForeachCb) (CoglAtlasNode *node, typedef void (* CoglRectangleMapInternalForeachCb) (CoglRectangleMapNode *node,
gpointer data); void *data);
typedef enum typedef enum
{ {
COGL_ATLAS_BRANCH, COGL_RECTANGLE_MAP_BRANCH,
COGL_ATLAS_FILLED_LEAF, COGL_RECTANGLE_MAP_FILLED_LEAF,
COGL_ATLAS_EMPTY_LEAF COGL_RECTANGLE_MAP_EMPTY_LEAF
} CoglAtlasNodeType; } CoglRectangleMapNodeType;
struct _CoglAtlas struct _CoglRectangleMap
{ {
CoglAtlasNode *root; CoglRectangleMapNode *root;
unsigned int space_remaining; unsigned int space_remaining;
unsigned int n_rectangles; unsigned int n_rectangles;
@ -73,80 +73,81 @@ struct _CoglAtlas
GDestroyNotify value_destroy_func; GDestroyNotify value_destroy_func;
}; };
struct _CoglAtlasNode struct _CoglRectangleMapNode
{ {
CoglAtlasNodeType type; CoglRectangleMapNodeType type;
CoglAtlasRectangle rectangle; CoglRectangleMapEntry rectangle;
CoglAtlasNode *parent; CoglRectangleMapNode *parent;
union union
{ {
/* Fields used when this is a branch */ /* Fields used when this is a branch */
struct struct
{ {
CoglAtlasNode *left; CoglRectangleMapNode *left;
CoglAtlasNode *right; CoglRectangleMapNode *right;
} branch; } branch;
/* Field used when this is a filled leaf */ /* Field used when this is a filled leaf */
gpointer data; void *data;
} d; } d;
}; };
struct _CoglAtlasStackEntry struct _CoglRectangleMapStackEntry
{ {
/* The node to search */ /* The node to search */
CoglAtlasNode *node; CoglRectangleMapNode *node;
/* Index of next branch of this node to explore. Basically either 0 /* Index of next branch of this node to explore. Basically either 0
to go left or 1 to go right */ to go left or 1 to go right */
gboolean next_index; gboolean next_index;
/* Next entry in the stack */ /* Next entry in the stack */
CoglAtlasStackEntry *next; CoglRectangleMapStackEntry *next;
}; };
static CoglAtlasNode * static CoglRectangleMapNode *
_cogl_atlas_node_new (void) _cogl_rectangle_map_node_new (void)
{ {
return g_slice_new (CoglAtlasNode); return g_slice_new (CoglRectangleMapNode);
} }
static void static void
_cogl_atlas_node_free (CoglAtlasNode *node) _cogl_rectangle_map_node_free (CoglRectangleMapNode *node)
{ {
g_slice_free (CoglAtlasNode, node); g_slice_free (CoglRectangleMapNode, node);
} }
CoglAtlas * CoglRectangleMap *
_cogl_atlas_new (unsigned int width, _cogl_rectangle_map_new (unsigned int width,
unsigned int height, unsigned int height,
GDestroyNotify value_destroy_func) GDestroyNotify value_destroy_func)
{ {
CoglAtlas *atlas = g_new (CoglAtlas, 1); CoglRectangleMap *map = g_new (CoglRectangleMap, 1);
CoglAtlasNode *root = _cogl_atlas_node_new (); CoglRectangleMapNode *root = _cogl_rectangle_map_node_new ();
root->type = COGL_ATLAS_EMPTY_LEAF; root->type = COGL_RECTANGLE_MAP_EMPTY_LEAF;
root->parent = NULL; root->parent = NULL;
root->rectangle.x = 0; root->rectangle.x = 0;
root->rectangle.y = 0; root->rectangle.y = 0;
root->rectangle.width = width; root->rectangle.width = width;
root->rectangle.height = height; root->rectangle.height = height;
atlas->root = root; map->root = root;
atlas->space_remaining = width * height; map->space_remaining = width * height;
atlas->n_rectangles = 0; map->n_rectangles = 0;
atlas->value_destroy_func = value_destroy_func; map->value_destroy_func = value_destroy_func;
return atlas; return map;
} }
static CoglAtlasStackEntry * static CoglRectangleMapStackEntry *
_cogl_atlas_stack_push (CoglAtlasStackEntry *stack, _cogl_rectangle_map_stack_push (CoglRectangleMapStackEntry *stack,
CoglAtlasNode *node, CoglRectangleMapNode *node,
gboolean next_index) gboolean next_index)
{ {
CoglAtlasStackEntry *new_entry = g_slice_new (CoglAtlasStackEntry); CoglRectangleMapStackEntry *new_entry =
g_slice_new (CoglRectangleMapStackEntry);
new_entry->node = node; new_entry->node = node;
new_entry->next_index = next_index; new_entry->next_index = next_index;
@ -155,19 +156,19 @@ _cogl_atlas_stack_push (CoglAtlasStackEntry *stack,
return new_entry; return new_entry;
} }
static CoglAtlasStackEntry * static CoglRectangleMapStackEntry *
_cogl_atlas_stack_pop (CoglAtlasStackEntry *stack) _cogl_rectangle_map_stack_pop (CoglRectangleMapStackEntry *stack)
{ {
CoglAtlasStackEntry *next = stack->next; CoglRectangleMapStackEntry *next = stack->next;
g_slice_free (CoglAtlasStackEntry, stack); g_slice_free (CoglRectangleMapStackEntry, stack);
return next; return next;
} }
static CoglAtlasNode * static CoglRectangleMapNode *
_cogl_atlas_node_split_horizontally (CoglAtlasNode *node, _cogl_rectangle_map_node_split_horizontally (CoglRectangleMapNode *node,
unsigned int left_width) unsigned int left_width)
{ {
/* Splits the node horizontally (according to emacs' definition, not /* Splits the node horizontally (according to emacs' definition, not
vim) by converting it to a branch and adding two new leaf vim) by converting it to a branch and adding two new leaf
@ -175,13 +176,13 @@ _cogl_atlas_node_split_horizontally (CoglAtlasNode *node,
will be returned. If the node is already just the right size it will be returned. If the node is already just the right size it
won't do anything */ won't do anything */
CoglAtlasNode *left_node, *right_node; CoglRectangleMapNode *left_node, *right_node;
if (node->rectangle.width == left_width) if (node->rectangle.width == left_width)
return node; return node;
left_node = _cogl_atlas_node_new (); left_node = _cogl_rectangle_map_node_new ();
left_node->type = COGL_ATLAS_EMPTY_LEAF; left_node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF;
left_node->parent = node; left_node->parent = node;
left_node->rectangle.x = node->rectangle.x; left_node->rectangle.x = node->rectangle.x;
left_node->rectangle.y = node->rectangle.y; left_node->rectangle.y = node->rectangle.y;
@ -189,8 +190,8 @@ _cogl_atlas_node_split_horizontally (CoglAtlasNode *node,
left_node->rectangle.height = node->rectangle.height; left_node->rectangle.height = node->rectangle.height;
node->d.branch.left = left_node; node->d.branch.left = left_node;
right_node = _cogl_atlas_node_new (); right_node = _cogl_rectangle_map_node_new ();
right_node->type = COGL_ATLAS_EMPTY_LEAF; right_node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF;
right_node->parent = node; right_node->parent = node;
right_node->rectangle.x = node->rectangle.x + left_width; right_node->rectangle.x = node->rectangle.x + left_width;
right_node->rectangle.y = node->rectangle.y; right_node->rectangle.y = node->rectangle.y;
@ -198,14 +199,14 @@ _cogl_atlas_node_split_horizontally (CoglAtlasNode *node,
right_node->rectangle.height = node->rectangle.height; right_node->rectangle.height = node->rectangle.height;
node->d.branch.right = right_node; node->d.branch.right = right_node;
node->type = COGL_ATLAS_BRANCH; node->type = COGL_RECTANGLE_MAP_BRANCH;
return left_node; return left_node;
} }
static CoglAtlasNode * static CoglRectangleMapNode *
_cogl_atlas_node_split_vertically (CoglAtlasNode *node, _cogl_rectangle_map_node_split_vertically (CoglRectangleMapNode *node,
unsigned int top_height) unsigned int top_height)
{ {
/* Splits the node vertically (according to emacs' definition, not /* Splits the node vertically (according to emacs' definition, not
vim) by converting it to a branch and adding two new leaf vim) by converting it to a branch and adding two new leaf
@ -213,13 +214,13 @@ _cogl_atlas_node_split_vertically (CoglAtlasNode *node,
will be returned. If the node is already just the right size it will be returned. If the node is already just the right size it
won't do anything */ won't do anything */
CoglAtlasNode *top_node, *bottom_node; CoglRectangleMapNode *top_node, *bottom_node;
if (node->rectangle.height == top_height) if (node->rectangle.height == top_height)
return node; return node;
top_node = _cogl_atlas_node_new (); top_node = _cogl_rectangle_map_node_new ();
top_node->type = COGL_ATLAS_EMPTY_LEAF; top_node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF;
top_node->parent = node; top_node->parent = node;
top_node->rectangle.x = node->rectangle.x; top_node->rectangle.x = node->rectangle.x;
top_node->rectangle.y = node->rectangle.y; top_node->rectangle.y = node->rectangle.y;
@ -227,8 +228,8 @@ _cogl_atlas_node_split_vertically (CoglAtlasNode *node,
top_node->rectangle.height = top_height; top_node->rectangle.height = top_height;
node->d.branch.left = top_node; node->d.branch.left = top_node;
bottom_node = _cogl_atlas_node_new (); bottom_node = _cogl_rectangle_map_node_new ();
bottom_node->type = COGL_ATLAS_EMPTY_LEAF; bottom_node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF;
bottom_node->parent = node; bottom_node->parent = node;
bottom_node->rectangle.x = node->rectangle.x; bottom_node->rectangle.x = node->rectangle.x;
bottom_node->rectangle.y = node->rectangle.y + top_height; bottom_node->rectangle.y = node->rectangle.y + top_height;
@ -236,36 +237,36 @@ _cogl_atlas_node_split_vertically (CoglAtlasNode *node,
bottom_node->rectangle.height = node->rectangle.height - top_height; bottom_node->rectangle.height = node->rectangle.height - top_height;
node->d.branch.right = bottom_node; node->d.branch.right = bottom_node;
node->type = COGL_ATLAS_BRANCH; node->type = COGL_RECTANGLE_MAP_BRANCH;
return top_node; return top_node;
} }
gboolean gboolean
_cogl_atlas_add_rectangle (CoglAtlas *atlas, _cogl_rectangle_map_add (CoglRectangleMap *map,
unsigned int width, unsigned int width,
unsigned int height, unsigned int height,
gpointer data, void *data,
CoglAtlasRectangle *rectangle) CoglRectangleMapEntry *rectangle)
{ {
/* Stack of nodes to search in */ /* Stack of nodes to search in */
CoglAtlasStackEntry *node_stack; CoglRectangleMapStackEntry *node_stack;
CoglAtlasNode *found_node = NULL; CoglRectangleMapNode *found_node = NULL;
/* Zero-sized rectangles break the algorithm for removing rectangles /* Zero-sized rectangles break the algorithm for removing rectangles
so we'll disallow them */ so we'll disallow them */
g_return_val_if_fail (width > 0 && height > 0, FALSE); g_return_val_if_fail (width > 0 && height > 0, FALSE);
/* Start with the root node */ /* Start with the root node */
node_stack = _cogl_atlas_stack_push (NULL, atlas->root, FALSE); node_stack = _cogl_rectangle_map_stack_push (NULL, map->root, FALSE);
/* Depth-first search for an empty node that is big enough */ /* Depth-first search for an empty node that is big enough */
while (node_stack) while (node_stack)
{ {
/* Pop an entry off the stack */ /* Pop an entry off the stack */
CoglAtlasNode *node = node_stack->node; CoglRectangleMapNode *node = node_stack->node;
int next_index = node_stack->next_index; int next_index = node_stack->next_index;
node_stack = _cogl_atlas_stack_pop (node_stack); node_stack = _cogl_rectangle_map_stack_pop (node_stack);
/* Regardless of the type of the node, there's no point /* Regardless of the type of the node, there's no point
descending any further if the new rectangle won't fit within descending any further if the new rectangle won't fit within
@ -273,30 +274,33 @@ _cogl_atlas_add_rectangle (CoglAtlas *atlas,
if (node->rectangle.width >= width && if (node->rectangle.width >= width &&
node->rectangle.height >= height) node->rectangle.height >= height)
{ {
if (node->type == COGL_ATLAS_EMPTY_LEAF) if (node->type == COGL_RECTANGLE_MAP_EMPTY_LEAF)
{ {
/* We've found a node we can use */ /* We've found a node we can use */
found_node = node; found_node = node;
break; break;
} }
else if (node->type == COGL_ATLAS_BRANCH) else if (node->type == COGL_RECTANGLE_MAP_BRANCH)
{ {
if (next_index) if (next_index)
/* Try the right branch */ /* Try the right branch */
node_stack = _cogl_atlas_stack_push (node_stack, node_stack =
node->d.branch.right, _cogl_rectangle_map_stack_push (node_stack,
0); node->d.branch.right,
0);
else else
{ {
/* Make sure we remember to try the right branch once /* Make sure we remember to try the right branch once
we've finished descending the left branch */ we've finished descending the left branch */
node_stack = _cogl_atlas_stack_push (node_stack, node_stack =
node, _cogl_rectangle_map_stack_push (node_stack,
1); node,
1);
/* Try the left branch */ /* Try the left branch */
node_stack = _cogl_atlas_stack_push (node_stack, node_stack =
node->d.branch.left, _cogl_rectangle_map_stack_push (node_stack,
0); node->d.branch.left,
0);
} }
} }
} }
@ -304,7 +308,7 @@ _cogl_atlas_add_rectangle (CoglAtlas *atlas,
/* Free the stack */ /* Free the stack */
while (node_stack) while (node_stack)
node_stack = _cogl_atlas_stack_pop (node_stack); node_stack = _cogl_rectangle_map_stack_pop (node_stack);
if (found_node) if (found_node)
{ {
@ -313,29 +317,33 @@ _cogl_atlas_add_rectangle (CoglAtlas *atlas,
if (found_node->rectangle.width - width > if (found_node->rectangle.width - width >
found_node->rectangle.height - height) found_node->rectangle.height - height)
{ {
found_node = _cogl_atlas_node_split_horizontally (found_node, width); found_node =
found_node = _cogl_atlas_node_split_vertically (found_node, height); _cogl_rectangle_map_node_split_horizontally (found_node, width);
found_node =
_cogl_rectangle_map_node_split_vertically (found_node, height);
} }
else else
{ {
found_node = _cogl_atlas_node_split_vertically (found_node, height); found_node =
found_node = _cogl_atlas_node_split_horizontally (found_node, width); _cogl_rectangle_map_node_split_vertically (found_node, height);
found_node =
_cogl_rectangle_map_node_split_horizontally (found_node, width);
} }
found_node->type = COGL_ATLAS_FILLED_LEAF; found_node->type = COGL_RECTANGLE_MAP_FILLED_LEAF;
found_node->d.data = data; found_node->d.data = data;
if (rectangle) if (rectangle)
*rectangle = found_node->rectangle; *rectangle = found_node->rectangle;
/* Record how much empty space is remaining after this rectangle /* Record how much empty space is remaining after this rectangle
is added */ is added */
g_assert (width * height <= atlas->space_remaining); g_assert (width * height <= map->space_remaining);
atlas->space_remaining -= width * height; map->space_remaining -= width * height;
atlas->n_rectangles++; map->n_rectangles++;
#ifdef COGL_ENABLE_DEBUG #ifdef COGL_ENABLE_DEBUG
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE)) if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE))
_cogl_atlas_dump_image (atlas); _cogl_rectangle_map_dump_image (map);
#endif #endif
return TRUE; return TRUE;
@ -345,15 +353,15 @@ _cogl_atlas_add_rectangle (CoglAtlas *atlas,
} }
void void
_cogl_atlas_remove_rectangle (CoglAtlas *atlas, _cogl_rectangle_map_remove (CoglRectangleMap *map,
const CoglAtlasRectangle *rectangle) const CoglRectangleMapEntry *rectangle)
{ {
CoglAtlasNode *node = atlas->root; CoglRectangleMapNode *node = map->root;
/* We can do a binary-chop down the search tree to find the rectangle */ /* We can do a binary-chop down the search tree to find the rectangle */
while (node->type == COGL_ATLAS_BRANCH) while (node->type == COGL_RECTANGLE_MAP_BRANCH)
{ {
CoglAtlasNode *left_node = node->d.branch.left; CoglRectangleMapNode *left_node = node->d.branch.left;
/* If and only if the rectangle is in the left node then the x,y /* 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 position of the rectangle will be within the node's
@ -368,103 +376,103 @@ _cogl_atlas_remove_rectangle (CoglAtlas *atlas,
} }
/* Make sure we found the right node */ /* Make sure we found the right node */
if (node->type != COGL_ATLAS_FILLED_LEAF || if (node->type != COGL_RECTANGLE_MAP_FILLED_LEAF ||
node->rectangle.x != rectangle->x || node->rectangle.x != rectangle->x ||
node->rectangle.y != rectangle->y || node->rectangle.y != rectangle->y ||
node->rectangle.width != rectangle->width || node->rectangle.width != rectangle->width ||
node->rectangle.height != rectangle->height) node->rectangle.height != rectangle->height)
/* This should only happen if someone tried to remove a rectangle /* This should only happen if someone tried to remove a rectangle
that was not in the atlas so something has gone wrong */ that was not in the map so something has gone wrong */
g_return_if_reached (); g_return_if_reached ();
else else
{ {
/* Convert the node back to an empty node */ /* Convert the node back to an empty node */
if (atlas->value_destroy_func) if (map->value_destroy_func)
atlas->value_destroy_func (node->d.data); map->value_destroy_func (node->d.data);
node->type = COGL_ATLAS_EMPTY_LEAF; node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF;
/* Walk back up the tree combining branch nodes that have two /* Walk back up the tree combining branch nodes that have two
empty leaves back into a single empty leaf */ empty leaves back into a single empty leaf */
for (node = node->parent; node; node = node->parent) for (node = node->parent; node; node = node->parent)
{ {
/* This node is a parent so it should always be a branch */ /* This node is a parent so it should always be a branch */
g_assert (node->type == COGL_ATLAS_BRANCH); g_assert (node->type == COGL_RECTANGLE_MAP_BRANCH);
if (node->d.branch.left->type == COGL_ATLAS_EMPTY_LEAF && if (node->d.branch.left->type == COGL_RECTANGLE_MAP_EMPTY_LEAF &&
node->d.branch.right->type == COGL_ATLAS_EMPTY_LEAF) node->d.branch.right->type == COGL_RECTANGLE_MAP_EMPTY_LEAF)
{ {
_cogl_atlas_node_free (node->d.branch.left); _cogl_rectangle_map_node_free (node->d.branch.left);
_cogl_atlas_node_free (node->d.branch.right); _cogl_rectangle_map_node_free (node->d.branch.right);
node->type = COGL_ATLAS_EMPTY_LEAF; node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF;
} }
else else
break; break;
} }
/* There is now more free space and one less rectangle */ /* There is now more free space and one less rectangle */
atlas->space_remaining += rectangle->width * rectangle->height; map->space_remaining += rectangle->width * rectangle->height;
g_assert (atlas->n_rectangles > 0); g_assert (map->n_rectangles > 0);
atlas->n_rectangles--; map->n_rectangles--;
} }
#ifdef COGL_ENABLE_DEBUG #ifdef COGL_ENABLE_DEBUG
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE)) if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DUMP_ATLAS_IMAGE))
_cogl_atlas_dump_image (atlas); _cogl_rectangle_map_dump_image (map);
#endif #endif
} }
unsigned int unsigned int
_cogl_atlas_get_width (CoglAtlas *atlas) _cogl_rectangle_map_get_width (CoglRectangleMap *map)
{ {
return atlas->root->rectangle.width; return map->root->rectangle.width;
} }
unsigned int unsigned int
_cogl_atlas_get_height (CoglAtlas *atlas) _cogl_rectangle_map_get_height (CoglRectangleMap *map)
{ {
return atlas->root->rectangle.height; return map->root->rectangle.height;
} }
unsigned int unsigned int
_cogl_atlas_get_remaining_space (CoglAtlas *atlas) _cogl_rectangle_map_get_remaining_space (CoglRectangleMap *map)
{ {
return atlas->space_remaining; return map->space_remaining;
} }
unsigned int unsigned int
_cogl_atlas_get_n_rectangles (CoglAtlas *atlas) _cogl_rectangle_map_get_n_rectangles (CoglRectangleMap *map)
{ {
return atlas->n_rectangles; return map->n_rectangles;
} }
static void static void
_cogl_atlas_internal_foreach (CoglAtlas *atlas, _cogl_rectangle_map_internal_foreach (CoglRectangleMap *map,
CoglAtlasInternalForeachCb callback, CoglRectangleMapInternalForeachCb func,
gpointer data) void *data)
{ {
/* Stack of nodes to search in */ /* Stack of nodes to search in */
CoglAtlasStackEntry *node_stack; CoglRectangleMapStackEntry *node_stack;
/* Start with the root node */ /* Start with the root node */
node_stack = _cogl_atlas_stack_push (NULL, atlas->root, 0); node_stack = _cogl_rectangle_map_stack_push (NULL, map->root, 0);
/* Iterate all nodes depth-first */ /* Iterate all nodes depth-first */
while (node_stack) while (node_stack)
{ {
CoglAtlasNode *node = node_stack->node; CoglRectangleMapNode *node = node_stack->node;
switch (node->type) switch (node->type)
{ {
case COGL_ATLAS_BRANCH: case COGL_RECTANGLE_MAP_BRANCH:
if (node_stack->next_index == 0) if (node_stack->next_index == 0)
{ {
/* Next time we come back to this node, go to the right */ /* Next time we come back to this node, go to the right */
node_stack->next_index = 1; node_stack->next_index = 1;
/* Explore the left branch next */ /* Explore the left branch next */
node_stack = _cogl_atlas_stack_push (node_stack, node_stack = _cogl_rectangle_map_stack_push (node_stack,
node->d.branch.left, node->d.branch.left,
0); 0);
} }
else if (node_stack->next_index == 1) else if (node_stack->next_index == 1)
{ {
@ -472,22 +480,22 @@ _cogl_atlas_internal_foreach (CoglAtlas *atlas,
node_stack->next_index = 2; node_stack->next_index = 2;
/* Explore the right branch next */ /* Explore the right branch next */
node_stack = _cogl_atlas_stack_push (node_stack, node_stack = _cogl_rectangle_map_stack_push (node_stack,
node->d.branch.right, node->d.branch.right,
0); 0);
} }
else else
{ {
/* We're finished with this node so we can call the callback */ /* We're finished with this node so we can call the callback */
callback (node, data); func (node, data);
node_stack = _cogl_atlas_stack_pop (node_stack); node_stack = _cogl_rectangle_map_stack_pop (node_stack);
} }
break; break;
default: default:
/* Some sort of leaf node, just call the callback */ /* Some sort of leaf node, just call the callback */
callback (node, data); func (node, data);
node_stack = _cogl_atlas_stack_pop (node_stack); node_stack = _cogl_rectangle_map_stack_pop (node_stack);
break; break;
} }
} }
@ -496,65 +504,69 @@ _cogl_atlas_internal_foreach (CoglAtlas *atlas,
g_assert (node_stack == NULL); g_assert (node_stack == NULL);
} }
typedef struct _CoglAtlasForeachClosure typedef struct _CoglRectangleMapForeachClosure
{ {
CoglAtlasCallback callback; CoglRectangleMapCallback callback;
gpointer data; void *data;
} CoglAtlasForeachClosure; } CoglRectangleMapForeachClosure;
static void static void
_cogl_atlas_foreach_cb (CoglAtlasNode *node, gpointer data) _cogl_rectangle_map_foreach_cb (CoglRectangleMapNode *node, void *data)
{ {
CoglAtlasForeachClosure *closure = data; CoglRectangleMapForeachClosure *closure = data;
if (node->type == COGL_ATLAS_FILLED_LEAF) if (node->type == COGL_RECTANGLE_MAP_FILLED_LEAF)
closure->callback (&node->rectangle, node->d.data, closure->data); closure->callback (&node->rectangle, node->d.data, closure->data);
} }
void void
_cogl_atlas_foreach (CoglAtlas *atlas, _cogl_rectangle_map_foreach (CoglRectangleMap *map,
CoglAtlasCallback callback, CoglRectangleMapCallback callback,
gpointer data) void *data)
{ {
CoglAtlasForeachClosure closure; CoglRectangleMapForeachClosure closure;
closure.callback = callback; closure.callback = callback;
closure.data = data; closure.data = data;
_cogl_atlas_internal_foreach (atlas, _cogl_atlas_foreach_cb, &closure); _cogl_rectangle_map_internal_foreach (map,
_cogl_rectangle_map_foreach_cb,
&closure);
} }
static void static void
_cogl_atlas_free_cb (CoglAtlasNode *node, gpointer data) _cogl_rectangle_map_free_cb (CoglRectangleMapNode *node, void *data)
{ {
CoglAtlas *atlas = data; CoglRectangleMap *map = data;
if (node->type == COGL_ATLAS_FILLED_LEAF && atlas->value_destroy_func) if (node->type == COGL_RECTANGLE_MAP_FILLED_LEAF && map->value_destroy_func)
atlas->value_destroy_func (node->d.data); map->value_destroy_func (node->d.data);
_cogl_atlas_node_free (node); _cogl_rectangle_map_node_free (node);
} }
void void
_cogl_atlas_free (CoglAtlas *atlas) _cogl_rectangle_map_free (CoglRectangleMap *map)
{ {
_cogl_atlas_internal_foreach (atlas, _cogl_atlas_free_cb, atlas); _cogl_rectangle_map_internal_foreach (map,
g_free (atlas); _cogl_rectangle_map_free_cb,
map);
g_free (map);
} }
#ifdef COGL_ENABLE_DEBUG #ifdef COGL_ENABLE_DEBUG
static void static void
_cogl_atlas_dump_image_cb (CoglAtlasNode *node, gpointer data) _cogl_rectangle_map_dump_image_cb (CoglRectangleMapNode *node, void *data)
{ {
cairo_t *cr = data; cairo_t *cr = data;
if (node->type == COGL_ATLAS_FILLED_LEAF || if (node->type == COGL_RECTANGLE_MAP_FILLED_LEAF ||
node->type == COGL_ATLAS_EMPTY_LEAF) node->type == COGL_RECTANGLE_MAP_EMPTY_LEAF)
{ {
/* Fill the rectangle using a different colour depending on /* Fill the rectangle using a different colour depending on
whether the rectangle is used */ whether the rectangle is used */
if (node->type == COGL_ATLAS_FILLED_LEAF) if (node->type == COGL_RECTANGLE_MAP_FILLED_LEAF)
cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); cairo_set_source_rgb (cr, 0.0, 0.0, 1.0);
else else
cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
@ -574,23 +586,25 @@ _cogl_atlas_dump_image_cb (CoglAtlasNode *node, gpointer data)
} }
static void static void
_cogl_atlas_dump_image (CoglAtlas *atlas) _cogl_rectangle_map_dump_image (CoglRectangleMap *map)
{ {
/* This dumps a png to help visualize the atlas. Each leaf rectangle /* This dumps a png to help visualize the map. Each leaf rectangle
is drawn with a white outline. Unused leaves are filled in black is drawn with a white outline. Unused leaves are filled in black
and used leaves are blue */ and used leaves are blue */
cairo_surface_t *surface = cairo_surface_t *surface =
cairo_image_surface_create (CAIRO_FORMAT_RGB24, cairo_image_surface_create (CAIRO_FORMAT_RGB24,
_cogl_atlas_get_width (atlas), _cogl_rectangle_map_get_width (map),
_cogl_atlas_get_height (atlas)); _cogl_rectangle_map_get_height (map));
cairo_t *cr = cairo_create (surface); cairo_t *cr = cairo_create (surface);
_cogl_atlas_internal_foreach (atlas, _cogl_atlas_dump_image_cb, cr); _cogl_rectangle_map_internal_foreach (map,
_cogl_rectangle_map_dump_image_cb,
cr);
cairo_destroy (cr); cairo_destroy (cr);
cairo_surface_write_to_png (surface, "cogl-atlas-dump.png"); cairo_surface_write_to_png (surface, "cogl-rectangle-map-dump.png");
cairo_surface_destroy (surface); cairo_surface_destroy (surface);
} }

78
cogl/cogl-rectangle-map.h Normal file
View File

@ -0,0 +1,78 @@
/*
* 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_RECTANGLE_MAP_H
#define __COGL_RECTANGLE_MAP_H
#include <glib.h>
typedef struct _CoglRectangleMap CoglRectangleMap;
typedef struct _CoglRectangleMapEntry CoglRectangleMapEntry;
typedef void (* CoglRectangleMapCallback) (const CoglRectangleMapEntry *entry,
void *rectangle_data,
void *user_data);
struct _CoglRectangleMapEntry
{
unsigned int x, y;
unsigned int width, height;
};
CoglRectangleMap *
_cogl_rectangle_map_new (unsigned int width,
unsigned int height,
GDestroyNotify value_destroy_func);
gboolean
_cogl_rectangle_map_add (CoglRectangleMap *map,
unsigned int width,
unsigned int height,
void *data,
CoglRectangleMapEntry *rectangle);
void
_cogl_rectangle_map_remove (CoglRectangleMap *map,
const CoglRectangleMapEntry *rectangle);
unsigned int
_cogl_rectangle_map_get_width (CoglRectangleMap *map);
unsigned int
_cogl_rectangle_map_get_height (CoglRectangleMap *map);
unsigned int
_cogl_rectangle_map_get_remaining_space (CoglRectangleMap *map);
unsigned int
_cogl_rectangle_map_get_n_rectangles (CoglRectangleMap *map);
void
_cogl_rectangle_map_foreach (CoglRectangleMap *map,
CoglRectangleMapCallback callback,
void *data);
void
_cogl_rectangle_map_free (CoglRectangleMap *map);
#endif /* __COGL_RECTANGLE_MAP_H */