/* * 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); } } else { rgba_format = RGBA32_FLOAT; rgba_stride = width * 4 * sizeof(GLfloat); } /* If we are lucky and the dst format matches the RGBA format we need to * 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) { need_convert = false; rgba = dst; } else { need_convert = true; rgba = malloc(height * rgba_stride); if (!rgba) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); goto done_unmap; } } /* Convert to RGBA now */ _mesa_format_convert(rgba, rgba_format, rgba_stride, map, rb_format, rb_stride, width, height, needs_rebase ? rebase_swizzle : NULL); /* Handle transfer ops if necessary */ if (transferOps) _mesa_apply_rgba_transfer_ops(ctx, transferOps, width * height, rgba); /* If we had to rebase, we have already taken care of that */ needs_rebase = false; /* If we were lucky and our RGBA conversion matches the dst format, then * we are done. */ if (!need_convert) goto done_swap; /* Otherwise, we need to convert from RGBA to dst next */ src = rgba; src_format = rgba_format; src_stride = rgba_stride; } else { /* No RGBA conversion needed, convert directly to dst */ src = map; src_format = rb_format; src_stride = rb_stride; } /* Do the conversion. * * If the dst format is Luminance, we need to do the conversion by computing * L=R+G+B values. */ if (!dst_is_luminance) { _mesa_format_convert(dst, dst_format, dst_stride, src, src_format, src_stride, width, height, needs_rebase ? rebase_swizzle : NULL); } else if (!dst_is_integer) { /* Compute float Luminance values from RGBA float */ int luminance_stride, luminance_bytes; void *luminance; uint32_t luminance_format; luminance_stride = width * sizeof(GL_FLOAT); if (format == GL_LUMINANCE_ALPHA) luminance_stride *= 2; luminance_bytes = height * luminance_stride; luminance = malloc(luminance_bytes); if (!luminance) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glReadPixels"); free(rgba); goto done_unmap; } _mesa_pack_luminance_from_rgba_float(width * height, src, luminance, format, transferOps); /* Convert from Luminance float to dst (this will hadle type conversion * from float to the type of dst if necessary) */ luminance_format = _mesa_format_from_format_and_type(format, GL_FLOAT); _mesa_format_convert(dst, dst_format, dst_stride, luminance, luminance_format, luminance_stride, width, height, NULL); } else { _mesa_pack_luminance_from_rgba_integer(width * height, src, !src_is_uint, dst, format, type); } if (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); } done_unmap: ctx->Driver.UnmapRenderbuffer(ctx, rb); }
/** * Get an uncompressed color texture image. */ static void get_tex_rgba_uncompressed(struct gl_context *ctx, GLuint dimensions, GLenum format, GLenum type, GLvoid *pixels, struct gl_texture_image *texImage, GLbitfield transferOps) { /* don't want to apply sRGB -> RGB conversion here so override the format */ const gl_format texFormat = _mesa_get_srgb_format_linear(texImage->TexFormat); const GLuint width = texImage->Width; const GLenum destBaseFormat = _mesa_base_tex_format(ctx, format); GLenum rebaseFormat = GL_NONE; GLuint height = texImage->Height; GLuint depth = texImage->Depth; GLuint img, row; GLfloat (*rgba)[4]; GLuint (*rgba_uint)[4]; GLboolean tex_is_integer = _mesa_is_format_integer_color(texImage->TexFormat); GLboolean tex_is_uint = _mesa_is_format_unsigned(texImage->TexFormat); /* Allocate buffer for one row of texels */ rgba = (GLfloat (*)[4]) malloc(4 * width * sizeof(GLfloat)); rgba_uint = (GLuint (*)[4]) rgba; if (!rgba) { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage()"); return; } if (texImage->TexObject->Target == GL_TEXTURE_1D_ARRAY) { depth = height; height = 1; } if (texImage->_BaseFormat == GL_LUMINANCE || texImage->_BaseFormat == GL_INTENSITY || texImage->_BaseFormat == GL_LUMINANCE_ALPHA) { /* If a luminance (or intensity) texture is read back as RGB(A), the * returned value should be (L,0,0,1), not (L,L,L,1). Set rebaseFormat * here to get G=B=0. */ rebaseFormat = texImage->_BaseFormat; } else if ((texImage->_BaseFormat == GL_RGBA || texImage->_BaseFormat == GL_RGB || texImage->_BaseFormat == GL_RG) && (destBaseFormat == GL_LUMINANCE || destBaseFormat == GL_LUMINANCE_ALPHA || destBaseFormat == GL_LUMINANCE_INTEGER_EXT || destBaseFormat == GL_LUMINANCE_ALPHA_INTEGER_EXT)) { /* If we're reading back an RGB(A) texture as luminance then we need * to return L=tex(R). Note, that's different from glReadPixels which * returns L=R+G+B. */ rebaseFormat = GL_LUMINANCE_ALPHA; /* this covers GL_LUMINANCE too */ } for (img = 0; img < depth; img++) { GLubyte *srcMap; GLint rowstride; /* map src texture buffer */ ctx->Driver.MapTextureImage(ctx, texImage, img, 0, 0, width, height, GL_MAP_READ_BIT, &srcMap, &rowstride); if (srcMap) { for (row = 0; row < height; row++) { const GLubyte *src = srcMap + row * rowstride; void *dest = _mesa_image_address(dimensions, &ctx->Pack, pixels, width, height, format, type, img, row, 0); if (tex_is_integer) { _mesa_unpack_uint_rgba_row(texFormat, width, src, rgba_uint); if (rebaseFormat) _mesa_rebase_rgba_uint(width, rgba_uint, rebaseFormat); if (tex_is_uint) { _mesa_pack_rgba_span_from_uints(ctx, width, (GLuint (*)[4]) rgba_uint, format, type, dest); } else { _mesa_pack_rgba_span_from_ints(ctx, width, (GLint (*)[4]) rgba_uint, format, type, dest); } } else { _mesa_unpack_rgba_row(texFormat, width, src, rgba); if (rebaseFormat) _mesa_rebase_rgba_float(width, rgba, rebaseFormat); _mesa_pack_rgba_span_float(ctx, width, (GLfloat (*)[4]) rgba, format, type, dest, &ctx->Pack, transferOps); } } /* Unmap the src texture buffer */ ctx->Driver.UnmapTextureImage(ctx, texImage, img); } else { _mesa_error(ctx, GL_OUT_OF_MEMORY, "glGetTexImage"); break; } } free(rgba); }