summaryrefslogtreecommitdiff
path: root/cogl/cogl-framebuffer.c
diff options
context:
space:
mode:
authorNeil Roberts <neil@linux.intel.com>2012-02-25 19:23:51 +0000
committerNeil Roberts <neil@linux.intel.com>2012-03-05 18:16:10 +0000
commit10a38bb14fac3ba8dc05115feb04bde56dc590e7 (patch)
tree4ce5f5fd788549fb7e087e15f1c8e5acb1c8197b /cogl/cogl-framebuffer.c
parent2501899044759a4e9bd91b78cf622b61bad7d0fb (diff)
downloadcogl-10a38bb14fac3ba8dc05115feb04bde56dc590e7.tar.gz
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. Reviewed-by: Robert Bragg <robert@linux.intel.com>
Diffstat (limited to 'cogl/cogl-framebuffer.c')
-rw-r--r--cogl/cogl-framebuffer.c258
1 files changed, 254 insertions, 4 deletions
diff --git a/cogl/cogl-framebuffer.c b/cogl/cogl-framebuffer.c
index 5a549abd..e3f12397 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,268 @@ _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;
+ guint8 *tmp_data;
+ CoglPixelFormat read_format;
+ int bpp, rowstride;
+ int succeeded;
+
+ 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 (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (read_format))
+ 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) );
+
+ succeeded = _cogl_bitmap_convert_into_bitmap (tmp_bmp, bitmap);
+
+ 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 (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 (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,