get_texture_bits_via_offscreen(): use meta texture format

When reading a texture back by first wrapping it as an offscreen
framebuffer and using _read_pixels_into_bitmap() we now make sure the
offscreen framebuffer has an internal format that matches the
meta-texture being read not that of the current sub-texture being
iterated. In the case of atlas textures the subtexture is a shared
texture whose format doesn't reflect the premultipled alpha status of
individual atlas-textures, nor whether the alpha component is valid.

Reviewed-by: Neil Roberts <neil@linux.intel.com>
(cherry picked from commit 6ee425d4f10acd8b008a2c17e5c701fc1d850f59)
This commit is contained in:
Robert Bragg 2013-12-01 21:31:33 +00:00
parent 99cdcc9e3c
commit 3f780e2f3f
3 changed files with 69 additions and 25 deletions

View File

@ -223,6 +223,22 @@ _cogl_framebuffer_init (CoglFramebuffer *framebuffer,
int width,
int height);
/* XXX: For a public api we might instead want a way to explicitly
* set the _premult status of a framebuffer or what components we
* care about instead of exposing the CoglPixelFormat
* internal_format.
*
* The current use case for this api is where we create an offscreen
* framebuffer for a shared atlas texture that has a format of
* RGBA_8888 disregarding the premultiplied alpha status for
* individual atlased textures or whether the alpha component is being
* discarded. We want to overried the internal_format that will be
* derived from the texture.
*/
void
_cogl_framebuffer_set_internal_format (CoglFramebuffer *framebuffer,
CoglPixelFormat internal_format);
void _cogl_framebuffer_free (CoglFramebuffer *framebuffer);
const CoglWinsysVtable *

View File

@ -164,6 +164,13 @@ _cogl_framebuffer_init (CoglFramebuffer *framebuffer,
ctx->framebuffers = g_list_prepend (ctx->framebuffers, framebuffer);
}
void
_cogl_framebuffer_set_internal_format (CoglFramebuffer *framebuffer,
CoglPixelFormat internal_format)
{
framebuffer->internal_format = internal_format;
}
void
_cogl_framebuffer_free (CoglFramebuffer *framebuffer)
{

View File

@ -802,27 +802,29 @@ EXIT:
}
static CoglBool
get_texture_bits_via_offscreen (CoglTexture *texture,
int x,
int y,
int width,
int height,
uint8_t *dst_bits,
unsigned int dst_rowstride,
CoglPixelFormat dst_format)
get_texture_bits_via_offscreen (CoglTexture *meta_texture,
CoglTexture *sub_texture,
int x,
int y,
int width,
int height,
uint8_t *dst_bits,
unsigned int dst_rowstride,
CoglPixelFormat closest_format)
{
CoglContext *ctx = texture->context;
CoglContext *ctx = sub_texture->context;
CoglOffscreen *offscreen;
CoglFramebuffer *framebuffer;
CoglBitmap *bitmap;
CoglBool ret;
CoglError *ignore_error = NULL;
CoglPixelFormat real_format;
if (!cogl_has_feature (ctx, COGL_FEATURE_ID_OFFSCREEN))
return FALSE;
offscreen = _cogl_offscreen_new_with_texture_full
(texture,
(sub_texture,
COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL,
0);
@ -833,9 +835,23 @@ get_texture_bits_via_offscreen (CoglTexture *texture,
return FALSE;
}
/* Currently the framebuffer's internal format corresponds to the
* internal format of @sub_texture but in the case of atlas textures
* it's possible that this format doesn't reflect the correct
* premultiplied alpha status or what components are valid since
* atlas textures are always stored in a shared texture with a
* format of _RGBA_8888.
*
* Here we override the internal format to make sure the
* framebuffer's internal format matches the internal format of the
* parent meta_texture instead.
*/
real_format = _cogl_texture_get_format (meta_texture);
_cogl_framebuffer_set_internal_format (framebuffer, real_format);
bitmap = cogl_bitmap_new_for_data (ctx,
width, height,
dst_format,
closest_format,
dst_rowstride,
dst_bits);
ret = _cogl_framebuffer_read_pixels_into_bitmap (framebuffer,
@ -904,6 +920,7 @@ get_texture_bits_via_copy (CoglTexture *texture,
typedef struct
{
CoglTexture *meta_texture;
int orig_width;
int orig_height;
CoglBitmap *target_bmp;
@ -913,17 +930,18 @@ typedef struct
} CoglTextureGetData;
static void
texture_get_cb (CoglTexture *texture,
texture_get_cb (CoglTexture *subtexture,
const float *subtexture_coords,
const float *virtual_coords,
void *user_data)
{
CoglTextureGetData *tg_data = user_data;
CoglPixelFormat format = cogl_bitmap_get_format (tg_data->target_bmp);
int bpp = _cogl_pixel_format_get_bytes_per_pixel (format);
CoglTexture *meta_texture = tg_data->meta_texture;
CoglPixelFormat closest_format = cogl_bitmap_get_format (tg_data->target_bmp);
int bpp = _cogl_pixel_format_get_bytes_per_pixel (closest_format);
unsigned int rowstride = cogl_bitmap_get_rowstride (tg_data->target_bmp);
int subtexture_width = cogl_texture_get_width (texture);
int subtexture_height = cogl_texture_get_height (texture);
int subtexture_width = cogl_texture_get_width (subtexture);
int subtexture_height = cogl_texture_get_height (subtexture);
int x_in_subtexture = (int) (0.5 + subtexture_width * subtexture_coords[0]);
int y_in_subtexture = (int) (0.5 + subtexture_height * subtexture_coords[1]);
@ -944,33 +962,35 @@ texture_get_cb (CoglTexture *texture,
/* If we can read everything as a single slice, then go ahead and do that
* to avoid allocating an FBO. We'll leave it up to the GL implementation to
* do glGetTexImage as efficiently as possible. (GLES doesn't have that,
* so we'll fall through) */
* so we'll fall through)
*/
if (x_in_subtexture == 0 && y_in_subtexture == 0 &&
width == subtexture_width && height == subtexture_height)
{
if (texture->vtable->get_data (texture,
format,
rowstride,
dst_bits))
if (subtexture->vtable->get_data (subtexture,
closest_format,
rowstride,
dst_bits))
return;
}
/* Next best option is a FBO and glReadPixels */
if (get_texture_bits_via_offscreen (texture,
if (get_texture_bits_via_offscreen (meta_texture,
subtexture,
x_in_subtexture, y_in_subtexture,
width, height,
dst_bits,
rowstride,
format))
closest_format))
return;
/* Getting ugly: read the entire texture, copy out the part we want */
if (get_texture_bits_via_copy (texture,
if (get_texture_bits_via_copy (subtexture,
x_in_subtexture, y_in_subtexture,
width, height,
dst_bits,
rowstride,
format))
closest_format))
return;
/* No luck, the caller will fall back to the draw-to-backbuffer and
@ -1084,6 +1104,7 @@ cogl_texture_get_data (CoglTexture *texture,
&ignore_error);
if (tg_data.target_bits)
{
tg_data.meta_texture = texture;
tg_data.orig_width = tex_width;
tg_data.orig_height = tex_height;
tg_data.target_bmp = target_bmp;