[cogl/wip/rib/master-next: 26/44] meta-texture: This publicly exposes CoglMetaTexture



commit b65992fda2f72aa1c0ee1d6feb7c6b996c031c90
Author: Robert Bragg <robert linux intel com>
Date:   Sat Oct 8 14:13:03 2011 +0100

    meta-texture: This publicly exposes CoglMetaTexture
    
    CoglMetaTexture is an interface for dealing with high level textures
    that may be comprised of one or more low-level textures internally. The
    interface allows the development of primitive drawing APIs that can draw
    with high-level textures (such as atlas textures) even though the
    GPU doesn't natively understand these texture types.
    
    There is currently just one function that's part of this interface:
    cogl_meta_texture_foreach_in_region() which allows an application to
    resolve the internal, low-level textures of a high-level texture.
    cogl_rectangle() uses this API for example so that it can easily emulate
    the _REPEAT wrap mode for textures that the hardware can't natively
    handle repeating of.

 cogl-pango/cogl-pango-render.c        |   16 +-
 cogl/Makefile.am                      |    2 +
 cogl/cogl-atlas-texture.c             |   18 +-
 cogl/cogl-meta-texture.c              |  391 +++++++++++++++++++++++++++++++++
 cogl/cogl-meta-texture.h              |  190 ++++++++++++++++
 cogl/cogl-primitives.c                |   74 ++++---
 cogl/cogl-spans.c                     |   24 ++-
 cogl/cogl-spans.h                     |   37 ++--
 cogl/cogl-sub-texture.c               |  220 ++++++++-----------
 cogl/cogl-texture-2d-sliced.c         |  172 ++++++---------
 cogl/cogl-texture-2d.c                |   71 +------
 cogl/cogl-texture-3d.c                |   76 +-------
 cogl/cogl-texture-private.h           |   45 ++---
 cogl/cogl-texture-rectangle-private.h |    3 +
 cogl/cogl-texture-rectangle.c         |   81 +-------
 cogl/cogl-texture.c                   |  264 +++++++++++-----------
 cogl/cogl.h                           |    1 +
 cogl/winsys/cogl-texture-pixmap-x11.c |   81 ++++++-
 18 files changed, 1071 insertions(+), 695 deletions(-)
---
diff --git a/cogl-pango/cogl-pango-render.c b/cogl-pango/cogl-pango-render.c
index ff974d6..3f87b52 100644
--- a/cogl-pango/cogl-pango-render.c
+++ b/cogl-pango/cogl-pango-render.c
@@ -140,13 +140,15 @@ cogl_pango_renderer_draw_glyph (CoglPangoRenderer        *priv,
      the neighbouring glyphs are coming from the same atlas and bundle
      them together into a single VBO */
 
-  _cogl_texture_foreach_sub_texture_in_region (cache_value->texture,
-                                               cache_value->tx1,
-                                               cache_value->ty1,
-                                               cache_value->tx2,
-                                               cache_value->ty2,
-                                               cogl_pango_renderer_slice_cb,
-                                               &data);
+  cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (cache_value->texture),
+                                       cache_value->tx1,
+                                       cache_value->ty1,
+                                       cache_value->tx2,
+                                       cache_value->ty2,
+                                       COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                       COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                       cogl_pango_renderer_slice_cb,
+                                       &data);
 }
 
 static void cogl_pango_renderer_finalize (GObject *object);
diff --git a/cogl/Makefile.am b/cogl/Makefile.am
index 3135afd..7e3e943 100644
--- a/cogl/Makefile.am
+++ b/cogl/Makefile.am
@@ -78,6 +78,7 @@ cogl_public_h = \
 	$(srcdir)/cogl-texture-3d.h             \
 	$(srcdir)/cogl-texture-2d.h             \
 	$(srcdir)/cogl-texture-2d-sliced.h      \
+	$(srcdir)/cogl-meta-texture.h		\
 	$(srcdir)/cogl-types.h 			\
 	$(srcdir)/cogl-vertex-buffer.h 		\
 	$(srcdir)/cogl-index-buffer.h 		\
@@ -295,6 +296,7 @@ cogl_sources_c = \
 	$(srcdir)/cogl-atlas.c                          \
 	$(srcdir)/cogl-atlas-texture-private.h          \
 	$(srcdir)/cogl-atlas-texture.c                  \
+	$(srcdir)/cogl-meta-texture.c			\
 	$(srcdir)/cogl-blit.h				\
 	$(srcdir)/cogl-blit.c				\
 	$(srcdir)/cogl-spans.h				\
diff --git a/cogl/cogl-atlas-texture.c b/cogl/cogl-atlas-texture.c
index 3fc459b..5b6b2ff 100644
--- a/cogl/cogl-atlas-texture.c
+++ b/cogl/cogl-atlas-texture.c
@@ -225,19 +225,21 @@ _cogl_atlas_texture_foreach_sub_texture_in_region (
                                        float virtual_ty_1,
                                        float virtual_tx_2,
                                        float virtual_ty_2,
-                                       CoglTextureSliceCallback callback,
+                                       CoglMetaTextureCallback callback,
                                        void *user_data)
 {
   CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex);
 
   /* Forward on to the sub texture */
-  _cogl_texture_foreach_sub_texture_in_region (atlas_tex->sub_texture,
-                                               virtual_tx_1,
-                                               virtual_ty_1,
-                                               virtual_tx_2,
-                                               virtual_ty_2,
-                                               callback,
-                                               user_data);
+  cogl_meta_texture_foreach_in_region (atlas_tex->sub_texture,
+                                       virtual_tx_1,
+                                       virtual_ty_1,
+                                       virtual_tx_2,
+                                       virtual_ty_2,
+                                       COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                       COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                       callback,
+                                       user_data);
 }
 
 static void
diff --git a/cogl/cogl-meta-texture.c b/cogl/cogl-meta-texture.c
new file mode 100644
index 0000000..7fc21bf
--- /dev/null
+++ b/cogl/cogl-meta-texture.c
@@ -0,0 +1,391 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2011 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/>.
+ *
+ *
+ * Authors:
+ *  Robert Bragg   <robert linux intel com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "cogl-texture.h"
+#include "cogl-matrix.h"
+#include "cogl-spans.h"
+#include "cogl-meta-texture.h"
+#include "cogl-texture-rectangle-private.h"
+
+#include <string.h>
+#include <math.h>
+
+typedef struct _ForeachData
+{
+  float meta_region_coords[4];
+  CoglPipelineWrapMode wrap_s;
+  CoglPipelineWrapMode wrap_t;
+  CoglMetaTextureCallback callback;
+  void *user_data;
+
+  int width;
+  int height;
+
+  CoglTexture *padded_textures[9];
+  const float *grid_slice_texture_coords;
+  float slice_offset_s;
+  float slice_offset_t;
+  float slice_range_s;
+  float slice_range_t;
+} ForeachData;
+
+static void
+padded_grid_repeat_cb (CoglTexture *slice_texture,
+                       const float *slice_texture_coords,
+                       const float *meta_coords,
+                       void *user_data)
+{
+  ForeachData *data;
+  float mapped_coords[4];
+
+  /* Ignore padding slices for the current grid */
+  if (!slice_texture)
+    return;
+
+  data = user_data;
+
+  /* NB: the slice_texture_coords[] we get here will always be
+   * normalized.
+   *
+   * We now need to map the normalized slice_texture_coords[] we have
+   * here back to the real slice coordinates we saved in the previous
+   * stage...
+   */
+  mapped_coords[0] =
+    (slice_texture_coords[0] + data->slice_offset_s) * data->slice_range_s;
+  mapped_coords[1] =
+    (slice_texture_coords[1] + data->slice_offset_t) * data->slice_range_t;
+  mapped_coords[2] =
+    (slice_texture_coords[2] + data->slice_offset_s) * data->slice_range_s;
+  mapped_coords[3] =
+    (slice_texture_coords[3] + data->slice_offset_t) * data->slice_range_t;
+
+  data->callback (slice_texture,
+                  mapped_coords, meta_coords, data->user_data);
+}
+
+static int
+setup_padded_spans (CoglSpan *spans,
+                    float start,
+                    float end,
+                    float range,
+                    int *real_index)
+{
+  int span_index = 0;
+
+  if (start > 0)
+    {
+      spans[0].start = 0;
+      spans[0].size = start;
+      spans[0].waste = 0;
+      span_index++;
+      spans[1].start = spans[0].size;
+    }
+  else
+    spans[span_index].start = 0;
+
+  spans[span_index].size = end - start;
+  spans[span_index].waste = 0;
+  *real_index = span_index;
+  span_index++;
+
+  if (end < range)
+    {
+      spans[span_index].start =
+        spans[span_index - 1].start + spans[span_index - 1].size;
+      spans[span_index].size = range - end;
+      spans[span_index].waste = 0;
+      span_index++;
+    }
+
+  return span_index;
+}
+
+/* This handles each sub-texture within the range [0,1] of our
+ * original meta texture and repeats each one separately across the
+ * users requested virtual texture coordinates.
+ *
+ * A notable advantage of this approach is that we will batch
+ * together callbacks corresponding to the same underlying slice
+ * together.
+ */
+void
+create_grid_and_repeat_cb (CoglTexture *slice_texture,
+                           const float *slice_texture_coords,
+                           const float *meta_coords,
+                           void *user_data)
+{
+  ForeachData *data = user_data;
+  CoglSpan x_spans[3];
+  int n_x_spans;
+  int x_real_index;
+  CoglSpan y_spans[3];
+  int n_y_spans;
+  int y_real_index;
+
+  /* NB: This callback is called for each slice of the meta-texture
+   * in the range [0,1].
+   *
+   * We define a "padded grid" for each slice of the meta-texture in
+   * the range [0,1]. The x axis and y axis grid lines are defined
+   * using CoglSpans.
+   *
+   * The padded grid maps over the meta-texture coordinates in the
+   * range [0,1] but only contains one valid cell that corresponds to
+   * current slice being iterated and all the surrounding cells just
+   * provide padding.
+   *
+   * Once we've defined our padded grid we then repeat that across
+   * the user's original region, calling their callback whenever
+   * we see our current slice - ignoring padding.
+   *
+   * NB: we can assume meta_coords[] are normalized at this point
+   * since TextureRectangles aren't iterated with this code-path.
+   *
+   * NB: spans are always defined using non-normalized coordinates
+   */
+  n_x_spans = setup_padded_spans (x_spans,
+                                  meta_coords[0] * data->width,
+                                  meta_coords[2] * data->width,
+                                  data->width,
+                                  &x_real_index);
+  n_y_spans = setup_padded_spans (y_spans,
+                                  meta_coords[1] * data->height,
+                                  meta_coords[3] * data->height,
+                                  data->height,
+                                  &y_real_index);
+
+  data->padded_textures[n_y_spans * y_real_index + x_real_index] =
+    slice_texture;
+
+  /* Our callback is going to be passed normalized slice texture
+   * coordinates, and we will need to map the range [0,1] to the real
+   * slice_texture_coords we have here... */
+  data->grid_slice_texture_coords = slice_texture_coords;
+  data->slice_range_s = fabs (data->grid_slice_texture_coords[2] -
+                              data->grid_slice_texture_coords[0]);
+  data->slice_range_t = fabs (data->grid_slice_texture_coords[3] -
+                              data->grid_slice_texture_coords[1]);
+  data->slice_offset_s = MIN (data->grid_slice_texture_coords[0],
+                              data->grid_slice_texture_coords[2]);
+  data->slice_offset_t = MIN (data->grid_slice_texture_coords[1],
+                              data->grid_slice_texture_coords[3]);
+
+  /* Now actually iterate the region the user originally requested
+   * using the current padded grid */
+  _cogl_texture_spans_foreach_in_region (x_spans,
+                                         n_x_spans,
+                                         y_spans,
+                                         n_y_spans,
+                                         data->padded_textures,
+                                         data->meta_region_coords,
+                                         data->width,
+                                         data->height,
+                                         data->wrap_s,
+                                         data->wrap_t,
+                                         padded_grid_repeat_cb,
+                                         data);
+
+  /* Clear the padded_textures ready for the next iteration */
+  data->padded_textures[n_y_spans * y_real_index + x_real_index] = NULL;
+}
+
+typedef struct _NormalizeData
+{
+  CoglMetaTextureCallback callback;
+  void *user_data;
+  float s_normalize_factor;
+  float t_normalize_factor;
+} NormalizeData;
+
+static void
+normalize_meta_coords_cb (CoglTexture *slice_texture,
+                          const float *slice_coords,
+                          const float *meta_coords,
+                          void *user_data)
+{
+  NormalizeData *data = user_data;
+  float normalized_meta_coords[4] = {
+      meta_coords[0] * data->s_normalize_factor,
+      meta_coords[1] * data->t_normalize_factor,
+      meta_coords[2] * data->s_normalize_factor,
+      meta_coords[3] * data->t_normalize_factor
+  };
+
+  data->callback (slice_texture,
+                  slice_coords, normalized_meta_coords,
+                  data->user_data);
+}
+
+typedef struct _UnNormalizeData
+{
+  CoglMetaTextureCallback callback;
+  void *user_data;
+  float width;
+  float height;
+} UnNormalizeData;
+
+static void
+un_normalize_slice_coords_cb (CoglTexture *slice_texture,
+                              const float *slice_coords,
+                              const float *meta_coords,
+                              void *user_data)
+{
+  UnNormalizeData *data = user_data;
+  float un_normalized_slice_coords[4] = {
+    slice_coords[0] * data->width,
+    slice_coords[1] * data->height,
+    slice_coords[2] * data->width,
+    slice_coords[3] * data->height
+  };
+
+  data->callback (slice_texture,
+                  un_normalized_slice_coords, meta_coords,
+                  data->user_data);
+}
+
+void
+cogl_meta_texture_foreach_in_region (CoglMetaTexture *meta_texture,
+                                     float tx_1,
+                                     float ty_1,
+                                     float tx_2,
+                                     float ty_2,
+                                     CoglPipelineWrapMode wrap_s,
+                                     CoglPipelineWrapMode wrap_t,
+                                     CoglMetaTextureCallback callback,
+                                     void *user_data)
+{
+  CoglTexture *texture = COGL_TEXTURE (meta_texture);
+  float width = cogl_texture_get_width (texture);
+  float height = cogl_texture_get_height (texture);
+  NormalizeData normalize_data;
+
+  /* It makes things simpler to deal with non-normalized region
+   * coordinates beyond this point and only re-normalize just before
+   * calling the user's callback... */
+
+  if (!_cogl_is_texture_rectangle (COGL_TEXTURE (meta_texture)))
+    {
+      normalize_data.callback = callback;
+      normalize_data.user_data = user_data;
+      normalize_data.s_normalize_factor = 1.0f / width;
+      normalize_data.t_normalize_factor = 1.0f / height;
+      callback = normalize_meta_coords_cb;
+      user_data = &normalize_data;
+      tx_1 *= width;
+      ty_1 *= height;
+      tx_2 *= width;
+      ty_2 *= height;
+    }
+
+  /* XXX: at some point this wont be routed through the CoglTexture
+   * vtable, instead there will be a separate CoglMetaTexture
+   * interface vtable. */
+
+  if (texture->vtable->foreach_sub_texture_in_region)
+    {
+      ForeachData data;
+
+      data.meta_region_coords[0] = tx_1;
+      data.meta_region_coords[1] = ty_1;
+      data.meta_region_coords[2] = tx_2;
+      data.meta_region_coords[3] = ty_2;
+      data.wrap_s = wrap_s;
+      data.wrap_t = wrap_t;
+      data.callback = callback;
+      data.user_data = user_data;
+
+      data.width = width;
+      data.height = height;
+
+      memset (data.padded_textures, 0, sizeof (data.padded_textures));
+
+      /*
+       * 1) We iterate all the slices of the meta-texture only within
+       *    the range [0,1].
+       *
+       * 2) We define a "padded grid" for each slice of the
+       *    meta-texture in the range [0,1].
+       *
+       *    The padded grid maps over the meta-texture coordinates in
+       *    the range [0,1] but only contains one valid cell that
+       *    corresponds to current slice being iterated and all the
+       *    surrounding cells just provide padding.
+       *
+       * 3) Once we've defined our padded grid we then repeat that
+       *    across the user's original region, calling their callback
+       *    whenever we see our current slice - ignoring padding.
+       *
+       * A notable benefit of this design is that repeating a texture
+       * made of multiple slices will result in us repeating each
+       * slice in-turn so the user gets repeat callbacks for the same
+       * texture batched together. For manual emulation of texture
+       * repeats done by drawing geometry this makes it more likely
+       * that we can batch geometry.
+       */
+
+      texture->vtable->foreach_sub_texture_in_region (texture,
+                                                      0, 0, 1, 1,
+                                                      create_grid_and_repeat_cb,
+                                                      &data);
+    }
+  else
+    {
+      CoglSpan x_span = { 0, width, 0 };
+      CoglSpan y_span = { 0, height, 0 };
+      float meta_region_coords[4] = { tx_1, ty_1, tx_2, ty_2 };
+      UnNormalizeData un_normalize_data;
+
+      /* If we are dealing with a CoglTextureRectangle then we need a shim
+       * callback that un_normalizes the slice coordinates we get from
+       * _cogl_texture_spans_foreach_in_region before passing them to
+       * the user's callback. */
+      if (_cogl_is_texture_rectangle (meta_texture))
+        {
+          un_normalize_data.callback = callback;
+          un_normalize_data.user_data = user_data;
+          un_normalize_data.width = width;
+          un_normalize_data.height = height;
+          callback = un_normalize_slice_coords_cb;
+          user_data = &un_normalize_data;
+        }
+
+      _cogl_texture_spans_foreach_in_region (&x_span, 1,
+                                             &y_span, 1,
+                                             &texture,
+                                             meta_region_coords,
+                                             width,
+                                             height,
+                                             wrap_s,
+                                             wrap_t,
+                                             callback,
+                                             user_data);
+    }
+}
diff --git a/cogl/cogl-meta-texture.h b/cogl/cogl-meta-texture.h
new file mode 100644
index 0000000..f78f1b3
--- /dev/null
+++ b/cogl/cogl-meta-texture.h
@@ -0,0 +1,190 @@
+/*
+ * Cogl
+ *
+ * An object oriented GL/GLES Abstraction/Utility Layer
+ *
+ * Copyright (C) 2011 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/>.
+ *
+ *
+ */
+
+#if !defined(__COGL_H_INSIDE__) && !defined(CLUTTER_COMPILATION)
+#error "Only <cogl/cogl.h> can be included directly."
+#endif
+
+#ifndef __COGL_META_TEXTURE_H__
+#define __COGL_META_TEXTURE_H__
+
+#include <cogl/cogl-pipeline-layer-state.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/**
+ * SECTION:cogl-meta-texture
+ * @short_description: Interface for high-level textures built from
+ *                     low-level textures like #CoglTexture2D and
+ *                     #CoglTexture3D.
+ *
+ * Cogl helps to make it easy to deal with high level textures such
+ * as #CoglAtlasTexture<!-- -->s, #CoglSubTexture<!-- -->s,
+ * #CoglTexturePixmapX11 textures and #CoglTexture2DSliced textures
+ * consistently.
+ *
+ * A #CoglMetaTexture is a texture that might internally be
+ * represented by one or more low-level #CoglTexture<!-- -->s
+ * such as #CoglTexture2D or #CoglTexture3D. These low-level textures
+ * are the only ones that a GPU really understands but because
+ * applications often want more high-level texture abstractions
+ * (such as storing multiple textures inside one larger "atlas"
+ * texture) it's desirable to be able to deal with these
+ * using a common interface.
+ *
+ * For example the GPU is not able to automatically handle repeating a
+ * texture that is part of a larger atlas texture but if you use
+ * %COGL_PIPELINE_WRAP_MODE_REPEAT with an atlas texture when drawing
+ * with cogl_rectangle() you should see that it "Just Worksâ" - at
+ * least if you don't use multi-texturing. The reason this works is
+ * because cogl_rectangle() internally understands the #CoglMetaTexture
+ * interface and is able to manually resolve the low-level textures
+ * using this interface and by making multiple draw calls it can
+ * emulate the texture repeat modes.
+ *
+ * Cogl doesn't aim to pretend that meta-textures are just like real
+ * textures because it would get extremely complex to try and emulate
+ * low-level GPU semantics transparently for these textures.  The low
+ * level drawing APIs of Cogl, such as cogl_draw_attributes() don't
+ * actually know anything about the #CoglMetaTexture interface and its
+ * the developer's responsibility to resolve all textures referenced by
+ * a #CoglPipeline to low-level textures before drawing.
+ *
+ * If you want to develop custom primitive APIs like cogl_rectangle()
+ * and you want to support drawing with #CoglAtlasTexture<!-- -->s
+ * or #CoglSubTexture<!-- -->s for example, then you will need to use
+ * this #CoglMetaTexture interface to be able to resolve high-level
+ * textures into low-level textures before drawing with Cogl's
+ * low-level drawing APIs such as cogl_draw_attributes().
+ *
+ * <note>Most developers won't need to use this interface directly
+ * but still it is worth understanding the distinction between
+ * low-level and meta textures because you may find other references
+ * in the documentation that detail limitations of using
+ * meta-textures.</note>
+ */
+
+typedef struct _CoglMetaTexture CoglMetaTexture;
+#define COGL_META_TEXTURE(X) ((CoglMetaTexture *)X)
+
+/**
+ * CoglMetaTextureCallback:
+ * @sub_texture: A low-level #CoglTexture making up part of a
+ *               #CoglMetaTexture.
+ * @sub_texture_coords: A float 4-tuple ordered like
+ *                      (tx1,ty1,tx2,ty2) defining what region of the
+ *                      current @sub_texture maps to a sub-region of a
+ *                      #CoglMetaTexture. (tx1,ty1) is the top-left
+ *                      sub-region coordinate and (tx2,ty2) is the
+ *                      bottom-right. These are low-level texture
+ *                      coordinates.
+ * @meta_coords: A float 4-tuple ordered like (tx1,ty1,tx2,ty2)
+ *               defining what sub-region of a #CoglMetaTexture this
+ *               low-level @sub_texture maps too. (tx1,ty1) is
+ *               the top-left sub-region coordinate and (tx2,ty2) is
+ *               the bottom-right. These are high-level meta-texture
+ *               coordinates.
+ * @user_data: A private pointer passed to
+ *             cogl_meta_texture_foreach_in_region().
+ *
+ * A callback used with cogl_meta_texture_foreach_in_region() to
+ * retrieve details of all the low-level #CoglTexture<!-- -->s that
+ * make up a given #CoglMetaTexture.
+ *
+ * Since: 1.10
+ * Stability: unstable
+ */
+typedef void (*CoglMetaTextureCallback) (CoglTexture *sub_texture,
+                                         const float *sub_texture_coords,
+                                         const float *meta_coords,
+                                         void *user_data);
+
+/**
+ * cogl_meta_texture_foreach_in_region:
+ * @meta_texture: An object implementing the #CoglMetaTexture
+ *                interface.
+ * @tx_1: The top-left x coordinate of the region to iterate
+ * @ty_1: The top-left y coordinate of the region to iterate
+ * @tx_2: The bottom-right x coordinate of the region to iterate
+ * @ty_2: The bottom-right y coordinate of the region to iterate
+ * @wrap_x: The wrap mode for the x-axis (Only
+ *          %COGL_PIPELINE_WRAP_MODE_REPEAT may be passed here.)
+ * @wrap_y: The wrap mode for the y-axis (Only
+ *          %COGL_PIPELINE_WRAP_MODE_REPEAT may be passed here.)
+ * @callback: A #CoglMetaTextureCallback pointer to be called
+ *            for each low-level texture within the specified region.
+ * @user_data: A private pointer that is passed to @callback.
+ *
+ * Allows you to manually iterate the low-level textures that define a
+ * given region of a high-level #CoglMetaTexture.
+ *
+ * For example cogl_texture_2d_sliced_new_with_size() can be used to
+ * create a meta texture that may slice a large image into multiple,
+ * smaller power-of-two sized textures. These high level textures are
+ * not directly understood by a GPU and so this API must be used to
+ * manually resolve the underlying textures for drawing.
+ *
+ * All high level textures (#CoglAtlasTexture, #CoglSubTexture,
+ * #CoglTexturePixmapX11, and #CoglTexture2DSliced) can be handled
+ * consistently using this interface which greately simplifies
+ * implementing primitives that support all texture types.
+ *
+ * For example if you use the cogl_rectangle() API then Cogl will
+ * internally use this API to resolve the low level textures of any
+ * meta textures you have associated with CoglPipeline layers.
+ *
+ * <note>The low level drawing APIs such as cogl_draw_attributes()
+ * don't understand the #CoglMetaTexture interface and so it is your
+ * responsibility to use this API to resolve all CoglPipeline
+ * textures into low-level textures before drawing.</note>
+ *
+ * For each low-level texture that makes up part of the given region
+ * of the @meta_texture, @callback is called specifying how the
+ * low-level texture maps to the original region.
+ *
+ * <note>Currently this interface can only be used to iterate in terms
+ * of %COGL_PIPELINE_WRAP_MODE_REPEAT) which means if you want to
+ * support %COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE for a custom
+ * primitive then you need to do that manually by using this interface
+ * to find edge textures and then stretching the edge texels out using
+ * geometry.</note>
+ *
+ * Since: 1.10
+ * Stability: unstable
+ */
+void
+cogl_meta_texture_foreach_in_region (CoglMetaTexture *meta_texture,
+                                     float tx_1,
+                                     float ty_1,
+                                     float tx_2,
+                                     float ty_2,
+                                     CoglPipelineWrapMode wrap_s,
+                                     CoglPipelineWrapMode wrap_t,
+                                     CoglMetaTextureCallback callback,
+                                     void *user_data);
+
+G_END_DECLS
+
+#endif /* __COGL_META_TEXTURE_H__ */
diff --git a/cogl/cogl-primitives.c b/cogl/cogl-primitives.c
index 9281306..27707bb 100644
--- a/cogl/cogl-primitives.c
+++ b/cogl/cogl-primitives.c
@@ -37,6 +37,7 @@
 #include "cogl-framebuffer-private.h"
 #include "cogl-attribute-private.h"
 #include "cogl-private.h"
+#include "cogl-meta-texture.h"
 
 #include <string.h>
 #include <math.h>
@@ -139,6 +140,8 @@ validate_first_layer_cb (CoglPipeline *pipeline,
   ValidateFirstLayerState *state = user_data;
   CoglPipelineWrapMode clamp_to_edge =
     COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE;
+  CoglPipelineWrapMode wrap_s;
+  CoglPipelineWrapMode wrap_t;
 
   /* We can't use hardware repeat so we need to set clamp to edge
    * otherwise it might pull in edge pixels from the other side. By
@@ -184,15 +187,16 @@ validate_first_layer_cb (CoglPipeline *pipeline,
  */
 /* TODO: support multitexturing */
 static void
-_cogl_texture_quad_multiple_primitives (CoglTexture  *texture,
+_cogl_texture_quad_multiple_primitives (CoglTexture *texture,
                                         CoglPipeline *pipeline,
-                                        gboolean      clamp_s,
-                                        gboolean      clamp_t,
-                                        const float  *position,
-                                        float         tx_1,
-                                        float         ty_1,
-                                        float         tx_2,
-                                        float         ty_2)
+                                        int layer_index,
+                                        gboolean check_clamp_s,
+                                        gboolean check_clamp_t,
+                                        const float *position,
+                                        float tx_1,
+                                        float ty_1,
+                                        float tx_2,
+                                        float ty_2)
 {
   TextureSlicedQuadState state;
   gboolean tex_virtual_flipped_x;
@@ -200,12 +204,17 @@ _cogl_texture_quad_multiple_primitives (CoglTexture  *texture,
   gboolean quad_flipped_x;
   gboolean quad_flipped_y;
   ValidateFirstLayerState validate_first_layer_state;
+  CoglPipelineWrapMode wrap_s, wrap_t;
 
   _COGL_GET_CONTEXT (ctx, NO_RETVAL);
 
+  wrap_s = cogl_pipeline_get_layer_wrap_mode_s (pipeline, layer_index);
+  wrap_t = cogl_pipeline_get_layer_wrap_mode_t (pipeline, layer_index);
+
   /* If the wrap mode is clamp to edge then we'll recursively draw the
      stretched part and replace the coordinates */
-  if (clamp_s && tx_1 != tx_2)
+  if (check_clamp_s &&
+      wrap_s == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE && tx_1 != tx_2)
     {
       float *replacement_position = g_newa (float, 4);
       float old_tx_1 = tx_1, old_tx_2 = tx_2;
@@ -225,7 +234,8 @@ _cogl_texture_quad_multiple_primitives (CoglTexture  *texture,
                (tx_1 - old_tx_1) / (old_tx_2 - old_tx_1)),
               position[3] };
           _cogl_texture_quad_multiple_primitives (texture, pipeline,
-                                                  FALSE, clamp_t,
+                                                  layer_index,
+                                                  FALSE, TRUE,
                                                   tmp_position,
                                                   tx_1, ty_1, tx_1, ty_2);
           replacement_position[0] = tmp_position[2];
@@ -240,7 +250,8 @@ _cogl_texture_quad_multiple_primitives (CoglTexture  *texture,
                (tx_2 - old_tx_1) / (old_tx_2 - old_tx_1)),
               position[1], position[2], position[3] };
           _cogl_texture_quad_multiple_primitives (texture, pipeline,
-                                                  FALSE, clamp_t,
+                                                  layer_index,
+                                                  FALSE, TRUE,
                                                   tmp_position,
                                                   tx_2, ty_1, tx_2, ty_2);
           replacement_position[2] = tmp_position[0];
@@ -253,7 +264,8 @@ _cogl_texture_quad_multiple_primitives (CoglTexture  *texture,
       position = replacement_position;
     }
 
-  if (clamp_t && ty_1 != ty_2)
+  if (check_clamp_t &&
+      wrap_t == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE && ty_1 != ty_2)
     {
       float *replacement_position = g_newa (float, 4);
       float old_ty_1 = ty_1, old_ty_2 = ty_2;
@@ -272,7 +284,8 @@ _cogl_texture_quad_multiple_primitives (CoglTexture  *texture,
                (position[3] - position[1]) *
                (ty_1 - old_ty_1) / (old_ty_2 - old_ty_1)) };
           _cogl_texture_quad_multiple_primitives (texture, pipeline,
-                                                  clamp_s, FALSE,
+                                                  layer_index,
+                                                  TRUE, FALSE,
                                                   tmp_position,
                                                   tx_1, ty_1, tx_2, ty_1);
           replacement_position[1] = tmp_position[3];
@@ -288,7 +301,8 @@ _cogl_texture_quad_multiple_primitives (CoglTexture  *texture,
                (ty_2 - old_ty_1) / (old_ty_2 - old_ty_1)),
               position[2], position[3] };
           _cogl_texture_quad_multiple_primitives (texture, pipeline,
-                                                  clamp_s, FALSE,
+                                                  layer_index,
+                                                  TRUE, FALSE,
                                                   tmp_position,
                                                   tx_1, ty_2, tx_2, ty_2);
           replacement_position[3] = tmp_position[1];
@@ -354,10 +368,23 @@ _cogl_texture_quad_multiple_primitives (CoglTexture  *texture,
   state.v_to_q_scale_x = fabs (state.quad_len_x / (tx_2 - tx_1));
   state.v_to_q_scale_y = fabs (state.quad_len_y / (ty_2 - ty_1));
 
-  _cogl_texture_foreach_sub_texture_in_region (texture,
-                                               tx_1, ty_1, tx_2, ty_2,
-                                               log_quad_sub_textures_cb,
-                                               &state);
+  /* cogl_meta_texture_foreach_in_region only allows WRAP_MODE_REPEAT.
+   * If CLAMP_TO_EDGE is in use then we have already dealt with
+   * emulation for that and we can just pass WRAP_MODE_REPEAT here...
+   */
+  if (wrap_s == COGL_PIPELINE_WRAP_MODE_AUTOMATIC ||
+      wrap_s == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE)
+    wrap_s = COGL_PIPELINE_WRAP_MODE_REPEAT;
+  if (wrap_t == COGL_PIPELINE_WRAP_MODE_AUTOMATIC ||
+      wrap_t == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE)
+    wrap_t = COGL_PIPELINE_WRAP_MODE_REPEAT;
+
+  cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (texture),
+                                       tx_1, ty_1, tx_2, ty_2,
+                                       wrap_s,
+                                       wrap_t,
+                                       log_quad_sub_textures_cb,
+                                       &state);
 
   if (validate_first_layer_state.override_pipeline)
     cogl_object_unref (validate_first_layer_state.override_pipeline);
@@ -760,7 +787,6 @@ _cogl_rectangles_with_multitexture_coords (
       CoglTexture *texture;
       const float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0};
       const float *tex_coords;
-      gboolean clamp_s, clamp_t;
 
       if (!state.all_use_sliced_quad_fallback)
         {
@@ -788,18 +814,12 @@ _cogl_rectangles_with_multitexture_coords (
       else
         tex_coords = default_tex_coords;
 
-      clamp_s = (cogl_pipeline_get_layer_wrap_mode_s (pipeline,
-                                                      state.first_layer) ==
-                 COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
-      clamp_t = (cogl_pipeline_get_layer_wrap_mode_t (pipeline,
-                                                      state.first_layer) ==
-                 COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE);
-
       COGL_NOTE (DRAW, "Drawing Tex Quad (Multi-Prim Mode)");
 
       _cogl_texture_quad_multiple_primitives (texture,
                                               pipeline,
-                                              clamp_s, clamp_t,
+                                              state.first_layer,
+                                              TRUE, TRUE,
                                               rects[i].position,
                                               tex_coords[0],
                                               tex_coords[1],
diff --git a/cogl/cogl-spans.c b/cogl/cogl-spans.c
index d627930..3d9d3e5 100644
--- a/cogl/cogl-spans.c
+++ b/cogl/cogl-spans.c
@@ -35,7 +35,7 @@ void
 _cogl_span_iter_update (CoglSpanIter *iter)
 {
   /* Pick current span */
-  iter->span = &g_array_index (iter->array, CoglSpan, iter->index);
+  iter->span = &iter->spans[iter->index];
 
   /* Offset next position by span size */
   iter->next_pos = iter->pos + iter->span->size - iter->span->waste;
@@ -66,17 +66,24 @@ _cogl_span_iter_update (CoglSpanIter *iter)
 
 void
 _cogl_span_iter_begin (CoglSpanIter *iter,
-                       GArray       *spans,
-                       float         normalize_factor,
-                       float         cover_start,
-                       float         cover_end)
+                       const CoglSpan *spans,
+                       int n_spans,
+                       float normalize_factor,
+                       float cover_start,
+                       float cover_end,
+                       CoglPipelineWrapMode wrap_mode)
 {
   float cover_start_normalized;
 
+  /* XXX: If CLAMP_TO_EDGE needs to be emulated then it needs to be
+   * done at a higher level than here... */
+  g_return_if_fail (wrap_mode == COGL_PIPELINE_WRAP_MODE_REPEAT);
+
   iter->index = 0;
   iter->span = NULL;
 
-  iter->array = spans;
+  iter->spans = spans;
+  iter->n_spans = n_spans;
 
   /* We always iterate in a positive direction from the origin. If
    * iter->flipped == TRUE that means whoever is using this API should
@@ -100,6 +107,8 @@ _cogl_span_iter_begin (CoglSpanIter *iter,
   cover_start_normalized = cover_start / normalize_factor;
   iter->origin = floorf (cover_start_normalized) * normalize_factor;
 
+  iter->wrap_mode = wrap_mode;
+
   iter->cover_start = cover_start;
   iter->cover_end = cover_end;
   iter->pos = iter->origin;
@@ -117,8 +126,7 @@ _cogl_span_iter_next (CoglSpanIter *iter)
   /* Move current position */
   iter->pos = iter->next_pos;
 
-  /* Pick next slice (wrap when last reached) */
-  iter->index = (iter->index + 1) % iter->array->len;
+  iter->index = (iter->index + 1) % iter->n_spans;
 
   /* Update intersection */
   _cogl_span_iter_update (iter);
diff --git a/cogl/cogl-spans.h b/cogl/cogl-spans.h
index c3b77c0..e328b72 100644
--- a/cogl/cogl-spans.h
+++ b/cogl/cogl-spans.h
@@ -25,6 +25,7 @@
 #define __COGL_SPANS_PRIVATE_H
 
 #include "cogl-handle.h"
+#include "cogl-pipeline-layer-state.h"
 
 typedef struct _CoglSpan
 {
@@ -35,18 +36,20 @@ typedef struct _CoglSpan
 
 typedef struct _CoglSpanIter
 {
-  int       index;
-  GArray   *array;
-  CoglSpan *span;
-  float     pos;
-  float     next_pos;
-  float     origin;
-  float     cover_start;
-  float     cover_end;
-  float     intersect_start;
-  float     intersect_end;
-  gboolean  intersects;
-  gboolean  flipped;
+  int index;
+  const CoglSpan *spans;
+  int n_spans;
+  const CoglSpan *span;
+  float pos;
+  float next_pos;
+  float origin;
+  float cover_start;
+  float cover_end;
+  float intersect_start;
+  float intersect_end;
+  gboolean intersects;
+  gboolean flipped;
+  CoglPipelineWrapMode wrap_mode;
 } CoglSpanIter;
 
 void
@@ -54,10 +57,12 @@ _cogl_span_iter_update (CoglSpanIter *iter);
 
 void
 _cogl_span_iter_begin (CoglSpanIter *iter,
-                       GArray       *spans,
-                       float         normalize_factor,
-                       float         cover_start,
-                       float         cover_end);
+                       const CoglSpan *spans,
+                       int n_spans,
+                       float normalize_factor,
+                       float cover_start,
+                       float cover_end,
+                       CoglPipelineWrapMode wrap_mode);
 
 void
 _cogl_span_iter_next (CoglSpanIter *iter);
diff --git a/cogl/cogl-sub-texture.c b/cogl/cogl-sub-texture.c
index 331eecc..15c278f 100644
--- a/cogl/cogl-sub-texture.c
+++ b/cogl/cogl-sub-texture.c
@@ -36,6 +36,7 @@
 #include "cogl-context-private.h"
 #include "cogl-handle.h"
 #include "cogl-texture-driver.h"
+#include "cogl-texture-rectangle-private.h"
 
 #include <string.h>
 #include <math.h>
@@ -47,155 +48,88 @@ COGL_TEXTURE_INTERNAL_DEFINE (SubTexture, sub_texture);
 static const CoglTextureVtable cogl_sub_texture_vtable;
 
 static void
-_cogl_sub_texture_map_range (float *t1, float *t2,
-                             int sub_offset,
-                             int sub_size,
-                             int full_size)
+_cogl_sub_texture_unmap_quad (CoglSubTexture *sub_tex,
+                              float *coords)
 {
-  float t1_frac, t1_int, t2_frac, t2_int;
-
-  t1_frac = modff (*t1, &t1_int);
-  t2_frac = modff (*t2, &t2_int);
-
-  if (t1_frac < 0.0f)
+  /* NB: coords[] come in as non-normalized if sub_tex->full_texture
+   * is a CoglTextureRectangle otherwhise they are normalized. The
+   * coordinates we write out though must always be normalized.
+   *
+   * NB: sub_tex->sub_x/y/width/height are in non-normalized
+   * coordinates.
+   */
+  if (_cogl_is_texture_rectangle (sub_tex->full_texture))
     {
-      t1_frac += 1.0f;
-      t1_int -= 1.0f;
-    }
-  if (t2_frac < 0.0f)
-    {
-      t2_frac += 1.0f;
-      t2_int -= 1.0f;
-    }
-
-  /* If one of the coordinates is zero we need to make sure it is
-     still greater than the other coordinate if it was originally so
-     we'll flip it to the other side  */
-  if (*t1 < *t2)
-    {
-      if (t2_frac == 0.0f)
-        {
-          t2_frac = 1.0f;
-          t2_int -= 1.0f;
-        }
+      coords[0] = (coords[0] - sub_tex->sub_x) / sub_tex->sub_width;
+      coords[1] = (coords[1] - sub_tex->sub_y) / sub_tex->sub_height;
+      coords[2] = (coords[2] - sub_tex->sub_x) / sub_tex->sub_width;
+      coords[3] = (coords[3] - sub_tex->sub_y) / sub_tex->sub_height;
     }
   else
     {
-      if (t1_frac == 0.0f)
-        {
-          t1_frac = 1.0f;
-          t1_int -= 1.0f;
-        }
+      float width = cogl_texture_get_width (sub_tex->full_texture);
+      float height = cogl_texture_get_height (sub_tex->full_texture);
+      coords[0] = (coords[0] * width - sub_tex->sub_x) / sub_tex->sub_width;
+      coords[1] = (coords[1] * height - sub_tex->sub_y) / sub_tex->sub_height;
+      coords[2] = (coords[2] * width - sub_tex->sub_x) / sub_tex->sub_width;
+      coords[3] = (coords[3] * height - sub_tex->sub_y) / sub_tex->sub_height;
     }
-
-  /* Convert the fractional part leaving the integer part intact */
-  t1_frac = (sub_offset + t1_frac * sub_size) / full_size;
-  *t1 = t1_frac + t1_int;
-
-  t2_frac = (sub_offset + t2_frac * sub_size) / full_size;
-  *t2 = t2_frac + t2_int;
 }
 
 static void
 _cogl_sub_texture_map_quad (CoglSubTexture *sub_tex,
                             float *coords)
 {
-  unsigned int full_width = cogl_texture_get_width (sub_tex->full_texture);
-  unsigned int full_height = cogl_texture_get_height (sub_tex->full_texture);
-
-  _cogl_sub_texture_map_range (coords + 0, coords + 2,
-                               sub_tex->sub_x, sub_tex->sub_width,
-                               full_width);
-  _cogl_sub_texture_map_range (coords + 1, coords + 3,
-                               sub_tex->sub_y, sub_tex->sub_height,
-                               full_height);
-}
-
-/* Maps from the texture coordinates of the full texture to the
-   texture coordinates of the sub texture */
-static float
-_cogl_sub_texture_unmap_coord (float t,
-                               int sub_offset,
-                               int sub_size,
-                               int full_size)
-{
-  float frac_part, int_part;
-
-  /* Convert the fractional part leaving the integer part in tact */
-  frac_part = modff (t, &int_part);
-
-  if (cogl_util_float_signbit (frac_part))
-    frac_part = ((1.0f + frac_part) * full_size -
-                 sub_offset - sub_size) / sub_size;
+  /* NB: coords[] always come in as normalized coordinates but may go
+   * out as non-normalized if sub_tex->full_texture is a
+   * CoglTextureRectangle.
+   *
+   * NB: sub_tex->sub_x/y/width/height are in non-normalized
+   * coordinates.
+   */
+
+  if (_cogl_is_texture_rectangle (sub_tex->full_texture))
+    {
+      coords[0] = coords[0] * sub_tex->sub_width + sub_tex->sub_x;
+      coords[1] = coords[1] * sub_tex->sub_height + sub_tex->sub_y;
+      coords[2] = coords[2] * sub_tex->sub_width + sub_tex->sub_x;
+      coords[3] = coords[3] * sub_tex->sub_height + sub_tex->sub_y;
+    }
   else
-    frac_part = (frac_part * full_size - sub_offset) / sub_size;
-
-  return frac_part + int_part;
-}
-
-static void
-_cogl_sub_texture_unmap_coords (CoglSubTexture *sub_tex,
-                                float *s,
-                                float *t)
-{
-  unsigned int full_width = cogl_texture_get_width (sub_tex->full_texture);
-  unsigned int full_height = cogl_texture_get_height (sub_tex->full_texture);
-
-  *s = _cogl_sub_texture_unmap_coord (*s, sub_tex->sub_x, sub_tex->sub_width,
-                                      full_width);
-  *t = _cogl_sub_texture_unmap_coord (*t, sub_tex->sub_y, sub_tex->sub_height,
-                                      full_height);
+    {
+      float width = cogl_texture_get_width (sub_tex->full_texture);
+      float height = cogl_texture_get_height (sub_tex->full_texture);
+      coords[0] = (coords[0] * sub_tex->sub_width + sub_tex->sub_x) / width;
+      coords[1] = (coords[1] * sub_tex->sub_height + sub_tex->sub_y) / height;
+      coords[2] = (coords[2] * sub_tex->sub_width + sub_tex->sub_x) / width;
+      coords[3] = (coords[3] * sub_tex->sub_height + sub_tex->sub_y) / height;
+    }
 }
 
 typedef struct _CoglSubTextureForeachData
 {
   CoglSubTexture *sub_tex;
-  CoglTextureSliceCallback callback;
+  CoglMetaTextureCallback callback;
   void *user_data;
 } CoglSubTextureForeachData;
 
 static void
-_cogl_sub_texture_foreach_cb (CoglTexture *texture,
-                              const float *slice_coords,
-                              const float *full_virtual_coords,
-                              void *user_data)
-{
-  CoglSubTextureForeachData *data = user_data;
-  float virtual_coords[4];
-
-  memcpy (virtual_coords, full_virtual_coords, sizeof (virtual_coords));
-  /* Convert the virtual coords from the full-texture space to the sub
-     texture space */
-  _cogl_sub_texture_unmap_coords (data->sub_tex,
-                                  &virtual_coords[0],
-                                  &virtual_coords[1]);
-  _cogl_sub_texture_unmap_coords (data->sub_tex,
-                                  &virtual_coords[2],
-                                  &virtual_coords[3]);
-
-  data->callback (texture,
-                  slice_coords, virtual_coords,
-                  data->user_data);
-}
-
-static void
-_cogl_sub_texture_manual_repeat_cb (const float *coords,
-                                    void *user_data)
+unmap_coords_cb (CoglTexture *slice_texture,
+                 const float *slice_texture_coords,
+                 const float *meta_coords,
+                 void *user_data)
 {
   CoglSubTextureForeachData *data = user_data;
-  float mapped_coords[4];
+  float unmapped_coords[4];
 
-  memcpy (mapped_coords, coords, sizeof (mapped_coords));
+  memcpy (unmapped_coords, meta_coords, sizeof (unmapped_coords));
 
-  _cogl_sub_texture_map_quad (data->sub_tex, mapped_coords);
+  _cogl_sub_texture_unmap_quad (data->sub_tex, unmapped_coords);
 
-  _cogl_texture_foreach_sub_texture_in_region (data->sub_tex->full_texture,
-                                               mapped_coords[0],
-                                               mapped_coords[1],
-                                               mapped_coords[2],
-                                               mapped_coords[3],
-                                               _cogl_sub_texture_foreach_cb,
-                                               user_data);
+  data->callback (slice_texture,
+                  slice_texture_coords,
+                  unmapped_coords,
+                  data->user_data);
 }
 
 static void
@@ -205,20 +139,44 @@ _cogl_sub_texture_foreach_sub_texture_in_region (
                                        float virtual_ty_1,
                                        float virtual_tx_2,
                                        float virtual_ty_2,
-                                       CoglTextureSliceCallback callback,
+                                       CoglMetaTextureCallback callback,
                                        void *user_data)
 {
   CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex);
-  CoglSubTextureForeachData data;
+  CoglTexture *full_texture = sub_tex->full_texture;
+  float mapped_coords[4] =
+    { virtual_tx_1, virtual_ty_1, virtual_tx_2, virtual_ty_2};
+  float virtual_coords[4] =
+    { virtual_tx_1, virtual_ty_1, virtual_tx_2, virtual_ty_2};
 
-  data.sub_tex = sub_tex;
-  data.callback = callback;
-  data.user_data = user_data;
+  /* map the virtual coordinates to ->full_texture coordinates */
+  _cogl_sub_texture_map_quad (sub_tex, mapped_coords);
 
-  _cogl_texture_iterate_manual_repeats (_cogl_sub_texture_manual_repeat_cb,
-                                        virtual_tx_1, virtual_ty_1,
-                                        virtual_tx_2, virtual_ty_2,
-                                        &data);
+  if (!cogl_texture_is_sliced (full_texture))
+    {
+      callback (sub_tex->full_texture,
+                mapped_coords,
+                virtual_coords,
+                user_data);
+    }
+  else
+    {
+      CoglSubTextureForeachData data;
+
+      data.sub_tex = sub_tex;
+      data.callback = callback;
+      data.user_data = user_data;
+
+      cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (full_texture),
+                                           mapped_coords[0],
+                                           mapped_coords[1],
+                                           mapped_coords[2],
+                                           mapped_coords[3],
+                                           COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                           COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                           unmap_coords_cb,
+                                           &data);
+    }
 }
 
 static void
diff --git a/cogl/cogl-texture-2d-sliced.c b/cogl/cogl-texture-2d-sliced.c
index 150f05d..ec8db2f 100644
--- a/cogl/cogl-texture-2d-sliced.c
+++ b/cogl/cogl-texture-2d-sliced.c
@@ -56,14 +56,33 @@ COGL_TEXTURE_DEFINE (Texture2DSliced, texture_2d_sliced);
 
 static const CoglTextureVtable cogl_texture_2d_sliced_vtable;
 
-/* To differentiate between texture coordinates of a specific, real, slice
- * texture and the texture coordinates of the composite, sliced texture, the
- * coordinates of the sliced texture are called "virtual" coordinates and the
- * coordinates of slices are called "slice" coordinates. */
-/* This function lets you iterate all the slices that lie within the given
- * virtual coordinates of the parent sliced texture. */
-/* Note: no guarantee is given about the order in which the slices will be
- * visited */
+typedef struct _ForeachData
+{
+  CoglMetaTextureCallback callback;
+  void *user_data;
+  float x_normalize_factor;
+  float y_normalize_factor;
+} ForeachData;
+
+static void
+re_normalize_sub_texture_coords_cb (CoglTexture *sub_texture,
+                                    const float *sub_texture_coords,
+                                    const float *meta_coords,
+                                    void *user_data)
+{
+  ForeachData *data = user_data;
+  float re_normalized_coords[4] =
+    {
+      sub_texture_coords[0] * data->x_normalize_factor,
+      sub_texture_coords[1] * data->y_normalize_factor,
+      sub_texture_coords[2] * data->x_normalize_factor,
+      sub_texture_coords[3] * data->y_normalize_factor
+    };
+
+  data->callback (sub_texture, re_normalized_coords, meta_coords,
+                  data->user_data);
+}
+
 static void
 _cogl_texture_2d_sliced_foreach_sub_texture_in_region (
                                        CoglTexture *tex,
@@ -71,103 +90,44 @@ _cogl_texture_2d_sliced_foreach_sub_texture_in_region (
                                        float virtual_ty_1,
                                        float virtual_tx_2,
                                        float virtual_ty_2,
-                                       CoglTextureSliceCallback callback,
+                                       CoglMetaTextureCallback callback,
                                        void *user_data)
 {
   CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex);
-  float width = tex_2ds->width;
-  float height = tex_2ds->height;
-  CoglSpanIter iter_x;
-  CoglSpanIter iter_y;
-  float slice_coords[4];
-
-  /* Slice spans are stored in denormalized coordinates, and this is what
-   * the _cogl_span_iter_* funcs expect to be given, so we scale the given
-   * virtual coordinates by the texture size to denormalize.
+  CoglSpan *x_spans = (CoglSpan *)tex_2ds->slice_x_spans->data;
+  CoglSpan *y_spans = (CoglSpan *)tex_2ds->slice_y_spans->data;
+  CoglTexture **textures = (CoglTexture **)tex_2ds->slice_textures->data;
+  float un_normalized_coords[4];
+  ForeachData data;
+
+  /* NB: its convenient for us to store non-normalized coordinates in
+   * our CoglSpans but that means we need to un-normalize the incoming
+   * virtual coordinates and make sure we re-normalize the coordinates
+   * before calling the given callback.
    */
-  /* XXX: I wonder if it's worth changing how we store spans so we can avoid
-   * the need to denormalize here */
-  virtual_tx_1 *= width;
-  virtual_ty_1 *= height;
-  virtual_tx_2 *= width;
-  virtual_ty_2 *= height;
-
-  /* Iterate the y axis of the virtual rectangle */
-  for (_cogl_span_iter_begin (&iter_y,
-                              tex_2ds->slice_y_spans,
-                              height,
-                              virtual_ty_1,
-                              virtual_ty_2);
-       !_cogl_span_iter_end (&iter_y);
-       _cogl_span_iter_next (&iter_y))
-    {
-      if (iter_y.flipped)
-        {
-          slice_coords[1] = iter_y.intersect_end;
-          slice_coords[3] = iter_y.intersect_start;
-        }
-      else
-        {
-          slice_coords[1] = iter_y.intersect_start;
-          slice_coords[3] = iter_y.intersect_end;
-        }
-
-      /* Localize slice texture coordinates */
-      slice_coords[1] -= iter_y.pos;
-      slice_coords[3] -= iter_y.pos;
-
-      /* Normalize slice texture coordinates */
-      slice_coords[1] /= iter_y.span->size;
-      slice_coords[3] /= iter_y.span->size;
-
-      /* Iterate the x axis of the virtual rectangle */
-      for (_cogl_span_iter_begin (&iter_x,
-                                  tex_2ds->slice_x_spans,
-                                  width,
-                                  virtual_tx_1,
-                                  virtual_tx_2);
-	   !_cogl_span_iter_end (&iter_x);
-	   _cogl_span_iter_next (&iter_x))
-        {
-          CoglTexture2D *slice_tex;
-          float normalized_virtual_coords[4];
 
-          if (iter_x.flipped)
-            {
-              slice_coords[0] = iter_x.intersect_end;
-              slice_coords[2] = iter_x.intersect_start;
-            }
-          else
-            {
-              slice_coords[0] = iter_x.intersect_start;
-              slice_coords[2] = iter_x.intersect_end;
-            }
-
-	  /* Localize slice texture coordinates */
-          slice_coords[0] -= iter_x.pos;
-          slice_coords[2] -= iter_x.pos;
-
-          /* Normalize slice texture coordinates */
-          slice_coords[0] /= iter_x.span->size;
-          slice_coords[2] /= iter_x.span->size;
-
-	  /* Pluck out the cogl texture for this slice */
-          slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *,
-				     iter_y.index *
-                                     tex_2ds->slice_x_spans->len +
-                                     iter_x.index);
-
-          normalized_virtual_coords[0] = iter_x.intersect_start / width;
-          normalized_virtual_coords[1] = iter_y.intersect_start / height;
-          normalized_virtual_coords[2] = iter_x.intersect_end / width;
-          normalized_virtual_coords[3] = iter_y.intersect_end / height;
-
-          callback (COGL_TEXTURE (slice_tex),
-                    slice_coords,
-                    normalized_virtual_coords,
-                    user_data);
-	}
-    }
+  data.callback = callback;
+  data.user_data = user_data;
+  data.x_normalize_factor = 1.0f / tex_2ds->width;
+  data.y_normalize_factor = 1.0f / tex_2ds->height;
+
+  un_normalized_coords[0] = virtual_tx_1 * data.x_normalize_factor;
+  un_normalized_coords[1] = virtual_ty_1 * data.y_normalize_factor;
+  un_normalized_coords[2] = virtual_tx_2 * data.x_normalize_factor;
+  un_normalized_coords[3] = virtual_ty_2 * data.y_normalize_factor;
+
+  _cogl_texture_spans_foreach_in_region (x_spans,
+                                         tex_2ds->slice_x_spans->len,
+                                         y_spans,
+                                         tex_2ds->slice_y_spans->len,
+                                         textures,
+                                         un_normalized_coords,
+                                         1, /* x_normalize_factor */
+                                         1, /* y_normalize_factor */
+                                         COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                         COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                         re_normalize_sub_texture_coords_cb,
+                                         &data);
 }
 
 static guint8 *
@@ -455,10 +415,12 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds,
   /* Iterate vertical spans */
   for (source_y = src_y,
        _cogl_span_iter_begin (&y_iter,
-                              tex_2ds->slice_y_spans,
+                              (CoglSpan *)tex_2ds->slice_y_spans->data,
+                              tex_2ds->slice_y_spans->len,
                               tex_2ds->height,
                               dst_y,
-                              dst_y + height);
+                              dst_y + height,
+                              COGL_PIPELINE_WRAP_MODE_REPEAT);
 
        !_cogl_span_iter_end (&y_iter);
 
@@ -471,10 +433,12 @@ _cogl_texture_2d_sliced_upload_subregion_to_gl (CoglTexture2DSliced *tex_2ds,
       /* Iterate horizontal spans */
       for (source_x = src_x,
            _cogl_span_iter_begin (&x_iter,
-                                  tex_2ds->slice_x_spans,
+                                  (CoglSpan *)tex_2ds->slice_x_spans->data,
+                                  tex_2ds->slice_x_spans->len,
                                   tex_2ds->width,
                                   dst_x,
-                                  dst_x + width);
+                                  dst_x + width,
+                                  COGL_PIPELINE_WRAP_MODE_REPEAT);
 
            !_cogl_span_iter_end (&x_iter);
 
diff --git a/cogl/cogl-texture-2d.c b/cogl/cogl-texture-2d.c
index 4697917..c64b14e 100644
--- a/cogl/cogl-texture-2d.c
+++ b/cogl/cogl-texture-2d.c
@@ -57,78 +57,11 @@ static const CoglTextureVtable cogl_texture_2d_vtable;
 typedef struct _CoglTexture2DManualRepeatData
 {
   CoglTexture2D *tex_2d;
-  CoglTextureSliceCallback callback;
+  CoglMetaTextureCallback callback;
   void *user_data;
 } CoglTexture2DManualRepeatData;
 
 static void
-_cogl_texture_2d_wrap_coords (float t_1, float t_2,
-                              float *out_t_1, float *out_t_2)
-{
-  float int_part;
-
-  /* Wrap t_1 and t_2 to the range [0,1] */
-
-  modff (t_1 < t_2 ? t_1 : t_2, &int_part);
-  t_1 -= int_part;
-  t_2 -= int_part;
-  if (cogl_util_float_signbit (int_part))
-    {
-      *out_t_1 = 1.0f + t_1;
-      *out_t_2 = 1.0f + t_2;
-    }
-  else
-    {
-      *out_t_1 = t_1;
-      *out_t_2 = t_2;
-    }
-}
-
-static void
-_cogl_texture_2d_manual_repeat_cb (const float *coords,
-                                   void *user_data)
-{
-  CoglTexture2DManualRepeatData *data = user_data;
-  float slice_coords[4];
-
-  _cogl_texture_2d_wrap_coords (coords[0], coords[2],
-                                slice_coords + 0, slice_coords + 2);
-  _cogl_texture_2d_wrap_coords (coords[1], coords[3],
-                                slice_coords + 1, slice_coords + 3);
-
-  data->callback (COGL_TEXTURE (data->tex_2d),
-                  slice_coords,
-                  coords,
-                  data->user_data);
-}
-
-static void
-_cogl_texture_2d_foreach_sub_texture_in_region (
-                                       CoglTexture *tex,
-                                       float virtual_tx_1,
-                                       float virtual_ty_1,
-                                       float virtual_tx_2,
-                                       float virtual_ty_2,
-                                       CoglTextureSliceCallback callback,
-                                       void *user_data)
-{
-  CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex);
-  CoglTexture2DManualRepeatData data;
-
-  data.tex_2d = tex_2d;
-  data.callback = callback;
-  data.user_data = user_data;
-
-  /* We need to implement manual repeating because if Cogl is calling
-     this function then it will set the wrap mode to GL_CLAMP_TO_EDGE
-     and hardware repeating can't be done */
-  _cogl_texture_iterate_manual_repeats (_cogl_texture_2d_manual_repeat_cb,
-                                        virtual_tx_1, virtual_ty_1,
-                                        virtual_tx_2, virtual_ty_2,
-                                        &data);
-}
-
-static void
 _cogl_texture_2d_set_wrap_mode_parameters (CoglTexture *tex,
                                            GLenum wrap_mode_s,
                                            GLenum wrap_mode_t,
@@ -923,7 +856,7 @@ cogl_texture_2d_vtable =
   {
     _cogl_texture_2d_set_region,
     _cogl_texture_2d_get_data,
-    _cogl_texture_2d_foreach_sub_texture_in_region,
+    NULL, /* foreach_sub_texture_in_region */
     _cogl_texture_2d_get_max_waste,
     _cogl_texture_2d_is_sliced,
     _cogl_texture_2d_can_hardware_repeat,
diff --git a/cogl/cogl-texture-3d.c b/cogl/cogl-texture-3d.c
index 58adee6..584bbbd 100644
--- a/cogl/cogl-texture-3d.c
+++ b/cogl/cogl-texture-3d.c
@@ -55,80 +55,6 @@ COGL_TEXTURE_DEFINE (Texture3D, texture_3d);
 
 static const CoglTextureVtable cogl_texture_3d_vtable;
 
-typedef struct _CoglTexture3DManualRepeatData
-{
-  CoglTexture3D *tex_3d;
-  CoglTextureSliceCallback callback;
-  void *user_data;
-} CoglTexture3DManualRepeatData;
-
-static void
-_cogl_texture_3d_wrap_coords (float t_1, float t_2,
-                              float *out_t_1, float *out_t_2)
-{
-  float int_part;
-
-  /* Wrap t_1 and t_2 to the range [0,1] */
-
-  modff (t_1 < t_2 ? t_1 : t_2, &int_part);
-  t_1 -= int_part;
-  t_2 -= int_part;
-  if (cogl_util_float_signbit (int_part))
-    {
-      *out_t_1 = 1.0f + t_1;
-      *out_t_2 = 1.0f + t_2;
-    }
-  else
-    {
-      *out_t_1 = t_1;
-      *out_t_2 = t_2;
-    }
-}
-
-static void
-_cogl_texture_3d_manual_repeat_cb (const float *coords,
-                                   void *user_data)
-{
-  CoglTexture3DManualRepeatData *data = user_data;
-  float slice_coords[4];
-
-  _cogl_texture_3d_wrap_coords (coords[0], coords[2],
-                                slice_coords + 0, slice_coords + 2);
-  _cogl_texture_3d_wrap_coords (coords[1], coords[3],
-                                slice_coords + 1, slice_coords + 3);
-
-  data->callback (COGL_TEXTURE (data->tex_3d),
-                  slice_coords,
-                  coords,
-                  data->user_data);
-}
-
-static void
-_cogl_texture_3d_foreach_sub_texture_in_region (
-                                       CoglTexture *tex,
-                                       float virtual_tx_1,
-                                       float virtual_ty_1,
-                                       float virtual_tx_2,
-                                       float virtual_ty_2,
-                                       CoglTextureSliceCallback callback,
-                                       void *user_data)
-{
-  CoglTexture3D *tex_3d = COGL_TEXTURE_3D (tex);
-  CoglTexture3DManualRepeatData data;
-
-  data.tex_3d = tex_3d;
-  data.callback = callback;
-  data.user_data = user_data;
-
-  /* We need to implement manual repeating because if Cogl is calling
-     this function then it will set the wrap mode to GL_CLAMP_TO_EDGE
-     and hardware repeating can't be done */
-  _cogl_texture_iterate_manual_repeats (_cogl_texture_3d_manual_repeat_cb,
-                                        virtual_tx_1, virtual_ty_1,
-                                        virtual_tx_2, virtual_ty_2,
-                                        &data);
-}
-
 static void
 _cogl_texture_3d_set_wrap_mode_parameters (CoglTexture *tex,
                                            GLenum wrap_mode_s,
@@ -671,7 +597,7 @@ cogl_texture_3d_vtable =
   {
     _cogl_texture_3d_set_region,
     _cogl_texture_3d_get_data,
-    _cogl_texture_3d_foreach_sub_texture_in_region,
+    NULL, /* foreach_sub_texture_in_region */
     _cogl_texture_3d_get_max_waste,
     _cogl_texture_3d_is_sliced,
     _cogl_texture_3d_can_hardware_repeat,
diff --git a/cogl/cogl-texture-private.h b/cogl/cogl-texture-private.h
index 0ca0a45..1ca0561 100644
--- a/cogl/cogl-texture-private.h
+++ b/cogl/cogl-texture-private.h
@@ -27,17 +27,10 @@
 #include "cogl-bitmap-private.h"
 #include "cogl-handle.h"
 #include "cogl-pipeline-private.h"
+#include "cogl-spans.h"
 
 typedef struct _CoglTextureVtable     CoglTextureVtable;
 
-typedef void (*CoglTextureSliceCallback) (CoglTexture *texture,
-                                          const float *slice_coords,
-                                          const float *virtual_coords,
-                                          void *user_data);
-
-typedef void (* CoglTextureManualRepeatCallback) (const float *coords,
-                                                  void *user_data);
-
 /* Encodes three possibiloities result of transforming a quad */
 typedef enum {
   /* quad doesn't cross the boundaries of a texture */
@@ -92,7 +85,7 @@ struct _CoglTextureVtable
                                           float virtual_ty_1,
                                           float virtual_tx_2,
                                           float virtual_ty_2,
-                                          CoglTextureSliceCallback callback,
+                                          CoglMetaTextureCallback callback,
                                           void *user_data);
 
   int (* get_max_waste) (CoglTexture *tex);
@@ -189,15 +182,6 @@ _cogl_texture_register_texture_type (GQuark type);
    _cogl_texture_register_texture_type (_cogl_handle_                   \
                                         ## type_name ## _get_type ()))
 
-void
-_cogl_texture_foreach_sub_texture_in_region (CoglTexture *texture,
-                                             float virtual_tx_1,
-                                             float virtual_ty_1,
-                                             float virtual_tx_2,
-                                             float virtual_ty_2,
-                                             CoglTextureSliceCallback callback,
-                                             void *user_data);
-
 gboolean
 _cogl_texture_can_hardware_repeat (CoglTexture *texture);
 
@@ -256,17 +240,6 @@ _cogl_texture_prep_gl_alignment_for_pixels_upload (int pixels_rowstride);
 void
 _cogl_texture_prep_gl_alignment_for_pixels_download (int pixels_rowstride);
 
-/* Utility function for implementing manual repeating. Even texture
-   backends that always support hardware repeating need this because
-   when foreach_sub_texture_in_region is invoked Cogl will set the
-   wrap mode to GL_CLAMP_TO_EDGE so hardware repeating can't be
-   done */
-void
-_cogl_texture_iterate_manual_repeats (CoglTextureManualRepeatCallback callback,
-                                      float tx_1, float ty_1,
-                                      float tx_2, float ty_2,
-                                      void *user_data);
-
 /* Utility function to use as a fallback for getting the data of any
    texture via the framebuffer */
 
@@ -289,4 +262,18 @@ _cogl_texture_get_associated_framebuffers (CoglTexture *texture);
 void
 _cogl_texture_flush_journal_rendering (CoglTexture *texture);
 
+void
+_cogl_texture_spans_foreach_in_region (CoglSpan *x_spans,
+                                       int n_x_spans,
+                                       CoglSpan *y_spans,
+                                       int n_y_spans,
+                                       CoglTexture **textures,
+                                       float *virtual_coords,
+                                       float x_normalize_factor,
+                                       float y_normalize_factor,
+                                       CoglPipelineWrapMode wrap_x,
+                                       CoglPipelineWrapMode wrap_y,
+                                       CoglMetaTextureCallback callback,
+                                       void *user_data);
+
 #endif /* __COGL_TEXTURE_PRIVATE_H */
diff --git a/cogl/cogl-texture-rectangle-private.h b/cogl/cogl-texture-rectangle-private.h
index 9a0dca2..07eca5e 100644
--- a/cogl/cogl-texture-rectangle-private.h
+++ b/cogl/cogl-texture-rectangle-private.h
@@ -52,6 +52,9 @@ struct _CoglTextureRectangle
   gboolean        is_foreign;
 };
 
+gboolean
+_cogl_is_texture_rectangle (void *object);
+
 GQuark
 _cogl_handle_texture_rectangle_get_type (void);
 
diff --git a/cogl/cogl-texture-rectangle.c b/cogl/cogl-texture-rectangle.c
index 558def6..a201153 100644
--- a/cogl/cogl-texture-rectangle.c
+++ b/cogl/cogl-texture-rectangle.c
@@ -59,85 +59,6 @@ COGL_TEXTURE_INTERNAL_DEFINE (TextureRectangle, texture_rectangle);
 
 static const CoglTextureVtable cogl_texture_rectangle_vtable;
 
-typedef struct _CoglTextureRectangleManualRepeatData
-{
-  CoglTextureRectangle *tex_rect;
-  CoglTextureSliceCallback callback;
-  void *user_data;
-} CoglTextureRectangleManualRepeatData;
-
-static void
-_cogl_texture_rectangle_wrap_coords (float t_1, float t_2,
-                                     float *out_t_1, float *out_t_2)
-{
-  float int_part;
-
-  /* Wrap t_1 and t_2 to the range [0,1] */
-
-  modff (t_1 < t_2 ? t_1 : t_2, &int_part);
-  t_1 -= int_part;
-  t_2 -= int_part;
-  if (cogl_util_float_signbit (int_part))
-    {
-      *out_t_1 = 1.0f + t_1;
-      *out_t_2 = 1.0f + t_2;
-    }
-  else
-    {
-      *out_t_1 = t_1;
-      *out_t_2 = t_2;
-    }
-}
-
-static void
-_cogl_texture_rectangle_manual_repeat_cb (const float *coords,
-                                          void *user_data)
-{
-  CoglTextureRectangleManualRepeatData *data = user_data;
-  float slice_coords[4];
-
-  _cogl_texture_rectangle_wrap_coords (coords[0], coords[2],
-                                       slice_coords + 0, slice_coords + 2);
-  _cogl_texture_rectangle_wrap_coords (coords[1], coords[3],
-                                       slice_coords + 1, slice_coords + 3);
-
-  slice_coords[0] *= data->tex_rect->width;
-  slice_coords[1] *= data->tex_rect->height;
-  slice_coords[2] *= data->tex_rect->width;
-  slice_coords[3] *= data->tex_rect->height;
-
-  data->callback (COGL_TEXTURE (data->tex_rect),
-                  slice_coords,
-                  coords,
-                  data->user_data);
-}
-
-static void
-_cogl_texture_rectangle_foreach_sub_texture_in_region (
-                                           CoglTexture *tex,
-                                           float virtual_tx_1,
-                                           float virtual_ty_1,
-                                           float virtual_tx_2,
-                                           float virtual_ty_2,
-                                           CoglTextureSliceCallback callback,
-                                           void *user_data)
-{
-  CoglTextureRectangle *tex_rect = COGL_TEXTURE_RECTANGLE (tex);
-  CoglTextureRectangleManualRepeatData data;
-
-  data.tex_rect = tex_rect;
-  data.callback = callback;
-  data.user_data = user_data;
-
-  /* We need to implement manual repeating because if Cogl is calling
-     this function then it will set the wrap mode to GL_CLAMP_TO_EDGE
-     and hardware repeating can't be done */
-  _cogl_texture_iterate_manual_repeats
-    (_cogl_texture_rectangle_manual_repeat_cb,
-     virtual_tx_1, virtual_ty_1, virtual_tx_2, virtual_ty_2,
-     &data);
-}
-
 static gboolean
 can_use_wrap_mode (GLenum wrap_mode)
 {
@@ -653,7 +574,7 @@ cogl_texture_rectangle_vtable =
   {
     _cogl_texture_rectangle_set_region,
     _cogl_texture_rectangle_get_data,
-    _cogl_texture_rectangle_foreach_sub_texture_in_region,
+    NULL, /* foreach_sub_texture_in_region */
     _cogl_texture_rectangle_get_max_waste,
     _cogl_texture_rectangle_is_sliced,
     _cogl_texture_rectangle_can_hardware_repeat,
diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c
index b2e0a40..fa3857d 100644
--- a/cogl/cogl-texture.c
+++ b/cogl/cogl-texture.c
@@ -292,101 +292,6 @@ _cogl_texture_set_wrap_mode_parameters (CoglTexture *texture,
                                              wrap_mode_p);
 }
 
-/* This is like CoglSpanIter except it deals with floats and it
-   effectively assumes there is only one span from 0.0 to 1.0 */
-typedef struct _CoglTextureIter
-{
-  float pos, end, next_pos;
-  gboolean flipped;
-  float t_1, t_2;
-} CoglTextureIter;
-
-static void
-_cogl_texture_iter_update (CoglTextureIter *iter)
-{
-  float t_2;
-  float frac_part;
-
-  frac_part = modff (iter->pos, &iter->next_pos);
-
-  /* modff rounds the int part towards zero so we need to add one if
-     we're meant to be heading away from zero */
-  if (iter->pos >= 0.0f || frac_part == 0.0f)
-    iter->next_pos += 1.0f;
-
-  if (iter->next_pos > iter->end)
-    t_2 = iter->end;
-  else
-    t_2 = iter->next_pos;
-
-  if (iter->flipped)
-    {
-      iter->t_1 = t_2;
-      iter->t_2 = iter->pos;
-    }
-  else
-    {
-      iter->t_1 = iter->pos;
-      iter->t_2 = t_2;
-    }
-}
-
-static void
-_cogl_texture_iter_begin (CoglTextureIter *iter,
-                          float t_1, float t_2)
-{
-  if (t_1 <= t_2)
-    {
-      iter->pos = t_1;
-      iter->end = t_2;
-      iter->flipped = FALSE;
-    }
-  else
-    {
-      iter->pos = t_2;
-      iter->end = t_1;
-      iter->flipped = TRUE;
-    }
-
-  _cogl_texture_iter_update (iter);
-}
-
-static void
-_cogl_texture_iter_next (CoglTextureIter *iter)
-{
-  iter->pos = iter->next_pos;
-  _cogl_texture_iter_update (iter);
-}
-
-static gboolean
-_cogl_texture_iter_end (CoglTextureIter *iter)
-{
-  return iter->pos >= iter->end;
-}
-
-/* This invokes the callback with enough quads to cover the manually
-   repeated range specified by the virtual texture coordinates without
-   emitting coordinates outside the range [0,1] */
-void
-_cogl_texture_iterate_manual_repeats (CoglTextureManualRepeatCallback callback,
-                                      float tx_1, float ty_1,
-                                      float tx_2, float ty_2,
-                                      void *user_data)
-{
-  CoglTextureIter x_iter, y_iter;
-
-  for (_cogl_texture_iter_begin (&y_iter, ty_1, ty_2);
-       !_cogl_texture_iter_end (&y_iter);
-       _cogl_texture_iter_next (&y_iter))
-    for (_cogl_texture_iter_begin (&x_iter, tx_1, tx_2);
-         !_cogl_texture_iter_end (&x_iter);
-         _cogl_texture_iter_next (&x_iter))
-      {
-        float coords[4] = { x_iter.t_1, y_iter.t_1, x_iter.t_2, y_iter.t_2 };
-        callback (coords, user_data);
-      }
-}
-
 CoglTexture *
 cogl_texture_new_with_size (unsigned int     width,
 			    unsigned int     height,
@@ -527,6 +432,11 @@ cogl_texture_new_from_foreign (GLuint           gl_handle,
 #if HAVE_COGL_GL
   if (gl_target == GL_TEXTURE_RECTANGLE_ARB)
     {
+      CoglTextureRectangle *texture_rectangle;
+      CoglSubTexture *sub_texture;
+
+      _COGL_GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
+
       if (x_pot_waste != 0 || y_pot_waste != 0)
         {
           /* It shouldn't be necessary to have waste in this case since
@@ -536,10 +446,17 @@ cogl_texture_new_from_foreign (GLuint           gl_handle,
           return COGL_INVALID_HANDLE;
         }
 
-      return _cogl_texture_rectangle_new_from_foreign (gl_handle,
-                                                       width,
-                                                       height,
-                                                       format);
+      texture_rectangle = _cogl_texture_rectangle_new_from_foreign (gl_handle,
+                                                                    width,
+                                                                    height,
+                                                                    format);
+      /* CoglTextureRectangle textures work with non-normalized
+       * coordinates, but the semantics for this function that people
+       * depend on are that all returned texture works with normalized
+       * coordinates so we wrap with a CoglSubTexture... */
+      sub_texture = _cogl_sub_texture_new (COGL_TEXTURE (texture_rectangle),
+                                           0, 0, width, height);
+      return COGL_TEXTURE (sub_texture);
     }
 #endif
 
@@ -681,33 +598,6 @@ cogl_texture_is_sliced (CoglTexture *texture)
   return texture->vtable->is_sliced (texture);
 }
 
-/* Some CoglTextures, notably sliced textures or atlas textures when repeating
- * is used, will need to divide the coordinate space into multiple GL textures
- * (or rather; in the case of atlases duplicate a single texture in multiple
- * positions to handle repeating)
- *
- * This function helps you implement primitives using such textures by
- * invoking a callback once for each sub texture that intersects a given
- * region specified in texture coordinates.
- */
-void
-_cogl_texture_foreach_sub_texture_in_region (CoglTexture *texture,
-                                             float virtual_tx_1,
-                                             float virtual_ty_1,
-                                             float virtual_tx_2,
-                                             float virtual_ty_2,
-                                             CoglTextureSliceCallback callback,
-                                             void *user_data)
-{
-  texture->vtable->foreach_sub_texture_in_region (texture,
-                                                  virtual_tx_1,
-                                                  virtual_ty_1,
-                                                  virtual_tx_2,
-                                                  virtual_ty_2,
-                                                  callback,
-                                                  user_data);
-}
-
 /* If this returns FALSE, that implies _foreach_sub_texture_in_region
  * will be needed to iterate over multiple sub textures for regions whos
  * texture coordinates extend out of the range [0,1]
@@ -1353,10 +1243,12 @@ cogl_texture_get_data (CoglTexture     *texture,
    * the data for a sliced texture, and allows us to do the
    * read-from-framebuffer logic here in a simple fashion rather than
    * passing offsets down through the code. */
-  _cogl_texture_foreach_sub_texture_in_region (texture,
-                                               0, 0, 1, 1,
-                                               texture_get_cb,
-                                               &tg_data);
+  cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (texture),
+                                       0, 0, 1, 1,
+                                       COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                       COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                       texture_get_cb,
+                                       &tg_data);
 
   _cogl_bitmap_unmap (target_bmp);
 
@@ -1457,3 +1349,115 @@ _cogl_texture_flush_journal_rendering (CoglTexture *texture)
   for (l = texture->framebuffers; l; l = l->next)
     _cogl_framebuffer_flush_journal (l->data);
 }
+
+/* This function lets you define a meta texture as a grid of textures
+ * whereby the x and y grid-lines are defined by an array of
+ * CoglSpans. With that grid based description this function can then
+ * iterate all the cells of the grid that lye within a region
+ * specified as virtual, meta-texture, coordinates.  This function can
+ * also cope with regions that extend beyond the original meta-texture
+ * grid by iterating cells repeatedly according to the wrap_x/y
+ * arguments.
+ *
+ * To differentiate between texture coordinates of a specific, real,
+ * slice texture and the texture coordinates of a composite, meta
+ * texture, the coordinates of the meta texture are called "virtual"
+ * coordinates and the coordinates of spans are called "slice"
+ * coordinates.
+ *
+ * Note: no guarantee is given about the order in which the slices
+ * will be visited.
+ *
+ * Note: The slice coordinates passed to @callback are always
+ * normalized coordinates even if the span coordinates aren't
+ * normalized.
+ */
+void
+_cogl_texture_spans_foreach_in_region (CoglSpan *x_spans,
+                                       int n_x_spans,
+                                       CoglSpan *y_spans,
+                                       int n_y_spans,
+                                       CoglTexture **textures,
+                                       float *virtual_coords,
+                                       float x_normalize_factor,
+                                       float y_normalize_factor,
+                                       CoglPipelineWrapMode wrap_x,
+                                       CoglPipelineWrapMode wrap_y,
+                                       CoglMetaTextureCallback callback,
+                                       void *user_data)
+{
+  CoglSpanIter iter_x;
+  CoglSpanIter iter_y;
+  float slice_coords[4];
+
+  /* Iterate the y axis of the virtual rectangle */
+  for (_cogl_span_iter_begin (&iter_y,
+                              y_spans,
+                              n_y_spans,
+                              y_normalize_factor,
+                              virtual_coords[1],
+                              virtual_coords[3],
+                              wrap_y);
+       !_cogl_span_iter_end (&iter_y);
+       _cogl_span_iter_next (&iter_y))
+    {
+      if (iter_y.flipped)
+        {
+          slice_coords[1] = iter_y.intersect_end;
+          slice_coords[3] = iter_y.intersect_start;
+        }
+      else
+        {
+          slice_coords[1] = iter_y.intersect_start;
+          slice_coords[3] = iter_y.intersect_end;
+        }
+
+      /* Map the current intersection to normalized slice coordinates */
+      slice_coords[1] = (slice_coords[1] - iter_y.pos) / iter_y.span->size;
+      slice_coords[3] = (slice_coords[3] - iter_y.pos) / iter_y.span->size;
+
+      /* Iterate the x axis of the virtual rectangle */
+      for (_cogl_span_iter_begin (&iter_x,
+                                  x_spans,
+                                  n_x_spans,
+                                  x_normalize_factor,
+                                  virtual_coords[0],
+                                  virtual_coords[2],
+                                  wrap_x);
+	   !_cogl_span_iter_end (&iter_x);
+	   _cogl_span_iter_next (&iter_x))
+        {
+          CoglTexture *span_tex;
+          float span_virtual_coords[4];
+
+          if (iter_x.flipped)
+            {
+              slice_coords[0] = iter_x.intersect_end;
+              slice_coords[2] = iter_x.intersect_start;
+            }
+          else
+            {
+              slice_coords[0] = iter_x.intersect_start;
+              slice_coords[2] = iter_x.intersect_end;
+            }
+
+          /* Map the current intersection to normalized slice coordinates */
+          slice_coords[0] = (slice_coords[0] - iter_x.pos) / iter_x.span->size;
+          slice_coords[2] = (slice_coords[2] - iter_x.pos) / iter_x.span->size;
+
+	  /* Pluck out the cogl texture for this span */
+          span_tex = textures[iter_y.index * n_y_spans + iter_x.index];
+
+          span_virtual_coords[0] = iter_x.intersect_start;
+          span_virtual_coords[1] = iter_y.intersect_start;
+          span_virtual_coords[2] = iter_x.intersect_end;
+          span_virtual_coords[3] = iter_y.intersect_end;
+
+          callback (COGL_TEXTURE (span_tex),
+                    slice_coords,
+                    span_virtual_coords,
+                    user_data);
+	}
+    }
+}
+
diff --git a/cogl/cogl.h b/cogl/cogl.h
index 1f738a7..3925a0b 100644
--- a/cogl/cogl.h
+++ b/cogl/cogl.h
@@ -82,6 +82,7 @@ typedef struct _CoglFramebuffer CoglFramebuffer;
 #include <cogl/cogl-texture-2d.h>
 #include <cogl/cogl-texture-3d.h>
 #include <cogl/cogl-texture-2d-sliced.h>
+#include <cogl/cogl-meta-texture.h>
 #include <cogl/cogl-index-buffer.h>
 #include <cogl/cogl-attribute-buffer.h>
 #include <cogl/cogl-indices.h>
diff --git a/cogl/winsys/cogl-texture-pixmap-x11.c b/cogl/winsys/cogl-texture-pixmap-x11.c
index 44ac4cd..76ad7fe 100644
--- a/cogl/winsys/cogl-texture-pixmap-x11.c
+++ b/cogl/winsys/cogl-texture-pixmap-x11.c
@@ -739,6 +739,33 @@ _cogl_texture_pixmap_x11_get_data (CoglTexture     *tex,
   return cogl_texture_get_data (child_tex, format, rowstride, data);
 }
 
+typedef struct _NormalizeCoordsWrapperData
+{
+  int width;
+  int height;
+  CoglMetaTextureCallback callback;
+  void *user_data;
+} NormalizeCoordsWrapperData;
+
+static void
+normalize_coords_wrapper_cb (CoglTexture *child_texture,
+                             const float *child_texture_coords,
+                             const float *meta_coords,
+                             void *user_data)
+{
+  NormalizeCoordsWrapperData *data = user_data;
+  float normalized_coords[4];
+
+  normalized_coords[0] = meta_coords[0] / data->width;
+  normalized_coords[1] = meta_coords[1] / data->height;
+  normalized_coords[2] = meta_coords[2] / data->width;
+  normalized_coords[3] = meta_coords[3] / data->height;
+
+  data->callback (child_texture,
+                  child_texture_coords, normalized_coords,
+                  data->user_data);
+}
+
 static void
 _cogl_texture_pixmap_x11_foreach_sub_texture_in_region
                                   (CoglTexture              *tex,
@@ -746,22 +773,54 @@ _cogl_texture_pixmap_x11_foreach_sub_texture_in_region
                                    float                     virtual_ty_1,
                                    float                     virtual_tx_2,
                                    float                     virtual_ty_2,
-                                   CoglTextureSliceCallback  callback,
+                                   CoglMetaTextureCallback   callback,
                                    void                     *user_data)
 {
   CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex);
-  CoglHandle child_tex;
-
-  child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap);
+  CoglHandle child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap);
 
   /* Forward on to the child texture */
-  _cogl_texture_foreach_sub_texture_in_region (child_tex,
-                                               virtual_tx_1,
-                                               virtual_ty_1,
-                                               virtual_tx_2,
-                                               virtual_ty_2,
-                                               callback,
-                                               user_data);
+
+  /* tfp textures may be implemented in terms of a
+   * CoglTextureRectangle texture which uses un-normalized texture
+   * coordinates but we want to consistently deal with normalized
+   * texture coordinates with CoglTexturePixmapX11... */
+  if (_cogl_is_texture_rectangle (child_tex))
+    {
+      NormalizeCoordsWrapperData data;
+      int width = tex_pixmap->width;
+      int height = tex_pixmap->height;
+
+      virtual_tx_1 *= width;
+      virtual_ty_1 *= height;
+      virtual_tx_2 *= width;
+      virtual_ty_2 *= height;
+
+      data.width = width;
+      data.height = height;
+      data.callback = callback;
+      data.user_data = user_data;
+
+      cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (tex),
+                                           virtual_tx_1,
+                                           virtual_ty_1,
+                                           virtual_tx_2,
+                                           virtual_ty_2,
+                                           COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                           COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                           normalize_coords_wrapper_cb,
+                                           &data);
+    }
+  else
+    cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (tex),
+                                         virtual_tx_1,
+                                         virtual_ty_1,
+                                         virtual_tx_2,
+                                         virtual_ty_2,
+                                         COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                         COGL_PIPELINE_WRAP_MODE_REPEAT,
+                                         callback,
+                                         user_data);
 }
 
 static int



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