[cogl/wip/pbo-read-pixels: 2/7] Add a public cogl_framebuffer_read_pixels_into_bitmap



commit 3ac01854d172a01e4284bd4995b8c951b254d81b
Author: Neil Roberts <neil linux intel com>
Date:   Sat Feb 25 19:23:51 2012 +0000

    Add a public cogl_framebuffer_read_pixels_into_bitmap
    
    This adds a public function to read pixels from a framebuffer into a
    CoglBitmap. This replaces the internal function
    _cogl_read_pixels_with_rowstride because a CoglBitmap contains a
    rowstride so it can be used for the same purpose. A CoglBitmap already
    has public API to make one that points to a CoglPixelBuffer so this
    function can be used to read pixels into a PBO. It also avoids the
    need to push the framebuffer on to the context's stack so it provides
    a function which can be used in the 2.0 API after the stack is
    removed.

 cogl/cogl-bitmap-private.h                         |    2 +-
 cogl/cogl-bitmap.c                                 |    7 +-
 cogl/cogl-framebuffer-private.h                    |    8 -
 cogl/cogl-framebuffer.c                            |  270 +++++++++++++++++++-
 cogl/cogl-framebuffer.h                            |   34 +++
 cogl/cogl-journal-private.h                        |    3 +-
 cogl/cogl-journal.c                                |   15 +-
 cogl/cogl-private.h                                |   10 -
 cogl/cogl-texture.c                                |   25 +-
 cogl/cogl.c                                        |  213 +---------------
 .../cogl-2.0-experimental-sections.txt             |    1 +
 11 files changed, 347 insertions(+), 241 deletions(-)
---
diff --git a/cogl/cogl-bitmap-private.h b/cogl/cogl-bitmap-private.h
index 7a398d5..70d54b8 100644
--- a/cogl/cogl-bitmap-private.h
+++ b/cogl/cogl-bitmap-private.h
@@ -126,7 +126,7 @@ CoglBitmap *
 _cogl_bitmap_convert_format_and_premult (CoglBitmap *bmp,
                                          CoglPixelFormat   dst_format);
 
-void
+gboolean
 _cogl_bitmap_copy_subregion (CoglBitmap *src,
 			     CoglBitmap *dst,
 			     int         src_x,
diff --git a/cogl/cogl-bitmap.c b/cogl/cogl-bitmap.c
index 20fd808..978e89d 100644
--- a/cogl/cogl-bitmap.c
+++ b/cogl/cogl-bitmap.c
@@ -192,7 +192,7 @@ _cogl_bitmap_copy (CoglBitmap *src_bmp)
   return dst_bmp;
 }
 
-void
+gboolean
 _cogl_bitmap_copy_subregion (CoglBitmap *src,
 			     CoglBitmap *dst,
 			     int         src_x,
@@ -206,6 +206,7 @@ _cogl_bitmap_copy_subregion (CoglBitmap *src,
   guint8 *dstdata;
   int    bpp;
   int    line;
+  int    succeeded;
 
   /* Intended only for fast copies when format is equal! */
   g_assert (src->format == dst->format);
@@ -225,11 +226,15 @@ _cogl_bitmap_copy_subregion (CoglBitmap *src,
               dstdata += dst->rowstride;
             }
 
+          succeeded = TRUE;
+
           _cogl_bitmap_unmap (dst);
         }
 
       _cogl_bitmap_unmap (src);
     }
+
+  return succeeded;
 }
 
 gboolean
diff --git a/cogl/cogl-framebuffer-private.h b/cogl/cogl-framebuffer-private.h
index 6685d31..0081a2f 100644
--- a/cogl/cogl-framebuffer-private.h
+++ b/cogl/cogl-framebuffer-private.h
@@ -244,14 +244,6 @@ _cogl_framebuffer_flush_journal (CoglFramebuffer *framebuffer);
 void
 _cogl_framebuffer_flush_dependency_journals (CoglFramebuffer *framebuffer);
 
-gboolean
-_cogl_framebuffer_try_fast_read_pixel (CoglFramebuffer *framebuffer,
-                                       int x,
-                                       int y,
-                                       CoglReadPixelsFlags source,
-                                       CoglPixelFormat format,
-                                       guint8 *pixel);
-
 void
 _cogl_framebuffer_flush_state (CoglFramebuffer *draw_buffer,
                                CoglFramebuffer *read_buffer,
diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c
index 5a549ab..046d911 100644
--- a/cogl/cogl-framebuffer.c
+++ b/cogl/cogl-framebuffer.c
@@ -25,6 +25,8 @@
 #include "config.h"
 #endif
 
+#include <string.h>
+
 #include "cogl-debug.h"
 #include "cogl-internal.h"
 #include "cogl-context-private.h"
@@ -43,6 +45,7 @@
 #include "cogl-primitive-private.h"
 #include "cogl-offscreen.h"
 #include "cogl1-context.h"
+#include "cogl-private.h"
 
 #ifndef GL_FRAMEBUFFER
 #define GL_FRAMEBUFFER		0x8D40
@@ -1860,15 +1863,15 @@ cogl_framebuffer_get_context (CoglFramebuffer *framebuffer)
   return framebuffer->context;
 }
 
-gboolean
+static gboolean
 _cogl_framebuffer_try_fast_read_pixel (CoglFramebuffer *framebuffer,
                                        int x,
                                        int y,
                                        CoglReadPixelsFlags source,
-                                       CoglPixelFormat format,
-                                       guint8 *pixel)
+                                       CoglBitmap *bitmap)
 {
   gboolean found_intersection;
+  CoglPixelFormat format;
 
   if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_FAST_READ_PIXEL)))
     return FALSE;
@@ -1876,12 +1879,14 @@ _cogl_framebuffer_try_fast_read_pixel (CoglFramebuffer *framebuffer,
   if (source != COGL_READ_PIXELS_COLOR_BUFFER)
     return FALSE;
 
+  format = _cogl_bitmap_get_format (bitmap);
+
   if (format != COGL_PIXEL_FORMAT_RGBA_8888_PRE &&
       format != COGL_PIXEL_FORMAT_RGBA_8888)
     return FALSE;
 
   if (!_cogl_journal_try_read_pixel (framebuffer->journal,
-                                     x, y, format, pixel,
+                                     x, y, bitmap,
                                      &found_intersection))
     return FALSE;
 
@@ -1906,23 +1911,280 @@ _cogl_framebuffer_try_fast_read_pixel (CoglFramebuffer *framebuffer,
       y >= framebuffer->clear_clip_y0 &&
       y < framebuffer->clear_clip_y1)
     {
+      guint8 *pixel;
 
       /* we currently only care about cases where the premultiplied or
        * unpremultipled colors are equivalent... */
       if (framebuffer->clear_color_alpha != 1.0)
         return FALSE;
 
+      pixel = _cogl_bitmap_map (bitmap,
+                                COGL_BUFFER_ACCESS_WRITE,
+                                COGL_BUFFER_MAP_HINT_DISCARD);
+      if (pixel == NULL)
+        return FALSE;
+
       pixel[0] = framebuffer->clear_color_red * 255.0;
       pixel[1] = framebuffer->clear_color_green * 255.0;
       pixel[2] = framebuffer->clear_color_blue * 255.0;
       pixel[3] = framebuffer->clear_color_alpha * 255.0;
 
+      _cogl_bitmap_unmap (bitmap);
+
       return TRUE;
     }
 
   return FALSE;
 }
 
+gboolean
+cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer,
+                                          int x,
+                                          int y,
+                                          CoglReadPixelsFlags source,
+                                          CoglBitmap *bitmap)
+{
+  CoglContext *ctx;
+  int framebuffer_height;
+  CoglPixelFormat format;
+  CoglPixelFormat required_format;
+  GLenum gl_intformat;
+  GLenum gl_format;
+  GLenum gl_type;
+  gboolean pack_invert_set;
+  int width;
+  int height;
+
+  _COGL_RETURN_VAL_IF_FAIL (source == COGL_READ_PIXELS_COLOR_BUFFER, FALSE);
+  _COGL_RETURN_VAL_IF_FAIL (_cogl_is_framebuffer (framebuffer), FALSE);
+
+  ctx = cogl_framebuffer_get_context (framebuffer);
+
+  width = _cogl_bitmap_get_width (bitmap);
+  height = _cogl_bitmap_get_height (bitmap);
+
+  if (width == 1 && height == 1 && !framebuffer->clear_clip_dirty)
+    {
+      /* If everything drawn so far for this frame is still in the
+       * Journal then if all of the rectangles only have a flat
+       * opaque color we have a fast-path for reading a single pixel
+       * that avoids the relatively high cost of flushing primitives
+       * to be drawn on the GPU (considering how simple the geometry
+       * is in this case) and then blocking on the long GPU pipelines
+       * for the result.
+       */
+      if (_cogl_framebuffer_try_fast_read_pixel (framebuffer,
+                                                 x, y, source, bitmap))
+        return TRUE;
+    }
+
+  /* make sure any batched primitives get emitted to the GL driver
+   * before issuing our read pixels...
+   */
+  _cogl_framebuffer_flush_journal (framebuffer);
+
+  _cogl_framebuffer_flush_state (framebuffer,
+                                 framebuffer,
+                                 COGL_FRAMEBUFFER_STATE_BIND);
+
+  framebuffer_height = cogl_framebuffer_get_height (framebuffer);
+
+  /* The y co-ordinate should be given in OpenGL's coordinate system
+   * so 0 is the bottom row
+   *
+   * NB: all offscreen rendering is done upside down so no conversion
+   * is necissary in this case.
+   */
+  if (!cogl_is_offscreen (framebuffer))
+    y = framebuffer_height - y - height;
+
+  format = _cogl_bitmap_get_format (bitmap);
+
+  required_format = ctx->texture_driver->pixel_format_to_gl (format,
+                                                             &gl_intformat,
+                                                             &gl_format,
+                                                             &gl_type);
+
+  /* NB: All offscreen rendering is done upside down so there is no need
+   * to flip in this case... */
+  if ((ctx->private_feature_flags & COGL_PRIVATE_FEATURE_MESA_PACK_INVERT) &&
+      !cogl_is_offscreen (framebuffer))
+    {
+      GE (ctx, glPixelStorei (GL_PACK_INVERT_MESA, TRUE));
+      pack_invert_set = TRUE;
+    }
+  else
+    pack_invert_set = FALSE;
+
+  /* Under GLES only GL_RGBA with GL_UNSIGNED_BYTE as well as an
+     implementation specific format under
+     GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES and
+     GL_IMPLEMENTATION_COLOR_READ_TYPE_OES is supported. We could try
+     to be more clever and check if the requested type matches that
+     but we would need some reliable functions to convert from GL
+     types to Cogl types. For now, lets just always read in
+     GL_RGBA/GL_UNSIGNED_BYTE and convert if necessary. We also need
+     to use this intermediate buffer if the rowstride has padding
+     because GLES does not support setting GL_ROW_LENGTH */
+  if ((ctx->driver != COGL_DRIVER_GL &&
+       (gl_format != GL_RGBA || gl_type != GL_UNSIGNED_BYTE ||
+        _cogl_bitmap_get_rowstride (bitmap) != 4 * width)) ||
+      (required_format & ~COGL_PREMULT_BIT) != (format & ~COGL_PREMULT_BIT))
+    {
+      CoglBitmap *tmp_bmp, *dst_bmp;
+      guint8 *tmp_data;
+      CoglPixelFormat read_format;
+      int bpp, rowstride;
+      int succeeded = FALSE;
+
+      if (ctx->driver == COGL_DRIVER_GL)
+        read_format = required_format;
+      else
+        {
+          read_format = COGL_PIXEL_FORMAT_RGBA_8888;
+          gl_format = GL_RGBA;
+          gl_type = GL_UNSIGNED_BYTE;
+        }
+
+      if ((read_format & COGL_A_BIT))
+        read_format = ((read_format & ~COGL_PREMULT_BIT) |
+                       (framebuffer->format & COGL_PREMULT_BIT));
+
+      bpp = _cogl_pixel_format_get_bytes_per_pixel (read_format);
+      rowstride = (width * bpp + 3) & ~3;
+      tmp_data = g_malloc (rowstride * height);
+
+      tmp_bmp = _cogl_bitmap_new_from_data (tmp_data,
+                                            read_format,
+                                            width, height, rowstride,
+                                            (CoglBitmapDestroyNotify) g_free,
+                                            NULL);
+
+      ctx->texture_driver->prep_gl_for_pixels_download (rowstride, bpp);
+
+      GE( ctx, glReadPixels (x, y, width, height,
+                             gl_format, gl_type,
+                             tmp_data) );
+
+      /* CoglBitmap doesn't currently have a way to convert without
+         allocating its own buffer so we have to copy the data
+         again */
+      if ((dst_bmp = _cogl_bitmap_convert_format_and_premult (tmp_bmp,
+                                                              format)))
+        {
+          if (_cogl_bitmap_copy_subregion (dst_bmp,
+                                           bitmap,
+                                           0, 0,
+                                           0, 0,
+                                           width, height))
+            succeeded = TRUE;
+
+          cogl_object_unref (dst_bmp);
+        }
+
+      cogl_object_unref (tmp_bmp);
+
+      if (!succeeded)
+        return FALSE;
+    }
+  else
+    {
+      CoglBitmap *shared_bmp;
+      CoglPixelFormat bmp_format;
+      int bpp, rowstride;
+      gboolean succeeded = FALSE;
+      guint8 *pixels;
+
+      rowstride = _cogl_bitmap_get_rowstride (bitmap);
+
+      /* We match the premultiplied state of the target buffer to the
+       * premultiplied state of the framebuffer so that it will get
+       * converted to the right format below */
+      if ((format & COGL_A_BIT))
+        bmp_format = ((format & ~COGL_PREMULT_BIT) |
+                      (framebuffer->format & COGL_PREMULT_BIT));
+
+      if (bmp_format != format)
+        shared_bmp = _cogl_bitmap_new_shared (bitmap,
+                                              bmp_format,
+                                              width, height,
+                                              rowstride);
+      else
+        shared_bmp = cogl_object_ref (bitmap);
+
+      bpp = _cogl_pixel_format_get_bytes_per_pixel (bmp_format);
+
+      ctx->texture_driver->prep_gl_for_pixels_download (rowstride, bpp);
+
+      pixels = _cogl_bitmap_bind (shared_bmp,
+                                  COGL_BUFFER_ACCESS_WRITE,
+                                  0 /* hints */);
+
+      GE( ctx, glReadPixels (x, y,
+                             width, height,
+                             gl_format, gl_type,
+                             pixels) );
+
+      _cogl_bitmap_unbind (shared_bmp);
+
+      /* Convert to the premult format specified by the caller
+         in-place. This will do nothing if the premult status is already
+         correct. */
+      if (_cogl_bitmap_convert_premult_status (shared_bmp, format))
+        succeeded = TRUE;
+
+      cogl_object_unref (shared_bmp);
+
+      if (!succeeded)
+        return FALSE;
+    }
+
+  /* Currently this function owns the pack_invert state and we don't want this
+   * to interfere with other Cogl components so all other code can assume that
+   * we leave the pack_invert state off. */
+  if (pack_invert_set)
+    GE (ctx, glPixelStorei (GL_PACK_INVERT_MESA, FALSE));
+
+  /* NB: All offscreen rendering is done upside down so there is no need
+   * to flip in this case... */
+  if (!cogl_is_offscreen (framebuffer) && !pack_invert_set)
+    {
+      guint8 *temprow;
+      int rowstride;
+      guint8 *pixels;
+
+      rowstride = _cogl_bitmap_get_rowstride (bitmap);
+      pixels = _cogl_bitmap_map (bitmap,
+                                 COGL_BUFFER_ACCESS_READ |
+                                 COGL_BUFFER_ACCESS_WRITE,
+                                 0 /* hints */);
+
+      if (pixels == NULL)
+        return FALSE;
+
+      temprow = g_alloca (rowstride * sizeof (guint8));
+
+      /* vertically flip the buffer in-place */
+      for (y = 0; y < height / 2; y++)
+        {
+          if (y != height - y - 1) /* skip center row */
+            {
+              memcpy (temprow,
+                      pixels + y * rowstride, rowstride);
+              memcpy (pixels + y * rowstride,
+                      pixels + (height - y - 1) * rowstride, rowstride);
+              memcpy (pixels + (height - y - 1) * rowstride,
+                      temprow,
+                      rowstride);
+            }
+        }
+
+      _cogl_bitmap_unmap (bitmap);
+    }
+
+  return TRUE;
+}
+
 void
 _cogl_blit_framebuffer (unsigned int src_x,
                         unsigned int src_y,
diff --git a/cogl/cogl-framebuffer.h b/cogl/cogl-framebuffer.h
index 1fbd23d..39dc543 100644
--- a/cogl/cogl-framebuffer.h
+++ b/cogl/cogl-framebuffer.h
@@ -46,6 +46,7 @@
 
 #include <cogl/cogl-pipeline.h>
 #include <cogl/cogl-indices.h>
+#include <cogl/cogl-bitmap.h>
 
 G_BEGIN_DECLS
 
@@ -1246,6 +1247,39 @@ void
 cogl_framebuffer_finish (CoglFramebuffer *framebuffer);
 
 /**
+ * cogl_framebuffer_read_pixels_into_bitmap:
+ * @framebuffer: A #CoglFramebuffer
+ * @x: The x position to read from
+ * @y: The y position to read from
+ * @source: Identifies which auxillary buffer you want to read
+ *          (only COGL_READ_PIXELS_COLOR_BUFFER supported currently)
+ * @bitmap: The bitmap to store the results in.
+ *
+ * This reads a rectangle of pixels from the given framebuffer where
+ * position (0, 0) is the top left. The pixel at (x, y) is the first
+ * read, and a rectangle of pixels with the same size as the bitmap is
+ * read left and downwards from that point.
+ *
+ * Currently Cogl assumes that the framebuffer is in a premultiplied
+ * format so if the format of @bitmap is non-premultiplied it will
+ * convert it. To read the pixel values without any conversion you
+ * should either specify a format that doesn't use an alpha channel or
+ * use one of the formats ending in PRE.
+ *
+ * Return value: %TRUE if the read succeeded or %FALSE otherwise. The
+ *  function is only likely to fail if the bitmap points to a pixel
+ *  buffer and it could not be mapped.
+ * Since: 1.10
+ * Stability: unstable
+ */
+gboolean
+cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer,
+                                          int x,
+                                          int y,
+                                          CoglReadPixelsFlags source,
+                                          CoglBitmap *bitmap);
+
+/**
  * cogl_get_draw_framebuffer:
  *
  * Gets the current #CoglFramebuffer as set using
diff --git a/cogl/cogl-journal-private.h b/cogl/cogl-journal-private.h
index c561a10..e3dbaa8 100644
--- a/cogl/cogl-journal-private.h
+++ b/cogl/cogl-journal-private.h
@@ -105,8 +105,7 @@ gboolean
 _cogl_journal_try_read_pixel (CoglJournal *journal,
                               int x,
                               int y,
-                              CoglPixelFormat format,
-                              guint8 *pixel,
+                              CoglBitmap *bitmap,
                               gboolean *found_intersection);
 
 #endif /* __COGL_JOURNAL_PRIVATE_H */
diff --git a/cogl/cogl-journal.c b/cogl/cogl-journal.c
index bec9e6d..a581129 100644
--- a/cogl/cogl-journal.c
+++ b/cogl/cogl-journal.c
@@ -1791,10 +1791,10 @@ gboolean
 _cogl_journal_try_read_pixel (CoglJournal *journal,
                               int x,
                               int y,
-                              CoglPixelFormat format,
-                              guint8 *pixel,
+                              CoglBitmap *bitmap,
                               gboolean *found_intersection)
 {
+  CoglPixelFormat format;
   int i;
 
   _COGL_GET_CONTEXT (ctx, FALSE);
@@ -1810,6 +1810,8 @@ _cogl_journal_try_read_pixel (CoglJournal *journal,
   if (journal->fast_read_pixel_count > 50)
     return FALSE;
 
+  format = _cogl_bitmap_get_format (bitmap);
+
   if (format != COGL_PIXEL_FORMAT_RGBA_8888_PRE &&
       format != COGL_PIXEL_FORMAT_RGBA_8888)
     return FALSE;
@@ -1832,6 +1834,7 @@ _cogl_journal_try_read_pixel (CoglJournal *journal,
       float *vertices = (float *)color + 1;
       float poly[16];
       CoglFramebuffer *framebuffer = journal->framebuffer;
+      guint8 *pixel;
 
       entry_to_screen_polygon (framebuffer, entry, vertices, poly);
 
@@ -1870,11 +1873,19 @@ _cogl_journal_try_read_pixel (CoglJournal *journal,
       if (color[3] != 0xff)
         return FALSE;
 
+      pixel = _cogl_bitmap_map (bitmap,
+                                COGL_BUFFER_ACCESS_WRITE,
+                                COGL_BUFFER_MAP_HINT_DISCARD);
+      if (pixel == NULL)
+        return FALSE;
+
       pixel[0] = color[0];
       pixel[1] = color[1];
       pixel[2] = color[2];
       pixel[3] = color[3];
 
+      _cogl_bitmap_unmap (bitmap);
+
       goto success;
     }
 
diff --git a/cogl/cogl-private.h b/cogl/cogl-private.h
index 8f2a72e..df86813 100644
--- a/cogl/cogl-private.h
+++ b/cogl/cogl-private.h
@@ -45,16 +45,6 @@ void
 _cogl_clear (const CoglColor *color, unsigned long buffers);
 
 void
-_cogl_read_pixels_with_rowstride (int x,
-                                  int y,
-                                  int width,
-                                  int height,
-                                  CoglReadPixelsFlags source,
-                                  CoglPixelFormat format,
-                                  guint8 *pixels,
-                                  int rowstride);
-
-void
 _cogl_init (void);
 
 void
diff --git a/cogl/cogl-texture.c b/cogl/cogl-texture.c
index 79f5070..53e087a 100644
--- a/cogl/cogl-texture.c
+++ b/cogl/cogl-texture.c
@@ -1021,6 +1021,7 @@ get_texture_bits_via_offscreen (CoglTexture    *texture,
 {
   CoglOffscreen *offscreen;
   CoglFramebuffer *framebuffer;
+  CoglBitmap *bitmap;
 
   _COGL_GET_CONTEXT (ctx, FALSE);
 
@@ -1037,19 +1038,17 @@ get_texture_bits_via_offscreen (CoglTexture    *texture,
 
   framebuffer = COGL_FRAMEBUFFER (offscreen);
 
-  if (!cogl_framebuffer_allocate (framebuffer, NULL))
-    {
-      cogl_object_unref (framebuffer);
-      return FALSE;
-    }
-
-  cogl_push_framebuffer (framebuffer);
-
-  _cogl_read_pixels_with_rowstride (x, y, width, height,
-                                    COGL_READ_PIXELS_COLOR_BUFFER,
-                                    dst_format, dst_bits, dst_rowstride);
-
-  cogl_pop_framebuffer ();
+  bitmap = _cogl_bitmap_new_from_data (dst_bits,
+                                       dst_format,
+                                       width, height,
+                                       dst_rowstride,
+                                       NULL, /* destroy_fn */
+                                       NULL /* destroy_fn_data */);
+  cogl_framebuffer_read_pixels_into_bitmap (framebuffer,
+                                            x, y,
+                                            COGL_READ_PIXELS_COLOR_BUFFER,
+                                            bitmap);
+  cogl_object_unref (bitmap);
 
   cogl_object_unref (framebuffer);
 
diff --git a/cogl/cogl.c b/cogl/cogl.c
index 3784836..bfd7ab5 100644
--- a/cogl/cogl.c
+++ b/cogl/cogl.c
@@ -369,202 +369,6 @@ cogl_flush (void)
 }
 
 void
-_cogl_read_pixels_with_rowstride (int x,
-                                  int y,
-                                  int width,
-                                  int height,
-                                  CoglReadPixelsFlags source,
-                                  CoglPixelFormat format,
-                                  guint8 *pixels,
-                                  int rowstride)
-{
-  CoglFramebuffer *framebuffer = _cogl_get_read_framebuffer ();
-  int              framebuffer_height;
-  int              bpp;
-  CoglBitmap      *bmp;
-  GLenum           gl_intformat;
-  GLenum           gl_format;
-  GLenum           gl_type;
-  CoglPixelFormat  bmp_format;
-  gboolean         pack_invert_set;
-
-  _COGL_GET_CONTEXT (ctx, NO_RETVAL);
-
-  _COGL_RETURN_IF_FAIL (source == COGL_READ_PIXELS_COLOR_BUFFER);
-
-  if (width == 1 && height == 1 && !framebuffer->clear_clip_dirty)
-    {
-      /* If everything drawn so far for this frame is still in the
-       * Journal then if all of the rectangles only have a flat
-       * opaque color we have a fast-path for reading a single pixel
-       * that avoids the relatively high cost of flushing primitives
-       * to be drawn on the GPU (considering how simple the geometry
-       * is in this case) and then blocking on the long GPU pipelines
-       * for the result.
-       */
-      if (_cogl_framebuffer_try_fast_read_pixel (framebuffer,
-                                                 x, y, source, format,
-                                                 pixels))
-        return;
-    }
-
-  /* make sure any batched primitives get emitted to the GL driver
-   * before issuing our read pixels...
-   *
-   * XXX: Note we currently use cogl_flush to ensure *all* journals
-   * are flushed here and not _cogl_journal_flush because we don't
-   * track the dependencies between framebuffers so we don't know if
-   * the current framebuffer depends on the contents of other
-   * framebuffers which could also have associated journal entries.
-   */
-  cogl_flush ();
-
-  _cogl_framebuffer_flush_state (cogl_get_draw_framebuffer (),
-                                 framebuffer,
-                                 COGL_FRAMEBUFFER_STATE_BIND);
-
-  framebuffer_height = cogl_framebuffer_get_height (framebuffer);
-
-  /* The y co-ordinate should be given in OpenGL's coordinate system
-   * so 0 is the bottom row
-   *
-   * NB: all offscreen rendering is done upside down so no conversion
-   * is necissary in this case.
-   */
-  if (!cogl_is_offscreen (framebuffer))
-    y = framebuffer_height - y - height;
-
-  /* Initialise the CoglBitmap */
-  bpp = _cogl_pixel_format_get_bytes_per_pixel (format);
-  bmp_format = format;
-
-  if ((format & COGL_A_BIT))
-    {
-      /* We match the premultiplied state of the target buffer to the
-       * premultiplied state of the framebuffer so that it will get
-       * converted to the right format below */
-
-      if ((framebuffer->format & COGL_PREMULT_BIT))
-        bmp_format |= COGL_PREMULT_BIT;
-      else
-        bmp_format &= ~COGL_PREMULT_BIT;
-    }
-
-  bmp = _cogl_bitmap_new_from_data (pixels,
-                                    bmp_format, width, height, rowstride,
-                                    NULL, NULL);
-
-  ctx->texture_driver->pixel_format_to_gl (format,
-                                           &gl_intformat,
-                                           &gl_format,
-                                           &gl_type);
-
-  /* NB: All offscreen rendering is done upside down so there is no need
-   * to flip in this case... */
-  if ((ctx->private_feature_flags & COGL_PRIVATE_FEATURE_MESA_PACK_INVERT) &&
-      !cogl_is_offscreen (framebuffer))
-    {
-      GE (ctx, glPixelStorei (GL_PACK_INVERT_MESA, TRUE));
-      pack_invert_set = TRUE;
-    }
-  else
-    pack_invert_set = FALSE;
-
-  /* Under GLES only GL_RGBA with GL_UNSIGNED_BYTE as well as an
-     implementation specific format under
-     GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES and
-     GL_IMPLEMENTATION_COLOR_READ_TYPE_OES is supported. We could try
-     to be more clever and check if the requested type matches that
-     but we would need some reliable functions to convert from GL
-     types to Cogl types. For now, lets just always read in
-     GL_RGBA/GL_UNSIGNED_BYTE and convert if necessary. We also need
-     to use this intermediate buffer if the rowstride has padding
-     because GLES does not support setting GL_ROW_LENGTH */
-  if (ctx->driver != COGL_DRIVER_GL &&
-      (gl_format != GL_RGBA || gl_type != GL_UNSIGNED_BYTE ||
-       rowstride != 4 * width))
-    {
-      CoglBitmap *tmp_bmp, *dst_bmp;
-      guint8 *tmp_data = g_malloc (width * height * 4);
-
-      tmp_bmp = _cogl_bitmap_new_from_data (tmp_data,
-                                            COGL_PIXEL_FORMAT_RGBA_8888 |
-                                            (bmp_format & COGL_PREMULT_BIT),
-                                            width, height, 4 * width,
-                                            (CoglBitmapDestroyNotify) g_free,
-                                            NULL);
-
-      ctx->texture_driver->prep_gl_for_pixels_download (4 * width, 4);
-
-      GE( ctx, glReadPixels (x, y, width, height,
-                             GL_RGBA, GL_UNSIGNED_BYTE,
-                             tmp_data) );
-
-      /* CoglBitmap doesn't currently have a way to convert without
-         allocating its own buffer so we have to copy the data
-         again */
-      if ((dst_bmp = _cogl_bitmap_convert_format_and_premult (tmp_bmp,
-                                                              format)))
-        {
-          _cogl_bitmap_copy_subregion (dst_bmp,
-                                       bmp,
-                                       0, 0,
-                                       0, 0,
-                                       width, height);
-          cogl_object_unref (dst_bmp);
-        }
-      else
-        {
-          /* FIXME: there's no way to report an error here so we'll
-             just have to leave the data initialised */
-        }
-
-      cogl_object_unref (tmp_bmp);
-    }
-  else
-    {
-      ctx->texture_driver->prep_gl_for_pixels_download (rowstride, bpp);
-
-      GE( ctx, glReadPixels (x, y, width, height, gl_format, gl_type, pixels) );
-
-      /* Convert to the premult format specified by the caller
-         in-place. This will do nothing if the premult status is already
-         correct. */
-      _cogl_bitmap_convert_premult_status (bmp, format);
-    }
-
-  /* Currently this function owns the pack_invert state and we don't want this
-   * to interfere with other Cogl components so all other code can assume that
-   * we leave the pack_invert state off. */
-  if (pack_invert_set)
-    GE (ctx, glPixelStorei (GL_PACK_INVERT_MESA, FALSE));
-
-  /* NB: All offscreen rendering is done upside down so there is no need
-   * to flip in this case... */
-  if (!cogl_is_offscreen (framebuffer) && !pack_invert_set)
-    {
-      guint8 *temprow = g_alloca (rowstride * sizeof (guint8));
-
-      /* vertically flip the buffer in-place */
-      for (y = 0; y < height / 2; y++)
-        {
-          if (y != height - y - 1) /* skip center row */
-            {
-              memcpy (temprow,
-                      pixels + y * rowstride, rowstride);
-              memcpy (pixels + y * rowstride,
-                      pixels + (height - y - 1) * rowstride, rowstride);
-              memcpy (pixels + (height - y - 1) * rowstride,
-                      temprow,
-                      rowstride);
-            }
-        }
-    }
-
-  cogl_object_unref (bmp);
-}
-
-void
 cogl_read_pixels (int x,
                   int y,
                   int width,
@@ -574,10 +378,19 @@ cogl_read_pixels (int x,
                   guint8 *pixels)
 {
   int bpp = _cogl_pixel_format_get_bytes_per_pixel (format);
-  _cogl_read_pixels_with_rowstride (x, y, width, height,
-                                    source, format, pixels,
-                                    /* rowstride */
-                                    bpp * width);
+  CoglBitmap *bitmap;
+
+  bitmap = _cogl_bitmap_new_from_data (pixels,
+                                       format,
+                                       width, height,
+                                       bpp * width, /* rowstride */
+                                       NULL, /* destroy_fn */
+                                       NULL /* destroy_fn_data */);
+  cogl_framebuffer_read_pixels_into_bitmap (_cogl_get_read_framebuffer (),
+                                            x, y,
+                                            source,
+                                            bitmap);
+  cogl_object_unref (bitmap);
 }
 
 void
diff --git a/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt
index 787e7d5..1cb8ae7 100644
--- a/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt
+++ b/doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-sections.txt
@@ -406,6 +406,7 @@ cogl_framebuffer_set_point_samples_per_pixel
 cogl_framebuffer_get_context
 cogl_framebuffer_clear
 cogl_framebuffer_clear4f
+cogl_framebuffer_read_pixels_into_bitmap
 
 <SUBSECTION>
 cogl_framebuffer_draw_primitive



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