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-rectangle-private.h \
$(srcdir)/cogl-texture-rectangle.c \
$(srcdir)/cogl-atlas.h \
$(srcdir)/cogl-atlas.c \
$(srcdir)/cogl-rectangle-map.h \
$(srcdir)/cogl-rectangle-map.c \
$(srcdir)/cogl-atlas-texture-private.h \
$(srcdir)/cogl-atlas-texture.c \
$(srcdir)/cogl-spans.h \

View File

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

View File

@ -39,7 +39,7 @@
#include "cogl-context.h"
#include "cogl-handle.h"
#include "cogl-texture-driver.h"
#include "cogl-atlas.h"
#include "cogl-rectangle-map.h"
#include "cogl-journal-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_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",
atlas_tex->rectangle.width,
atlas_tex->rectangle.height);
COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste",
_cogl_atlas_get_width (ctx->atlas),
_cogl_atlas_get_height (ctx->atlas),
_cogl_atlas_get_n_rectangles (ctx->atlas),
_cogl_atlas_get_remaining_space (ctx->atlas) * 100 /
(_cogl_atlas_get_width (ctx->atlas) *
_cogl_atlas_get_height (ctx->atlas)));
_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;
}
@ -599,7 +599,7 @@ _cogl_atlas_texture_get_height (CoglTexture *tex)
static CoglHandle
_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
1-pixel border */
@ -615,7 +615,7 @@ typedef struct _CoglAtlasTextureRepositionData
/* The current texture which already has a position */
CoglAtlasTexture *texture;
/* The new position of the texture */
CoglAtlasRectangle new_position;
CoglRectangleMapEntry new_position;
} CoglAtlasTextureRepositionData;
static void
@ -679,32 +679,32 @@ typedef struct _CoglAtlasTextureGetRectanglesData
} CoglAtlasTextureGetRectanglesData;
static void
_cogl_atlas_texture_get_rectangles_cb (const CoglAtlasRectangle *rectangle,
gpointer rectangle_data,
gpointer user_data)
_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 = rectangle_data;
data->textures[data->n_textures++].texture = rect_data;
}
static void
_cogl_atlas_texture_get_next_size (unsigned int *atlas_width,
unsigned int *atlas_height)
_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 (*atlas_width < *atlas_height)
*atlas_width <<= 1;
if (*map_width < *map_height)
*map_width <<= 1;
else
*atlas_height <<= 1;
*map_height <<= 1;
}
static CoglAtlas *
_cogl_atlas_texture_create_atlas (unsigned int atlas_width,
unsigned int atlas_height,
unsigned int n_textures,
CoglAtlasTextureRepositionData *textures)
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;
@ -717,18 +717,20 @@ _cogl_atlas_texture_create_atlas (unsigned int atlas_width,
/* Keep trying increasingly larger atlases until we can fit all of
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;
/* Add all of the textures and keep track of the new position */
for (i = 0; i < n_textures; i++)
if (!_cogl_atlas_add_rectangle (new_atlas,
textures[i].texture->rectangle.width,
textures[i].texture->rectangle.height,
textures[i].texture,
&textures[i].new_position))
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
@ -736,8 +738,8 @@ _cogl_atlas_texture_create_atlas (unsigned int atlas_width,
if (i >= n_textures)
return new_atlas;
_cogl_atlas_free (new_atlas);
_cogl_atlas_texture_get_next_size (&atlas_width, &atlas_height);
_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
@ -766,25 +768,26 @@ _cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex,
unsigned int height)
{
CoglAtlasTextureGetRectanglesData data;
CoglAtlas *new_atlas;
CoglRectangleMap *new_map;
CoglHandle new_tex;
unsigned int atlas_width, atlas_height;
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->atlas && _cogl_atlas_add_rectangle (ctx->atlas, width, height,
new_sub_tex,
&new_sub_tex->rectangle))
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_atlas_get_width (ctx->atlas),
_cogl_atlas_get_height (ctx->atlas),
_cogl_atlas_get_n_rectangles (ctx->atlas),
_cogl_atlas_get_remaining_space (ctx->atlas) * 100 /
(_cogl_atlas_get_width (ctx->atlas) *
_cogl_atlas_get_height (ctx->atlas)));
_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;
}
@ -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
textures currently in the atlas. */
data.n_textures = 0;
if (ctx->atlas == NULL)
if (ctx->rectangle_map == NULL)
data.textures = g_malloc (sizeof (CoglAtlasTextureRepositionData));
else
{
data.textures =
g_malloc (sizeof (CoglAtlasTextureRepositionData) *
(_cogl_atlas_get_n_rectangles (ctx->atlas) + 1));
_cogl_atlas_foreach (ctx->atlas, _cogl_atlas_texture_get_rectangles_cb,
&data);
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
@ -815,59 +820,61 @@ _cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex,
_cogl_atlas_texture_compare_size_cb);
/* 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);
atlas_height = _cogl_atlas_get_height (ctx->atlas);
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_atlas_get_remaining_space (ctx->atlas) < width * height)
_cogl_atlas_texture_get_next_size (&atlas_width, &atlas_height);
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 */
atlas_width = 256;
atlas_height = 256;
map_width = 256;
map_height = 256;
}
new_atlas = _cogl_atlas_texture_create_atlas (atlas_width, atlas_height,
data.n_textures, data.textures);
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 an atlas with the texture then give up */
if (new_atlas == NULL)
/* 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_atlas_get_width (new_atlas),
_cogl_atlas_get_height (new_atlas),
_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_atlas_free (new_atlas);
_cogl_rectangle_map_free (new_map);
ret = FALSE;
}
else
{
COGL_NOTE (ATLAS,
"Atlas %s with size %ix%i",
ctx->atlas == NULL ||
_cogl_atlas_get_width (ctx->atlas) !=
_cogl_atlas_get_width (new_atlas) ||
_cogl_atlas_get_height (ctx->atlas) !=
_cogl_atlas_get_height (new_atlas) ?
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_atlas_get_width (new_atlas),
_cogl_atlas_get_height (new_atlas));
_cogl_rectangle_map_get_width (new_map),
_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
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,
new_tex,
new_sub_tex);
_cogl_atlas_free (ctx->atlas);
_cogl_rectangle_map_free (ctx->rectangle_map);
cogl_handle_unref (ctx->atlas_texture);
}
else
@ -884,16 +891,16 @@ _cogl_atlas_texture_reserve_space (CoglAtlasTexture *new_sub_tex,
update the rectangle from its 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;
COGL_NOTE (ATLAS, "Atlas is %ix%i, has %i textures and is %i%% waste",
_cogl_atlas_get_width (ctx->atlas),
_cogl_atlas_get_height (ctx->atlas),
_cogl_atlas_get_n_rectangles (ctx->atlas),
_cogl_atlas_get_remaining_space (ctx->atlas) * 100 /
(_cogl_atlas_get_width (ctx->atlas) *
_cogl_atlas_get_height (ctx->atlas)));
_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;
}
@ -1003,7 +1010,7 @@ _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp,
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);
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_flush_face_winding ();
_context->atlas = NULL;
_context->rectangle_map = NULL;
_context->atlas_texture = COGL_INVALID_HANDLE;
/* 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)
cogl_handle_unref (_context->default_layer_0);
if (_context->atlas)
_cogl_atlas_free (_context->atlas);
if (_context->rectangle_map)
_cogl_rectangle_map_free (_context->rectangle_map);
if (_context->atlas_texture)
cogl_handle_unref (_context->atlas_texture);

View File

@ -31,7 +31,7 @@
#include "cogl-clip-stack.h"
#include "cogl-matrix-stack.h"
#include "cogl-material-private.h"
#include "cogl-atlas.h"
#include "cogl-rectangle-map.h"
#include "cogl-buffer-private.h"
#include "cogl-bitmask.h"
@ -143,7 +143,7 @@ typedef struct
CoglMaterial *texture_download_material;
CoglAtlas *atlas;
CoglRectangleMap *rectangle_map;
CoglHandle atlas_texture;
/* This debugging variable is used to pick a colour for visually

View File

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