[Cogl] [PATCH] framebuffer: move last use of GL into -framebuffer-gl.c
Robert Bragg
robert at sixbynine.org
Thu Dec 6 03:53:44 PST 2012
From: Robert Bragg <robert at linux.intel.com>
This moves the last direct use of GL in cogl-framebuffer.c for handling
cogl_framebuffer_read_pixels_into_bitmap() into
driver/gl/cogl-framebuffer-gl.c and adds a
->framebuffer_read_pixels_into_bitmap vfunc to CoglDriverVtable.
---
cogl/cogl-driver.h | 8 +
cogl/cogl-framebuffer.c | 370 +-----------------------
cogl/driver/gl/cogl-framebuffer-gl-private.h | 8 +
cogl/driver/gl/cogl-framebuffer-gl.c | 369 +++++++++++++++++++++++
cogl/driver/gl/gl/cogl-driver-gl.c | 1 +
cogl/driver/gl/gles/cogl-driver-gles.c | 1 +
cogl/driver/nop/cogl-driver-nop.c | 1 +
cogl/driver/nop/cogl-framebuffer-nop-private.h | 8 +
cogl/driver/nop/cogl-framebuffer-nop.c | 11 +
9 files changed, 414 insertions(+), 363 deletions(-)
diff --git a/cogl/cogl-driver.h b/cogl/cogl-driver.h
index 03afc1a..5a49599 100644
--- a/cogl/cogl-driver.h
+++ b/cogl/cogl-driver.h
@@ -108,6 +108,14 @@ struct _CoglDriverVtable
int n_attributes,
CoglDrawFlags flags);
+ CoglBool
+ (* framebuffer_read_pixels_into_bitmap) (CoglFramebuffer *framebuffer,
+ int x,
+ int y,
+ CoglReadPixelsFlags source,
+ CoglBitmap *bitmap,
+ CoglError **error);
+
/* Destroys any driver specific resources associated with the given
* 2D texture. */
void
diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c
index b26de2b..abb859f 100644
--- a/cogl/cogl-framebuffer.c
+++ b/cogl/cogl-framebuffer.c
@@ -30,7 +30,6 @@
#include "cogl-debug.h"
#include "cogl-context-private.h"
-#include "cogl-util-gl-private.h"
#include "cogl-display-private.h"
#include "cogl-renderer-private.h"
#include "cogl-object-private.h"
@@ -1115,99 +1114,6 @@ _cogl_framebuffer_try_fast_read_pixel (CoglFramebuffer *framebuffer,
return FALSE;
}
-static CoglBool
-_cogl_framebuffer_slow_read_pixels_workaround (CoglFramebuffer *framebuffer,
- int x,
- int y,
- CoglReadPixelsFlags source,
- CoglBitmap *bitmap,
- CoglError **error)
-{
- CoglContext *ctx;
- CoglPixelFormat format;
- CoglBitmap *pbo;
- int width;
- int height;
- CoglBool res;
- uint8_t *dst;
- const uint8_t *src;
-
- _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);
- format = cogl_bitmap_get_format (bitmap);
-
- pbo = cogl_bitmap_new_with_size (ctx, width, height, format);
-
- /* Read into the pbo. We need to disable the flipping because the
- blit fast path in the driver does not work with
- GL_PACK_INVERT_MESA is set */
- res = cogl_framebuffer_read_pixels_into_bitmap (framebuffer,
- x, y,
- source |
- COGL_READ_PIXELS_NO_FLIP,
- pbo,
- error);
- if (!res)
- {
- cogl_object_unref (pbo);
- return FALSE;
- }
-
- /* Copy the pixels back into application's buffer */
- dst = _cogl_bitmap_map (bitmap,
- COGL_BUFFER_ACCESS_WRITE,
- COGL_BUFFER_MAP_HINT_DISCARD,
- error);
- if (!dst)
- {
- cogl_object_unref (pbo);
- return FALSE;
- }
-
- src = _cogl_bitmap_map (pbo,
- COGL_BUFFER_ACCESS_READ,
- 0, /* hints */
- error);
- if (src)
- {
- int src_rowstride = cogl_bitmap_get_rowstride (pbo);
- int dst_rowstride = cogl_bitmap_get_rowstride (bitmap);
- int to_copy =
- _cogl_pixel_format_get_bytes_per_pixel (format) * width;
- int y;
-
- /* If the framebuffer is onscreen we need to flip the
- data while copying */
- if (!cogl_is_offscreen (framebuffer))
- {
- src += src_rowstride * (height - 1);
- src_rowstride = -src_rowstride;
- }
-
- for (y = 0; y < height; y++)
- {
- memcpy (dst, src, to_copy);
- dst += dst_rowstride;
- src += src_rowstride;
- }
-
- _cogl_bitmap_unmap (pbo);
- }
- else
- res = FALSE;
-
- _cogl_bitmap_unmap (bitmap);
-
- cogl_object_unref (pbo);
-
- return res;
-}
-
CoglBool
cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer,
int x,
@@ -1217,17 +1123,8 @@ cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer,
CoglError **error)
{
CoglContext *ctx;
- int framebuffer_height;
- CoglPixelFormat format;
- CoglPixelFormat required_format;
- GLenum gl_intformat;
- GLenum gl_format;
- GLenum gl_type;
- CoglBool pack_invert_set;
int width;
int height;
- int status = FALSE;
- CoglError *ignore_error = NULL;
_COGL_RETURN_VAL_IF_FAIL (source & COGL_READ_PIXELS_COLOR_BUFFER, FALSE);
_COGL_RETURN_VAL_IF_FAIL (cogl_is_framebuffer (framebuffer), FALSE);
@@ -1235,11 +1132,8 @@ cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer,
if (!cogl_framebuffer_allocate (framebuffer, error))
return FALSE;
- ctx = cogl_framebuffer_get_context (framebuffer);
-
width = cogl_bitmap_get_width (bitmap);
height = cogl_bitmap_get_height (bitmap);
- format = cogl_bitmap_get_format (bitmap);
if (width == 1 && height == 1 && !framebuffer->clear_clip_dirty)
{
@@ -1256,268 +1150,18 @@ cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer,
return TRUE;
}
- /* Workaround for cases where its faster to read into a temporary
- * PBO. This is only worth doing if:
- *
- * • The GPU is an Intel GPU. In that case there is a known
- * fast-path when reading into a PBO that will use the blitter
- * instead of the Mesa fallback code. The driver bug will only be
- * set if this is the case.
- * • We're not already reading into a PBO.
- * • The target format is BGRA. The fast-path blit does not get hit
- * otherwise.
- * • The size of the data is not trivially small. This isn't a
- * requirement to hit the fast-path blit but intuitively it feels
- * like if the amount of data is too small then the cost of
- * allocating a PBO will outweigh the cost of temporarily
- * converting the data to floats.
- */
- if ((ctx->gpu.driver_bugs &
- COGL_GPU_INFO_DRIVER_BUG_MESA_46631_SLOW_READ_PIXELS) &&
- (width > 8 || height > 8) &&
- (format & ~COGL_PREMULT_BIT) == COGL_PIXEL_FORMAT_BGRA_8888 &&
- cogl_bitmap_get_buffer (bitmap) == NULL)
- {
-
- if (_cogl_framebuffer_slow_read_pixels_workaround (framebuffer,
- x, y,
- source,
- bitmap,
- &ignore_error))
- return TRUE;
- else
- cogl_error_free (ignore_error);
- }
+ ctx = cogl_framebuffer_get_context (framebuffer);
- /* make sure any batched primitives get emitted to the GL driver
+ /* make sure any batched primitives get emitted to the 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;
-
- required_format = ctx->driver_vtable->pixel_format_to_gl (ctx,
- 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) &&
- (source & COGL_READ_PIXELS_NO_FLIP) == 0 &&
- !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->private_feature_flags &
- COGL_PRIVATE_FEATURE_READ_PIXELS_ANY_FORMAT) &&
- (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;
- CoglPixelFormat read_format;
- int bpp, rowstride;
- uint8_t *tmp_data;
- CoglBool succeeded;
-
- if ((ctx->private_feature_flags &
- COGL_PRIVATE_FEATURE_READ_PIXELS_ANY_FORMAT))
- read_format = required_format;
- else
- {
- read_format = COGL_PIXEL_FORMAT_RGBA_8888;
- gl_format = GL_RGBA;
- gl_type = GL_UNSIGNED_BYTE;
- }
-
- if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (read_format))
- read_format = ((read_format & ~COGL_PREMULT_BIT) |
- (framebuffer->format & COGL_PREMULT_BIT));
-
- tmp_bmp = _cogl_bitmap_new_with_malloc_buffer (ctx,
- width, height,
- read_format,
- error);
- if (!tmp_bmp)
- goto EXIT;
-
- bpp = _cogl_pixel_format_get_bytes_per_pixel (read_format);
- rowstride = cogl_bitmap_get_rowstride (tmp_bmp);
-
- ctx->texture_driver->prep_gl_for_pixels_download (ctx,
- rowstride,
- width,
- bpp);
-
- /* Note: we don't worry about catching errors here since we know
- * we won't be lazily allocating storage for this buffer so it
- * won't fail due to lack of memory. */
- tmp_data = _cogl_bitmap_gl_bind (tmp_bmp,
- COGL_BUFFER_ACCESS_WRITE,
- COGL_BUFFER_MAP_HINT_DISCARD,
- NULL);
-
- GE( ctx, glReadPixels (x, y, width, height,
- gl_format, gl_type,
- tmp_data) );
-
- _cogl_bitmap_gl_unbind (tmp_bmp);
-
- succeeded = _cogl_bitmap_convert_into_bitmap (tmp_bmp, bitmap, error);
-
- cogl_object_unref (tmp_bmp);
-
- if (!succeeded)
- goto EXIT;
- }
- else
- {
- CoglBitmap *shared_bmp;
- CoglPixelFormat bmp_format;
- int bpp, rowstride;
- CoglBool succeeded = FALSE;
- uint8_t *pixels;
- CoglError *internal_error = NULL;
-
- 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 (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (format))
- bmp_format = ((format & ~COGL_PREMULT_BIT) |
- (framebuffer->format & COGL_PREMULT_BIT));
- else
- bmp_format = format;
-
- 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 (ctx,
- rowstride,
- width,
- bpp);
-
- pixels = _cogl_bitmap_gl_bind (shared_bmp,
- COGL_BUFFER_ACCESS_WRITE,
- 0, /* hints */
- &internal_error);
- /* NB: _cogl_bitmap_gl_bind() can return NULL in sucessfull
- * cases so we have to explicitly check the cogl error pointer
- * to know if there was a problem */
- if (internal_error)
- {
- cogl_object_unref (shared_bmp);
- _cogl_propagate_error (error, internal_error);
- goto EXIT;
- }
-
- GE( ctx, glReadPixels (x, y,
- width, height,
- gl_format, gl_type,
- pixels) );
-
- _cogl_bitmap_gl_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, error))
- succeeded = TRUE;
-
- cogl_object_unref (shared_bmp);
-
- if (!succeeded)
- goto EXIT;
- }
-
- /* NB: All offscreen rendering is done upside down so there is no need
- * to flip in this case... */
- if (!cogl_is_offscreen (framebuffer) &&
- (source & COGL_READ_PIXELS_NO_FLIP) == 0 &&
- !pack_invert_set)
- {
- uint8_t *temprow;
- int rowstride;
- uint8_t *pixels;
-
- rowstride = cogl_bitmap_get_rowstride (bitmap);
- pixels = _cogl_bitmap_map (bitmap,
- COGL_BUFFER_ACCESS_READ |
- COGL_BUFFER_ACCESS_WRITE,
- 0, /* hints */
- error);
-
- if (pixels == NULL)
- goto EXIT;
-
- temprow = g_alloca (rowstride * sizeof (uint8_t));
-
- /* 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);
- }
-
- status = TRUE;
-
-EXIT:
-
- /* 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));
-
- return status;
+ return ctx->driver_vtable->framebuffer_read_pixels_into_bitmap (framebuffer,
+ x, y,
+ source,
+ bitmap,
+ error);
}
CoglBool
diff --git a/cogl/driver/gl/cogl-framebuffer-gl-private.h b/cogl/driver/gl/cogl-framebuffer-gl-private.h
index 5ab3817..43588ea 100644
--- a/cogl/driver/gl/cogl-framebuffer-gl-private.h
+++ b/cogl/driver/gl/cogl-framebuffer-gl-private.h
@@ -86,6 +86,14 @@ _cogl_framebuffer_gl_draw_indexed_attributes (CoglFramebuffer *framebuffer,
int n_attributes,
CoglDrawFlags flags);
+CoglBool
+_cogl_framebuffer_gl_read_pixels_into_bitmap (CoglFramebuffer *framebuffer,
+ int x,
+ int y,
+ CoglReadPixelsFlags source,
+ CoglBitmap *bitmap,
+ CoglError **error);
+
#endif /* __COGL_FRAMEBUFFER_GL_PRIVATE_H__ */
diff --git a/cogl/driver/gl/cogl-framebuffer-gl.c b/cogl/driver/gl/cogl-framebuffer-gl.c
index 1e8139c..7fe807a 100644
--- a/cogl/driver/gl/cogl-framebuffer-gl.c
+++ b/cogl/driver/gl/cogl-framebuffer-gl.c
@@ -35,6 +35,7 @@
#include "cogl-texture-gl-private.h"
#include <glib.h>
+#include <string.h>
#ifndef GL_FRAMEBUFFER
#define GL_FRAMEBUFFER 0x8D40
@@ -1101,3 +1102,371 @@ _cogl_framebuffer_gl_draw_indexed_attributes (CoglFramebuffer *framebuffer,
_cogl_buffer_gl_unbind (buffer);
}
+
+static CoglBool
+mesa_46631_slow_read_pixels_workaround (CoglFramebuffer *framebuffer,
+ int x,
+ int y,
+ CoglReadPixelsFlags source,
+ CoglBitmap *bitmap,
+ CoglError **error)
+{
+ CoglContext *ctx;
+ CoglPixelFormat format;
+ CoglBitmap *pbo;
+ int width;
+ int height;
+ CoglBool res;
+ uint8_t *dst;
+ const uint8_t *src;
+
+ ctx = cogl_framebuffer_get_context (framebuffer);
+
+ width = cogl_bitmap_get_width (bitmap);
+ height = cogl_bitmap_get_height (bitmap);
+ format = cogl_bitmap_get_format (bitmap);
+
+ pbo = cogl_bitmap_new_with_size (ctx, width, height, format);
+
+ /* Read into the pbo. We need to disable the flipping because the
+ blit fast path in the driver does not work with
+ GL_PACK_INVERT_MESA is set */
+ res = cogl_framebuffer_read_pixels_into_bitmap (framebuffer,
+ x, y,
+ source |
+ COGL_READ_PIXELS_NO_FLIP,
+ pbo,
+ error);
+ if (!res)
+ {
+ cogl_object_unref (pbo);
+ return FALSE;
+ }
+
+ /* Copy the pixels back into application's buffer */
+ dst = _cogl_bitmap_map (bitmap,
+ COGL_BUFFER_ACCESS_WRITE,
+ COGL_BUFFER_MAP_HINT_DISCARD,
+ error);
+ if (!dst)
+ {
+ cogl_object_unref (pbo);
+ return FALSE;
+ }
+
+ src = _cogl_bitmap_map (pbo,
+ COGL_BUFFER_ACCESS_READ,
+ 0, /* hints */
+ error);
+ if (src)
+ {
+ int src_rowstride = cogl_bitmap_get_rowstride (pbo);
+ int dst_rowstride = cogl_bitmap_get_rowstride (bitmap);
+ int to_copy =
+ _cogl_pixel_format_get_bytes_per_pixel (format) * width;
+ int y;
+
+ /* If the framebuffer is onscreen we need to flip the
+ data while copying */
+ if (!cogl_is_offscreen (framebuffer))
+ {
+ src += src_rowstride * (height - 1);
+ src_rowstride = -src_rowstride;
+ }
+
+ for (y = 0; y < height; y++)
+ {
+ memcpy (dst, src, to_copy);
+ dst += dst_rowstride;
+ src += src_rowstride;
+ }
+
+ _cogl_bitmap_unmap (pbo);
+ }
+ else
+ res = FALSE;
+
+ _cogl_bitmap_unmap (bitmap);
+
+ cogl_object_unref (pbo);
+
+ return res;
+}
+
+CoglBool
+_cogl_framebuffer_gl_read_pixels_into_bitmap (CoglFramebuffer *framebuffer,
+ int x,
+ int y,
+ CoglReadPixelsFlags source,
+ CoglBitmap *bitmap,
+ CoglError **error)
+{
+ CoglContext *ctx = framebuffer->context;
+ int framebuffer_height = cogl_framebuffer_get_height (framebuffer);
+ int width = cogl_bitmap_get_width (bitmap);
+ int height = cogl_bitmap_get_height (bitmap);
+ CoglPixelFormat format = cogl_bitmap_get_format (bitmap);
+ CoglPixelFormat required_format;
+ GLenum gl_intformat;
+ GLenum gl_format;
+ GLenum gl_type;
+ CoglBool pack_invert_set;
+ int status = FALSE;
+
+ /* Workaround for cases where its faster to read into a temporary
+ * PBO. This is only worth doing if:
+ *
+ * • The GPU is an Intel GPU. In that case there is a known
+ * fast-path when reading into a PBO that will use the blitter
+ * instead of the Mesa fallback code. The driver bug will only be
+ * set if this is the case.
+ * • We're not already reading into a PBO.
+ * • The target format is BGRA. The fast-path blit does not get hit
+ * otherwise.
+ * • The size of the data is not trivially small. This isn't a
+ * requirement to hit the fast-path blit but intuitively it feels
+ * like if the amount of data is too small then the cost of
+ * allocating a PBO will outweigh the cost of temporarily
+ * converting the data to floats.
+ */
+ if ((ctx->gpu.driver_bugs &
+ COGL_GPU_INFO_DRIVER_BUG_MESA_46631_SLOW_READ_PIXELS) &&
+ (width > 8 || height > 8) &&
+ (format & ~COGL_PREMULT_BIT) == COGL_PIXEL_FORMAT_BGRA_8888 &&
+ cogl_bitmap_get_buffer (bitmap) == NULL)
+ {
+ CoglError *ignore_error = NULL;
+
+ if (mesa_46631_slow_read_pixels_workaround (framebuffer,
+ x, y,
+ source,
+ bitmap,
+ &ignore_error))
+ return TRUE;
+ else
+ cogl_error_free (ignore_error);
+ }
+
+ _cogl_framebuffer_flush_state (framebuffer,
+ framebuffer,
+ COGL_FRAMEBUFFER_STATE_BIND);
+
+ /* 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;
+
+ required_format = ctx->driver_vtable->pixel_format_to_gl (ctx,
+ 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) &&
+ (source & COGL_READ_PIXELS_NO_FLIP) == 0 &&
+ !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->private_feature_flags &
+ COGL_PRIVATE_FEATURE_READ_PIXELS_ANY_FORMAT) &&
+ (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;
+ CoglPixelFormat read_format;
+ int bpp, rowstride;
+ uint8_t *tmp_data;
+ CoglBool succeeded;
+
+ if ((ctx->private_feature_flags &
+ COGL_PRIVATE_FEATURE_READ_PIXELS_ANY_FORMAT))
+ read_format = required_format;
+ else
+ {
+ read_format = COGL_PIXEL_FORMAT_RGBA_8888;
+ gl_format = GL_RGBA;
+ gl_type = GL_UNSIGNED_BYTE;
+ }
+
+ if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (read_format))
+ read_format = ((read_format & ~COGL_PREMULT_BIT) |
+ (framebuffer->format & COGL_PREMULT_BIT));
+
+ tmp_bmp = _cogl_bitmap_new_with_malloc_buffer (ctx,
+ width, height,
+ read_format,
+ error);
+ if (!tmp_bmp)
+ goto EXIT;
+
+ bpp = _cogl_pixel_format_get_bytes_per_pixel (read_format);
+ rowstride = cogl_bitmap_get_rowstride (tmp_bmp);
+
+ ctx->texture_driver->prep_gl_for_pixels_download (ctx,
+ rowstride,
+ width,
+ bpp);
+
+ /* Note: we don't worry about catching errors here since we know
+ * we won't be lazily allocating storage for this buffer so it
+ * won't fail due to lack of memory. */
+ tmp_data = _cogl_bitmap_gl_bind (tmp_bmp,
+ COGL_BUFFER_ACCESS_WRITE,
+ COGL_BUFFER_MAP_HINT_DISCARD,
+ NULL);
+
+ GE( ctx, glReadPixels (x, y, width, height,
+ gl_format, gl_type,
+ tmp_data) );
+
+ _cogl_bitmap_gl_unbind (tmp_bmp);
+
+ succeeded = _cogl_bitmap_convert_into_bitmap (tmp_bmp, bitmap, error);
+
+ cogl_object_unref (tmp_bmp);
+
+ if (!succeeded)
+ goto EXIT;
+ }
+ else
+ {
+ CoglBitmap *shared_bmp;
+ CoglPixelFormat bmp_format;
+ int bpp, rowstride;
+ CoglBool succeeded = FALSE;
+ uint8_t *pixels;
+ CoglError *internal_error = NULL;
+
+ 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 (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (format))
+ bmp_format = ((format & ~COGL_PREMULT_BIT) |
+ (framebuffer->format & COGL_PREMULT_BIT));
+ else
+ bmp_format = format;
+
+ 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 (ctx,
+ rowstride,
+ width,
+ bpp);
+
+ pixels = _cogl_bitmap_gl_bind (shared_bmp,
+ COGL_BUFFER_ACCESS_WRITE,
+ 0, /* hints */
+ &internal_error);
+ /* NB: _cogl_bitmap_gl_bind() can return NULL in sucessfull
+ * cases so we have to explicitly check the cogl error pointer
+ * to know if there was a problem */
+ if (internal_error)
+ {
+ cogl_object_unref (shared_bmp);
+ _cogl_propagate_error (error, internal_error);
+ goto EXIT;
+ }
+
+ GE( ctx, glReadPixels (x, y,
+ width, height,
+ gl_format, gl_type,
+ pixels) );
+
+ _cogl_bitmap_gl_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, error))
+ succeeded = TRUE;
+
+ cogl_object_unref (shared_bmp);
+
+ if (!succeeded)
+ goto EXIT;
+ }
+
+ /* NB: All offscreen rendering is done upside down so there is no need
+ * to flip in this case... */
+ if (!cogl_is_offscreen (framebuffer) &&
+ (source & COGL_READ_PIXELS_NO_FLIP) == 0 &&
+ !pack_invert_set)
+ {
+ uint8_t *temprow;
+ int rowstride;
+ uint8_t *pixels;
+
+ rowstride = cogl_bitmap_get_rowstride (bitmap);
+ pixels = _cogl_bitmap_map (bitmap,
+ COGL_BUFFER_ACCESS_READ |
+ COGL_BUFFER_ACCESS_WRITE,
+ 0, /* hints */
+ error);
+
+ if (pixels == NULL)
+ goto EXIT;
+
+ temprow = g_alloca (rowstride * sizeof (uint8_t));
+
+ /* 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);
+ }
+
+ status = TRUE;
+
+EXIT:
+
+ /* 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));
+
+ return status;
+}
diff --git a/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/driver/gl/gl/cogl-driver-gl.c
index 7b66381..eddce23 100644
--- a/cogl/driver/gl/gl/cogl-driver-gl.c
+++ b/cogl/driver/gl/gl/cogl-driver-gl.c
@@ -601,6 +601,7 @@ _cogl_driver_gl =
_cogl_framebuffer_gl_discard_buffers,
_cogl_framebuffer_gl_draw_attributes,
_cogl_framebuffer_gl_draw_indexed_attributes,
+ _cogl_framebuffer_gl_read_pixels_into_bitmap,
_cogl_texture_2d_gl_free,
_cogl_texture_2d_gl_can_create,
_cogl_texture_2d_gl_init,
diff --git a/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/driver/gl/gles/cogl-driver-gles.c
index 9405e52..44ac366 100644
--- a/cogl/driver/gl/gles/cogl-driver-gles.c
+++ b/cogl/driver/gl/gles/cogl-driver-gles.c
@@ -348,6 +348,7 @@ _cogl_driver_gles =
_cogl_framebuffer_gl_discard_buffers,
_cogl_framebuffer_gl_draw_attributes,
_cogl_framebuffer_gl_draw_indexed_attributes,
+ _cogl_framebuffer_gl_read_pixels_into_bitmap,
_cogl_texture_2d_gl_free,
_cogl_texture_2d_gl_can_create,
_cogl_texture_2d_gl_init,
diff --git a/cogl/driver/nop/cogl-driver-nop.c b/cogl/driver/nop/cogl-driver-nop.c
index 6e53663..b3536ff 100644
--- a/cogl/driver/nop/cogl-driver-nop.c
+++ b/cogl/driver/nop/cogl-driver-nop.c
@@ -63,6 +63,7 @@ _cogl_driver_nop =
_cogl_framebuffer_nop_discard_buffers,
_cogl_framebuffer_nop_draw_attributes,
_cogl_framebuffer_nop_draw_indexed_attributes,
+ _cogl_framebuffer_nop_read_pixels_into_bitmap,
_cogl_texture_2d_nop_free,
_cogl_texture_2d_nop_can_create,
_cogl_texture_2d_nop_init,
diff --git a/cogl/driver/nop/cogl-framebuffer-nop-private.h b/cogl/driver/nop/cogl-framebuffer-nop-private.h
index 568cdea..0016ec8 100644
--- a/cogl/driver/nop/cogl-framebuffer-nop-private.h
+++ b/cogl/driver/nop/cogl-framebuffer-nop-private.h
@@ -86,4 +86,12 @@ _cogl_framebuffer_nop_draw_indexed_attributes (CoglFramebuffer *framebuffer,
int n_attributes,
CoglDrawFlags flags);
+CoglBool
+_cogl_framebuffer_nop_read_pixels_into_bitmap (CoglFramebuffer *framebuffer,
+ int x,
+ int y,
+ CoglReadPixelsFlags source,
+ CoglBitmap *bitmap,
+ CoglError **error);
+
#endif /* _COGL_FRAMEBUFFER_NOP_PRIVATE_H_ */
diff --git a/cogl/driver/nop/cogl-framebuffer-nop.c b/cogl/driver/nop/cogl-framebuffer-nop.c
index c4c1348..cd20c6e 100644
--- a/cogl/driver/nop/cogl-framebuffer-nop.c
+++ b/cogl/driver/nop/cogl-framebuffer-nop.c
@@ -108,3 +108,14 @@ _cogl_framebuffer_nop_draw_indexed_attributes (CoglFramebuffer *framebuffer,
CoglDrawFlags flags)
{
}
+
+CoglBool
+_cogl_framebuffer_nop_read_pixels_into_bitmap (CoglFramebuffer *framebuffer,
+ int x,
+ int y,
+ CoglReadPixelsFlags source,
+ CoglBitmap *bitmap,
+ CoglError **error)
+{
+ return TRUE;
+}
--
1.7.7.6
More information about the Cogl
mailing list