_cogl_bitmap_convert: Also handle premult conversions

If we are going to unpack the data into a known format anyway we might
as well do the premult conversion instead of delaying it to do
in-place. This helps because not all formats with alpha channels are
handled by the in-place premult conversion code. This removes the
_cogl_bitmap_convert_format_and_premult function so that now
_cogl_bitmap_convert is a completely general purpose function that can
convert from anything to anything. _cogl_bitmap_convert now includes a
fast path for when the base formats are the same and the premult
conversion can be handled with the in-place code so that we don't need
to unpack and can just copy the bitmap instead.

Reviewed-by: Robert Bragg <robert@linux.intel.com>
This commit is contained in:
Neil Roberts 2012-03-01 13:55:39 +00:00
parent 8ce5f5ade8
commit f4cd5aceb9
5 changed files with 127 additions and 111 deletions

View File

@ -203,6 +203,81 @@ _cogl_premult_alpha_last_four_pixels_sse2 (guint8 *p)
#endif /* COGL_USE_PREMULT_SSE2 */
static void
_cogl_bitmap_premult_unpacked_span_guint8 (guint8 *data,
int width)
{
#ifdef COGL_USE_PREMULT_SSE2
/* Process 4 pixels at a time */
while (width >= 4)
{
_cogl_premult_alpha_last_four_pixels_sse2 (data);
data += 4 * 4;
width -= 4;
}
/* If there are any pixels left we will fall through and
handle them below */
#endif /* COGL_USE_PREMULT_SSE2 */
while (width-- > 0)
{
_cogl_premult_alpha_last (data);
data += 4;
}
}
static void
_cogl_bitmap_unpremult_unpacked_span_guint8 (guint8 *data,
int width)
{
int x;
for (x = 0; x < width; x++)
{
if (data[3] == 0)
_cogl_unpremult_alpha_0 (data);
else
_cogl_unpremult_alpha_last (data);
data += 4;
}
}
static void
_cogl_bitmap_unpremult_unpacked_span_guint16 (guint16 *data,
int width)
{
while (width-- > 0)
{
guint16 alpha = data[3];
if (alpha == 0)
memset (data, 0, sizeof (guint16) * 3);
else
{
data[0] = (data[0] * 65535) / alpha;
data[1] = (data[1] * 65535) / alpha;
data[2] = (data[2] * 65535) / alpha;
}
}
}
static void
_cogl_bitmap_premult_unpacked_span_guint16 (guint16 *data,
int width)
{
while (width-- > 0)
{
guint16 alpha = data[3];
data[0] = (data[0] * alpha) / 65535;
data[1] = (data[1] * alpha) / 65535;
data[2] = (data[2] * alpha) / 65535;
}
}
static gboolean
_cogl_bitmap_can_premult (CoglPixelFormat format)
{
@ -283,12 +358,36 @@ _cogl_bitmap_convert (CoglBitmap *src_bmp,
int width, height;
CoglPixelFormat src_format;
gboolean use_16;
gboolean need_premult;
src_format = _cogl_bitmap_get_format (src_bmp);
src_rowstride = _cogl_bitmap_get_rowstride (src_bmp);
width = _cogl_bitmap_get_width (src_bmp);
height = _cogl_bitmap_get_height (src_bmp);
need_premult
= ((src_format & COGL_PREMULT_BIT) != (dst_format & COGL_PREMULT_BIT) &&
src_format != COGL_PIXEL_FORMAT_A_8 &&
dst_format != COGL_PIXEL_FORMAT_A_8 &&
(src_format & dst_format & COGL_A_BIT));
/* If the base format is the same then we can just copy the bitmap
instead */
if ((src_format & ~COGL_PREMULT_BIT) == (dst_format & ~COGL_PREMULT_BIT) &&
(!need_premult || _cogl_bitmap_can_premult (dst_format)))
{
CoglBitmap *dst_bmp = _cogl_bitmap_copy (src_bmp);
if (need_premult &&
!_cogl_bitmap_convert_premult_status (dst_bmp, dst_format))
{
cogl_object_unref (dst_bmp);
return NULL;
}
return dst_bmp;
}
src_data = _cogl_bitmap_map (src_bmp, COGL_BUFFER_ACCESS_READ, 0);
if (src_data == NULL)
return NULL;
@ -299,10 +398,6 @@ _cogl_bitmap_convert (CoglBitmap *src_bmp,
/* Initialize destination bitmap */
dst_rowstride = (sizeof(guint8) * dst_bpp * width + 3) & ~3;
/* Copy the premult bit if the new format has an alpha channel */
if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (dst_format))
dst_format = ((src_format & COGL_PREMULT_BIT) |
(dst_format & ~COGL_PREMULT_BIT));
/* Allocate a new buffer to hold converted data */
dst_data = g_malloc (height * dst_rowstride);
@ -321,6 +416,25 @@ _cogl_bitmap_convert (CoglBitmap *src_bmp,
else
_cogl_unpack_guint8 (src_format, src, tmp_row, width);
/* Handle premultiplication */
if (need_premult)
{
if (dst_format & COGL_PREMULT_BIT)
{
if (use_16)
_cogl_bitmap_premult_unpacked_span_guint16 (tmp_row, width);
else
_cogl_bitmap_premult_unpacked_span_guint8 (tmp_row, width);
}
else
{
if (use_16)
_cogl_bitmap_unpremult_unpacked_span_guint16 (tmp_row, width);
else
_cogl_bitmap_unpremult_unpacked_span_guint8 (tmp_row, width);
}
}
if (use_16)
_cogl_pack_guint16 (dst_format, tmp_row, dst, width);
else
@ -378,16 +492,7 @@ _cogl_bitmap_unpremult (CoglBitmap *bmp)
}
}
else
{
for (x = 0; x < width; x++)
{
if (p[3] == 0)
_cogl_unpremult_alpha_0 (p);
else
_cogl_unpremult_alpha_last (p);
p += 4;
}
}
_cogl_bitmap_unpremult_unpacked_span_guint8 (p, width);
}
_cogl_bitmap_unmap (bmp);
@ -434,30 +539,7 @@ _cogl_bitmap_premult (CoglBitmap *bmp)
}
}
else
{
x = width;
#ifdef COGL_USE_PREMULT_SSE2
/* Process 4 pixels at a time */
while (x >= 4)
{
_cogl_premult_alpha_last_four_pixels_sse2 (p);
p += 4 * 4;
x -= 4;
}
/* If there are any pixels left we will fall through and
handle them below */
#endif /* COGL_USE_PREMULT_SSE2 */
while (x-- > 0)
{
_cogl_premult_alpha_last (p);
p += 4;
}
}
_cogl_bitmap_premult_unpacked_span_guint8 (p, width);
}
_cogl_bitmap_unmap (bmp);

View File

@ -101,10 +101,6 @@ gboolean
_cogl_bitmap_convert_premult_status (CoglBitmap *bmp,
CoglPixelFormat dst_format);
CoglBitmap *
_cogl_bitmap_convert_format_and_premult (CoglBitmap *bmp,
CoglPixelFormat dst_format);
void
_cogl_bitmap_copy_subregion (CoglBitmap *src,
CoglBitmap *dst,

View File

@ -99,61 +99,6 @@ _cogl_bitmap_convert_premult_status (CoglBitmap *bmp,
return TRUE;
}
CoglBitmap *
_cogl_bitmap_convert_format_and_premult (CoglBitmap *bmp,
CoglPixelFormat dst_format)
{
CoglPixelFormat src_format = _cogl_bitmap_get_format (bmp);
CoglBitmap *dst_bmp;
/* Is base format different (not considering premult status)? */
if ((src_format & ~COGL_PREMULT_BIT) !=
(dst_format & ~COGL_PREMULT_BIT))
{
if ((dst_bmp = _cogl_bitmap_convert (bmp, dst_format)) == NULL)
return NULL;
}
else
{
int rowstride = _cogl_bitmap_get_rowstride (bmp);
int height = _cogl_bitmap_get_height (bmp);
guint8 *data;
/* Copy the bitmap so that we can premultiply in-place */
if ((data = _cogl_bitmap_map (bmp, COGL_BUFFER_ACCESS_READ, 0)) == NULL)
return NULL;
dst_bmp = _cogl_bitmap_new_from_data (g_memdup (data, height * rowstride),
src_format,
_cogl_bitmap_get_width (bmp),
height,
rowstride,
(CoglBitmapDestroyNotify) g_free,
NULL);
_cogl_bitmap_unmap (bmp);
}
src_format = _cogl_bitmap_get_format (dst_bmp);
/* We only need to do a premult conversion if both formats have an
alpha channel. If we're converting from RGB to RGBA then the
alpha will have been filled with 255 so the premult won't do
anything or if we are converting from RGBA to RGB we're losing
information so either converting or not will be wrong for
transparent pixels */
if ((src_format & COGL_A_BIT) == COGL_A_BIT &&
(dst_format & COGL_A_BIT) == COGL_A_BIT &&
!_cogl_bitmap_convert_premult_status (dst_bmp, dst_format))
{
cogl_object_unref (dst_bmp);
return NULL;
}
return dst_bmp;
}
CoglBitmap *
_cogl_bitmap_copy (CoglBitmap *src_bmp)
{

View File

@ -200,16 +200,12 @@ _cogl_texture_prepare_for_upload (CoglBitmap *src_bmp,
if (_cogl_texture_needs_premult_conversion (src_format,
dst_format))
{
dst_bmp = _cogl_bitmap_copy (src_bmp);
dst_bmp = _cogl_bitmap_convert (src_bmp,
src_format ^ COGL_PREMULT_BIT);
if (!_cogl_bitmap_convert_premult_status (dst_bmp,
src_format ^
COGL_PREMULT_BIT))
{
cogl_object_unref (dst_bmp);
if (dst_bmp == NULL)
return NULL;
}
}
else
dst_bmp = cogl_object_ref (src_bmp);
@ -236,8 +232,7 @@ _cogl_texture_prepare_for_upload (CoglBitmap *src_bmp,
out_gltype);
if (closest_format != src_format)
dst_bmp = _cogl_bitmap_convert_format_and_premult (src_bmp,
closest_format);
dst_bmp = _cogl_bitmap_convert (src_bmp, closest_format);
else
dst_bmp = cogl_object_ref (src_bmp);
}
@ -1302,8 +1297,7 @@ cogl_texture_get_data (CoglTexture *texture,
int new_bmp_rowstride;
/* Convert to requested format */
new_bmp = _cogl_bitmap_convert_format_and_premult (target_bmp,
format);
new_bmp = _cogl_bitmap_convert (target_bmp, format);
/* Free intermediate data and return if failed */
cogl_object_unref (target_bmp);

View File

@ -503,8 +503,7 @@ _cogl_read_pixels_with_rowstride (int x,
/* CoglBitmap doesn't currently have a way to convert without
allocating its own buffer so we have to copy the data
again */
if ((dst_bmp = _cogl_bitmap_convert_format_and_premult (tmp_bmp,
format)))
if ((dst_bmp = _cogl_bitmap_convert (tmp_bmp, format)))
{
_cogl_bitmap_copy_subregion (dst_bmp,
bmp,