[Mesa-dev] [PATCH 14/29] mesa: use _mesa_format_convert to implement glReadPixels.
Iago Toral Quiroga
itoral at igalia.com
Tue Nov 18 01:23:55 PST 2014
---
src/mesa/main/readpix.c | 354 +++++++++++++++++++++++++++++-------------------
1 file changed, 214 insertions(+), 140 deletions(-)
diff --git a/src/mesa/main/readpix.c b/src/mesa/main/readpix.c
index b09cf54..8f4894e 100644
--- a/src/mesa/main/readpix.c
+++ b/src/mesa/main/readpix.c
@@ -39,6 +39,8 @@
#include "state.h"
#include "glformats.h"
#include "fbobject.h"
+#include "format_utils.h"
+#include "pixeltransfer.h"
/**
@@ -405,174 +407,246 @@ read_stencil_pixels( struct gl_context *ctx,
ctx->Driver.UnmapRenderbuffer(ctx, rb);
}
-
-/**
- * Try to do glReadPixels of RGBA data using swizzle.
- * \return GL_TRUE if successful, GL_FALSE otherwise (use the slow path)
+/*
+ * Read R, G, B, A, RGB, L, or LA pixels.
*/
-static GLboolean
-read_rgba_pixels_swizzle(struct gl_context *ctx,
- GLint x, GLint y,
- GLsizei width, GLsizei height,
- GLenum format, GLenum type,
- GLvoid *pixels,
- const struct gl_pixelstore_attrib *packing)
+static void
+read_rgba_pixels( struct gl_context *ctx,
+ GLint x, GLint y,
+ GLsizei width, GLsizei height,
+ GLenum format, GLenum type, GLvoid *pixels,
+ const struct gl_pixelstore_attrib *packing )
{
- struct gl_renderbuffer *rb = ctx->ReadBuffer->_ColorReadBuffer;
- GLubyte *dst, *map;
- int dstStride, stride, j;
- GLboolean swizzle_rb = GL_FALSE, copy_xrgb = GL_FALSE;
-
- /* XXX we could check for other swizzle/special cases here as needed */
- if (rb->Format == MESA_FORMAT_R8G8B8A8_UNORM &&
- format == GL_BGRA &&
- type == GL_UNSIGNED_INT_8_8_8_8_REV &&
- !ctx->Pack.SwapBytes) {
- swizzle_rb = GL_TRUE;
- }
- else if (rb->Format == MESA_FORMAT_B8G8R8X8_UNORM &&
- format == GL_BGRA &&
- type == GL_UNSIGNED_INT_8_8_8_8_REV &&
- !ctx->Pack.SwapBytes) {
- copy_xrgb = GL_TRUE;
- }
- else {
- return GL_FALSE;
- }
+ GLbitfield transferOps;
+ struct gl_framebuffer *fb = ctx->ReadBuffer;
+ struct gl_renderbuffer *rb = fb->_ColorReadBuffer;
- dstStride = _mesa_image_row_stride(packing, width, format, type);
- dst = (GLubyte *) _mesa_image_address2d(packing, pixels, width, height,
- format, type, 0, 0);
+ if (!rb)
+ return;
+ transferOps = get_readpixels_transfer_ops(ctx, rb->Format, format, type,
+ GL_FALSE);
+ /* Describe the dst format */
+ GLboolean dst_is_integer = _mesa_is_enum_format_integer(format);
+ int dst_stride = _mesa_image_row_stride(packing, width, format, type);
+ uint32_t dst_format = _mesa_format_from_format_and_type(format, type);
+ mesa_format dst_mesa_format;
+ if (dst_format & MESA_ARRAY_FORMAT_BIT)
+ dst_mesa_format = _mesa_format_from_array_format(dst_format);
+ else
+ dst_mesa_format = dst_format;
+ GLenum dst_base_format = _mesa_get_format_base_format(dst_mesa_format);
+ GLubyte *dst = (GLubyte *)
+ _mesa_image_address2d(packing, pixels, width, height,
+ format, type, 0, 0);
+
+ /* Map the source render buffer */
+ GLubyte *map;
+ int rb_stride;
ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT,
- &map, &stride);
+ &map, &rb_stride);
if (!map) {
_mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
- return GL_TRUE; /* don't bother trying the slow path */
+ return;
}
+ const mesa_format rb_format = _mesa_get_srgb_format_linear(rb->Format);
+ GLenum rb_base_format = _mesa_get_format_base_format(rb_format);
- if (swizzle_rb) {
- /* swap R/B */
- for (j = 0; j < height; j++) {
- int i;
- for (i = 0; i < width; i++) {
- GLuint *dst4 = (GLuint *) dst, *map4 = (GLuint *) map;
- GLuint pixel = map4[i];
- dst4[i] = (pixel & 0xff00ff00)
- | ((pixel & 0x00ff0000) >> 16)
- | ((pixel & 0x000000ff) << 16);
+ /* Since _mesa_format_convert does not handle transferOps we need to handle
+ * them before we call the function. This requires to convert to RGBA float
+ * first so we can call _mesa_apply_rgba_transfer_ops. If the dst format is
+ * integer we can ignore transferOps.
+ *
+ * Depending on the base formats involved in the conversion we might need to
+ * rebase some values and for that we need to convert to RGBA first too.
+ */
+ assert(!transferOps || (transferOps && !dst_is_integer));
+ GLenum rebase_format = GL_NONE;
+ if (rb->_BaseFormat == GL_LUMINANCE ||
+ rb->_BaseFormat == GL_INTENSITY ||
+ rb->_BaseFormat == GL_LUMINANCE_ALPHA) {
+ /* If luminance (or intensity) is read back as RGB(A), the returned value
+ * should be (L,0,0,1), not (L,L,L,1), so we need to rebase.
+ */
+ rebase_format = rb->_BaseFormat;
+ } else if (dst_base_format == GL_LUMINANCE ||
+ dst_base_format == GL_LUMINANCE_ALPHA) {
+ /* If dst is luminance we will convert to RGBA first so we can then
+ * compute luminance values as L=R+G+B. We will need a rebase or not
+ * depending on the base format of the render buffer.
+ */
+ rebase_format = rb->_BaseFormat;
+ } else if (rb->_BaseFormat != rb_base_format) {
+ /* If the internal format and the real format differ we can't rely
+ * on the convert functions setting the correct constant values
+ * (e.g. reading back GL_RGB8 which is actually RGBA won't set alpha=1),
+ * so we will have to rebase in certain cases.
+ */
+ switch (rb->_BaseFormat) {
+ case GL_RED:
+ if ((rb_base_format == GL_RGBA ||
+ rb_base_format == GL_RGB ||
+ rb_base_format == GL_RG) &&
+ (dst_base_format == GL_RGBA ||
+ dst_base_format == GL_RGB ||
+ dst_base_format == GL_RG ||
+ dst_base_format == GL_GREEN)) {
+ rebase_format = rb->_BaseFormat;
+ break;
}
- dst += dstStride;
- map += stride;
- }
- } else if (copy_xrgb) {
- /* convert xrgb -> argb */
- for (j = 0; j < height; j++) {
- GLuint *dst4 = (GLuint *) dst, *map4 = (GLuint *) map;
- int i;
- for (i = 0; i < width; i++) {
- dst4[i] = map4[i] | 0xff000000; /* set A=0xff */
+ /* fall through */
+ case GL_RG:
+ if ((rb_base_format == GL_RGBA ||
+ rb_base_format == GL_RGB) &&
+ (dst_base_format == GL_RGBA ||
+ dst_base_format == GL_RGB ||
+ dst_base_format == GL_BLUE)) {
+ rebase_format = rb->_BaseFormat;
+ break;
}
- dst += dstStride;
- map += stride;
+ /* fall through */
+ case GL_RGB:
+ if (rb_base_format == GL_RGBA &&
+ (dst_base_format == GL_RGBA ||
+ dst_base_format == GL_ALPHA)) {
+ rebase_format = rb->_BaseFormat;
+ }
+ break;
+ case GL_ALPHA:
+ if (dst_base_format != GL_ALPHA) {
+ rebase_format = rb->_BaseFormat;
+ }
+ break;
}
}
- ctx->Driver.UnmapRenderbuffer(ctx, rb);
-
- return GL_TRUE;
-}
+ bool needs_rgba = transferOps || rebase_format;
+ void *rgba = NULL;
-static void
-slow_read_rgba_pixels( struct gl_context *ctx,
- GLint x, GLint y,
- GLsizei width, GLsizei height,
- GLenum format, GLenum type,
- GLvoid *pixels,
- const struct gl_pixelstore_attrib *packing,
- GLbitfield transferOps )
-{
- struct gl_renderbuffer *rb = ctx->ReadBuffer->_ColorReadBuffer;
- const mesa_format rbFormat = _mesa_get_srgb_format_linear(rb->Format);
- void *rgba;
- GLubyte *dst, *map;
- int dstStride, stride, j;
- GLboolean dst_is_integer = _mesa_is_enum_format_integer(format);
- GLboolean dst_is_uint = _mesa_is_format_unsigned(rbFormat);
-
- dstStride = _mesa_image_row_stride(packing, width, format, type);
- dst = (GLubyte *) _mesa_image_address2d(packing, pixels, width, height,
- format, type, 0, 0);
-
- ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT,
- &map, &stride);
- if (!map) {
- _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
- return;
- }
-
- rgba = malloc(width * MAX_PIXEL_BYTES);
- if (!rgba)
- goto done;
-
- for (j = 0; j < height; j++) {
+ void *src;
+ int src_stride;
+ uint32_t src_format;
+ if (needs_rgba) {
+ uint32_t rgba_format;
+ int rgba_stride;
+ /* Convert to RGBA float or int/uint depending on the type of the dst. */
if (dst_is_integer) {
- _mesa_unpack_uint_rgba_row(rbFormat, width, map, (GLuint (*)[4]) rgba);
- _mesa_rebase_rgba_uint(width, (GLuint (*)[4]) rgba,
- rb->_BaseFormat);
- if (dst_is_uint) {
- _mesa_pack_rgba_span_from_uints(ctx, width, (GLuint (*)[4]) rgba, format,
- type, dst);
+ GLboolean src_is_uint = _mesa_is_format_unsigned(rb_format);
+ if (src_is_uint) {
+ rgba_format = RGBA8888_UINT.as_uint;
+ rgba_stride = width * 4 * sizeof(GLuint);
} else {
- _mesa_pack_rgba_span_from_ints(ctx, width, (GLint (*)[4]) rgba, format,
- type, dst);
+ rgba_format = RGBA8888_INT.as_uint;
+ rgba_stride = width * 4 * sizeof(GLint);
}
} else {
- _mesa_unpack_rgba_row(rbFormat, width, map, (GLfloat (*)[4]) rgba);
- _mesa_rebase_rgba_float(width, (GLfloat (*)[4]) rgba,
- rb->_BaseFormat);
- _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) rgba, format,
- type, dst, packing, transferOps);
+ rgba_format = RGBA8888_FLOAT.as_uint;
+ rgba_stride = width * 4 * sizeof(GLfloat);
}
- dst += dstStride;
- map += stride;
- }
- free(rgba);
+ /* If we are lucky and the dst format matches the RGBA format we need to
+ * convert to, then we can convert directly into the dst buffer and avoid
+ * the final conversion/copy from the rgba buffer to the dst buffer.
+ */
+ bool need_convert;
+ if (dst_format == rgba_format) {
+ need_convert = false;
+ rgba = dst;
+ } else {
+ need_convert = true;
+ rgba = malloc(height * rgba_stride);
+ if (!rgba) {
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
+ goto done_unmap;
+ }
+ }
-done:
- ctx->Driver.UnmapRenderbuffer(ctx, rb);
-}
+ /* Convert to RGBA now */
+ _mesa_format_convert(rgba, rgba_format, rgba_stride,
+ map, rb_format, rb_stride,
+ width, height, GL_RGBA);
-/*
- * Read R, G, B, A, RGB, L, or LA pixels.
- */
-static void
-read_rgba_pixels( struct gl_context *ctx,
- GLint x, GLint y,
- GLsizei width, GLsizei height,
- GLenum format, GLenum type, GLvoid *pixels,
- const struct gl_pixelstore_attrib *packing )
-{
- GLbitfield transferOps;
- struct gl_framebuffer *fb = ctx->ReadBuffer;
- struct gl_renderbuffer *rb = fb->_ColorReadBuffer;
+ /* Rebase and handle transfer ops as necessary */
+ if (dst_is_integer) {
+ if (rebase_format)
+ _mesa_rebase_rgba_uint(width * height, (GLuint (*)[4]) rgba, rebase_format);
+ } else {
+ if (rebase_format)
+ _mesa_rebase_rgba_float(width * height, (GLfloat (*)[4]) rgba, rebase_format);
+ if (transferOps)
+ _mesa_apply_rgba_transfer_ops(ctx, transferOps, width * height, rgba);
+ }
- if (!rb)
- return;
+ /* If we were lucky and our RGBA conversion matches the dst format, then
+ * we are done.
+ */
+ if (!need_convert)
+ goto done_swap;
+
+ /* Otherwise, we need to convert from RGBA to dst next */
+ src = rgba;
+ src_format = rgba_format;
+ src_stride = rgba_stride;
+ } else {
+ /* No RGBA conversion needed, convert directly to dst */
+ src = map;
+ src_format = rb_format;
+ src_stride = rb_stride;
+ }
- transferOps = get_readpixels_transfer_ops(ctx, rb->Format, format, type,
- GL_FALSE);
+ /* Do the conversion.
+ *
+ * If the dst format is Luminance, we need to do the conversion by computing
+ * L=R+G+B values.
+ */
+ if (format != GL_LUMINANCE && format != GL_LUMINANCE_ALPHA) {
+ _mesa_format_convert(dst, dst_format, dst_stride,
+ src, src_format, src_stride,
+ width, height,
+ dst_base_format);
+ } else if (!dst_is_integer) {
+ /* Compute float Luminance values from RGBA float */
+ int luminance_stride = width * sizeof(GL_FLOAT);
+ if (format == GL_LUMINANCE_ALPHA)
+ luminance_stride *= 2;
+ int luminance_bytes = height * luminance_stride;
+ void *luminance = malloc(luminance_bytes);
+ if (!luminance) {
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
+ free(rgba);
+ goto done_unmap;
+ }
+ _mesa_pack_luminance_from_rgba_float(width * height, src,
+ luminance, format, transferOps);
+
+ /* Convert from Luminance float to dst (this will hadle type conversion
+ * from float to the type of dst if necessary)
+ */
+ uint32_t luminance_format =
+ _mesa_format_from_format_and_type(format, GL_FLOAT);
+ _mesa_format_convert(dst, dst_format, dst_stride,
+ luminance, luminance_format, luminance_stride,
+ width, height, dst_base_format);
+ } else {
+ _mesa_pack_luminance_from_rgba_integer(width * height, src, dst, format);
+ }
- /* Try the optimized paths first. */
- if (!transferOps &&
- read_rgba_pixels_swizzle(ctx, x, y, width, height,
- format, type, pixels, packing)) {
- return;
+ if (rgba)
+ free(rgba);
+
+done_swap:
+ /* Handle byte swapping if required */
+ if (packing->SwapBytes) {
+ int components = _mesa_components_in_format(format);
+ GLint swapSize = _mesa_sizeof_packed_type(type);
+ if (swapSize == 2)
+ _mesa_swap2((GLushort *) dst, width * height * components);
+ else if (swapSize == 4)
+ _mesa_swap4((GLuint *) dst, width * height * components);
}
- slow_read_rgba_pixels(ctx, x, y, width, height,
- format, type, pixels, packing, transferOps);
+done_unmap:
+ ctx->Driver.UnmapRenderbuffer(ctx, rb);
}
/**
--
1.9.1
More information about the mesa-dev
mailing list