[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