mesa: use _mesa_format_convert to implement glReadPixels.
authorIago Toral Quiroga <itoral@igalia.com>
Thu, 23 Oct 2014 08:04:56 +0000 (10:04 +0200)
committerIago Toral Quiroga <itoral@igalia.com>
Mon, 12 Jan 2015 10:20:29 +0000 (11:20 +0100)
Reviewed-by: Jason Ekstrand <jason.ekstrand@intel.com>
src/mesa/main/readpix.c

index b09cf54994a638c27e29da96b07640e62cd427f1..c589ca4e202eb50b0e4fc4313fbf1b13cd6401cd 100644 (file)
@@ -39,6 +39,8 @@
 #include "state.h"
 #include "glformats.h"
 #include "fbobject.h"
+#include "format_utils.h"
+#include "pixeltransfer.h"
 
 
 /**
@@ -405,174 +407,214 @@ 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;
+   GLbitfield transferOps;
+   bool dst_is_integer, dst_is_luminance, needs_rebase;
+   int dst_stride, src_stride, rb_stride;
+   uint32_t dst_format, src_format;
    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;
-   }
+   mesa_format rb_format;
+   bool needs_rgba;
+   void *rgba, *src;
+   bool src_is_uint = false;
+   uint8_t rebase_swizzle[4];
+   struct gl_framebuffer *fb = ctx->ReadBuffer;
+   struct gl_renderbuffer *rb = fb->_ColorReadBuffer;
 
-   dstStride = _mesa_image_row_stride(packing, width, format, type);
+   if (!rb)
+      return;
+
+   transferOps = get_readpixels_transfer_ops(ctx, rb->Format, format, type,
+                                             GL_FALSE);
+   /* Describe the dst format */
+   dst_is_integer = _mesa_is_enum_format_integer(format);
+   dst_stride = _mesa_image_row_stride(packing, width, format, type);
+   dst_format = _mesa_format_from_format_and_type(format, type);
+   dst_is_luminance = format == GL_LUMINANCE ||
+                      format == GL_LUMINANCE_ALPHA ||
+                      format == GL_LUMINANCE_INTEGER_EXT ||
+                      format == GL_LUMINANCE_ALPHA_INTEGER_EXT;
    dst = (GLubyte *) _mesa_image_address2d(packing, pixels, width, height,
-                                          format, type, 0, 0);
+                                           format, type, 0, 0);
 
+   /* Map the source render buffer */
    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;
    }
+   rb_format = _mesa_get_srgb_format_linear(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);
-         }
-         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 */
-         }
-         dst += dstStride;
-         map += stride;
-      }
+   /*
+    * Depending on the base formats involved in the conversion we might need to
+    * rebase some values, so for these formats we compute a rebase swizzle.
+    */
+   if (rb->_BaseFormat == GL_LUMINANCE || rb->_BaseFormat == GL_INTENSITY) {
+      needs_rebase = true;
+      rebase_swizzle[0] = MESA_FORMAT_SWIZZLE_X;
+      rebase_swizzle[1] = MESA_FORMAT_SWIZZLE_ZERO;
+      rebase_swizzle[2] = MESA_FORMAT_SWIZZLE_ZERO;
+      rebase_swizzle[3] = MESA_FORMAT_SWIZZLE_ONE;
+   } else if (rb->_BaseFormat == GL_LUMINANCE_ALPHA) {
+      needs_rebase = true;
+      rebase_swizzle[0] = MESA_FORMAT_SWIZZLE_X;
+      rebase_swizzle[1] = MESA_FORMAT_SWIZZLE_ZERO;
+      rebase_swizzle[2] = MESA_FORMAT_SWIZZLE_ZERO;
+      rebase_swizzle[3] = MESA_FORMAT_SWIZZLE_W;
+   } else if (_mesa_get_format_base_format(rb_format) != rb->_BaseFormat) {
+      needs_rebase =
+         _mesa_compute_rgba2base2rgba_component_mapping(rb->_BaseFormat,
+                                                        rebase_swizzle);
+   } else {
+      needs_rebase = false;
    }
 
-   ctx->Driver.UnmapRenderbuffer(ctx, rb);
-
-   return GL_TRUE;
-}
-
-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;
-   }
+   /* 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 transferOps do not apply.
+    *
+    * Converting to luminance also requires converting to RGBA first, so we can
+    * then compute luminance values as L=R+G+B. Notice that this is different
+    * from GetTexImage, where we compute L=R.
+    */
+   assert(!transferOps || (transferOps && !dst_is_integer));
 
-   rgba = malloc(width * MAX_PIXEL_BYTES);
-   if (!rgba)
-      goto done;
+   needs_rgba = transferOps || dst_is_luminance;
+   rgba = NULL;
+   if (needs_rgba) {
+      uint32_t rgba_format;
+      int rgba_stride;
+      bool need_convert;
 
-   for (j = 0; j < height; j++) {
+      /* Convert to RGBA float or int/uint depending on the type of the src */
       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);
+         src_is_uint = _mesa_is_format_unsigned(rb_format);
+         if (src_is_uint) {
+            rgba_format = RGBA8888_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;
+            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;
+         rgba_stride = width * 4 * sizeof(GLfloat);
       }
-      dst += dstStride;
-      map += stride;
-   }
-
-   free(rgba);
-
-done:
-   ctx->Driver.UnmapRenderbuffer(ctx, rb);
-}
 
-/*
- * 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;
+      /* 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.
+       */
+      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;
+         }
+      }
 
-   if (!rb)
-      return;
+      /* Convert to RGBA now */
+      _mesa_format_convert(rgba, rgba_format, rgba_stride,
+                           map, rb_format, rb_stride,
+                           width, height,
+                           needs_rebase ? rebase_swizzle : NULL);
+
+      /* Handle transfer ops if necessary */
+      if (transferOps)
+         _mesa_apply_rgba_transfer_ops(ctx, transferOps, width * height, rgba);
+
+      /* If we had to rebase, we have already taken care of that */
+      needs_rebase = false;
+
+      /* 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 (!dst_is_luminance) {
+      _mesa_format_convert(dst, dst_format, dst_stride,
+                           src, src_format, src_stride,
+                           width, height,
+                           needs_rebase ? rebase_swizzle : NULL);
+   } else if (!dst_is_integer) {
+      /* Compute float Luminance values from RGBA float */
+      int luminance_stride, luminance_bytes;
+      void *luminance;
+      uint32_t luminance_format;
+
+      luminance_stride = width * sizeof(GL_FLOAT);
+      if (format == GL_LUMINANCE_ALPHA)
+         luminance_stride *= 2;
+      luminance_bytes = height * luminance_stride;
+      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)
+       */
+      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, NULL);
+   } else {
+      _mesa_pack_luminance_from_rgba_integer(width * height, src, !src_is_uint,
+                                             dst, format, type);
+   }
 
-   /* 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);
 }
 
 /**