+
+ /* Conversion between signed and unsigned integers needs masking
+ * (it isn't just memcpy). */
+ srcType = _mesa_get_format_datatype(rb->Format);
+
+ if ((srcType == GL_INT &&
+ (type == GL_UNSIGNED_INT ||
+ type == GL_UNSIGNED_SHORT ||
+ type == GL_UNSIGNED_BYTE)) ||
+ (srcType == GL_UNSIGNED_INT &&
+ (type == GL_INT ||
+ type == GL_SHORT ||
+ type == GL_BYTE))) {
+ return GL_TRUE;
+ }
+
+ /* And finally, see if there are any transfer ops. */
+ return get_readpixels_transfer_ops(ctx, rb->Format, format, type,
+ uses_blit) != 0;
+ }
+ return GL_FALSE;
+}
+
+
+static GLboolean
+readpixels_can_use_memcpy(const struct gl_context *ctx, GLenum format, GLenum type,
+ const struct gl_pixelstore_attrib *packing)
+{
+ struct gl_renderbuffer *rb =
+ _mesa_get_read_renderbuffer_for_format(ctx, format);
+
+ ASSERT(rb);
+
+ if (_mesa_readpixels_needs_slow_path(ctx, format, type, GL_FALSE)) {
+ return GL_FALSE;
+ }
+
+ /* The base internal format and the base Mesa format must match. */
+ if (rb->_BaseFormat != _mesa_get_format_base_format(rb->Format)) {
+ return GL_FALSE;
+ }
+
+ /* The Mesa format must match the input format and type. */
+ if (!_mesa_format_matches_format_and_type(rb->Format, format, type,
+ packing->SwapBytes)) {
+ return GL_FALSE;
+ }
+
+ return GL_TRUE;
+}
+
+
+static GLboolean
+readpixels_memcpy(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 =
+ _mesa_get_read_renderbuffer_for_format(ctx, format);
+ GLubyte *dst, *map;
+ int dstStride, stride, j, texelBytes;
+
+ /* Fail if memcpy cannot be used. */
+ if (!readpixels_can_use_memcpy(ctx, format, type, packing)) {
+ return GL_FALSE;
+ }
+
+ 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 GL_TRUE; /* don't bother trying the slow path */
+ }
+
+ texelBytes = _mesa_get_format_bytes(rb->Format);
+
+ /* memcpy*/
+ for (j = 0; j < height; j++) {
+ memcpy(dst, map, width * texelBytes);
+ dst += dstStride;
+ map += stride;
+ }
+
+ ctx->Driver.UnmapRenderbuffer(ctx, rb);
+ return GL_TRUE;
+}
+
+
+/**
+ * Optimized path for conversion of depth values to GL_DEPTH_COMPONENT,
+ * GL_UNSIGNED_INT.
+ */
+static GLboolean
+read_uint_depth_pixels( struct gl_context *ctx,
+ GLint x, GLint y,
+ GLsizei width, GLsizei height,
+ GLenum type, GLvoid *pixels,
+ const struct gl_pixelstore_attrib *packing )
+{
+ struct gl_framebuffer *fb = ctx->ReadBuffer;
+ struct gl_renderbuffer *rb = fb->Attachment[BUFFER_DEPTH].Renderbuffer;
+ GLubyte *map, *dst;
+ int stride, dstStride, j;
+
+ if (ctx->Pixel.DepthScale != 1.0 || ctx->Pixel.DepthBias != 0.0)
+ return GL_FALSE;
+
+ if (packing->SwapBytes)
+ return GL_FALSE;
+
+ if (_mesa_get_format_datatype(rb->Format) != GL_UNSIGNED_NORMALIZED)
+ return GL_FALSE;
+
+ 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 GL_TRUE; /* don't bother trying the slow path */
+ }
+
+ dstStride = _mesa_image_row_stride(packing, width, GL_DEPTH_COMPONENT, type);
+ dst = (GLubyte *) _mesa_image_address2d(packing, pixels, width, height,
+ GL_DEPTH_COMPONENT, type, 0, 0);
+
+ for (j = 0; j < height; j++) {
+ _mesa_unpack_uint_z_row(rb->Format, width, map, (GLuint *)dst);
+
+ map += stride;
+ dst += dstStride;
+ }
+ ctx->Driver.UnmapRenderbuffer(ctx, rb);
+
+ return GL_TRUE;
+}
+
+/**
+ * Read pixels for format=GL_DEPTH_COMPONENT.
+ */
+static void
+read_depth_pixels( struct gl_context *ctx,
+ GLint x, GLint y,
+ GLsizei width, GLsizei height,
+ GLenum type, GLvoid *pixels,
+ const struct gl_pixelstore_attrib *packing )
+{
+ struct gl_framebuffer *fb = ctx->ReadBuffer;
+ struct gl_renderbuffer *rb = fb->Attachment[BUFFER_DEPTH].Renderbuffer;
+ GLint j;
+ GLubyte *dst, *map;
+ int dstStride, stride;
+ GLfloat *depthValues;
+
+ if (!rb)
+ return;
+
+ /* clipping should have been done already */
+ ASSERT(x >= 0);
+ ASSERT(y >= 0);
+ ASSERT(x + width <= (GLint) rb->Width);
+ ASSERT(y + height <= (GLint) rb->Height);
+
+ if (type == GL_UNSIGNED_INT &&
+ read_uint_depth_pixels(ctx, x, y, width, height, type, pixels, packing)) {
+ return;
+ }
+
+ dstStride = _mesa_image_row_stride(packing, width, GL_DEPTH_COMPONENT, type);
+ dst = (GLubyte *) _mesa_image_address2d(packing, pixels, width, height,
+ GL_DEPTH_COMPONENT, 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;
+ }
+
+ depthValues = malloc(width * sizeof(GLfloat));
+
+ if (depthValues) {
+ /* General case (slower) */
+ for (j = 0; j < height; j++, y++) {
+ _mesa_unpack_float_z_row(rb->Format, width, map, depthValues);
+ _mesa_pack_depth_span(ctx, width, dst, type, depthValues, packing);
+
+ dst += dstStride;
+ map += stride;
+ }
+ }
+ else {
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
+ }
+
+ free(depthValues);
+
+ ctx->Driver.UnmapRenderbuffer(ctx, rb);
+}
+
+
+/**
+ * Read pixels for format=GL_STENCIL_INDEX.
+ */
+static void
+read_stencil_pixels( struct gl_context *ctx,
+ GLint x, GLint y,
+ GLsizei width, GLsizei height,
+ GLenum type, GLvoid *pixels,
+ const struct gl_pixelstore_attrib *packing )
+{
+ struct gl_framebuffer *fb = ctx->ReadBuffer;
+ struct gl_renderbuffer *rb = fb->Attachment[BUFFER_STENCIL].Renderbuffer;
+ GLint j;
+ GLubyte *map, *stencil;
+ GLint stride;
+
+ if (!rb)
+ return;
+
+ 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;
+ }
+
+ stencil = malloc(width * sizeof(GLubyte));
+
+ if (stencil) {
+ /* process image row by row */
+ for (j = 0; j < height; j++) {
+ GLvoid *dest;
+
+ _mesa_unpack_ubyte_stencil_row(rb->Format, width, map, stencil);
+ dest = _mesa_image_address2d(packing, pixels, width, height,
+ GL_STENCIL_INDEX, type, j, 0);
+
+ _mesa_pack_stencil_span(ctx, width, type, dest, stencil, packing);
+
+ map += stride;
+ }
+ }
+ else {
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
+ }
+
+ free(stencil);
+
+ 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;
+ 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;
+ 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;
+
+ 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);
+
+ /* Map the source render buffer */
+ ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT,
+ &map, &rb_stride);
+ if (!map) {
+ _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels");
+ return;
+ }
+ rb_format = _mesa_get_srgb_format_linear(rb->Format);
+
+ /*
+ * 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;
+ }
+
+ /* 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));
+
+ needs_rgba = transferOps || dst_is_luminance;
+ rgba = NULL;
+ if (needs_rgba) {
+ uint32_t rgba_format;
+ int rgba_stride;
+ bool need_convert;
+
+ /* Convert to RGBA float or int/uint depending on the type of the src */
+ if (dst_is_integer) {
+ src_is_uint = _mesa_is_format_unsigned(rb_format);
+ if (src_is_uint) {
+ rgba_format = RGBA32_UINT;
+ rgba_stride = width * 4 * sizeof(GLuint);
+ } else {
+ rgba_format = RGBA32_INT;
+ rgba_stride = width * 4 * sizeof(GLint);