static void _cogl_atlas_texture_post_reorganize_cb (void *user_data) { CoglAtlas *atlas = user_data; if (atlas->map) { CoglAtlasTextureGetRectanglesData data; unsigned int i; data.textures = g_new (CoglAtlasTexture *, _cogl_rectangle_map_get_n_rectangles (atlas->map)); data.n_textures = 0; /* We need to remove all of the references that we took during the preorganize callback. We have to get a separate array of the textures because CoglRectangleMap doesn't support removing rectangles during iteration */ _cogl_rectangle_map_foreach (atlas->map, _cogl_atlas_texture_get_rectangles_cb, &data); for (i = 0; i < data.n_textures; i++) { /* Ignore textures that don't have an atlas yet. This will happen when a new texture is added because we allocate the structure for the texture so that it can get stored in the atlas but it isn't a valid object yet */ if (data.textures[i]->atlas) cogl_object_unref (data.textures[i]); } g_free (data.textures); }
static void _cogl_atlas_texture_pre_reorganize_cb (void *data) { CoglAtlas *atlas = data; /* We don't know if any journal entries currently depend on OpenGL * texture coordinates that would be invalidated by reorganizing * this atlas so we flush all journals before migrating. * * We are assuming that texture atlas migration never happens * during a flush so we don't have to consider recursion here. */ cogl_flush (); if (atlas->map) _cogl_rectangle_map_foreach (atlas->map, _cogl_atlas_texture_pre_reorganize_foreach_cb, NULL); }
CoglBool _cogl_atlas_reserve_space (CoglAtlas *atlas, unsigned int width, unsigned int height, void *user_data) { CoglAtlasGetRectanglesData data; CoglRectangleMap *new_map; CoglTexture2D *new_tex; unsigned int map_width = 0, map_height = 0; CoglBool 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)) { COGL_NOTE (ATLAS, "%p: Atlas is %ix%i, has %i textures and is %i%% waste", atlas, _cogl_rectangle_map_get_width (atlas->map), _cogl_rectangle_map_get_height (atlas->map), _cogl_rectangle_map_get_n_rectangles (atlas->map), /* waste as a percentage */ _cogl_rectangle_map_get_remaining_space (atlas->map) * 100 / (_cogl_rectangle_map_get_width (atlas->map) * _cogl_rectangle_map_get_height (atlas->map))); atlas->update_position_cb (user_data, atlas->texture, &new_position); return TRUE; } /* If we make it here then we need to reorganize the atlas. First we'll notify any users of the atlas that this is going to happen so that for example in CoglAtlasTexture it can notify that the storage has changed and cause a flush */ _cogl_atlas_notify_pre_reorganize (atlas); /* 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 for the new rectangle in the existing atlas with at least 6% waste we'll start with the same size, otherwise we'll immediately double it */ if ((map_width * map_height - _cogl_rectangle_map_get_remaining_space (atlas->map) + width * height) * 53 / 50 > map_width * map_height) _cogl_atlas_get_next_size (&map_width, &map_height); } else _cogl_atlas_get_initial_size (atlas->texture_format, &map_width, &map_height); new_map = _cogl_atlas_create_map (atlas->texture_format, 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, "%p: Could not fit texture in the atlas", atlas); ret = FALSE; } /* We need to migrate the existing textures into a new texture */ else if ((new_tex = _cogl_atlas_create_texture (atlas, _cogl_rectangle_map_get_width (new_map), _cogl_rectangle_map_get_height (new_map))) == NULL) { COGL_NOTE (ATLAS, "%p: Could not create a CoglTexture2D", atlas); _cogl_rectangle_map_free (new_map); ret = FALSE; } else { int waste; COGL_NOTE (ATLAS, "%p: Atlas %s with size %ix%i", atlas, 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, COGL_TEXTURE (new_tex), user_data); _cogl_rectangle_map_free (atlas->map); cogl_object_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, COGL_TEXTURE (new_tex), &data.textures[0].new_position); atlas->map = new_map; atlas->texture = COGL_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, "%p: Atlas is %ix%i, has %i textures and is %i%% waste", atlas, _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); _cogl_atlas_notify_post_reorganize (atlas); return ret; }