[cogl/wip/public-atlas-apis: 13/13] stash: Adds initial CoglAtlasSet api



commit dba101b68b80212eca274ab07414b8e61bca7544
Author: Robert Bragg <robert linux intel com>
Date:   Wed May 8 01:42:24 2013 +0100

    stash: Adds initial CoglAtlasSet api
    
    The aim is to make CoglAtlasSet and CoglAtlas public apis.

 cogl-pango/cogl-pango-glyph-cache.c |  257 ++++++++++++++-----------
 cogl/Makefile.am                    |    2 +-
 cogl/Makefile.sources               |   10 +-
 cogl/cogl-atlas-private.h           |   88 +++++++++
 cogl/cogl-atlas-set-private.h       |   58 ++++++
 cogl/cogl-atlas-set.c               |  267 +++++++++++++++++++++++++
 cogl/cogl-atlas-set.h               |  120 +++++++++++
 cogl/cogl-atlas-texture-private.h   |   20 +--
 cogl/cogl-atlas-texture.c           |  287 +++++++++++----------------
 cogl/cogl-atlas.c                   |  369 +++++++++++++++++++++-------------
 cogl/cogl-atlas.h                   |  117 ++++++-----
 cogl/cogl-context-private.h         |    8 +-
 cogl/cogl-context.c                 |   27 ++-
 cogl/cogl-texture-private.h         |   15 ++-
 cogl/cogl-texture.c                 |   22 ++-
 cogl/cogl.h                         |    2 +
 cogl/winsys/cogl-winsys-egl.c       |    2 +-
 examples/cogl-crate.c               |   11 +-
 18 files changed, 1165 insertions(+), 517 deletions(-)
---
diff --git a/cogl-pango/cogl-pango-glyph-cache.c b/cogl-pango/cogl-pango-glyph-cache.c
index 6659ed2..78421ea 100644
--- a/cogl-pango/cogl-pango-glyph-cache.c
+++ b/cogl-pango/cogl-pango-glyph-cache.c
@@ -26,46 +26,50 @@
  * SOFTWARE.
  */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
+#include <config.h>
 
 #include <glib.h>
 
 #include "cogl-pango-glyph-cache.h"
 #include "cogl-pango-private.h"
-#include "cogl/cogl-atlas.h"
+#include "cogl/cogl-atlas-set.h"
 #include "cogl/cogl-atlas-texture-private.h"
+#include "cogl/cogl-context-private.h"
 
 typedef struct _CoglPangoGlyphCacheKey     CoglPangoGlyphCacheKey;
 
+typedef struct _AtlasClosureState
+{
+  CoglList list_node;
+  CoglAtlas *atlas;
+  CoglAtlasReorganizeClosure *reorganize_closure;
+  CoglAtlasAllocateClosure *allocate_closure;
+} AtlasClosureState;
+
 struct _CoglPangoGlyphCache
 {
   CoglContext *ctx;
 
   /* Hash table to quickly check whether a particular glyph in a
      particular font is already cached */
-  GHashTable       *hash_table;
+  GHashTable *hash_table;
 
-  /* List of CoglAtlases */
-  GSList           *atlases;
+  /* Set of CoglAtlases */
+  CoglAtlasSet *atlas_set;
 
-  /* List of callbacks to invoke when an atlas is reorganized */
-  GHookList         reorganize_callbacks;
+  CoglList atlas_closures;
 
-  /* TRUE if we've ever stored a texture in the global atlas. This is
-     used to make sure we only register one callback to listen for
-     global atlas reorganizations */
-  CoglBool          using_global_atlas;
+  /* List of callbacks to invoke when an atlas is reorganized */
+  GHookList reorganize_callbacks;
 
   /* True if some of the glyphs are dirty. This is used as an
      optimization in _cogl_pango_glyph_cache_set_dirty_glyphs to avoid
      iterating the hash table if we know none of them are dirty */
-  CoglBool          has_dirty_glyphs;
+  CoglBool has_dirty_glyphs;
 
   /* Whether mipmapping is being used for this cache. This only
      affects whether we decide to put the glyph in the global atlas */
-  CoglBool          use_mipmapping;
+  CoglBool use_mipmapping;
 };
 
 struct _CoglPangoGlyphCacheKey
@@ -117,6 +121,87 @@ cogl_pango_glyph_cache_equal_func (const void *a, const void *b)
     && key_a->glyph == key_b->glyph;
 }
 
+static void
+atlas_reorganize_cb (CoglAtlas *atlas, void *user_data)
+{
+  CoglPangoGlyphCache *cache = user_data;
+
+  g_hook_list_invoke (&cache->reorganize_callbacks, FALSE);
+}
+
+static void
+allocate_glyph_cb (CoglAtlas *atlas,
+                   CoglTexture *texture,
+                   const CoglAtlasAllocation *allocation,
+                   void *allocation_data,
+                   void *user_data)
+{
+  CoglPangoGlyphCacheValue *value = allocation_data;
+  float tex_width, tex_height;
+
+  if (value->texture)
+    cogl_object_unref (value->texture);
+  value->texture = cogl_object_ref (texture);
+
+  tex_width = cogl_texture_get_width (texture);
+  tex_height = cogl_texture_get_height (texture);
+
+  value->tx1 = allocation->x / tex_width;
+  value->ty1 = allocation->y / tex_height;
+  value->tx2 = (allocation->x + value->draw_width) / tex_width;
+  value->ty2 = (allocation->y + value->draw_height) / tex_height;
+
+  value->tx_pixel = allocation->x;
+  value->ty_pixel = allocation->y;
+
+  /* The glyph has changed position so it will need to be redrawn */
+  value->dirty = TRUE;
+}
+
+static void
+atlas_callback (CoglAtlasSet *set,
+                CoglAtlas *atlas,
+                CoglAtlasSetEvent event,
+                void *user_data)
+{
+  CoglPangoGlyphCache *cache = user_data;
+  AtlasClosureState *state;
+
+  switch (event)
+    {
+    case COGL_ATLAS_SET_EVENT_ADDED:
+      state = g_slice_new (AtlasClosureState);
+      state->atlas = atlas;
+      state->reorganize_closure =
+        cogl_atlas_add_post_reorganize_callback (atlas,
+                                                 atlas_reorganize_cb,
+                                                 cache,
+                                                 NULL); /* destroy */
+      state->allocate_closure =
+        cogl_atlas_add_allocate_callback (atlas,
+                                          allocate_glyph_cb,
+                                          cache,
+                                          NULL); /* destroy */
+
+      _cogl_list_insert (cache->atlas_closures.prev, &state->list_node);
+      break;
+    case COGL_ATLAS_SET_EVENT_REMOVED:
+      break;
+    }
+}
+
+static void
+add_global_atlas_cb (CoglAtlas *atlas,
+                     void *user_data)
+{
+  CoglPangoGlyphCache *cache = user_data;
+
+  atlas_callback (_cogl_get_atlas_set (cache->ctx),
+                  atlas,
+                  COGL_ATLAS_SET_EVENT_ADDED,
+                  cache);
+}
+
 CoglPangoGlyphCache *
 cogl_pango_glyph_cache_new (CoglContext *ctx,
                             CoglBool use_mipmapping)
@@ -135,32 +220,41 @@ cogl_pango_glyph_cache_new (CoglContext *ctx,
      (GDestroyNotify) cogl_pango_glyph_cache_key_free,
      (GDestroyNotify) cogl_pango_glyph_cache_value_free);
 
-  cache->atlases = NULL;
+  _cogl_list_init (&cache->atlas_closures);
+
+  cache->atlas_set = cogl_atlas_set_new (ctx);
+
+  /* We want to be notified when new atlases are added to our local
+   * atlas set so they can be monitored for being re-arranged... */
+  cogl_atlas_set_add_atlas_callback (cache->atlas_set,
+                                     atlas_callback,
+                                     cache,
+                                     NULL); /* destroy */
+
+  /* We want to be notified when new atlases are added to the global
+   * atlas set so they can be monitored for being re-arranged... */
+  cogl_atlas_set_add_atlas_callback (_cogl_get_atlas_set (ctx),
+                                     atlas_callback,
+                                     cache,
+                                     NULL); /* destroy */
+  /* The global atlas set may already have atlases that we will
+   * want to monitor... */
+  cogl_atlas_set_foreach (_cogl_get_atlas_set (ctx),
+                          add_global_atlas_cb,
+                          cache);
+
   g_hook_list_init (&cache->reorganize_callbacks, sizeof (GHook));
 
   cache->has_dirty_glyphs = FALSE;
 
-  cache->using_global_atlas = FALSE;
-
   cache->use_mipmapping = use_mipmapping;
 
   return cache;
 }
 
-static void
-cogl_pango_glyph_cache_reorganize_cb (void *user_data)
-{
-  CoglPangoGlyphCache *cache = user_data;
-
-  g_hook_list_invoke (&cache->reorganize_callbacks, FALSE);
-}
-
 void
 cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache)
 {
-  g_slist_foreach (cache->atlases, (GFunc) cogl_object_unref, NULL);
-  g_slist_free (cache->atlases);
-  cache->atlases = NULL;
   cache->has_dirty_glyphs = FALSE;
 
   g_hash_table_remove_all (cache->hash_table);
@@ -169,11 +263,16 @@ cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache)
 void
 cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache)
 {
-  if (cache->using_global_atlas)
+  AtlasClosureState *state, *tmp;
+
+  _cogl_list_for_each_safe (state, tmp, &cache->atlas_closures, list_node)
     {
-      _cogl_atlas_texture_remove_reorganize_callback (
-                                  cache->ctx,
-                                  cogl_pango_glyph_cache_reorganize_cb, cache);
+      cogl_atlas_remove_post_reorganize_callback (state->atlas,
+                                                  state->reorganize_closure);
+      cogl_atlas_remove_allocate_callback (state->atlas,
+                                           state->allocate_closure);
+      _cogl_list_remove (&state->list_node);
+      g_slice_free (AtlasClosureState, state);
     }
 
   cogl_pango_glyph_cache_clear (cache);
@@ -185,33 +284,6 @@ cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache)
   g_free (cache);
 }
 
-static void
-cogl_pango_glyph_cache_update_position_cb (void *user_data,
-                                           CoglTexture *new_texture,
-                                           const CoglRectangleMapEntry *rect)
-{
-  CoglPangoGlyphCacheValue *value = user_data;
-  float tex_width, tex_height;
-
-  if (value->texture)
-    cogl_object_unref (value->texture);
-  value->texture = cogl_object_ref (new_texture);
-
-  tex_width = cogl_texture_get_width (new_texture);
-  tex_height = cogl_texture_get_height (new_texture);
-
-  value->tx1 = rect->x / tex_width;
-  value->ty1 = rect->y / tex_height;
-  value->tx2 = (rect->x + value->draw_width) / tex_width;
-  value->ty2 = (rect->y + value->draw_height) / tex_height;
-
-  value->tx_pixel = rect->x;
-  value->ty_pixel = rect->y;
-
-  /* The glyph has changed position so it will need to be redrawn */
-  value->dirty = TRUE;
-}
-
 static CoglBool
 cogl_pango_glyph_cache_add_to_global_atlas (CoglPangoGlyphCache *cache,
                                             PangoFont *font,
@@ -246,18 +318,6 @@ cogl_pango_glyph_cache_add_to_global_atlas (CoglPangoGlyphCache *cache,
   value->tx_pixel = 0;
   value->ty_pixel = 0;
 
-  /* The first time we store a texture in the global atlas we'll
-     register for notifications when the global atlas is reorganized
-     so we can forward the notification on as a glyph
-     reorganization */
-  if (!cache->using_global_atlas)
-    {
-      _cogl_atlas_texture_add_reorganize_callback
-        (cache->ctx,
-         cogl_pango_glyph_cache_reorganize_cb, cache);
-      cache->using_global_atlas = TRUE;
-    }
-
   return TRUE;
 }
 
@@ -267,44 +327,17 @@ cogl_pango_glyph_cache_add_to_local_atlas (CoglPangoGlyphCache *cache,
                                            PangoGlyph glyph,
                                            CoglPangoGlyphCacheValue *value)
 {
-  CoglAtlas *atlas = NULL;
-  GSList *l;
-
-  /* Look for an atlas that can reserve the space */
-  for (l = cache->atlases; l; l = l->next)
-    if (_cogl_atlas_reserve_space (l->data,
-                                   value->draw_width + 1,
-                                   value->draw_height + 1,
-                                   value))
-      {
-        atlas = l->data;
-        break;
-      }
-
-  /* If we couldn't find one then start a new atlas */
-  if (atlas == NULL)
-    {
-      atlas = _cogl_atlas_new (COGL_PIXEL_FORMAT_A_8,
-                               COGL_ATLAS_CLEAR_TEXTURE |
-                               COGL_ATLAS_DISABLE_MIGRATION,
-                               cogl_pango_glyph_cache_update_position_cb);
-      COGL_NOTE (ATLAS, "Created new atlas for glyphs: %p", atlas);
-      /* If we still can't reserve space then something has gone
-         seriously wrong so we'll just give up */
-      if (!_cogl_atlas_reserve_space (atlas,
-                                      value->draw_width + 1,
-                                      value->draw_height + 1,
-                                      value))
-        {
-          cogl_object_unref (atlas);
-          return FALSE;
-        }
-
-      _cogl_atlas_add_reorganize_callback
-        (atlas, cogl_pango_glyph_cache_reorganize_cb, NULL, cache);
-
-      cache->atlases = g_slist_prepend (cache->atlases, atlas);
-    }
+  CoglAtlas *atlas;
+
+  /* Add two pixels for the border
+   * FIXME: two pixels isn't enough if mipmapping is in use
+   */
+  atlas = cogl_atlas_set_allocate_space (cache->atlas_set,
+                                         value->draw_width + 2,
+                                         value->draw_height + 2,
+                                         value);
+  if (!atlas)
+    return FALSE;
 
   return TRUE;
 }
@@ -345,12 +378,12 @@ cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache,
         value->dirty = FALSE;
       else
         {
-          /* Try adding the glyph to the global atlas... */
+          /* Try adding the glyph to the global atlas set... */
           if (!cogl_pango_glyph_cache_add_to_global_atlas (cache,
                                                            font,
                                                            glyph,
                                                            value) &&
-              /* If it fails try the local atlas */
+              /* If it fails try the local atlas set */
               !cogl_pango_glyph_cache_add_to_local_atlas (cache,
                                                           font,
                                                           glyph,
diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index d83d6dc..f9697d6 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -157,7 +157,7 @@ libcogl2_la_LDFLAGS = \
        -no-undefined \
        -version-info @COGL_LT_CURRENT@:@COGL_LT_REVISION@:@COGL_LT_AGE@ \
        -export-dynamic \
-       -export-symbols-regex 
"^(cogl|_cogl_debug_flags|_cogl_atlas_new|_cogl_atlas_add_reorganize_callback|_cogl_atlas_reserve_space|_cogl_callback|_cogl_util_get_eye_planes_for_screen_poly|_cogl_atlas_texture_remove_reorganize_callback|_cogl_atlas_texture_add_reorganize_callback|_cogl_texture_get_format|_cogl_texture_foreach_sub_texture_in_region|_cogl_profile_trace_message|_cogl_context_get_default|_cogl_framebuffer_get_stencil_bits|_cogl_clip_stack_push_rectangle|_cogl_framebuffer_get_modelview_stack|_cogl_object_default_unref|_cogl_pipeline_foreach_layer_internal|_cogl_clip_stack_push_primitive|_cogl_buffer_unmap_for_fill_or_fallback|_cogl_primitive_draw|_cogl_debug_instances|_cogl_framebuffer_get_projection_stack|_cogl_pipeline_layer_get_texture|_cogl_buffer_map_for_fill_or_fallback|_cogl_texture_can_hardware_repeat|_cogl_pipeline_prune_to_n_layers|test_|unit_test_).*"
+       -export-symbols-regex 
"^(cogl|_cogl_list_remove|_cogl_list_insert|_cogl_list_init|_cogl_get_atlas_set|_cogl_debug_flags|_cogl_atlas_new|_cogl_atlas_add_reorganize_callback|_cogl_atlas_reserve_space|_cogl_callback|_cogl_util_get_eye_planes_for_screen_poly|_cogl_atlas_texture_remove_reorganize_callback|_cogl_atlas_texture_add_reorganize_callback|_cogl_texture_get_format|_cogl_texture_foreach_sub_texture_in_region|_cogl_profile_trace_message|_cogl_context_get_default|_cogl_framebuffer_get_stencil_bits|_cogl_clip_stack_push_rectangle|_cogl_framebuffer_get_modelview_stack|_cogl_object_default_unref|_cogl_pipeline_foreach_layer_internal|_cogl_clip_stack_push_primitive|_cogl_buffer_unmap_for_fill_or_fallback|_cogl_primitive_draw|_cogl_debug_instances|_cogl_framebuffer_get_projection_stack|_cogl_pipeline_layer_get_texture|_cogl_buffer_map_for_fill_or_fallback|_cogl_texture_can_hardware_repeat|_cogl_pipeline_prune_to_n_layers|test_|unit_test_).*"
 
 libcogl2_la_SOURCES = $(cogl_sources_c)
 nodist_libcogl2_la_SOURCES = $(BUILT_SOURCES)
diff --git a/cogl/Makefile.sources b/cogl/Makefile.sources
index dedf408..fd37391 100644
--- a/cogl/Makefile.sources
+++ b/cogl/Makefile.sources
@@ -36,7 +36,9 @@ cogl_public_h = \
        cogl-renderer.h                 \
        cogl-snippet.h          \
        cogl-sub-texture.h            \
-       cogl-atlas-texture.h          \
+       cogl-atlas-set.h                \
+       cogl-atlas.h                    \
+       cogl-atlas-texture.h            \
        cogl-texture-2d-gl.h            \
        cogl-texture-2d-sliced.h      \
        cogl-texture-2d.h             \
@@ -221,8 +223,10 @@ cogl_sources_c = \
        cogl-texture-rectangle.c              \
        cogl-rectangle-map.h                  \
        cogl-rectangle-map.c                  \
-       cogl-atlas.h                          \
-       cogl-atlas.c                          \
+       cogl-atlas-set-private.h                \
+       cogl-atlas-set.c                        \
+       cogl-atlas-private.h                    \
+       cogl-atlas.c                            \
        cogl-atlas-texture-private.h          \
        cogl-atlas-texture.c                  \
        cogl-meta-texture.c                     \
diff --git a/cogl/cogl-atlas-private.h b/cogl/cogl-atlas-private.h
new file mode 100644
index 0000000..a47bb8f
--- /dev/null
+++ b/cogl/cogl-atlas-private.h
@@ -0,0 +1,88 @@
+/*
+ * Cogl
+ *
+ * A Low-Level GPU Graphics and Utilities API
+ *
+ * Copyright (C) 2010,2011 Intel Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _COGL_ATLAS_PRIVATE_H_
+#define _COGL_ATLAS_PRIVATE_H_
+
+#include "cogl-object-private.h"
+#include "cogl-texture.h"
+#include "cogl-list.h"
+#include "cogl-rectangle-map.h"
+#include "cogl-atlas.h"
+
+typedef enum
+{
+  COGL_ATLAS_CLEAR_TEXTURE     = (1 << 0),
+  COGL_ATLAS_DISABLE_MIGRATION = (1 << 1)
+} CoglAtlasFlags;
+
+struct _CoglAtlas
+{
+  CoglObject _parent;
+
+  CoglContext *context;
+
+  CoglRectangleMap *map;
+
+  CoglTexture *texture;
+  CoglPixelFormat internal_format;
+  CoglAtlasFlags flags;
+
+  CoglList allocate_closures;
+
+  CoglList pre_reorganize_closures;
+  CoglList post_reorganize_closures;
+};
+
+CoglAtlas *
+_cogl_atlas_new (CoglContext *context,
+                 CoglPixelFormat internal_format,
+                 CoglAtlasFlags flags);
+
+CoglBool
+_cogl_atlas_allocate_space (CoglAtlas *atlas,
+                            int width,
+                            int height,
+                            void *allocation_data);
+
+void
+_cogl_atlas_remove (CoglAtlas *atlas,
+                    int x,
+                    int y,
+                    int width,
+                    int height);
+
+CoglTexture *
+_cogl_atlas_migrate_allocation (CoglAtlas *atlas,
+                                int x,
+                                int y,
+                                int width,
+                                int height,
+                                CoglPixelFormat internal_format);
+
+#endif /* _COGL_ATLAS_PRIVATE_H_ */
diff --git a/cogl/cogl-atlas-set-private.h b/cogl/cogl-atlas-set-private.h
new file mode 100644
index 0000000..d49cec4
--- /dev/null
+++ b/cogl/cogl-atlas-set-private.h
@@ -0,0 +1,58 @@
+/*
+ * Cogl
+ *
+ * A Low-Level GPU Graphics and Utilities API
+ *
+ * Copyright (C) 2013,2014 Intel Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ *
+ */
+
+#ifndef _COGL_ATLAS_SET_PRIVATE_H_
+#define _COGL_ATLAS_SET_PRIVATE_H_
+
+#include <glib.h>
+
+#include "cogl-atlas.h"
+#include "cogl-atlas-set.h"
+#include "cogl-list.h"
+#include "cogl-object-private.h"
+
+struct _CoglAtlasSet
+{
+  CoglObject _parent;
+
+  CoglContext *context;
+  GSList *atlases;
+
+  CoglTextureComponents components;
+  CoglPixelFormat internal_format;
+
+  CoglList atlas_closures;
+
+  unsigned int clear_enabled : 1;
+  unsigned int premultiplied : 1;
+  unsigned int migration_enabled : 1;
+};
+
+#endif /* _COGL_ATLAS_SET_PRIVATE_H_ */
diff --git a/cogl/cogl-atlas-set.c b/cogl/cogl-atlas-set.c
new file mode 100644
index 0000000..a15f357
--- /dev/null
+++ b/cogl/cogl-atlas-set.c
@@ -0,0 +1,267 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2013 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/>.
+ *
+ *
+ */
+
+#include <config.h>
+
+#include "cogl-atlas-set.h"
+#include "cogl-atlas-set-private.h"
+#include "cogl-atlas-private.h"
+#include "cogl-closure-list-private.h"
+#include "cogl-texture-private.h"
+#include "cogl-error-private.h"
+
+static void
+_cogl_atlas_set_free (CoglAtlasSet *set);
+
+COGL_OBJECT_DEFINE (AtlasSet, atlas_set);
+
+static CoglUserDataKey atlas_private_key;
+
+static void
+dissociate_atlases (CoglAtlasSet *set)
+{
+  GSList *l;
+
+  /* NB: The set doesn't maintain a reference on the atlases since we don't
+   * want to keep them alive if they become empty. */
+  for (l = set->atlases; l; l = l->next)
+    cogl_object_set_user_data (l->data, &atlas_private_key, NULL, NULL);
+
+  g_slist_free (set->atlases);
+  set->atlases = NULL;
+}
+
+static void
+_cogl_atlas_set_free (CoglAtlasSet *set)
+{
+  dissociate_atlases (set);
+
+  _cogl_closure_list_disconnect_all (&set->atlas_closures);
+
+  g_slice_free (CoglAtlasSet, set);
+}
+
+static void
+_update_internal_format (CoglAtlasSet *set)
+{
+  set->internal_format = _cogl_texture_derive_format (set->context,
+                                                      COGL_PIXEL_FORMAT_ANY,
+                                                      set->components,
+                                                      set->premultiplied);
+}
+
+CoglAtlasSet *
+cogl_atlas_set_new (CoglContext *context)
+{
+  CoglAtlasSet *set = g_slice_new0 (CoglAtlasSet);
+
+  set->context = context;
+  set->atlases = NULL;
+
+  set->components = COGL_TEXTURE_COMPONENTS_RGBA;
+  set->premultiplied = TRUE;
+  _update_internal_format (set);
+
+  set->clear_enabled = FALSE;
+  set->migration_enabled = TRUE;
+
+  _cogl_list_init (&set->atlas_closures);
+
+  return _cogl_atlas_set_object_new (set);
+}
+
+void
+cogl_atlas_set_set_components (CoglAtlasSet *set,
+                               CoglTextureComponents components)
+{
+  g_return_if_fail (set->atlases == NULL);
+
+  set->components = components;
+  _update_internal_format (set);
+}
+
+CoglTextureComponents
+cogl_atlas_set_get_components (CoglAtlasSet *set)
+{
+  return set->components;
+}
+
+void
+cogl_atlas_set_set_premultiplied (CoglAtlasSet *set,
+                                  CoglBool premultiplied)
+{
+  g_return_if_fail (set->atlases == NULL);
+
+  set->premultiplied = premultiplied;
+  _update_internal_format (set);
+}
+
+CoglBool
+cogl_atlas_set_get_premultiplied (CoglAtlasSet *set)
+{
+  return set->premultiplied;
+}
+
+void
+cogl_atlas_set_set_clear_enabled (CoglAtlasSet *set,
+                                  CoglBool clear_enabled)
+{
+  _COGL_RETURN_IF_FAIL (set->atlases == NULL);
+
+  set->clear_enabled = clear_enabled;
+}
+
+CoglBool
+cogl_atlas_set_get_clear_enabled (CoglAtlasSet *set,
+                                  CoglBool clear_enabled)
+{
+  return set->clear_enabled;
+}
+
+void
+cogl_atlas_set_set_migration_enabled (CoglAtlasSet *set,
+                                      CoglBool migration_enabled)
+{
+  _COGL_RETURN_IF_FAIL (set->atlases == NULL);
+
+  set->migration_enabled = migration_enabled;
+}
+
+CoglBool
+cogl_atlas_set_get_migration_enabled (CoglAtlasSet *set)
+{
+  return set->migration_enabled;
+}
+
+CoglAtlasSetAtlasClosure *
+cogl_atlas_set_add_atlas_callback (CoglAtlasSet *set,
+                                   CoglAtlasSetAtlasCallback callback,
+                                   void *user_data,
+                                   CoglUserDataDestroyCallback destroy)
+{
+  return _cogl_closure_list_add (&set->atlas_closures,
+                                 callback,
+                                 user_data,
+                                 destroy);
+}
+
+void
+cogl_atlas_set_remove_atlas_callback (CoglAtlasSet *set,
+                                      CoglAtlasSetAtlasClosure *closure)
+{
+  _cogl_closure_disconnect (closure);
+}
+
+static void
+atlas_destroyed_cb (void *user_data, void *instance)
+{
+  CoglAtlasSet *set = user_data;
+  CoglAtlas *atlas = instance;
+
+  set->atlases = g_slist_remove (set->atlases, atlas);
+}
+
+CoglAtlas *
+cogl_atlas_set_allocate_space (CoglAtlasSet *set,
+                               int width,
+                               int height,
+                               void *allocation_data)
+{
+  GSList *l;
+  CoglAtlasFlags flags = 0;
+  CoglAtlas *atlas;
+
+  /* Look for an existing atlas that can hold the texture */
+  for (l = set->atlases; l; l = l->next)
+    {
+      if (_cogl_atlas_allocate_space (l->data, width, height, allocation_data))
+        return l->data;
+    }
+
+  if (set->clear_enabled)
+    flags |= COGL_ATLAS_CLEAR_TEXTURE;
+
+  if (!set->migration_enabled)
+    flags |= COGL_ATLAS_DISABLE_MIGRATION;
+
+  atlas = _cogl_atlas_new (set->context,
+                           set->internal_format,
+                           flags);
+
+  _cogl_closure_list_invoke (&set->atlas_closures,
+                             CoglAtlasSetAtlasCallback,
+                             set,
+                             atlas,
+                             COGL_ATLAS_SET_EVENT_ADDED);
+
+  COGL_NOTE (ATLAS, "Created new atlas for textures: %p", atlas);
+  if (!_cogl_atlas_allocate_space (atlas, width, height, allocation_data))
+    {
+      _cogl_closure_list_invoke (&set->atlas_closures,
+                                 CoglAtlasSetAtlasCallback,
+                                 set,
+                                 atlas,
+                                 COGL_ATLAS_SET_EVENT_REMOVED);
+
+      /* Ok, this means we really can't add it to an atlas */
+      cogl_object_unref (atlas);
+
+      return NULL;
+    }
+
+  set->atlases = g_slist_prepend (set->atlases, atlas);
+
+  /* Set some data on the atlas so we can get notification when it is
+     destroyed in order to remove it from the list. set->atlases
+     effectively holds a weak reference. We don't need a strong
+     reference because the atlas textures take a reference on the
+     atlas so it will stay alive */
+  _cogl_object_set_user_data ((CoglObject *)atlas,
+                              &atlas_private_key,
+                              set,
+                              atlas_destroyed_cb);
+
+  /* XXX: If we have successfully allocated space then we can drop our
+   * reference on the atlas since the allocated texture will now hold
+   * a reference and we want the atlas itself to be freed when the
+   * last allocated texture is freed.
+   */
+  cogl_object_unref (atlas);
+/* It looks like the previous code didn't do this so either this is
+ * wrong or perhaps we had a leak before? */
+#warning "XXX: double check atlas ref counting"
+
+  return atlas;
+}
+
+void
+cogl_atlas_set_foreach (CoglAtlasSet *atlas_set,
+                        CoglAtlasSetForeachCallback callback,
+                        void *user_data)
+{
+  GSList *l;
+
+  for (l = atlas_set->atlases; l; l = l->next)
+    callback (l->data, user_data);
+}
diff --git a/cogl/cogl-atlas-set.h b/cogl/cogl-atlas-set.h
new file mode 100644
index 0000000..eceae82
--- /dev/null
+++ b/cogl/cogl-atlas-set.h
@@ -0,0 +1,120 @@
+/*
+ * Cogl
+ *
+ * A Low-Level GPU Graphics and Utilities API
+ *
+ * Copyright (C) 2013,2014 Intel Corporation.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ *
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef _COGL_ATLAS_SET_H_
+#define _COGL_ATLAS_SET_H_
+
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-object.h>
+#include <cogl/cogl-atlas.h>
+
+typedef struct _CoglAtlasSet CoglAtlasSet;
+
+CoglAtlasSet *
+cogl_atlas_set_new (CoglContext *context);
+
+CoglBool
+cogl_is_atlas_set (void *object);
+
+void
+cogl_atlas_set_set_components (CoglAtlasSet *set,
+                               CoglTextureComponents components);
+
+CoglTextureComponents
+cogl_atlas_set_get_components (CoglAtlasSet *set);
+
+void
+cogl_atlas_set_set_premultiplied (CoglAtlasSet *set,
+                                  CoglBool premultiplied);
+
+CoglBool
+cogl_atlas_set_get_premultiplied (CoglAtlasSet *set);
+
+void
+cogl_atlas_set_set_clear_enabled (CoglAtlasSet *set,
+                                  CoglBool clear_enabled);
+
+CoglBool
+cogl_atlas_set_get_clear_enabled (CoglAtlasSet *set,
+                                  CoglBool clear_enabled);
+
+void
+cogl_atlas_set_set_migration_enabled (CoglAtlasSet *set,
+                                      CoglBool migration_enabled);
+
+CoglBool
+cogl_atlas_set_get_migration_enabled (CoglAtlasSet *set);
+
+
+void
+cogl_atlas_set_clear (CoglAtlasSet *set);
+
+typedef enum _CoglAtlasSetEvent
+{
+  COGL_ATLAS_SET_EVENT_ADDED = 1,
+  COGL_ATLAS_SET_EVENT_REMOVED = 2
+} CoglAtlasSetEvent;
+
+typedef struct _CoglClosure CoglAtlasSetAtlasClosure;
+
+typedef void (*CoglAtlasSetAtlasCallback) (CoglAtlasSet *set,
+                                           CoglAtlas *atlas,
+                                           CoglAtlasSetEvent event,
+                                           void *user_data);
+
+CoglAtlasSetAtlasClosure *
+cogl_atlas_set_add_atlas_callback (CoglAtlasSet *set,
+                                   CoglAtlasSetAtlasCallback callback,
+                                   void *user_data,
+                                   CoglUserDataDestroyCallback destroy);
+
+void
+cogl_atlas_set_remove_atlas_callback (CoglAtlasSet *set,
+                                      CoglAtlasSetAtlasClosure *closure);
+
+CoglAtlas *
+cogl_atlas_set_allocate_space (CoglAtlasSet *set,
+                               int width,
+                               int height,
+                               void *allocation_data);
+
+typedef void (* CoglAtlasSetForeachCallback) (CoglAtlas *atlas,
+                                              void *user_data);
+
+void
+cogl_atlas_set_foreach (CoglAtlasSet *atlas_set,
+                        CoglAtlasSetForeachCallback callback,
+                        void *user_data);
+
+#endif /* _COGL_ATLAS_SET_H_ */
diff --git a/cogl/cogl-atlas-texture-private.h b/cogl/cogl-atlas-texture-private.h
index 427e9cc..64f6b64 100644
--- a/cogl/cogl-atlas-texture-private.h
+++ b/cogl/cogl-atlas-texture-private.h
@@ -34,7 +34,7 @@
 #include "cogl-object-private.h"
 #include "cogl-texture-private.h"
 #include "cogl-rectangle-map.h"
-#include "cogl-atlas.h"
+#include "cogl-atlas-set-private.h"
 #include "cogl-atlas-texture.h"
 
 struct _CoglAtlasTexture
@@ -48,7 +48,7 @@ struct _CoglAtlasTexture
 
   /* The rectangle that was used to add this texture to the
      atlas. This includes the 1-pixel border */
-  CoglRectangleMapEntry rectangle;
+  CoglAtlasAllocation allocation;
 
   /* The atlas that this texture is in. If the texture is no longer in
      an atlas then this will be NULL. A reference is taken on the
@@ -61,17 +61,13 @@ struct _CoglAtlasTexture
   CoglTexture          *sub_texture;
 };
 
-void
-_cogl_atlas_texture_add_reorganize_callback (CoglContext *ctx,
-                                             GHookFunc callback,
-                                             void *user_data);
-
-void
-_cogl_atlas_texture_remove_reorganize_callback (CoglContext *ctx,
-                                                GHookFunc callback,
-                                                void *user_data);
-
 CoglBool
 _cogl_is_atlas_texture (void *object);
 
+void
+_cogl_atlas_texture_atlas_event_handler (CoglAtlasSet *set,
+                                         CoglAtlas *atlas,
+                                         CoglAtlasSetEvent event,
+                                         void *user_data);
+
 #endif /* _COGL_ATLAS_TEXTURE_PRIVATE_H_ */
diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c
index a812a00..68b3b2f 100644
--- a/cogl/cogl-atlas-texture.c
+++ b/cogl/cogl-atlas-texture.c
@@ -31,9 +31,7 @@
  *  Neil Roberts   <neil linux intel com>
  */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
+#include <config.h>
 
 #include "cogl-debug.h"
 #include "cogl-util.h"
@@ -47,7 +45,8 @@
 #include "cogl-rectangle-map.h"
 #include "cogl-journal-private.h"
 #include "cogl-pipeline-opengl-private.h"
-#include "cogl-atlas.h"
+#include "cogl-atlas-set-private.h"
+#include "cogl-atlas-private.h"
 #include "cogl-sub-texture.h"
 #include "cogl-error-private.h"
 #include "cogl-texture-gl-private.h"
@@ -63,60 +62,57 @@ static const CoglTextureVtable cogl_atlas_texture_vtable;
 
 static CoglSubTexture *
 _cogl_atlas_texture_create_sub_texture (CoglTexture *full_texture,
-                                        const CoglRectangleMapEntry *rectangle)
+                                        const CoglAtlasAllocation *allocation)
 {
   CoglContext *ctx = full_texture->context;
   /* Create a subtexture for the given rectangle not including the
      1-pixel border */
   return cogl_sub_texture_new (ctx,
                                full_texture,
-                               rectangle->x + 1,
-                               rectangle->y + 1,
-                               rectangle->width - 2,
-                               rectangle->height - 2);
+                               allocation->x + 1,
+                               allocation->y + 1,
+                               allocation->width - 2,
+                               allocation->height - 2);
 }
 
 static void
-_cogl_atlas_texture_update_position_cb (void *user_data,
-                                        CoglTexture *new_texture,
-                                        const CoglRectangleMapEntry *rectangle)
+_cogl_atlas_texture_allocate_cb (CoglAtlas *atlas,
+                                 CoglTexture *texture,
+                                 const CoglAtlasAllocation *allocation,
+                                 void *allocation_data,
+                                 void *user_data)
 {
-  CoglAtlasTexture *atlas_tex = user_data;
+  CoglAtlasTexture *atlas_tex = allocation_data;
 
   /* Update the sub texture */
   if (atlas_tex->sub_texture)
     cogl_object_unref (atlas_tex->sub_texture);
   atlas_tex->sub_texture = COGL_TEXTURE (
-    _cogl_atlas_texture_create_sub_texture (new_texture, rectangle));
+    _cogl_atlas_texture_create_sub_texture (texture, allocation));
 
   /* Update the position */
-  atlas_tex->rectangle = *rectangle;
+  atlas_tex->allocation = *allocation;
 }
 
 static void
 _cogl_atlas_texture_pre_reorganize_foreach_cb
-                                         (const CoglRectangleMapEntry *entry,
-                                          void *rectangle_data,
+                                         (CoglAtlas *atlas,
+                                          const CoglAtlasAllocation *allocation,
+                                          void *allocation_data,
                                           void *user_data)
 {
-  CoglAtlasTexture *atlas_tex = rectangle_data;
+  CoglAtlasTexture *atlas_tex = allocation_data;
 
   /* Keep a reference to the texture because we don't want it to be
      destroyed during the reorganization */
   cogl_object_ref (atlas_tex);
-
-  /* Notify cogl-pipeline.c that the texture's underlying GL texture
-   * storage is changing so it knows it may need to bind a new texture
-   * if the CoglTexture is reused with the same texture unit. */
-  _cogl_pipeline_texture_storage_change_notify (COGL_TEXTURE (atlas_tex));
 }
 
 static void
-_cogl_atlas_texture_pre_reorganize_cb (void *data)
+_cogl_atlas_texture_pre_reorganize_cb (CoglAtlas *atlas,
+                                       void *user_data)
 {
-  CoglAtlas *atlas = data;
-
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+  CoglContext *ctx = user_data;
 
   /* We don't know if any journal entries currently depend on OpenGL
    * texture coordinates that would be invalidated by reorganizing
@@ -127,52 +123,50 @@ _cogl_atlas_texture_pre_reorganize_cb (void *data)
    */
   _cogl_flush (ctx);
 
-  if (atlas->map)
-    _cogl_rectangle_map_foreach (atlas->map,
-                                 _cogl_atlas_texture_pre_reorganize_foreach_cb,
-                                 NULL);
+  cogl_atlas_foreach (atlas,
+                      _cogl_atlas_texture_pre_reorganize_foreach_cb,
+                      NULL);
 }
 
 typedef struct
 {
   CoglAtlasTexture **textures;
   /* Number of textures found so far */
-  unsigned int n_textures;
-} CoglAtlasTextureGetRectanglesData;
+  int n_textures;
+} CoglAtlasTextureGetAllocationsData;
 
 static void
-_cogl_atlas_texture_get_rectangles_cb (const CoglRectangleMapEntry *entry,
-                                       void *rectangle_data,
-                                       void *user_data)
+_cogl_atlas_texture_get_allocations_cb (CoglAtlas *atlas,
+                                        const CoglAtlasAllocation *allocation,
+                                        void *allocation_data,
+                                        void *user_data)
 {
-  CoglAtlasTextureGetRectanglesData *data = user_data;
+  CoglAtlasTextureGetAllocationsData *data = user_data;
 
-  data->textures[data->n_textures++] = rectangle_data;
+  data->textures[data->n_textures++] = allocation_data;
 }
 
 static void
-_cogl_atlas_texture_post_reorganize_cb (void *user_data)
+_cogl_atlas_texture_post_reorganize_cb (CoglAtlas *atlas,
+                                        void *user_data)
 {
-  CoglAtlas *atlas = user_data;
-
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+  int n_allocations = cogl_atlas_get_n_allocations (atlas);
 
-  if (atlas->map)
+  if (n_allocations)
     {
-      CoglAtlasTextureGetRectanglesData data;
-      unsigned int i;
+      CoglAtlasTextureGetAllocationsData data;
+      int i;
 
-      data.textures = g_new (CoglAtlasTexture *,
-                             _cogl_rectangle_map_get_n_rectangles (atlas->map));
+      data.textures = g_alloca (sizeof (CoglAtlasTexture *) * n_allocations);
       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);
+       * the preorganize callback. We have to get a separate array of
+       * the textures because CoglAtlas doesn't support removing
+       * allocations during iteration */
+      cogl_atlas_foreach (atlas,
+                          _cogl_atlas_texture_get_allocations_cb,
+                          &data);
 
       for (i = 0; i < data.n_textures; i++)
         {
@@ -183,48 +177,7 @@ _cogl_atlas_texture_post_reorganize_cb (void *user_data)
           if (data.textures[i]->atlas)
             cogl_object_unref (data.textures[i]);
         }
-
-      g_free (data.textures);
     }
-
-  /* Notify any listeners that an atlas has changed */
-  g_hook_list_invoke (&ctx->atlas_reorganize_callbacks, FALSE);
-}
-
-static void
-_cogl_atlas_texture_atlas_destroyed_cb (void *user_data)
-{
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  /* Remove the atlas from the global list */
-  ctx->atlases = g_slist_remove (ctx->atlases, user_data);
-}
-
-static CoglAtlas *
-_cogl_atlas_texture_create_atlas (CoglContext *ctx)
-{
-  static CoglUserDataKey atlas_private_key;
-
-  CoglAtlas *atlas = _cogl_atlas_new (COGL_PIXEL_FORMAT_RGBA_8888,
-                                      0,
-                                      _cogl_atlas_texture_update_position_cb);
-
-  _cogl_atlas_add_reorganize_callback (atlas,
-                                       _cogl_atlas_texture_pre_reorganize_cb,
-                                       _cogl_atlas_texture_post_reorganize_cb,
-                                       atlas);
-
-  ctx->atlases = g_slist_prepend (ctx->atlases, atlas);
-
-  /* Set some data on the atlas so we can get notification when it is
-     destroyed in order to remove it from the list. ctx->atlases
-     effectively holds a weak reference. We don't need a strong
-     reference because the atlas textures take a reference on the
-     atlas so it will stay alive */
-  cogl_object_set_user_data (COGL_OBJECT (atlas), &atlas_private_key, atlas,
-                             _cogl_atlas_texture_atlas_destroyed_cb);
-
-  return atlas;
 }
 
 static void
@@ -273,7 +226,10 @@ _cogl_atlas_texture_remove_from_atlas (CoglAtlasTexture *atlas_tex)
   if (atlas_tex->atlas)
     {
       _cogl_atlas_remove (atlas_tex->atlas,
-                          &atlas_tex->rectangle);
+                          atlas_tex->allocation.x,
+                          atlas_tex->allocation.y,
+                          atlas_tex->allocation.width,
+                          atlas_tex->allocation.height);
 
       cogl_object_unref (atlas_tex->atlas);
       atlas_tex->atlas = NULL;
@@ -382,12 +338,12 @@ _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex)
   _cogl_flush (ctx);
 
   standalone_tex =
-    _cogl_atlas_copy_rectangle (atlas_tex->atlas,
-                                atlas_tex->rectangle.x + 1,
-                                atlas_tex->rectangle.y + 1,
-                                atlas_tex->rectangle.width - 2,
-                                atlas_tex->rectangle.height - 2,
-                                atlas_tex->internal_format);
+    _cogl_atlas_migrate_allocation (atlas_tex->atlas,
+                                    atlas_tex->allocation.x + 1,
+                                    atlas_tex->allocation.y + 1,
+                                    atlas_tex->allocation.width - 2,
+                                    atlas_tex->allocation.height - 2,
+                                    atlas_tex->internal_format);
   /* Note: we simply silently ignore failures to migrate a texture
    * out (most likely due to lack of memory) and hope for the
    * best.
@@ -459,8 +415,8 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex,
                                             dst_width,
                                             dst_height,
                                             bmp,
-                                            dst_x + atlas_tex->rectangle.x + 1,
-                                            dst_y + atlas_tex->rectangle.y + 1,
+                                            dst_x + atlas_tex->allocation.x + 1,
+                                            dst_y + atlas_tex->allocation.y + 1,
                                             0, /* level 0 */
                                             error))
     return FALSE;
@@ -471,20 +427,20 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex,
                                             src_x, src_y,
                                             1, dst_height,
                                             bmp,
-                                            atlas_tex->rectangle.x,
-                                            dst_y + atlas_tex->rectangle.y + 1,
+                                            atlas_tex->allocation.x,
+                                            dst_y + atlas_tex->allocation.y + 1,
                                             0, /* level 0 */
                                             error))
     return FALSE;
   /* Update the right edge pixels */
-  if (dst_x + dst_width == atlas_tex->rectangle.width - 2 &&
+  if (dst_x + dst_width == atlas_tex->allocation.width - 2 &&
       !cogl_texture_set_region_from_bitmap (atlas->texture,
                                             src_x + dst_width - 1, src_y,
                                             1, dst_height,
                                             bmp,
-                                            atlas_tex->rectangle.x +
-                                            atlas_tex->rectangle.width - 1,
-                                            dst_y + atlas_tex->rectangle.y + 1,
+                                            atlas_tex->allocation.x +
+                                            atlas_tex->allocation.width - 1,
+                                            dst_y + atlas_tex->allocation.y + 1,
                                             0, /* level 0 */
                                             error))
     return FALSE;
@@ -494,20 +450,20 @@ _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex,
                                             src_x, src_y,
                                             dst_width, 1,
                                             bmp,
-                                            dst_x + atlas_tex->rectangle.x + 1,
-                                            atlas_tex->rectangle.y,
+                                            dst_x + atlas_tex->allocation.x + 1,
+                                            atlas_tex->allocation.y,
                                             0, /* level 0 */
                                             error))
     return FALSE;
   /* Update the bottom edge pixels */
-  if (dst_y + dst_height == atlas_tex->rectangle.height - 2 &&
+  if (dst_y + dst_height == atlas_tex->allocation.height - 2 &&
       !cogl_texture_set_region_from_bitmap (atlas->texture,
                                             src_x, src_y + dst_height - 1,
                                             dst_width, 1,
                                             bmp,
-                                            dst_x + atlas_tex->rectangle.x + 1,
-                                            atlas_tex->rectangle.y +
-                                            atlas_tex->rectangle.height - 1,
+                                            dst_x + atlas_tex->allocation.x + 1,
+                                            atlas_tex->allocation.y +
+                                            atlas_tex->allocation.height - 1,
                                             0, /* level 0 */
                                             error))
     return FALSE;
@@ -649,6 +605,38 @@ _cogl_atlas_texture_can_use_format (CoglPixelFormat format)
           format == COGL_PIXEL_FORMAT_RGBA_8888);
 }
 
+void
+_cogl_atlas_texture_atlas_event_handler (CoglAtlasSet *set,
+                                         CoglAtlas *atlas,
+                                         CoglAtlasSetEvent event,
+                                         void *user_data)
+{
+  switch (event)
+    {
+    case COGL_ATLAS_SET_EVENT_ADDED:
+      {
+        CoglAtlasReorganizeCallback pre_callback =
+          _cogl_atlas_texture_pre_reorganize_cb;
+        CoglAtlasReorganizeCallback post_callback =
+          _cogl_atlas_texture_post_reorganize_cb;
+
+        cogl_atlas_add_allocate_callback (atlas,
+                                          _cogl_atlas_texture_allocate_cb,
+                                          NULL, /* user data */
+                                          NULL); /* destroy */
+        cogl_atlas_add_pre_reorganize_callback (atlas, pre_callback,
+                                                set->context,
+                                                NULL); /* destroy */
+        cogl_atlas_add_post_reorganize_callback (atlas, post_callback,
+                                                 set->context,
+                                                 NULL); /* destroy */
+        break;
+      }
+    case COGL_ATLAS_SET_EVENT_REMOVED:
+      break;
+    }
+}
+
 static CoglAtlasTexture *
 _cogl_atlas_texture_create_base (CoglContext *ctx,
                                  int width,
@@ -712,7 +700,6 @@ allocate_space (CoglAtlasTexture *atlas_tex,
   CoglTexture *tex = COGL_TEXTURE (atlas_tex);
   CoglContext *ctx = tex->context;
   CoglAtlas *atlas;
-  GSList *l;
 
   /* If the texture is in a strange format then we won't use it */
   if (!_cogl_atlas_texture_can_use_format (internal_format))
@@ -738,37 +725,20 @@ allocate_space (CoglAtlasTexture *atlas_tex,
       return FALSE;
     }
 
-  /* Look for an existing atlas that can hold the texture */
-  for (l = ctx->atlases; l; l = l->next)
-    /* Try to make some space in the atlas for the texture */
-    if (_cogl_atlas_reserve_space (atlas = l->data,
-                                   /* Add two pixels for the border */
-                                   width + 2, height + 2,
-                                   atlas_tex))
-      {
-        cogl_object_ref (atlas);
-        break;
-      }
-
-  /* If we couldn't find a suitable atlas then start another */
-  if (l == NULL)
+  /* Add two pixels for the border
+   * FIXME: two pixels isn't enough if mipmapping is in use
+   */
+  atlas = cogl_atlas_set_allocate_space (ctx->atlas_set,
+                                         tex->width + 2,
+                                         tex->height + 2,
+                                         atlas_tex);
+  if (!atlas)
     {
-      atlas = _cogl_atlas_texture_create_atlas (ctx);
-      COGL_NOTE (ATLAS, "Created new atlas for textures: %p", atlas);
-      if (!_cogl_atlas_reserve_space (atlas,
-                                      /* Add two pixels for the border */
-                                      width + 2, height + 2,
-                                      atlas_tex))
-        {
-          /* Ok, this means we really can't add it to the atlas */
-          cogl_object_unref (atlas);
-
-          _cogl_set_error (error,
-                           COGL_SYSTEM_ERROR,
-                           COGL_SYSTEM_ERROR_NO_MEMORY,
-                           "Not enough memory to atlas texture");
-          return FALSE;
-        }
+      _cogl_set_error (error,
+                       COGL_SYSTEM_ERROR,
+                       COGL_SYSTEM_ERROR_NO_MEMORY,
+                       "Not enough memory to atlas texture");
+      return FALSE;
     }
 
   atlas_tex->internal_format = internal_format;
@@ -974,31 +944,6 @@ cogl_atlas_texture_new_from_file (CoglContext *ctx,
   return atlas_tex;
 }
 
-void
-_cogl_atlas_texture_add_reorganize_callback (CoglContext *ctx,
-                                             GHookFunc callback,
-                                             void *user_data)
-{
-  GHook *hook = g_hook_alloc (&ctx->atlas_reorganize_callbacks);
-  hook->func = callback;
-  hook->data = user_data;
-  g_hook_prepend (&ctx->atlas_reorganize_callbacks, hook);
-}
-
-void
-_cogl_atlas_texture_remove_reorganize_callback (CoglContext *ctx,
-                                                GHookFunc callback,
-                                                void *user_data)
-{
-  GHook *hook = g_hook_find_func_data (&ctx->atlas_reorganize_callbacks,
-                                       FALSE,
-                                       callback,
-                                       user_data);
-
-  if (hook)
-    g_hook_destroy_link (&ctx->atlas_reorganize_callbacks, hook);
-}
-
 static CoglTextureType
 _cogl_atlas_texture_get_type (CoglTexture *tex)
 {
diff --git a/cogl/cogl-atlas.c b/cogl/cogl-atlas.c
index 57405da..97a1cfe 100644
--- a/cogl/cogl-atlas.c
+++ b/cogl/cogl-atlas.c
@@ -29,11 +29,9 @@
  *  Neil Roberts   <neil linux intel com>
  */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
+#include <config.h>
 
-#include "cogl-atlas.h"
+#include "cogl-atlas-private.h"
 #include "cogl-rectangle-map.h"
 #include "cogl-context-private.h"
 #include "cogl-texture-private.h"
@@ -50,26 +48,48 @@
 
 static void _cogl_atlas_free (CoglAtlas *atlas);
 
-COGL_OBJECT_INTERNAL_DEFINE (Atlas, atlas);
+COGL_OBJECT_DEFINE (Atlas, atlas);
 
 CoglAtlas *
-_cogl_atlas_new (CoglPixelFormat texture_format,
-                 CoglAtlasFlags flags,
-                 CoglAtlasUpdatePositionCallback update_position_cb)
+_cogl_atlas_new (CoglContext *context,
+                 CoglPixelFormat internal_format,
+                 CoglAtlasFlags flags)
 {
   CoglAtlas *atlas = g_new (CoglAtlas, 1);
 
-  atlas->update_position_cb = update_position_cb;
+  atlas->context = context;
   atlas->map = NULL;
   atlas->texture = NULL;
   atlas->flags = flags;
-  atlas->texture_format = texture_format;
-  g_hook_list_init (&atlas->pre_reorganize_callbacks, sizeof (GHook));
-  g_hook_list_init (&atlas->post_reorganize_callbacks, sizeof (GHook));
+  atlas->internal_format = internal_format;
+
+  _cogl_list_init (&atlas->allocate_closures);
+
+  _cogl_list_init (&atlas->pre_reorganize_closures);
+  _cogl_list_init (&atlas->post_reorganize_closures);
 
   return _cogl_atlas_object_new (atlas);
 }
 
+CoglAtlasAllocateClosure *
+cogl_atlas_add_allocate_callback (CoglAtlas *atlas,
+                                  CoglAtlasAllocateCallback callback,
+                                  void *user_data,
+                                  CoglUserDataDestroyCallback destroy)
+{
+  return _cogl_closure_list_add (&atlas->allocate_closures,
+                                 callback,
+                                 user_data,
+                                 destroy);
+}
+
+void
+cogl_atlas_remove_allocate_callback (CoglAtlas *atlas,
+                                     CoglAtlasAllocateClosure *closure)
+{
+  _cogl_closure_disconnect (closure);
+}
+
 static void
 _cogl_atlas_free (CoglAtlas *atlas)
 {
@@ -80,8 +100,10 @@ _cogl_atlas_free (CoglAtlas *atlas)
   if (atlas->map)
     _cogl_rectangle_map_free (atlas->map);
 
-  g_hook_list_clear (&atlas->pre_reorganize_callbacks);
-  g_hook_list_clear (&atlas->post_reorganize_callbacks);
+  _cogl_closure_list_disconnect_all (&atlas->allocate_closures);
+
+  _cogl_closure_list_disconnect_all (&atlas->pre_reorganize_closures);
+  _cogl_closure_list_disconnect_all (&atlas->post_reorganize_closures);
 
   g_free (atlas);
 }
@@ -89,21 +111,22 @@ _cogl_atlas_free (CoglAtlas *atlas)
 typedef struct _CoglAtlasRepositionData
 {
   /* The current user data for this texture */
-  void *user_data;
+  void *allocation_data;
+
   /* The old and new positions of the texture */
   CoglRectangleMapEntry old_position;
   CoglRectangleMapEntry new_position;
 } CoglAtlasRepositionData;
 
 static void
-_cogl_atlas_migrate (CoglAtlas               *atlas,
-                     unsigned int             n_textures,
+_cogl_atlas_migrate (CoglAtlas *atlas,
+                     int n_textures,
                      CoglAtlasRepositionData *textures,
-                     CoglTexture             *old_texture,
-                     CoglTexture             *new_texture,
-                     void                    *skip_user_data)
+                     CoglTexture *old_texture,
+                     CoglTexture *new_texture,
+                     void *skip_allocation_data)
 {
-  unsigned int i;
+  int i;
   CoglBlitData blit_data;
 
   /* If the 'disable migrate' flag is set then we won't actually copy
@@ -111,19 +134,30 @@ _cogl_atlas_migrate (CoglAtlas               *atlas,
      callback to update the position */
   if ((atlas->flags & COGL_ATLAS_DISABLE_MIGRATION))
     for (i = 0; i < n_textures; i++)
-      /* Update the texture position */
-      atlas->update_position_cb (textures[i].user_data,
-                                 new_texture,
-                                 &textures[i].new_position);
+      {
+        CoglAtlasAllocation *allocation =
+          (CoglAtlasAllocation *)&textures[i].new_position;
+
+        /* Update the texture position */
+        _cogl_closure_list_invoke (&atlas->allocate_closures,
+                                   CoglAtlasAllocateCallback,
+                                   atlas,
+                                   new_texture,
+                                   allocation,
+                                   textures[i].allocation_data);
+      }
   else
     {
       _cogl_blit_begin (&blit_data, new_texture, old_texture);
 
       for (i = 0; i < n_textures; i++)
         {
+          CoglAtlasAllocation *allocation =
+            (CoglAtlasAllocation *)&textures[i].new_position;
+
           /* Skip the texture that is being added because it doesn't contain
              any data yet */
-          if (textures[i].user_data != skip_user_data)
+          if (textures[i].allocation_data != skip_allocation_data)
             _cogl_blit (&blit_data,
                         textures[i].old_position.x,
                         textures[i].old_position.y,
@@ -133,9 +167,12 @@ _cogl_atlas_migrate (CoglAtlas               *atlas,
                         textures[i].new_position.height);
 
           /* Update the texture position */
-          atlas->update_position_cb (textures[i].user_data,
+          _cogl_closure_list_invoke (&atlas->allocate_closures,
+                                     CoglAtlasAllocateCallback,
+                                     atlas,
                                      new_texture,
-                                     &textures[i].new_position);
+                                     allocation,
+                                     textures[i].allocation_data);
         }
 
       _cogl_blit_end (&blit_data);
@@ -146,23 +183,23 @@ typedef struct _CoglAtlasGetRectanglesData
 {
   CoglAtlasRepositionData *textures;
   /* Number of textures found so far */
-  unsigned int n_textures;
+  int n_textures;
 } CoglAtlasGetRectanglesData;
 
 static void
 _cogl_atlas_get_rectangles_cb (const CoglRectangleMapEntry *rectangle,
-                               void                        *rect_data,
-                               void                        *user_data)
+                               void *rect_data,
+                               void *user_data)
 {
   CoglAtlasGetRectanglesData *data = user_data;
 
   data->textures[data->n_textures].old_position = *rectangle;
-  data->textures[data->n_textures++].user_data = rect_data;
+  data->textures[data->n_textures++].allocation_data = rect_data;
 }
 
 static void
-_cogl_atlas_get_next_size (unsigned int *map_width,
-                           unsigned int *map_height)
+_cogl_atlas_get_next_size (int *map_width,
+                           int *map_height)
 {
   /* Double the size of the texture by increasing whichever dimension
      is smaller */
@@ -173,19 +210,18 @@ _cogl_atlas_get_next_size (unsigned int *map_width,
 }
 
 static void
-_cogl_atlas_get_initial_size (CoglPixelFormat format,
-                              unsigned int *map_width,
-                              unsigned int *map_height)
+_cogl_atlas_get_initial_size (CoglAtlas *atlas,
+                              int *map_width,
+                              int *map_height)
 {
+  CoglContext *ctx = atlas->context;
   unsigned int size;
   GLenum gl_intformat;
   GLenum gl_format;
   GLenum gl_type;
 
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
   ctx->driver_vtable->pixel_format_to_gl (ctx,
-                                          format,
+                                          atlas->internal_format,
                                           &gl_intformat,
                                           &gl_format,
                                           &gl_type);
@@ -195,7 +231,7 @@ _cogl_atlas_get_initial_size (CoglPixelFormat format,
      initial minimum size. If the format is only 1 byte per pixel we
      can use 1024x1024, otherwise we'll assume it will take 4 bytes
      per pixel and use 512x512. */
-  if (_cogl_pixel_format_get_bytes_per_pixel (format) == 1)
+  if (_cogl_pixel_format_get_bytes_per_pixel (atlas->internal_format) == 1)
     size = 1024;
   else
     size = 512;
@@ -216,20 +252,19 @@ _cogl_atlas_get_initial_size (CoglPixelFormat format,
 }
 
 static CoglRectangleMap *
-_cogl_atlas_create_map (CoglPixelFormat          format,
-                        unsigned int             map_width,
-                        unsigned int             map_height,
-                        unsigned int             n_textures,
+_cogl_atlas_create_map (CoglAtlas *atlas,
+                        int map_width,
+                        int map_height,
+                        int n_textures,
                         CoglAtlasRepositionData *textures)
 {
+  CoglContext *ctx = atlas->context;
   GLenum gl_intformat;
   GLenum gl_format;
   GLenum gl_type;
 
-  _COGL_GET_CONTEXT (ctx, NULL);
-
   ctx->driver_vtable->pixel_format_to_gl (ctx,
-                                          format,
+                                          atlas->internal_format,
                                           &gl_intformat,
                                           &gl_format,
                                           &gl_type);
@@ -246,7 +281,7 @@ _cogl_atlas_create_map (CoglPixelFormat          format,
       CoglRectangleMap *new_atlas = _cogl_rectangle_map_new (map_width,
                                                              map_height,
                                                              NULL);
-      unsigned int i;
+      int i;
 
       COGL_NOTE (ATLAS, "Trying to resize the atlas to %ux%u",
                  map_width, map_height);
@@ -256,7 +291,7 @@ _cogl_atlas_create_map (CoglPixelFormat          format,
         if (!_cogl_rectangle_map_add (new_atlas,
                                       textures[i].old_position.width,
                                       textures[i].old_position.height,
-                                      textures[i].user_data,
+                                      textures[i].allocation_data,
                                       &textures[i].new_position))
           break;
 
@@ -284,30 +319,29 @@ _cogl_atlas_create_texture (CoglAtlas *atlas,
                             int width,
                             int height)
 {
+  CoglContext *ctx = atlas->context;
   CoglTexture2D *tex;
   CoglError *ignore_error = NULL;
 
-  _COGL_GET_CONTEXT (ctx, NULL);
-
   if ((atlas->flags & COGL_ATLAS_CLEAR_TEXTURE))
     {
       uint8_t *clear_data;
       CoglBitmap *clear_bmp;
-      int bpp = _cogl_pixel_format_get_bytes_per_pixel (atlas->texture_format);
+      int bpp = _cogl_pixel_format_get_bytes_per_pixel (atlas->internal_format);
 
       /* Create a buffer of zeroes to initially clear the texture */
       clear_data = g_malloc0 (width * height * bpp);
       clear_bmp = cogl_bitmap_new_for_data (ctx,
                                             width,
                                             height,
-                                            atlas->texture_format,
+                                            atlas->internal_format,
                                             width * bpp,
                                             clear_data);
 
       tex = cogl_texture_2d_new_from_bitmap (clear_bmp);
 
       _cogl_texture_set_internal_format (COGL_TEXTURE (tex),
-                                         atlas->texture_format);
+                                         atlas->internal_format);
 
       if (!cogl_texture_allocate (COGL_TEXTURE (tex), &ignore_error))
         {
@@ -325,7 +359,7 @@ _cogl_atlas_create_texture (CoglAtlas *atlas,
       tex = cogl_texture_2d_new_with_size (ctx, width, height);
 
       _cogl_texture_set_internal_format (COGL_TEXTURE (tex),
-                                         atlas->texture_format);
+                                         atlas->internal_format);
 
       if (!cogl_texture_allocate (COGL_TEXTURE (tex), &ignore_error))
         {
@@ -352,36 +386,24 @@ _cogl_atlas_compare_size_cb (const void *a,
   return a_size < b_size ? 1 : a_size > b_size ? -1 : 0;
 }
 
-static void
-_cogl_atlas_notify_pre_reorganize (CoglAtlas *atlas)
-{
-  g_hook_list_invoke (&atlas->pre_reorganize_callbacks, FALSE);
-}
-
-static void
-_cogl_atlas_notify_post_reorganize (CoglAtlas *atlas)
-{
-  g_hook_list_invoke (&atlas->post_reorganize_callbacks, FALSE);
-}
-
 CoglBool
-_cogl_atlas_reserve_space (CoglAtlas             *atlas,
-                           unsigned int           width,
-                           unsigned int           height,
-                           void                  *user_data)
+_cogl_atlas_allocate_space (CoglAtlas *atlas,
+                            int width,
+                            int height,
+                            void *allocation_data)
 {
   CoglAtlasGetRectanglesData data;
   CoglRectangleMap *new_map;
   CoglTexture2D *new_tex;
-  unsigned int map_width, map_height;
+  int map_width, map_height;
   CoglBool ret;
-  CoglRectangleMapEntry new_position;
+  CoglAtlasAllocation new_allocation;
 
   /* 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))
+                               allocation_data,
+                               (CoglRectangleMapEntry *)&new_allocation))
     {
       COGL_NOTE (ATLAS, "%p: Atlas is %ix%i, has %i textures and is %i%% waste",
                  atlas,
@@ -393,9 +415,12 @@ _cogl_atlas_reserve_space (CoglAtlas             *atlas,
                  100 / (_cogl_rectangle_map_get_width (atlas->map) *
                         _cogl_rectangle_map_get_height (atlas->map)));
 
-      atlas->update_position_cb (user_data,
+      _cogl_closure_list_invoke (&atlas->allocate_closures,
+                                 CoglAtlasAllocateCallback,
+                                 atlas,
                                  atlas->texture,
-                                 &new_position);
+                                 &new_allocation,
+                                 allocation_data);
 
       return TRUE;
     }
@@ -404,7 +429,9 @@ _cogl_atlas_reserve_space (CoglAtlas             *atlas,
      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);
+  _cogl_closure_list_invoke (&atlas->pre_reorganize_closures,
+                             CoglAtlasReorganizeCallback,
+                             atlas);
 
   /* Get an array of all the textures currently in the atlas. */
   data.n_textures = 0;
@@ -412,7 +439,7 @@ _cogl_atlas_reserve_space (CoglAtlas             *atlas,
     data.textures = g_malloc (sizeof (CoglAtlasRepositionData));
   else
     {
-      unsigned int n_rectangles =
+      int n_rectangles =
         _cogl_rectangle_map_get_n_rectangles (atlas->map);
       data.textures = g_malloc (sizeof (CoglAtlasRepositionData) *
                                 (n_rectangles + 1));
@@ -427,7 +454,7 @@ _cogl_atlas_reserve_space (CoglAtlas             *atlas,
   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;
+  data.textures[data.n_textures++].allocation_data = allocation_data;
 
   /* The atlasing algorithm works a lot better if the rectangles are
      added in decreasing order of size so we'll first sort the
@@ -452,10 +479,9 @@ _cogl_atlas_reserve_space (CoglAtlas             *atlas,
         _cogl_atlas_get_next_size (&map_width, &map_height);
     }
   else
-    _cogl_atlas_get_initial_size (atlas->texture_format,
-                                  &map_width, &map_height);
+    _cogl_atlas_get_initial_size (atlas, &map_width, &map_height);
 
-  new_map = _cogl_atlas_create_map (atlas->texture_format,
+  new_map = _cogl_atlas_create_map (atlas,
                                     map_width, map_height,
                                     data.n_textures, data.textures);
 
@@ -500,16 +526,24 @@ _cogl_atlas_reserve_space (CoglAtlas             *atlas,
                                data.textures,
                                atlas->texture,
                                COGL_TEXTURE (new_tex),
-                               user_data);
+                               allocation_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);
+        {
+          CoglAtlasAllocation *allocation =
+            (CoglAtlasAllocation *)&data.textures[0].new_position;
+
+          /* We know there's only one texture so we can just directly
+             update the rectangle from its new position */
+          _cogl_closure_list_invoke (&atlas->allocate_closures,
+                                     CoglAtlasAllocateCallback,
+                                     atlas,
+                                     COGL_TEXTURE (new_tex),
+                                     allocation,
+                                     data.textures[0].allocation_data);
+        }
 
       atlas->map = new_map;
       atlas->texture = COGL_TEXTURE (new_tex);
@@ -530,21 +564,28 @@ _cogl_atlas_reserve_space (CoglAtlas             *atlas,
 
   g_free (data.textures);
 
-  _cogl_atlas_notify_post_reorganize (atlas);
+  _cogl_closure_list_invoke (&atlas->pre_reorganize_closures,
+                             CoglAtlasReorganizeCallback,
+                             atlas);
 
   return ret;
 }
 
 void
 _cogl_atlas_remove (CoglAtlas *atlas,
-                    const CoglRectangleMapEntry *rectangle)
+                    int x,
+                    int y,
+                    int width,
+                    int height)
 {
-  _cogl_rectangle_map_remove (atlas->map, rectangle);
+  CoglRectangleMapEntry rectangle = { x, y, width, height };
+
+  _cogl_rectangle_map_remove (atlas->map, &rectangle);
 
   COGL_NOTE (ATLAS, "%p: Removed rectangle sized %ix%i",
              atlas,
-             rectangle->width,
-             rectangle->height);
+             rectangle.width,
+             rectangle.height);
   COGL_NOTE (ATLAS, "%p: Atlas is %ix%i, has %i textures and is %i%% waste",
              atlas,
              _cogl_rectangle_map_get_width (atlas->map),
@@ -555,6 +596,12 @@ _cogl_atlas_remove (CoglAtlas *atlas,
                     _cogl_rectangle_map_get_height (atlas->map)));
 };
 
+CoglTexture *
+cogl_atlas_get_texture (CoglAtlas *atlas)
+{
+  return atlas->texture;
+}
+
 static CoglTexture *
 create_migration_texture (CoglContext *ctx,
                           int width,
@@ -606,19 +653,18 @@ create_migration_texture (CoglContext *ctx,
 }
 
 CoglTexture *
-_cogl_atlas_copy_rectangle (CoglAtlas *atlas,
-                            int x,
-                            int y,
-                            int width,
-                            int height,
-                            CoglPixelFormat internal_format)
+_cogl_atlas_migrate_allocation (CoglAtlas *atlas,
+                                int x,
+                                int y,
+                                int width,
+                                int height,
+                                CoglPixelFormat internal_format)
 {
+  CoglContext *ctx = atlas->context;
   CoglTexture *tex;
   CoglBlitData blit_data;
   CoglError *ignore_error = NULL;
 
-  _COGL_GET_CONTEXT (ctx, NULL);
-
   /* Create a new texture at the right size */
   tex = create_migration_texture (ctx, width, height, internal_format);
   if (!cogl_texture_allocate (tex, &ignore_error))
@@ -641,50 +687,91 @@ _cogl_atlas_copy_rectangle (CoglAtlas *atlas,
   return tex;
 }
 
+CoglAtlasReorganizeClosure *
+cogl_atlas_add_pre_reorganize_callback (CoglAtlas *atlas,
+                                        CoglAtlasReorganizeCallback callback,
+                                        void *user_data,
+                                        CoglUserDataDestroyCallback destroy)
+{
+  _COGL_RETURN_VAL_IF_FAIL (callback != NULL, NULL);
+
+  return _cogl_closure_list_add (&atlas->pre_reorganize_closures,
+                                 callback,
+                                 user_data,
+                                 destroy);
+}
+
 void
-_cogl_atlas_add_reorganize_callback (CoglAtlas            *atlas,
-                                     GHookFunc             pre_callback,
-                                     GHookFunc             post_callback,
-                                     void                 *user_data)
+cogl_atlas_remove_pre_reorganize_callback (CoglAtlas *atlas,
+                                           CoglAtlasReorganizeClosure *closure)
 {
-  if (pre_callback)
-    {
-      GHook *hook = g_hook_alloc (&atlas->post_reorganize_callbacks);
-      hook->func = pre_callback;
-      hook->data = user_data;
-      g_hook_prepend (&atlas->pre_reorganize_callbacks, hook);
-    }
-  if (post_callback)
-    {
-      GHook *hook = g_hook_alloc (&atlas->pre_reorganize_callbacks);
-      hook->func = post_callback;
-      hook->data = user_data;
-      g_hook_prepend (&atlas->post_reorganize_callbacks, hook);
-    }
+  _cogl_closure_disconnect (closure);
+}
+
+CoglAtlasReorganizeClosure *
+cogl_atlas_add_post_reorganize_callback (CoglAtlas *atlas,
+                                         CoglAtlasReorganizeCallback callback,
+                                         void *user_data,
+                                         CoglUserDataDestroyCallback destroy)
+{
+  _COGL_RETURN_VAL_IF_FAIL (callback != NULL, NULL);
+
+  return _cogl_closure_list_add (&atlas->post_reorganize_closures,
+                                 callback,
+                                 user_data,
+                                 destroy);
 }
 
 void
-_cogl_atlas_remove_reorganize_callback (CoglAtlas            *atlas,
-                                        GHookFunc             pre_callback,
-                                        GHookFunc             post_callback,
-                                        void                 *user_data)
+cogl_atlas_remove_post_reorganize_callback (CoglAtlas *atlas,
+                                            CoglAtlasReorganizeClosure *closure)
 {
-  if (pre_callback)
-    {
-      GHook *hook = g_hook_find_func_data (&atlas->pre_reorganize_callbacks,
-                                           FALSE,
-                                           pre_callback,
-                                           user_data);
-      if (hook)
-        g_hook_destroy_link (&atlas->pre_reorganize_callbacks, hook);
-    }
-  if (post_callback)
+  _cogl_closure_disconnect (closure);
+}
+
+typedef struct _ForeachState
+{
+  CoglAtlas *atlas;
+  CoglAtlasForeachCallback callback;
+  void *user_data;
+} ForeachState;
+
+static void
+foreach_rectangle_cb (const CoglRectangleMapEntry *entry,
+                      void *rectangle_data,
+                      void *user_data)
+{
+  ForeachState *state = user_data;
+
+  state->callback (state->atlas,
+                   (CoglAtlasAllocation *)entry,
+                   rectangle_data,
+                   state->user_data);
+}
+
+void
+cogl_atlas_foreach (CoglAtlas *atlas,
+                    CoglAtlasForeachCallback callback,
+                    void *user_data)
+{
+  if (atlas->map)
     {
-      GHook *hook = g_hook_find_func_data (&atlas->post_reorganize_callbacks,
-                                           FALSE,
-                                           post_callback,
-                                           user_data);
-      if (hook)
-        g_hook_destroy_link (&atlas->post_reorganize_callbacks, hook);
+      ForeachState state;
+
+      state.atlas = atlas;
+      state.callback = callback;
+      state.user_data = user_data;
+
+      _cogl_rectangle_map_foreach (atlas->map, foreach_rectangle_cb, &state);
     }
 }
+
+int
+cogl_atlas_get_n_allocations (CoglAtlas *atlas)
+{
+  if (atlas->map)
+    return _cogl_rectangle_map_get_n_rectangles (atlas->map);
+  else
+    return 0;
+}
+
diff --git a/cogl/cogl-atlas.h b/cogl/cogl-atlas.h
index e336cfb..05f506b 100644
--- a/cogl/cogl-atlas.h
+++ b/cogl/cogl-atlas.h
@@ -26,80 +26,89 @@
  * SOFTWARE.
  */
 
-#ifndef __COGL_ATLAS_H
-#define __COGL_ATLAS_H
+#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
 
-#include "cogl-rectangle-map.h"
-#include "cogl-object-private.h"
-#include "cogl-texture.h"
+#ifndef _COGL_ATLAS_H_
+#define _COGL_ATLAS_H_
 
-typedef void
-(* CoglAtlasUpdatePositionCallback) (void *user_data,
-                                     CoglTexture *new_texture,
-                                     const CoglRectangleMapEntry *rect);
+#include <cogl/cogl-types.h>
+#include <cogl/cogl-object.h>
+#include <cogl/cogl-texture.h>
 
-typedef enum
+typedef struct _CoglAtlasAllocation
 {
-  COGL_ATLAS_CLEAR_TEXTURE     = (1 << 0),
-  COGL_ATLAS_DISABLE_MIGRATION = (1 << 1)
-} CoglAtlasFlags;
+  int x;
+  int y;
+  int width;
+  int height;
+} CoglAtlasAllocation;
 
 typedef struct _CoglAtlas CoglAtlas;
 
-#define COGL_ATLAS(object) ((CoglAtlas *) object)
+/* XXX: Note that during migration _cogl_atlas_get_texture() may not match the
+ * @texture given here. @texture is more up to date... */
+typedef void
+(* CoglAtlasAllocateCallback) (CoglAtlas *atlas,
+                               CoglTexture *texture,
+                               const CoglAtlasAllocation *allocation,
+                               void *allocation_data,
+                               void *user_data);
 
-struct _CoglAtlas
-{
-  CoglObject _parent;
+typedef struct _CoglClosure CoglAtlasAllocateClosure;
 
-  CoglRectangleMap *map;
+CoglAtlasAllocateClosure *
+cogl_atlas_add_allocate_callback (CoglAtlas *atlas,
+                                  CoglAtlasAllocateCallback callback,
+                                  void *user_data,
+                                  CoglUserDataDestroyCallback destroy);
 
-  CoglTexture *texture;
-  CoglPixelFormat texture_format;
-  CoglAtlasFlags flags;
+void
+cogl_atlas_remove_allocate_callback (CoglAtlas *atlas,
+                                     CoglAtlasAllocateClosure *closure);
 
-  CoglAtlasUpdatePositionCallback update_position_cb;
+CoglTexture *
+cogl_atlas_get_texture (CoglAtlas *atlas);
 
-  GHookList pre_reorganize_callbacks;
-  GHookList post_reorganize_callbacks;
-};
+typedef void (*CoglAtlasForeachCallback) (CoglAtlas *atlas,
+                                          const CoglAtlasAllocation *allocation,
+                                          void *allocation_data,
+                                          void *user_data);
+void
+cogl_atlas_foreach (CoglAtlas *atlas,
+                    CoglAtlasForeachCallback callback,
+                    void *user_data);
 
-CoglAtlas *
-_cogl_atlas_new (CoglPixelFormat texture_format,
-                 CoglAtlasFlags flags,
-                 CoglAtlasUpdatePositionCallback update_position_cb);
+int
+cogl_atlas_get_n_allocations (CoglAtlas *atlas);
 
-CoglBool
-_cogl_atlas_reserve_space (CoglAtlas             *atlas,
-                           unsigned int           width,
-                           unsigned int           height,
-                           void                  *user_data);
+typedef struct _CoglClosure CoglAtlasReorganizeClosure;
 
-void
-_cogl_atlas_remove (CoglAtlas *atlas,
-                    const CoglRectangleMapEntry *rectangle);
+typedef void (*CoglAtlasReorganizeCallback) (CoglAtlas *atlas,
+                                             void *user_data);
 
-CoglTexture *
-_cogl_atlas_copy_rectangle (CoglAtlas *atlas,
-                            int x,
-                            int y,
-                            int width,
-                            int height,
-                            CoglPixelFormat format);
+CoglAtlasReorganizeClosure *
+cogl_atlas_add_pre_reorganize_callback (CoglAtlas *atlas,
+                                        CoglAtlasReorganizeCallback callback,
+                                        void *user_data,
+                                        CoglUserDataDestroyCallback destroy);
 
 void
-_cogl_atlas_add_reorganize_callback (CoglAtlas            *atlas,
-                                     GHookFunc             pre_callback,
-                                     GHookFunc             post_callback,
-                                     void                 *user_data);
+cogl_atlas_remove_pre_reorganize_callback (CoglAtlas *atlas,
+                                           CoglAtlasReorganizeClosure *closure);
+
+CoglAtlasReorganizeClosure *
+cogl_atlas_add_post_reorganize_callback (CoglAtlas *atlas,
+                                         CoglAtlasReorganizeCallback callback,
+                                         void *user_data,
+                                         CoglUserDataDestroyCallback destroy);
 
 void
-_cogl_atlas_remove_reorganize_callback (CoglAtlas            *atlas,
-                                        GHookFunc             pre_callback,
-                                        GHookFunc             post_callback,
-                                        void                 *user_data);
+cogl_atlas_remove_post_reorganize_callback (CoglAtlas *atlas,
+                                            CoglAtlasReorganizeClosure *closure);
 
 CoglBool
-_cogl_is_atlas (void *object);
+cogl_is_atlas (void *object);
 
-#endif /* __COGL_ATLAS_H */
+#endif /* _COGL_ATLAS_H_ */
diff --git a/cogl/cogl-context-private.h b/cogl/cogl-context-private.h
index c5a125d..cc3cf9a 100644
--- a/cogl/cogl-context-private.h
+++ b/cogl/cogl-context-private.h
@@ -45,7 +45,7 @@
 #include "cogl-pipeline-private.h"
 #include "cogl-buffer-private.h"
 #include "cogl-bitmask.h"
-#include "cogl-atlas.h"
+#include "cogl-atlas-set.h"
 #include "cogl-driver.h"
 #include "cogl-texture-driver.h"
 #include "cogl-pipeline-cache.h"
@@ -212,8 +212,7 @@ struct _CoglContext
   CoglPipeline     *texture_download_pipeline;
   CoglPipeline     *blit_texture_pipeline;
 
-  GSList           *atlases;
-  GHookList         atlas_reorganize_callbacks;
+  CoglAtlasSet     *atlas_set;
 
   /* This debugging variable is used to pick a colour for visually
      displaying the quad batches. It needs to be global so that it can
@@ -360,4 +359,7 @@ _cogl_context_get_gl_extensions (CoglContext *context);
 const char *
 _cogl_context_get_gl_version (CoglContext *context);
 
+CoglAtlasSet *
+_cogl_get_atlas_set (CoglContext *context);
+
 #endif /* __COGL_CONTEXT_PRIVATE_H */
diff --git a/cogl/cogl-context.c b/cogl/cogl-context.c
index f2d1696..1703c19 100644
--- a/cogl/cogl-context.c
+++ b/cogl/cogl-context.c
@@ -28,9 +28,7 @@
  *
  */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
+#include <config.h>
 
 #include "cogl-object.h"
 #include "cogl-private.h"
@@ -47,6 +45,8 @@
 #include "cogl-texture-2d-private.h"
 #include "cogl-texture-3d-private.h"
 #include "cogl-texture-rectangle-private.h"
+#include "cogl-atlas-set.h"
+#include "cogl-atlas-texture-private.h"
 #include "cogl-pipeline-private.h"
 #include "cogl-pipeline-opengl-private.h"
 #include "cogl-framebuffer-private.h"
@@ -432,9 +432,6 @@ cogl_context_new (CoglDisplay *display,
 
   cogl_object_unref (white_pixel_bitmap);
 
-  context->atlases = NULL;
-  g_hook_list_init (&context->atlas_reorganize_callbacks, sizeof (GHook));
-
   context->buffer_map_fallback_array = g_byte_array_new ();
   context->buffer_map_fallback_in_use = FALSE;
 
@@ -451,6 +448,12 @@ cogl_context_new (CoglDisplay *display,
 
   _cogl_list_init (&context->fences);
 
+  context->atlas_set = cogl_atlas_set_new (context);
+  cogl_atlas_set_add_atlas_callback (context->atlas_set,
+                                     _cogl_atlas_texture_atlas_event_handler,
+                                     NULL, /* user data */
+                                     NULL); /* destroy */
+
   return context;
 }
 
@@ -461,6 +464,9 @@ _cogl_context_free (CoglContext *context)
 
   winsys->context_deinit (context);
 
+  if (context->atlas_set)
+    cogl_object_unref (context->atlas_set);
+
   if (context->default_gl_texture_2d_tex)
     cogl_object_unref (context->default_gl_texture_2d_tex);
   if (context->default_gl_texture_3d_tex)
@@ -499,9 +505,6 @@ _cogl_context_free (CoglContext *context)
   if (context->current_clip_stack_valid)
     _cogl_clip_stack_unref (context->current_clip_stack);
 
-  g_slist_free (context->atlases);
-  g_hook_list_clear (&context->atlas_reorganize_callbacks);
-
   _cogl_bitmask_destroy (&context->enabled_builtin_attributes);
   _cogl_bitmask_destroy (&context->enable_builtin_attributes_tmp);
   _cogl_bitmask_destroy (&context->enabled_texcoord_attributes);
@@ -711,3 +714,9 @@ cogl_get_clock_time (CoglContext *context)
   else
     return 0;
 }
+
+CoglAtlasSet *
+_cogl_get_atlas_set (CoglContext *context)
+{
+  return context->atlas_set;
+}
diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h
index 878fb5f..b7195c2 100644
--- a/cogl/cogl-texture-private.h
+++ b/cogl/cogl-texture-private.h
@@ -279,9 +279,9 @@ void
 _cogl_texture_ensure_non_quad_rendering (CoglTexture *texture);
 
 /*
- * This determines a CoglPixelFormat according to texture::components
- * and texture::premultiplied (i.e. the user required components and
- * whether the texture should be considered premultiplied)
+ * This determines a CoglPixelFormat according to @components and
+ * @premultiplied (i.e. the user required components and whether the
+ * texture should be considered premultiplied)
  *
  * A reference/source format can be given (or COGL_PIXEL_FORMAT_ANY)
  * and wherever possible this function tries to simply return the
@@ -291,6 +291,15 @@ _cogl_texture_ensure_non_quad_rendering (CoglTexture *texture);
  * how to convert a source image in preparation for uploading.
  */
 CoglPixelFormat
+_cogl_texture_derive_format (CoglContext *ctx,
+                             CoglPixelFormat src_format,
+                             CoglTextureComponents components,
+                             CoglBool premultiplied);
+
+/* This is a thin wrapper around _cogl_texture_derive_format
+ * that simply passes texture->context, texture->components and
+ * texture->premultiplied in as arguments */
+CoglPixelFormat
 _cogl_texture_determine_internal_format (CoglTexture *texture,
                                          CoglPixelFormat src_format);
 
diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c
index 8378ec6..38f23c8 100644
--- a/cogl/cogl-texture.c
+++ b/cogl/cogl-texture.c
@@ -1340,18 +1340,18 @@ _cogl_texture_set_internal_format (CoglTexture *texture,
 }
 
 CoglPixelFormat
-_cogl_texture_determine_internal_format (CoglTexture *texture,
-                                         CoglPixelFormat src_format)
+_cogl_texture_derive_format (CoglContext *ctx,
+                             CoglPixelFormat src_format,
+                             CoglTextureComponents components,
+                             CoglBool premultiplied)
 {
-  switch (texture->components)
+  switch (components)
     {
     case COGL_TEXTURE_COMPONENTS_DEPTH:
       if (src_format & COGL_DEPTH_BIT)
         return src_format;
       else
         {
-          CoglContext *ctx = texture->context;
-
           if (_cogl_has_private_feature (ctx,
                   COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL) ||
               _cogl_has_private_feature (ctx,
@@ -1382,7 +1382,7 @@ _cogl_texture_determine_internal_format (CoglTexture *texture,
         else
           format = COGL_PIXEL_FORMAT_RGBA_8888;
 
-        if (texture->premultiplied)
+        if (premultiplied)
           {
             if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (format))
               return format |= COGL_PREMULT_BIT;
@@ -1397,6 +1397,16 @@ _cogl_texture_determine_internal_format (CoglTexture *texture,
   g_return_val_if_reached (COGL_PIXEL_FORMAT_RGBA_8888_PRE);
 }
 
+CoglPixelFormat
+_cogl_texture_determine_internal_format (CoglTexture *texture,
+                                         CoglPixelFormat src_format)
+{
+  return _cogl_texture_derive_format (texture->context,
+                                      src_format,
+                                      texture->components,
+                                      texture->premultiplied);
+}
+
 void
 cogl_texture_set_components (CoglTexture *texture,
                              CoglTextureComponents components)
diff --git a/cogl/cogl.h b/cogl/cogl.h
index f3f65b2..b98b301 100644
--- a/cogl/cogl.h
+++ b/cogl/cogl.h
@@ -75,6 +75,8 @@
 #include <cogl/cogl-texture-3d.h>
 #include <cogl/cogl-texture-2d-sliced.h>
 #include <cogl/cogl-sub-texture.h>
+#include <cogl/cogl-atlas-set.h>
+#include <cogl/cogl-atlas.h>
 #include <cogl/cogl-atlas-texture.h>
 #include <cogl/cogl-meta-texture.h>
 #include <cogl/cogl-primitive-texture.h>
diff --git a/cogl/winsys/cogl-winsys-egl.c b/cogl/winsys/cogl-winsys-egl.c
index e336bfb..ba97a5e 100644
--- a/cogl/winsys/cogl-winsys-egl.c
+++ b/cogl/winsys/cogl-winsys-egl.c
@@ -811,7 +811,7 @@ _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen,
                                  COGL_FRAMEBUFFER (onscreen),
                                  COGL_FRAMEBUFFER_STATE_BIND);
 
-  if (n_rectangles && egl_renderer->pf_eglSwapBuffersWithDamage)
+  if (egl_renderer->pf_eglSwapBuffersWithDamage)
     {
       CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen);
       size_t size = n_rectangles * sizeof (int) * 4;
diff --git a/examples/cogl-crate.c b/examples/cogl-crate.c
index 570c196..47f9852 100644
--- a/examples/cogl-crate.c
+++ b/examples/cogl-crate.c
@@ -284,8 +284,17 @@ main (int argc, char **argv)
 
       if (data.swap_ready)
         {
+          static gboolean swapped = FALSE;
+          int rect[4] = { 0, 0, 320, 240 };
+
           paint (&data);
-          cogl_onscreen_swap_buffers (COGL_ONSCREEN (fb));
+          if (!swapped)
+            {
+              cogl_onscreen_swap_buffers (COGL_ONSCREEN (fb));
+              swapped = TRUE;
+            }
+          else
+            cogl_onscreen_swap_buffers_with_damage (COGL_ONSCREEN (fb), rect, 1);
         }
 
       cogl_poll_renderer_get_info (cogl_context_get_renderer (ctx),



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]