+ case GL_DEPTH_STENCIL:
+ return !_mesa_has_depthstencil_combined(ctx->ReadBuffer) ||
+ ctx->Pixel.DepthScale != 1.0f || ctx->Pixel.DepthBias != 0.0f ||
+ ctx->Pixel.IndexShift || ctx->Pixel.IndexOffset ||
+ ctx->Pixel.MapStencilFlag;
+
+ case GL_DEPTH_COMPONENT:
+ return ctx->Pixel.DepthScale != 1.0f || ctx->Pixel.DepthBias != 0.0f;
+
+ case GL_STENCIL_INDEX:
+ return ctx->Pixel.IndexShift || ctx->Pixel.IndexOffset ||
+ ctx->Pixel.MapStencilFlag;
+
+ default:
+ /* Color formats. */
+ if (_mesa_need_rgb_to_luminance_conversion(rb->_BaseFormat,
+ dstBaseFormat)) {
+ return GL_TRUE;
+ }
+
+ /* And finally, see if there are any transfer ops. */
+ return _mesa_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, NULL)) {
+ 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, bytesPerRow;
+
+ /* 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);
+ bytesPerRow = texelBytes * width;
+
+ /* memcpy*/
+ if (dstStride == stride && dstStride == bytesPerRow) {
+ memcpy(dst, map, bytesPerRow * height);
+ } else {
+ for (j = 0; j < height; j++) {
+ memcpy(dst, map, bytesPerRow);
+ 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.0F || ctx->Pixel.DepthBias != 0.0F)
+ 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, convert_rgb_to_lum, 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;
+ GLenum dstBaseFormat = _mesa_unpack_format_to_base_format(format);
+
+ if (!rb)
+ return;
+
+ transferOps = _mesa_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);
+ convert_rgb_to_lum =
+ _mesa_need_rgb_to_luminance_conversion(rb->_BaseFormat, dstBaseFormat);
+ 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 || convert_rgb_to_lum;
+ 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);