+ 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 (need_rgb_to_luminance_conversion(rb->Format, format)) {
+ return GL_TRUE;
+ }
+
+ /* 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);
+}
+
+
+/**
+ * Try to do glReadPixels of RGBA data using swizzle.
+ * \return GL_TRUE if successful, GL_FALSE otherwise (use the slow path)
+ */
+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)
+{
+ 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;
+ }
+
+ 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 */
+ }
+
+ 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);