diff --git a/clutter/cogl/cogl/Makefile.am b/clutter/cogl/cogl/Makefile.am
index 87ed88983..e21426d2d 100644
--- a/clutter/cogl/cogl/Makefile.am
+++ b/clutter/cogl/cogl/Makefile.am
@@ -251,6 +251,8 @@ cogl_sources_c = \
 	$(srcdir)/cogl-atlas.c                          \
 	$(srcdir)/cogl-atlas-texture-private.h          \
 	$(srcdir)/cogl-atlas-texture.c                  \
+	$(srcdir)/cogl-blit.h				\
+	$(srcdir)/cogl-blit.c				\
 	$(srcdir)/cogl-spans.h				\
 	$(srcdir)/cogl-spans.c				\
 	$(srcdir)/cogl-journal-private.h		\
diff --git a/clutter/cogl/cogl/cogl-atlas.c b/clutter/cogl/cogl/cogl-atlas.c
index 2b3a743a8..60be9e51d 100644
--- a/clutter/cogl/cogl/cogl-atlas.c
+++ b/clutter/cogl/cogl/cogl-atlas.c
@@ -37,168 +37,15 @@
 #include "cogl-texture-driver.h"
 #include "cogl-pipeline-opengl-private.h"
 #include "cogl-debug.h"
+#include "cogl-framebuffer-private.h"
+#include "cogl-blit.h"
 
 #include <stdlib.h>
 
-#ifndef HAVE_COGL_GLES2
-
-#define glGenFramebuffers                 ctx->drv.pf_glGenFramebuffers
-#define glBindFramebuffer                 ctx->drv.pf_glBindFramebuffer
-#define glFramebufferTexture2D            ctx->drv.pf_glFramebufferTexture2D
-#define glCheckFramebufferStatus          ctx->drv.pf_glCheckFramebufferStatus
-#define glDeleteFramebuffers              ctx->drv.pf_glDeleteFramebuffers
-
-#endif /* HAVE_COGL_GLES2 */
-
-#ifndef GL_FRAMEBUFFER
-#define GL_FRAMEBUFFER          0x8D40
-#endif
-#ifndef GL_FRAMEBUFFER_BINDING
-#define GL_FRAMEBUFFER_BINDING  0x8CA6
-#endif
-#ifndef GL_COLOR_ATTACHMENT0
-#define GL_COLOR_ATTACHMENT0    0x8CE0
-#endif
-#ifndef GL_FRAMEBUFFER_COMPLETE
-#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
-#endif
-
 static void _cogl_atlas_free (CoglAtlas *atlas);
 
 COGL_OBJECT_INTERNAL_DEFINE (Atlas, atlas);
 
-/* If we want to do mulitple blits from a texture (such as when
-   reorganizing the atlas) then it's quicker to download all of the
-   data once and upload multiple times from that. This struct is used
-   to keep the image data for a series of blits */
-
-typedef struct _CoglAtlasBlitData
-{
-  CoglHandle src_tex, dst_tex;
-
-  /* If we're using an FBO to blit, then FBO will be non-zero and
-     old_fbo will be the previous framebuffer binding */
-  GLuint fbo, old_fbo;
-
-  /* If we're not using an FBO then we g_malloc a buffer and copy the
-     complete texture data in */
-  unsigned char *image_data;
-  CoglPixelFormat format;
-  int bpp;
-  unsigned int src_height, src_width;
-
-  GLenum dst_gl_target;
-} CoglAtlasBlitData;
-
-static void
-_cogl_atlas_blit_begin (CoglAtlasBlitData *data,
-                        CoglHandle dst_tex,
-                        CoglHandle src_tex)
-{
-  GLenum src_gl_target;
-  GLuint src_gl_texture;
-  GLuint dst_gl_texture;
-
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  data->dst_tex = dst_tex;
-  data->src_tex = src_tex;
-  data->fbo = 0;
-
-  /* If we can use an FBO then we don't need to download the data and
-     we can tell GL to blit directly between the textures */
-  if (cogl_features_available (COGL_FEATURE_OFFSCREEN) &&
-      !cogl_texture_is_sliced (dst_tex) &&
-      cogl_texture_get_gl_texture (src_tex, &src_gl_texture, &src_gl_target) &&
-      cogl_texture_get_gl_texture (dst_tex, &dst_gl_texture,
-                                   &data->dst_gl_target))
-    {
-      /* Ideally we would use the cogl-offscreen API here, but I'd
-         rather avoid creating a stencil renderbuffer which you can't
-         currently do */
-      /* Preserve the previous framebuffer binding so we don't trample
-         on cogl-offscreen */
-      data->old_fbo = 0;
-      GE( glGetIntegerv (GL_FRAMEBUFFER_BINDING, (GLint *) &data->old_fbo) );
-
-      _cogl_texture_set_filters (src_tex, GL_NEAREST, GL_NEAREST);
-
-      /* Create an FBO to read from the src texture */
-      GE( glGenFramebuffers (1, &data->fbo) );
-      GE( glBindFramebuffer (GL_FRAMEBUFFER, data->fbo) );
-      GE( glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
-                                  src_gl_target, src_gl_texture, 0) );
-      if (glCheckFramebufferStatus (GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
-        {
-          /* The FBO failed for whatever reason so we'll fallback to
-             reading the texture data */
-          GE( glBindFramebuffer (GL_FRAMEBUFFER, data->old_fbo) );
-          GE( glDeleteFramebuffers (1, &data->fbo) );
-          data->fbo = 0;
-        }
-
-      _cogl_bind_gl_texture_transient (data->dst_gl_target,
-                                       dst_gl_texture,
-                                       FALSE);
-    }
-
-  if (data->fbo)
-    COGL_NOTE (ATLAS, "Blit set up using an FBO");
-  else
-    {
-      /* We need to retrieve the entire texture data (there is no
-         glGetTexSubImage2D) */
-
-      data->format = cogl_texture_get_format (src_tex);
-      data->bpp = _cogl_get_format_bpp (data->format);
-      data->src_width = cogl_texture_get_width (src_tex);
-      data->src_height = cogl_texture_get_height (src_tex);
-
-      data->image_data = g_malloc (data->bpp * data->src_width *
-                                   data->src_height);
-      cogl_texture_get_data (src_tex, data->format,
-                             data->src_width * data->bpp, data->image_data);
-    }
-}
-
-static void
-_cogl_atlas_blit (CoglAtlasBlitData *data,
-                  unsigned int src_x,
-                  unsigned int src_y,
-                  unsigned int dst_x,
-                  unsigned int dst_y,
-                  unsigned int width,
-                  unsigned int height)
-{
-  /* If we have an FBO then we can do a fast blit */
-  if (data->fbo)
-    GE( glCopyTexSubImage2D (data->dst_gl_target, 0, dst_x, dst_y, src_x, src_y,
-                             width, height) );
-  else
-    cogl_texture_set_region (data->dst_tex,
-                             src_x, src_y,
-                             dst_x, dst_y,
-                             width, height,
-                             data->src_width, data->src_height,
-                             data->format,
-                             data->src_width * data->bpp,
-                             data->image_data);
-}
-
-static void
-_cogl_atlas_blit_end (CoglAtlasBlitData *data)
-{
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  if (data->fbo)
-    {
-      GE( glBindFramebuffer (GL_FRAMEBUFFER, data->old_fbo) );
-      GE( glDeleteFramebuffers (1, &data->fbo) );
-    }
-  else
-    g_free (data->image_data);
-}
-
 CoglAtlas *
 _cogl_atlas_new (CoglPixelFormat texture_format,
                  CoglAtlasFlags flags,
@@ -249,7 +96,7 @@ _cogl_atlas_migrate (CoglAtlas               *atlas,
                      void                    *skip_user_data)
 {
   unsigned int i;
-  CoglAtlasBlitData blit_data;
+  CoglBlitData blit_data;
 
   /* If the 'disable migrate' flag is set then we won't actually copy
      the textures to their new location. Instead we'll just invoke the
@@ -262,20 +109,20 @@ _cogl_atlas_migrate (CoglAtlas               *atlas,
                                  &textures[i].new_position);
   else
     {
-      _cogl_atlas_blit_begin (&blit_data, new_texture, old_texture);
+      _cogl_blit_begin (&blit_data, new_texture, old_texture);
 
       for (i = 0; i < n_textures; i++)
         {
           /* Skip the texture that is being added because it doesn't contain
              any data yet */
           if (textures[i].user_data != skip_user_data)
-            _cogl_atlas_blit (&blit_data,
-                              textures[i].old_position.x,
-                              textures[i].old_position.y,
-                              textures[i].new_position.x,
-                              textures[i].new_position.y,
-                              textures[i].new_position.width,
-                              textures[i].new_position.height);
+            _cogl_blit (&blit_data,
+                        textures[i].old_position.x,
+                        textures[i].old_position.y,
+                        textures[i].new_position.x,
+                        textures[i].new_position.y,
+                        textures[i].new_position.width,
+                        textures[i].new_position.height);
 
           /* Update the texture position */
           atlas->update_position_cb (textures[i].user_data,
@@ -283,7 +130,7 @@ _cogl_atlas_migrate (CoglAtlas               *atlas,
                                      &textures[i].new_position);
         }
 
-      _cogl_atlas_blit_end (&blit_data);
+      _cogl_blit_end (&blit_data);
     }
 }
 
@@ -663,7 +510,7 @@ _cogl_atlas_copy_rectangle (CoglAtlas        *atlas,
                             CoglPixelFormat   format)
 {
   CoglHandle tex;
-  CoglAtlasBlitData blit_data;
+  CoglBlitData blit_data;
 
   /* Create a new texture at the right size */
   tex = cogl_texture_new_with_size (width, height, flags, format);
@@ -671,12 +518,12 @@ _cogl_atlas_copy_rectangle (CoglAtlas        *atlas,
   /* Blit the data out of the atlas to the new texture. If FBOs
      aren't available this will end up having to copy the entire
      atlas texture */
-  _cogl_atlas_blit_begin (&blit_data, tex, atlas->texture);
-  _cogl_atlas_blit (&blit_data,
+  _cogl_blit_begin (&blit_data, tex, atlas->texture);
+  _cogl_blit (&blit_data,
                     x, y,
                     0, 0,
                     width, height);
-  _cogl_atlas_blit_end (&blit_data);
+  _cogl_blit_end (&blit_data);
 
   return tex;
 }
diff --git a/clutter/cogl/cogl/cogl-blit.c b/clutter/cogl/cogl/cogl-blit.c
new file mode 100644
index 000000000..1c40e348b
--- /dev/null
+++ b/clutter/cogl/cogl/cogl-blit.c
@@ -0,0 +1,389 @@
+/*
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors:
+ *  Neil Roberts   <neil@linux.intel.com>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#include "cogl.h"
+#include "cogl-blit.h"
+#include "cogl-context.h"
+#include "cogl-framebuffer-private.h"
+#include "cogl-texture-2d-private.h"
+
+static const CoglBlitMode *_cogl_blit_default_mode = NULL;
+
+static gboolean
+_cogl_blit_texture_render_begin (CoglBlitData *data)
+{
+  CoglHandle fbo;
+  CoglPipeline *pipeline;
+  unsigned int dst_width, dst_height;
+
+  _COGL_GET_CONTEXT (ctx, FALSE);
+
+  fbo = _cogl_offscreen_new_to_texture_full
+    (data->dst_tex, COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, 0 /* level */);
+
+  if (fbo == COGL_INVALID_HANDLE)
+    return FALSE;
+
+  cogl_push_framebuffer (fbo);
+  cogl_handle_unref (fbo);
+
+  dst_width = cogl_texture_get_width (data->dst_tex);
+  dst_height = cogl_texture_get_height (data->dst_tex);
+
+  /* Set up an orthographic projection so we can use pixel
+     coordinates to render to the texture */
+  cogl_ortho (0, /* left */
+              dst_width, /* right */
+              dst_height, /* bottom */
+              0, /* top */
+              -1, /* near */
+              1 /* far */);
+
+  /* We cache a pipeline used for migrating on to the context so
+     that it doesn't have to continuously regenerate a shader
+     program */
+  if (ctx->blit_texture_pipeline == NULL)
+    {
+      ctx->blit_texture_pipeline = cogl_pipeline_new ();
+
+      cogl_pipeline_set_layer_filters (ctx->blit_texture_pipeline, 0,
+                                       COGL_PIPELINE_FILTER_NEAREST,
+                                       COGL_PIPELINE_FILTER_NEAREST);
+    }
+
+  pipeline = ctx->blit_texture_pipeline;
+
+  cogl_pipeline_set_layer_texture (pipeline, 0, data->src_tex);
+
+  cogl_push_source (pipeline);
+
+  return TRUE;
+}
+
+static void
+_cogl_blit_texture_render_blit (CoglBlitData *data,
+                                unsigned int src_x,
+                                unsigned int src_y,
+                                unsigned int dst_x,
+                                unsigned int dst_y,
+                                unsigned int width,
+                                unsigned int height)
+{
+  cogl_rectangle_with_texture_coords (dst_x, dst_y,
+                                      dst_x + width,
+                                      dst_y + height,
+                                      src_x / (float) data->src_width,
+                                      src_y / (float) data->src_height,
+                                      (src_x + width) /
+                                      (float) data->src_width,
+                                      (src_y + height) /
+                                      (float) data->src_height);
+}
+
+static void
+_cogl_blit_texture_render_end (CoglBlitData *data)
+{
+  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
+
+  cogl_pop_source ();
+  cogl_pop_framebuffer ();
+
+  /* Attach the target texture to the texture render pipeline so that
+     we don't keep a reference to the source texture forever. This is
+     assuming that the destination texture will live for a long time
+     which is currently the case when cogl_blit_* is used from the
+     atlas code. It may be better in future to keep around a set of
+     dummy 1x1 textures for each texture target that we could bind
+     instead. This would also be useful when using a pipeline as a
+     hash table key such as for the ARBfp program cache. */
+  cogl_pipeline_set_layer_texture (ctx->blit_texture_pipeline, 0,
+                                   data->dst_tex);
+}
+
+static gboolean
+_cogl_blit_framebuffer_begin (CoglBlitData *data)
+{
+  CoglHandle dst_fbo, src_fbo;
+
+  _COGL_GET_CONTEXT (ctx, FALSE);
+
+  /* We can only blit between FBOs if both textures are the same
+     format and the blit framebuffer extension is supported */
+  if ((cogl_texture_get_format (data->src_tex) & ~COGL_A_BIT) !=
+      (cogl_texture_get_format (data->dst_tex) & ~COGL_A_BIT) ||
+      !cogl_features_available (COGL_FEATURE_OFFSCREEN_BLIT))
+    return FALSE;
+
+  dst_fbo = _cogl_offscreen_new_to_texture_full
+    (data->dst_tex, COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, 0 /* level */);
+
+  if (dst_fbo == COGL_INVALID_HANDLE)
+    return FALSE;
+
+  src_fbo = _cogl_offscreen_new_to_texture_full
+    (data->src_tex, COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, 0 /* level */);
+
+  if (src_fbo == COGL_INVALID_HANDLE)
+    {
+      cogl_handle_unref (dst_fbo);
+      return FALSE;
+    }
+
+  _cogl_push_framebuffers (dst_fbo, src_fbo);
+  cogl_handle_unref (src_fbo);
+  cogl_handle_unref (dst_fbo);
+
+  return TRUE;
+}
+
+static void
+_cogl_blit_framebuffer_blit (CoglBlitData *data,
+                             unsigned int src_x,
+                             unsigned int src_y,
+                             unsigned int dst_x,
+                             unsigned int dst_y,
+                             unsigned int width,
+                             unsigned int height)
+{
+  _cogl_blit_framebuffer (src_x, src_y,
+                          dst_x, dst_y,
+                          width, height);
+}
+
+static void
+_cogl_blit_framebuffer_end (CoglBlitData *data)
+{
+  cogl_pop_framebuffer ();
+}
+
+static gboolean
+_cogl_blit_copy_tex_sub_image_begin (CoglBlitData *data)
+{
+  CoglHandle fbo;
+
+  _COGL_GET_CONTEXT (ctx, FALSE);
+
+  /* This will only work if the target texture is a CoglTexture2D */
+  if (!_cogl_is_texture_2d (data->dst_tex))
+    return FALSE;
+
+  fbo = _cogl_offscreen_new_to_texture_full
+    (data->src_tex, COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, 0 /* level */);
+
+  if (fbo == COGL_INVALID_HANDLE)
+    return FALSE;
+
+  cogl_push_framebuffer (fbo);
+  cogl_handle_unref (fbo);
+
+  return TRUE;
+}
+
+static void
+_cogl_blit_copy_tex_sub_image_blit (CoglBlitData *data,
+                                    unsigned int src_x,
+                                    unsigned int src_y,
+                                    unsigned int dst_x,
+                                    unsigned int dst_y,
+                                    unsigned int width,
+                                    unsigned int height)
+{
+  _cogl_texture_2d_copy_from_framebuffer (data->dst_tex,
+                                          dst_x, dst_y,
+                                          src_x, src_y,
+                                          width, height);
+}
+
+static void
+_cogl_blit_copy_tex_sub_image_end (CoglBlitData *data)
+{
+  cogl_pop_framebuffer ();
+}
+
+static gboolean
+_cogl_blit_get_tex_data_begin (CoglBlitData *data)
+{
+  data->format = cogl_texture_get_format (data->src_tex);
+  data->bpp = _cogl_get_format_bpp (data->format);
+
+  data->image_data = g_malloc (data->bpp * data->src_width *
+                               data->src_height);
+  cogl_texture_get_data (data->src_tex, data->format,
+                         data->src_width * data->bpp, data->image_data);
+
+  return TRUE;
+}
+
+static void
+_cogl_blit_get_tex_data_blit (CoglBlitData *data,
+                              unsigned int src_x,
+                              unsigned int src_y,
+                              unsigned int dst_x,
+                              unsigned int dst_y,
+                              unsigned int width,
+                              unsigned int height)
+{
+  cogl_texture_set_region (data->dst_tex,
+                           src_x, src_y,
+                           dst_x, dst_y,
+                           width, height,
+                           data->src_width, data->src_height,
+                           data->format,
+                           data->src_width * data->bpp,
+                           data->image_data);
+}
+
+static void
+_cogl_blit_get_tex_data_end (CoglBlitData *data)
+{
+  g_free (data->image_data);
+}
+
+/* These should be specified in order of preference */
+static const CoglBlitMode
+_cogl_blit_modes[] =
+  {
+    {
+      "texture-render",
+      _cogl_blit_texture_render_begin,
+      _cogl_blit_texture_render_blit,
+      _cogl_blit_texture_render_end
+    },
+    {
+      "framebuffer",
+      _cogl_blit_framebuffer_begin,
+      _cogl_blit_framebuffer_blit,
+      _cogl_blit_framebuffer_end
+    },
+    {
+      "copy-tex-sub-image",
+      _cogl_blit_copy_tex_sub_image_begin,
+      _cogl_blit_copy_tex_sub_image_blit,
+      _cogl_blit_copy_tex_sub_image_end
+    },
+    {
+      "get-tex-data",
+      _cogl_blit_get_tex_data_begin,
+      _cogl_blit_get_tex_data_blit,
+      _cogl_blit_get_tex_data_end
+    }
+  };
+
+void
+_cogl_blit_begin (CoglBlitData *data,
+                  CoglHandle dst_tex,
+                  CoglHandle src_tex)
+{
+  int i;
+
+  if (_cogl_blit_default_mode == NULL)
+    {
+      const char *default_mode_string;
+
+      /* Allow the default to be specified with an environment
+         variable. For the time being these functions are only used
+         when blitting between atlas textures so the environment
+         variable is named to be specific to the atlas code. If we
+         want to use the code in other places we should create another
+         environment variable for each specific use case */
+      if ((default_mode_string = g_getenv ("COGL_ATLAS_DEFAULT_BLIT_MODE")))
+        {
+          for (i = 0; i < G_N_ELEMENTS (_cogl_blit_modes); i++)
+            if (!strcmp (_cogl_blit_modes[i].name, default_mode_string))
+              {
+                _cogl_blit_default_mode = _cogl_blit_modes + i;
+                break;
+              }
+
+          if (i >= G_N_ELEMENTS (_cogl_blit_modes))
+            {
+              g_warning ("Unknown blit mode %s", default_mode_string);
+              _cogl_blit_default_mode = _cogl_blit_modes;
+            }
+        }
+      else
+        /* Default to the first blit mode */
+        _cogl_blit_default_mode = _cogl_blit_modes;
+    }
+
+  data->dst_tex = dst_tex;
+  data->src_tex = src_tex;
+
+  data->src_width = cogl_texture_get_width (src_tex);
+  data->src_height = cogl_texture_get_height (src_tex);
+
+  /* Try the default blit mode first */
+  if (!_cogl_blit_default_mode->begin_func (data))
+    {
+      COGL_NOTE (ATLAS, "Failed to set up blit mode %s",
+                 _cogl_blit_default_mode->name);
+
+      /* Try all of the other modes in order */
+      for (i = 0; i < G_N_ELEMENTS (_cogl_blit_modes); i++)
+        if (_cogl_blit_modes + i != _cogl_blit_default_mode &&
+            _cogl_blit_modes[i].begin_func (data))
+          {
+            /* Use this mode as the default from now on */
+            _cogl_blit_default_mode = _cogl_blit_modes + i;
+            break;
+          }
+        else
+          COGL_NOTE (ATLAS,
+                     "Failed to set up blit mode %s",
+                     _cogl_blit_modes[i].name);
+
+      /* The last blit mode can't fail so this should never happen */
+      g_return_if_fail (i < G_N_ELEMENTS (_cogl_blit_modes));
+    }
+
+  data->blit_mode = _cogl_blit_default_mode;
+
+  COGL_NOTE (ATLAS, "Setup blit using %s", _cogl_blit_default_mode->name);
+}
+
+void
+_cogl_blit (CoglBlitData *data,
+            unsigned int src_x,
+            unsigned int src_y,
+            unsigned int dst_x,
+            unsigned int dst_y,
+            unsigned int width,
+            unsigned int height)
+{
+  data->blit_mode->blit_func (data, src_x, src_y, dst_x, dst_y, width, height);
+}
+
+void
+_cogl_blit_end (CoglBlitData *data)
+{
+  data->blit_mode->end_func (data);
+}
diff --git a/clutter/cogl/cogl/cogl-blit.h b/clutter/cogl/cogl/cogl-blit.h
new file mode 100644
index 000000000..95e708ea3
--- /dev/null
+++ b/clutter/cogl/cogl/cogl-blit.h
@@ -0,0 +1,89 @@
+/*
+ * 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __COGL_BLIT_H
+#define __COGL_BLIT_H
+
+#include <glib.h>
+#include "cogl-handle.h"
+
+/* This structures and functions are used when a series of blits needs
+   to be performed between two textures. In this case there are
+   multiple methods we can use, most of which involve transferring
+   between an FBO bound to the texture. */
+
+typedef struct _CoglBlitData CoglBlitData;
+
+typedef gboolean (* CoglBlitBeginFunc) (CoglBlitData *data);
+typedef void (* CoglBlitEndFunc) (CoglBlitData *data);
+
+typedef void (* CoglBlitFunc) (CoglBlitData *data,
+                               unsigned int src_x,
+                               unsigned int src_y,
+                               unsigned int dst_x,
+                               unsigned int dst_y,
+                               unsigned int width,
+                               unsigned int height);
+
+typedef struct
+{
+  const char *name;
+  CoglBlitBeginFunc begin_func;
+  CoglBlitFunc blit_func;
+  CoglBlitEndFunc end_func;
+} CoglBlitMode;
+
+struct _CoglBlitData
+{
+  CoglHandle src_tex, dst_tex;
+
+  unsigned int src_width;
+  unsigned int src_height;
+
+  const CoglBlitMode *blit_mode;
+
+  /* If we're not using an FBO then we g_malloc a buffer and copy the
+     complete texture data in */
+  unsigned char *image_data;
+  CoglPixelFormat format;
+  int bpp;
+};
+
+void
+_cogl_blit_begin (CoglBlitData *data,
+                  CoglHandle dst_tex,
+                  CoglHandle src_tex);
+
+void
+_cogl_blit (CoglBlitData *data,
+            unsigned int src_x,
+            unsigned int src_y,
+            unsigned int dst_x,
+            unsigned int dst_y,
+            unsigned int width,
+            unsigned int height);
+
+void
+_cogl_blit_end (CoglBlitData *data);
+
+#endif /* __COGL_BLIT_H */
diff --git a/clutter/cogl/cogl/cogl-context.c b/clutter/cogl/cogl/cogl-context.c
index bea9357bd..115fc5c67 100644
--- a/clutter/cogl/cogl/cogl-context.c
+++ b/clutter/cogl/cogl/cogl-context.c
@@ -244,6 +244,7 @@ cogl_create_context (void)
   _context->rectangle_short_indices_len = 0;
 
   _context->texture_download_pipeline = COGL_INVALID_HANDLE;
+  _context->blit_texture_pipeline = COGL_INVALID_HANDLE;
 
 #ifndef HAVE_COGL_GLES2
   /* The default for GL_ALPHA_TEST is to always pass which is equivalent to
@@ -333,6 +334,9 @@ _cogl_destroy_context (void)
   if (_context->texture_pipeline)
     cogl_handle_unref (_context->texture_pipeline);
 
+  if (_context->blit_texture_pipeline)
+    cogl_handle_unref (_context->blit_texture_pipeline);
+
   if (_context->journal_flush_attributes_array)
     g_array_free (_context->journal_flush_attributes_array, TRUE);
   if (_context->journal_clip_bounds)
diff --git a/clutter/cogl/cogl/cogl-context.h b/clutter/cogl/cogl/cogl-context.h
index 8c0161fab..eead5dc09 100644
--- a/clutter/cogl/cogl/cogl-context.h
+++ b/clutter/cogl/cogl/cogl-context.h
@@ -175,6 +175,7 @@ typedef struct
   gboolean          in_begin_gl_block;
 
   CoglPipeline     *texture_download_pipeline;
+  CoglPipeline     *blit_texture_pipeline;
 
   GSList           *atlases;