+GLboolean
+_mesa_readpixels_needs_slow_path(const struct gl_context *ctx, GLenum format,
+ GLenum type, GLboolean uses_blit)
+{
+ struct gl_renderbuffer *rb =
+ _mesa_get_read_renderbuffer_for_format(ctx, format);
+ GLenum dstBaseFormat = _mesa_unpack_format_to_base_format(format);
+
+ assert(rb);
+
+ /* There are different rules depending on the base format. */
+ switch (format) {
+ 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;
+}
+