X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fmesa%2Fmain%2Freadpix.c;h=bca9c0c90a43aafe0ad1e256664e5b6c860ad650;hb=9037005d6034d6bcbeb508e0f783622e2351b957;hp=c589ca4e202eb50b0e4fc4313fbf1b13cd6401cd;hpb=5038d839b8e4d5926ee2aeaa4a66367ba99a31c6;p=mesa.git diff --git a/src/mesa/main/readpix.c b/src/mesa/main/readpix.c index c589ca4e202..bca9c0c90a4 100644 --- a/src/mesa/main/readpix.c +++ b/src/mesa/main/readpix.c @@ -23,7 +23,7 @@ */ #include "glheader.h" -#include "imports.h" + #include "blend.h" #include "bufferobj.h" #include "context.h" @@ -46,26 +46,48 @@ /** * Return true if the conversion L=R+G+B is needed. */ -static GLboolean -need_rgb_to_luminance_conversion(mesa_format texFormat, GLenum format) +GLboolean +_mesa_need_rgb_to_luminance_conversion(GLenum srcBaseFormat, + GLenum dstBaseFormat) { - GLenum baseTexFormat = _mesa_get_format_base_format(texFormat); - - return (baseTexFormat == GL_RG || - baseTexFormat == GL_RGB || - baseTexFormat == GL_RGBA) && - (format == GL_LUMINANCE || format == GL_LUMINANCE_ALPHA); + return (srcBaseFormat == GL_RG || + srcBaseFormat == GL_RGB || + srcBaseFormat == GL_RGBA) && + (dstBaseFormat == GL_LUMINANCE || + dstBaseFormat == GL_LUMINANCE_ALPHA); } +/** + * Return true if the conversion L,I to RGB conversion is needed. + */ +GLboolean +_mesa_need_luminance_to_rgb_conversion(GLenum srcBaseFormat, + GLenum dstBaseFormat) +{ + return (srcBaseFormat == GL_LUMINANCE || + srcBaseFormat == GL_LUMINANCE_ALPHA || + srcBaseFormat == GL_INTENSITY) && + (dstBaseFormat == GL_GREEN || + dstBaseFormat == GL_BLUE || + dstBaseFormat == GL_RG || + dstBaseFormat == GL_RGB || + dstBaseFormat == GL_BGR || + dstBaseFormat == GL_RGBA || + dstBaseFormat == GL_BGRA); +} /** * Return transfer op flags for this ReadPixels operation. */ -static GLbitfield -get_readpixels_transfer_ops(const struct gl_context *ctx, mesa_format texFormat, - GLenum format, GLenum type, GLboolean uses_blit) +GLbitfield +_mesa_get_readpixels_transfer_ops(const struct gl_context *ctx, + mesa_format texFormat, + GLenum format, GLenum type, + GLboolean uses_blit) { GLbitfield transferOps = ctx->_ImageTransferState; + GLenum srcBaseFormat = _mesa_get_format_base_format(texFormat); + GLenum dstBaseFormat = _mesa_unpack_format_to_base_format(format); if (format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL || @@ -83,16 +105,18 @@ get_readpixels_transfer_ops(const struct gl_context *ctx, mesa_format texFormat, if (uses_blit) { /* For blit-based ReadPixels packing, the clamping is done automatically * unless the type is float. */ - if (_mesa_get_clamp_read_color(ctx) && - (type == GL_FLOAT || type == GL_HALF_FLOAT)) { + if (_mesa_get_clamp_read_color(ctx, ctx->ReadBuffer) && + (type == GL_FLOAT || type == GL_HALF_FLOAT || + type == GL_UNSIGNED_INT_10F_11F_11F_REV)) { transferOps |= IMAGE_CLAMP_BIT; } } else { /* For CPU-based ReadPixels packing, the clamping must always be done * for non-float types, */ - if (_mesa_get_clamp_read_color(ctx) || - (type != GL_FLOAT && type != GL_HALF_FLOAT)) { + if (_mesa_get_clamp_read_color(ctx, ctx->ReadBuffer) || + (type != GL_FLOAT && type != GL_HALF_FLOAT && + type != GL_UNSIGNED_INT_10F_11F_11F_REV)) { transferOps |= IMAGE_CLAMP_BIT; } } @@ -102,7 +126,7 @@ get_readpixels_transfer_ops(const struct gl_context *ctx, mesa_format texFormat, * have any effect anyway. */ if (_mesa_get_format_datatype(texFormat) == GL_UNSIGNED_NORMALIZED && - !need_rgb_to_luminance_conversion(texFormat, format)) { + !_mesa_need_rgb_to_luminance_conversion(srcBaseFormat, dstBaseFormat)) { transferOps &= ~IMAGE_CLAMP_BIT; } @@ -125,9 +149,9 @@ _mesa_readpixels_needs_slow_path(const struct gl_context *ctx, GLenum format, { struct gl_renderbuffer *rb = _mesa_get_read_renderbuffer_for_format(ctx, format); - GLenum srcType; + GLenum dstBaseFormat = _mesa_unpack_format_to_base_format(format); - ASSERT(rb); + assert(rb); /* There are different rules depending on the base format. */ switch (format) { @@ -146,28 +170,14 @@ _mesa_readpixels_needs_slow_path(const struct gl_context *ctx, GLenum format, 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))) { + if (_mesa_need_rgb_to_luminance_conversion(rb->_BaseFormat, + dstBaseFormat)) { 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 _mesa_get_readpixels_transfer_ops(ctx, rb->Format, format, type, + uses_blit) != 0; } return GL_FALSE; } @@ -180,7 +190,7 @@ readpixels_can_use_memcpy(const struct gl_context *ctx, GLenum format, GLenum ty struct gl_renderbuffer *rb = _mesa_get_read_renderbuffer_for_format(ctx, format); - ASSERT(rb); + assert(rb); if (_mesa_readpixels_needs_slow_path(ctx, format, type, GL_FALSE)) { return GL_FALSE; @@ -193,7 +203,7 @@ readpixels_can_use_memcpy(const struct gl_context *ctx, GLenum format, GLenum ty /* The Mesa format must match the input format and type. */ if (!_mesa_format_matches_format_and_type(rb->Format, format, type, - packing->SwapBytes)) { + packing->SwapBytes, NULL)) { return GL_FALSE; } @@ -212,7 +222,7 @@ readpixels_memcpy(struct gl_context *ctx, struct gl_renderbuffer *rb = _mesa_get_read_renderbuffer_for_format(ctx, format); GLubyte *dst, *map; - int dstStride, stride, j, texelBytes; + int dstStride, stride, j, texelBytes, bytesPerRow; /* Fail if memcpy cannot be used. */ if (!readpixels_can_use_memcpy(ctx, format, type, packing)) { @@ -224,19 +234,24 @@ readpixels_memcpy(struct gl_context *ctx, format, type, 0, 0); ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT, - &map, &stride); + &map, &stride, ctx->ReadBuffer->FlipY); 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*/ - for (j = 0; j < height; j++) { - memcpy(dst, map, width * texelBytes); - dst += dstStride; - map += stride; + 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); @@ -260,7 +275,7 @@ read_uint_depth_pixels( struct gl_context *ctx, GLubyte *map, *dst; int stride, dstStride, j; - if (ctx->Pixel.DepthScale != 1.0 || ctx->Pixel.DepthBias != 0.0) + if (ctx->Pixel.DepthScale != 1.0F || ctx->Pixel.DepthBias != 0.0F) return GL_FALSE; if (packing->SwapBytes) @@ -270,7 +285,7 @@ read_uint_depth_pixels( struct gl_context *ctx, return GL_FALSE; ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT, - &map, &stride); + &map, &stride, fb->FlipY); if (!map) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); @@ -313,10 +328,10 @@ read_depth_pixels( struct gl_context *ctx, 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); + 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)) { @@ -328,7 +343,7 @@ read_depth_pixels( struct gl_context *ctx, GL_DEPTH_COMPONENT, type, 0, 0); ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT, - &map, &stride); + &map, &stride, fb->FlipY); if (!map) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); return; @@ -376,7 +391,7 @@ read_stencil_pixels( struct gl_context *ctx, return; ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT, - &map, &stride); + &map, &stride, fb->FlipY); if (!map) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); return; @@ -418,7 +433,7 @@ read_rgba_pixels( struct gl_context *ctx, const struct gl_pixelstore_attrib *packing ) { GLbitfield transferOps; - bool dst_is_integer, dst_is_luminance, needs_rebase; + 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; @@ -429,26 +444,25 @@ read_rgba_pixels( struct gl_context *ctx, 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 = get_readpixels_transfer_ops(ctx, rb->Format, format, type, - GL_FALSE); + 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); - dst_is_luminance = format == GL_LUMINANCE || - format == GL_LUMINANCE_ALPHA || - format == GL_LUMINANCE_INTEGER_EXT || - format == GL_LUMINANCE_ALPHA_INTEGER_EXT; + 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); + &map, &rb_stride, fb->FlipY); if (!map) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); return; @@ -490,7 +504,7 @@ read_rgba_pixels( struct gl_context *ctx, */ assert(!transferOps || (transferOps && !dst_is_integer)); - needs_rgba = transferOps || dst_is_luminance; + needs_rgba = transferOps || convert_rgb_to_lum; rgba = NULL; if (needs_rgba) { uint32_t rgba_format; @@ -501,14 +515,14 @@ read_rgba_pixels( struct gl_context *ctx, if (dst_is_integer) { src_is_uint = _mesa_is_format_unsigned(rb_format); if (src_is_uint) { - rgba_format = RGBA8888_UINT; + rgba_format = RGBA32_UINT; rgba_stride = width * 4 * sizeof(GLuint); } else { - rgba_format = RGBA8888_INT; + rgba_format = RGBA32_INT; rgba_stride = width * 4 * sizeof(GLint); } } else { - rgba_format = RGBA8888_FLOAT; + rgba_format = RGBA32_FLOAT; rgba_stride = width * 4 * sizeof(GLfloat); } @@ -516,7 +530,8 @@ read_rgba_pixels( struct gl_context *ctx, * 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) { + if (dst_format == rgba_format && + dst_stride == rgba_stride) { need_convert = false; rgba = dst; } else { @@ -563,7 +578,7 @@ read_rgba_pixels( struct gl_context *ctx, * If the dst format is Luminance, we need to do the conversion by computing * L=R+G+B values. */ - if (!dst_is_luminance) { + if (!convert_rgb_to_lum) { _mesa_format_convert(dst, dst_format, dst_stride, src, src_format, src_stride, width, height, @@ -574,7 +589,7 @@ read_rgba_pixels( struct gl_context *ctx, void *luminance; uint32_t luminance_format; - luminance_stride = width * sizeof(GL_FLOAT); + luminance_stride = width * sizeof(GLfloat); if (format == GL_LUMINANCE_ALPHA) luminance_stride *= 2; luminance_bytes = height * luminance_stride; @@ -594,23 +609,19 @@ read_rgba_pixels( struct gl_context *ctx, _mesa_format_convert(dst, dst_format, dst_stride, luminance, luminance_format, luminance_stride, width, height, NULL); + free(luminance); } else { _mesa_pack_luminance_from_rgba_integer(width * height, src, !src_is_uint, dst, format, type); } - if (rgba) - free(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); + _mesa_swap_bytes_2d_image(format, type, packing, + width, height, dst, dst); } done_unmap: @@ -641,7 +652,7 @@ fast_read_depth_stencil_pixels(struct gl_context *ctx, return GL_FALSE; ctx->Driver.MapRenderbuffer(ctx, rb, x, y, width, height, GL_MAP_READ_BIT, - &map, &stride); + &map, &stride, fb->FlipY); if (!map) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); return GL_TRUE; /* don't bother trying the slow path */ @@ -681,14 +692,14 @@ fast_read_depth_stencil_pixels_separate(struct gl_context *ctx, return GL_FALSE; ctx->Driver.MapRenderbuffer(ctx, depthRb, x, y, width, height, - GL_MAP_READ_BIT, &depthMap, &depthStride); + GL_MAP_READ_BIT, &depthMap, &depthStride, fb->FlipY); if (!depthMap) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); return GL_TRUE; /* don't bother trying the slow path */ } ctx->Driver.MapRenderbuffer(ctx, stencilRb, x, y, width, height, - GL_MAP_READ_BIT, &stencilMap, &stencilStride); + GL_MAP_READ_BIT, &stencilMap, &stencilStride, fb->FlipY); if (!stencilMap) { ctx->Driver.UnmapRenderbuffer(ctx, depthRb); _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); @@ -745,7 +756,7 @@ slow_read_depth_stencil_pixels_separate(struct gl_context *ctx, * If one buffer, only map it once. */ ctx->Driver.MapRenderbuffer(ctx, depthRb, x, y, width, height, - GL_MAP_READ_BIT, &depthMap, &depthStride); + GL_MAP_READ_BIT, &depthMap, &depthStride, fb->FlipY); if (!depthMap) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); return; @@ -754,7 +765,7 @@ slow_read_depth_stencil_pixels_separate(struct gl_context *ctx, if (stencilRb != depthRb) { ctx->Driver.MapRenderbuffer(ctx, stencilRb, x, y, width, height, GL_MAP_READ_BIT, &stencilMap, - &stencilStride); + &stencilStride, fb->FlipY); if (!stencilMap) { ctx->Driver.UnmapRenderbuffer(ctx, depthRb); _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); @@ -810,7 +821,7 @@ read_depth_stencil_pixels(struct gl_context *ctx, const struct gl_pixelstore_attrib *packing ) { const GLboolean scaleOrBias - = ctx->Pixel.DepthScale != 1.0 || ctx->Pixel.DepthBias != 0.0; + = ctx->Pixel.DepthScale != 1.0F || ctx->Pixel.DepthBias != 0.0F; const GLboolean stencilTransfer = ctx->Pixel.IndexShift || ctx->Pixel.IndexOffset || ctx->Pixel.MapStencilFlag; GLubyte *dst; @@ -853,62 +864,54 @@ _mesa_readpixels(struct gl_context *ctx, const struct gl_pixelstore_attrib *packing, GLvoid *pixels) { - struct gl_pixelstore_attrib clippedPacking = *packing; - if (ctx->NewState) _mesa_update_state(ctx); - /* Do all needed clipping here, so that we can forget about it later */ - if (_mesa_clip_readpixels(ctx, &x, &y, &width, &height, &clippedPacking)) { - - pixels = _mesa_map_pbo_dest(ctx, &clippedPacking, pixels); - - if (pixels) { - /* Try memcpy first. */ - if (readpixels_memcpy(ctx, x, y, width, height, format, type, - pixels, packing)) { - _mesa_unmap_pbo_dest(ctx, &clippedPacking); - return; - } + pixels = _mesa_map_pbo_dest(ctx, packing, pixels); - /* Otherwise take the slow path. */ - switch (format) { - case GL_STENCIL_INDEX: - read_stencil_pixels(ctx, x, y, width, height, type, pixels, - &clippedPacking); - break; - case GL_DEPTH_COMPONENT: - read_depth_pixels(ctx, x, y, width, height, type, pixels, - &clippedPacking); - break; - case GL_DEPTH_STENCIL_EXT: - read_depth_stencil_pixels(ctx, x, y, width, height, type, pixels, - &clippedPacking); - break; - default: - /* all other formats should be color formats */ - read_rgba_pixels(ctx, x, y, width, height, format, type, pixels, - &clippedPacking); - } + if (pixels) { + /* Try memcpy first. */ + if (readpixels_memcpy(ctx, x, y, width, height, format, type, + pixels, packing)) { + _mesa_unmap_pbo_dest(ctx, packing); + return; + } - _mesa_unmap_pbo_dest(ctx, &clippedPacking); + /* Otherwise take the slow path. */ + switch (format) { + case GL_STENCIL_INDEX: + read_stencil_pixels(ctx, x, y, width, height, type, pixels, + packing); + break; + case GL_DEPTH_COMPONENT: + read_depth_pixels(ctx, x, y, width, height, type, pixels, + packing); + break; + case GL_DEPTH_STENCIL_EXT: + read_depth_stencil_pixels(ctx, x, y, width, height, type, pixels, + packing); + break; + default: + /* all other formats should be color formats */ + read_rgba_pixels(ctx, x, y, width, height, format, type, pixels, + packing); } + + _mesa_unmap_pbo_dest(ctx, packing); } } static GLenum -read_pixels_es3_error_check(GLenum format, GLenum type, +read_pixels_es3_error_check(struct gl_context *ctx, GLenum format, GLenum type, const struct gl_renderbuffer *rb) { const GLenum internalFormat = rb->InternalFormat; const GLenum data_type = _mesa_get_format_datatype(rb->Format); GLboolean is_unsigned_int = GL_FALSE; GLboolean is_signed_int = GL_FALSE; - - if (!_mesa_is_color_format(internalFormat)) { - return GL_INVALID_OPERATION; - } + GLboolean is_float_depth = (internalFormat == GL_DEPTH_COMPONENT32F) || + (internalFormat == GL_DEPTH32F_STENCIL8); is_unsigned_int = _mesa_is_enum_format_unsigned_int(internalFormat); if (!is_unsigned_int) { @@ -926,6 +929,44 @@ read_pixels_es3_error_check(GLenum format, GLenum type, return GL_NO_ERROR; if (internalFormat == GL_RGB10_A2UI && type == GL_UNSIGNED_BYTE) return GL_NO_ERROR; + if (type == GL_UNSIGNED_SHORT) { + switch (internalFormat) { + case GL_R16: + case GL_RG16: + case GL_RGB16: + case GL_RGBA16: + if (_mesa_has_EXT_texture_norm16(ctx)) + return GL_NO_ERROR; + } + } + if (type == GL_SHORT) { + switch (internalFormat) { + case GL_R16_SNORM: + case GL_RG16_SNORM: + case GL_RGBA16_SNORM: + if (_mesa_has_EXT_texture_norm16(ctx) && + _mesa_has_EXT_render_snorm(ctx)) + return GL_NO_ERROR; + } + } + if (type == GL_BYTE) { + switch (internalFormat) { + case GL_R8_SNORM: + case GL_RG8_SNORM: + case GL_RGBA8_SNORM: + if (_mesa_has_EXT_render_snorm(ctx)) + return GL_NO_ERROR; + } + } + if (type == GL_UNSIGNED_BYTE) { + switch (internalFormat) { + case GL_R8_SNORM: + case GL_RG8_SNORM: + case GL_RGBA8_SNORM: + if (_mesa_has_EXT_render_snorm(ctx)) + return GL_NO_ERROR; + } + } break; case GL_BGRA: /* GL_EXT_read_format_bgra */ @@ -939,33 +980,70 @@ read_pixels_es3_error_check(GLenum format, GLenum type, (is_unsigned_int && type == GL_UNSIGNED_INT)) return GL_NO_ERROR; break; + case GL_DEPTH_STENCIL: + switch (type) { + case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: + if (is_float_depth) + return GL_NO_ERROR; + break; + case GL_UNSIGNED_INT_24_8: + if (!is_float_depth) + return GL_NO_ERROR; + break; + default: + return GL_INVALID_ENUM; + } + break; + case GL_DEPTH_COMPONENT: + switch (type) { + case GL_FLOAT: + if (is_float_depth) + return GL_NO_ERROR; + break; + case GL_UNSIGNED_SHORT: + case GL_UNSIGNED_INT: + case GL_UNSIGNED_INT_24_8: + if (!is_float_depth) + return GL_NO_ERROR; + break; + default: + return GL_INVALID_ENUM; + } + break; + case GL_STENCIL_INDEX: + switch (type) { + case GL_UNSIGNED_BYTE: + return GL_NO_ERROR; + default: + return GL_INVALID_ENUM; + } + break; } return GL_INVALID_OPERATION; } -void GLAPIENTRY -_mesa_ReadnPixelsARB( GLint x, GLint y, GLsizei width, GLsizei height, - GLenum format, GLenum type, GLsizei bufSize, - GLvoid *pixels ) +static ALWAYS_INLINE void +read_pixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, + GLenum type, GLsizei bufSize, GLvoid *pixels, bool no_error) { GLenum err = GL_NO_ERROR; struct gl_renderbuffer *rb; + struct gl_pixelstore_attrib clippedPacking; GET_CURRENT_CONTEXT(ctx); FLUSH_VERTICES(ctx, 0); - FLUSH_CURRENT(ctx, 0); if (MESA_VERBOSE & VERBOSE_API) _mesa_debug(ctx, "glReadPixels(%d, %d, %s, %s, %p)\n", width, height, - _mesa_lookup_enum_by_nr(format), - _mesa_lookup_enum_by_nr(type), + _mesa_enum_to_string(format), + _mesa_enum_to_string(type), pixels); - if (width < 0 || height < 0) { + if (!no_error && (width < 0 || height < 0)) { _mesa_error( ctx, GL_INVALID_VALUE, "glReadPixels(width=%d height=%d)", width, height ); return; @@ -974,120 +1052,145 @@ _mesa_ReadnPixelsARB( GLint x, GLint y, GLsizei width, GLsizei height, if (ctx->NewState) _mesa_update_state(ctx); - if (ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { + if (!no_error && ctx->ReadBuffer->_Status != GL_FRAMEBUFFER_COMPLETE_EXT) { _mesa_error(ctx, GL_INVALID_FRAMEBUFFER_OPERATION_EXT, "glReadPixels(incomplete framebuffer)" ); return; } rb = _mesa_get_read_renderbuffer_for_format(ctx, format); - if (rb == NULL) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glReadPixels(read buffer)"); - return; - } + if (!no_error) { + if (rb == NULL) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glReadPixels(read buffer)"); + return; + } - /* OpenGL ES 1.x and OpenGL ES 2.0 impose additional restrictions on the - * combinations of format and type that can be used. - * - * Technically, only two combinations are actually allowed: - * GL_RGBA/GL_UNSIGNED_BYTE, and some implementation-specific internal - * preferred combination. This code doesn't know what that preferred - * combination is, and Mesa can handle anything valid. Just work instead. - */ - if (_mesa_is_gles(ctx)) { - if (ctx->API == API_OPENGLES2 && - _mesa_is_color_format(format) && - _mesa_get_color_read_format(ctx) == format && - _mesa_get_color_read_type(ctx) == type) { - err = GL_NO_ERROR; - } else if (ctx->Version < 30) { - err = _mesa_es_error_check_format_and_type(format, type, 2); - if (err == GL_NO_ERROR) { - if (type == GL_FLOAT || type == GL_HALF_FLOAT_OES) { - err = GL_INVALID_OPERATION; + /* OpenGL ES 1.x and OpenGL ES 2.0 impose additional restrictions on the + * combinations of format and type that can be used. + * + * Technically, only two combinations are actually allowed: + * GL_RGBA/GL_UNSIGNED_BYTE, and some implementation-specific internal + * preferred combination. This code doesn't know what that preferred + * combination is, and Mesa can handle anything valid. Just work instead. + */ + if (_mesa_is_gles(ctx)) { + if (ctx->API == API_OPENGLES2 && + _mesa_is_color_format(format) && + _mesa_get_color_read_format(ctx, NULL, "glReadPixels") == format && + _mesa_get_color_read_type(ctx, NULL, "glReadPixels") == type) { + err = GL_NO_ERROR; + } else if (ctx->Version < 30) { + err = _mesa_es_error_check_format_and_type(ctx, format, type, 2); + if (err == GL_NO_ERROR) { + if (type == GL_FLOAT || type == GL_HALF_FLOAT_OES) { + err = GL_INVALID_OPERATION; + } } + } else { + err = read_pixels_es3_error_check(ctx, format, type, rb); } - } else { - err = read_pixels_es3_error_check(format, type, rb); - } - if (err == GL_NO_ERROR && (format == GL_DEPTH_COMPONENT - || format == GL_DEPTH_STENCIL)) { - err = GL_INVALID_ENUM; + if (err != GL_NO_ERROR) { + _mesa_error(ctx, err, "glReadPixels(invalid format %s and/or type %s)", + _mesa_enum_to_string(format), + _mesa_enum_to_string(type)); + return; + } } + err = _mesa_error_check_format_and_type(ctx, format, type); if (err != GL_NO_ERROR) { _mesa_error(ctx, err, "glReadPixels(invalid format %s and/or type %s)", - _mesa_lookup_enum_by_nr(format), - _mesa_lookup_enum_by_nr(type)); + _mesa_enum_to_string(format), + _mesa_enum_to_string(type)); return; } - } - err = _mesa_error_check_format_and_type(ctx, format, type); - if (err != GL_NO_ERROR) { - _mesa_error(ctx, err, "glReadPixels(invalid format %s and/or type %s)", - _mesa_lookup_enum_by_nr(format), - _mesa_lookup_enum_by_nr(type)); - return; - } - - if (_mesa_is_user_fbo(ctx->ReadBuffer) && - ctx->ReadBuffer->Visual.samples > 0) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glReadPixels(multisample FBO)"); - return; - } - - if (!_mesa_source_buffer_exists(ctx, format)) { - _mesa_error(ctx, GL_INVALID_OPERATION, "glReadPixels(no readbuffer)"); - return; - } + if (_mesa_is_user_fbo(ctx->ReadBuffer) && + ctx->ReadBuffer->Visual.samples > 0) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glReadPixels(multisample FBO)"); + return; + } - /* Check that the destination format and source buffer are both - * integer-valued or both non-integer-valued. - */ - if (ctx->Extensions.EXT_texture_integer && _mesa_is_color_format(format)) { - const struct gl_renderbuffer *rb = ctx->ReadBuffer->_ColorReadBuffer; - const GLboolean srcInteger = _mesa_is_format_integer_color(rb->Format); - const GLboolean dstInteger = _mesa_is_enum_format_integer(format); - if (dstInteger != srcInteger) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glReadPixels(integer / non-integer format mismatch"); + if (!_mesa_source_buffer_exists(ctx, format)) { + _mesa_error(ctx, GL_INVALID_OPERATION, "glReadPixels(no readbuffer)"); return; } + + /* Check that the destination format and source buffer are both + * integer-valued or both non-integer-valued. + */ + if (ctx->Extensions.EXT_texture_integer && _mesa_is_color_format(format)) { + const struct gl_renderbuffer *rb = ctx->ReadBuffer->_ColorReadBuffer; + const GLboolean srcInteger = _mesa_is_format_integer_color(rb->Format); + const GLboolean dstInteger = _mesa_is_enum_format_integer(format); + if (dstInteger != srcInteger) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glReadPixels(integer / non-integer format mismatch"); + return; + } + } } - if (width == 0 || height == 0) + /* Do all needed clipping here, so that we can forget about it later */ + clippedPacking = ctx->Pack; + if (!_mesa_clip_readpixels(ctx, &x, &y, &width, &height, &clippedPacking)) return; /* nothing to do */ - if (!_mesa_validate_pbo_access(2, &ctx->Pack, width, height, 1, - format, type, bufSize, pixels)) { - if (_mesa_is_bufferobj(ctx->Pack.BufferObj)) { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glReadPixels(out of bounds PBO access)"); - } else { - _mesa_error(ctx, GL_INVALID_OPERATION, - "glReadnPixelsARB(out of bounds access:" - " bufSize (%d) is too small)", bufSize); + if (!no_error) { + if (!_mesa_validate_pbo_access(2, &ctx->Pack, width, height, 1, + format, type, bufSize, pixels)) { + if (ctx->Pack.BufferObj) { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glReadPixels(out of bounds PBO access)"); + } else { + _mesa_error(ctx, GL_INVALID_OPERATION, + "glReadnPixelsARB(out of bounds access:" + " bufSize (%d) is too small)", bufSize); + } + return; } - return; - } - if (_mesa_is_bufferobj(ctx->Pack.BufferObj) && - _mesa_check_disallowed_mapping(ctx->Pack.BufferObj)) { - /* buffer is mapped - that's an error */ - _mesa_error(ctx, GL_INVALID_OPERATION, "glReadPixels(PBO is mapped)"); - return; + if (ctx->Pack.BufferObj && + _mesa_check_disallowed_mapping(ctx->Pack.BufferObj)) { + /* buffer is mapped - that's an error */ + _mesa_error(ctx, GL_INVALID_OPERATION, "glReadPixels(PBO is mapped)"); + return; + } } ctx->Driver.ReadPixels(ctx, x, y, width, height, - format, type, &ctx->Pack, pixels); + format, type, &clippedPacking, pixels); +} + +void GLAPIENTRY +_mesa_ReadnPixelsARB_no_error(GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, GLsizei bufSize, + GLvoid *pixels) +{ + read_pixels(x, y, width, height, format, type, bufSize, pixels, true); +} + +void GLAPIENTRY +_mesa_ReadnPixelsARB(GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, GLsizei bufSize, + GLvoid *pixels) +{ + read_pixels(x, y, width, height, format, type, bufSize, pixels, false); +} + +void GLAPIENTRY +_mesa_ReadPixels_no_error(GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, GLvoid *pixels) +{ + _mesa_ReadnPixelsARB_no_error(x, y, width, height, format, type, INT_MAX, + pixels); } void GLAPIENTRY -_mesa_ReadPixels( GLint x, GLint y, GLsizei width, GLsizei height, - GLenum format, GLenum type, GLvoid *pixels ) +_mesa_ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, + GLenum format, GLenum type, GLvoid *pixels) { _mesa_ReadnPixelsARB(x, y, width, height, format, type, INT_MAX, pixels); }