cogl: Do the premult conversion in-place rather than copying to a new buffer

The premult part of _cogl_convert_premult has now been split out as
_cogl_convert_premult_status. _cogl_convert_premult has been renamed
to _cogl_convert_format to make it less confusing. The premult
conversion is now done in-place instead of copying the
buffer. Previously it was copying the buffer once for the format
conversion and then copying it again for the premult conversion. The
premult conversion never changes the size of the buffer so it's quite
easy to do in place. We can also use the separated out function
independently.
This commit is contained in:
Neil Roberts 2010-01-29 15:15:08 +00:00
parent 72fba19eac
commit e83ffb1fa3
8 changed files with 123 additions and 185 deletions

View File

@ -543,9 +543,9 @@ _cogl_atlas_texture_set_region (CoglTexture *tex,
if (closest_format != format) if (closest_format != format)
{ {
/* Convert to required format */ /* Convert to required format */
success = _cogl_bitmap_convert_and_premult (&source_bmp, success = _cogl_bitmap_convert_format_and_premult (&source_bmp,
&temp_bmp, &temp_bmp,
closest_format); closest_format);
/* Swap bitmaps if succeeded */ /* Swap bitmaps if succeeded */
if (!success) return FALSE; if (!success) return FALSE;

View File

@ -150,7 +150,7 @@ _cogl_rgba_to_abgr (const guchar *src, guchar *dst)
/* (Un)Premultiplication */ /* (Un)Premultiplication */
inline static void inline static void
_cogl_unpremult_alpha_0 (const guchar *src, guchar *dst) _cogl_unpremult_alpha_0 (guchar *dst)
{ {
dst[0] = 0; dst[0] = 0;
dst[1] = 0; dst[1] = 0;
@ -159,58 +159,58 @@ _cogl_unpremult_alpha_0 (const guchar *src, guchar *dst)
} }
inline static void inline static void
_cogl_unpremult_alpha_last (const guchar *src, guchar *dst) _cogl_unpremult_alpha_last (guchar *dst)
{ {
guchar alpha = src[3]; guchar alpha = dst[3];
dst[0] = (src[0] * 255) / alpha; dst[0] = (dst[0] * 255) / alpha;
dst[1] = (src[1] * 255) / alpha; dst[1] = (dst[1] * 255) / alpha;
dst[2] = (src[2] * 255) / alpha; dst[2] = (dst[2] * 255) / alpha;
dst[3] = alpha;
} }
inline static void inline static void
_cogl_unpremult_alpha_first (const guchar *src, guchar *dst) _cogl_unpremult_alpha_first (guchar *dst)
{ {
guchar alpha = src[0]; guchar alpha = dst[0];
dst[0] = alpha; dst[1] = (dst[1] * 255) / alpha;
dst[1] = (src[1] * 255) / alpha; dst[2] = (dst[2] * 255) / alpha;
dst[2] = (src[2] * 255) / alpha; dst[3] = (dst[3] * 255) / alpha;
dst[3] = (src[3] * 255) / alpha;
} }
/* No division form of floor((c*a + 128)/255) (I first encountered /* No division form of floor((c*a + 128)/255) (I first encountered
* this in the RENDER implementation in the X server.) Being exact * this in the RENDER implementation in the X server.) Being exact
* is important for a == 255 - we want to get exactly c. * is important for a == 255 - we want to get exactly c.
*/ */
#define MULT(d,c,a,t) G_STMT_START { t = c * a + 128; d = ((t >> 8) + t) >> 8; } G_STMT_END #define MULT(d,a,t) \
G_STMT_START { \
t = d * a + 128; \
d = ((t >> 8) + t) >> 8; \
} G_STMT_END
inline static void inline static void
_cogl_premult_alpha_last (const guchar *src, guchar *dst) _cogl_premult_alpha_last (guchar *dst)
{ {
guchar alpha = src[3]; guchar alpha = dst[3];
/* Using a separate temporary per component has given slightly better /* Using a separate temporary per component has given slightly better
* code generation with GCC in the past; it shouldn't do any worse in * code generation with GCC in the past; it shouldn't do any worse in
* any case. * any case.
*/ */
guint t1, t2, t3; guint t1, t2, t3;
MULT(dst[0], src[0], alpha, t1); MULT(dst[0], alpha, t1);
MULT(dst[1], src[1], alpha, t2); MULT(dst[1], alpha, t2);
MULT(dst[2], src[2], alpha, t3); MULT(dst[2], alpha, t3);
dst[3] = alpha;
} }
inline static void inline static void
_cogl_premult_alpha_first (const guchar *src, guchar *dst) _cogl_premult_alpha_first (guchar *dst)
{ {
guchar alpha = src[0]; guchar alpha = dst[0];
guint t1, t2, t3; guint t1, t2, t3;
dst[0] = alpha; MULT(dst[1], alpha, t1);
MULT(dst[1], src[1], alpha, t1); MULT(dst[2], alpha, t2);
MULT(dst[2], src[2], alpha, t2); MULT(dst[3], alpha, t3);
MULT(dst[3], src[3], alpha, t3);
} }
#undef MULT #undef MULT
@ -342,56 +342,39 @@ _cogl_bitmap_fallback_convert (const CoglBitmap *bmp,
} }
gboolean gboolean
_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp, _cogl_bitmap_fallback_unpremult (CoglBitmap *bmp)
CoglBitmap *dst_bmp)
{ {
guchar *src; guchar *p;
guchar *dst;
gint bpp;
gint x,y; gint x,y;
/* Make sure format supported for un-premultiplication */ /* Make sure format supported for un-premultiplication */
if (!_cogl_bitmap_fallback_can_unpremult (bmp->format)) if (!_cogl_bitmap_fallback_can_unpremult (bmp->format))
return FALSE; return FALSE;
bpp = _cogl_get_format_bpp (bmp->format);
/* Initialize destination bitmap */
*dst_bmp = *bmp;
dst_bmp->format = (bmp->format & COGL_UNPREMULT_MASK);
/* Allocate a new buffer to hold converted data */
dst_bmp->data = g_malloc (sizeof(guchar)
* dst_bmp->height
* dst_bmp->rowstride);
for (y = 0; y < bmp->height; y++) for (y = 0; y < bmp->height; y++)
{ {
src = (guchar*)bmp->data + y * bmp->rowstride; p = (guchar*) bmp->data + y * bmp->rowstride;
dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride;
if (bmp->format & COGL_AFIRST_BIT) if (bmp->format & COGL_AFIRST_BIT)
{ {
for (x = 0; x < bmp->width; x++) for (x = 0; x < bmp->width; x++)
{ {
if (src[0] == 0) if (p[0] == 0)
_cogl_unpremult_alpha_0 (src, dst); _cogl_unpremult_alpha_0 (p);
else else
_cogl_unpremult_alpha_first (src, dst); _cogl_unpremult_alpha_first (p);
src += bpp; p += 4;
dst += bpp;
} }
} }
else else
{ {
for (x = 0; x < bmp->width; x++) for (x = 0; x < bmp->width; x++)
{ {
if (src[0] == 0) if (p[3] == 0)
_cogl_unpremult_alpha_0 (src, dst); _cogl_unpremult_alpha_0 (p);
else else
_cogl_unpremult_alpha_last (src, dst); _cogl_unpremult_alpha_last (p);
src += bpp; p += 4;
dst += bpp;
} }
} }
} }
@ -400,52 +383,35 @@ _cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp,
} }
gboolean gboolean
_cogl_bitmap_fallback_premult (const CoglBitmap *bmp, _cogl_bitmap_fallback_premult (CoglBitmap *bmp)
CoglBitmap *dst_bmp)
{ {
guchar *src; guchar *p;
guchar *dst;
gint bpp;
gint x,y; gint x,y;
/* Make sure format supported for un-premultiplication */ /* Make sure format supported for un-premultiplication */
if (!_cogl_bitmap_fallback_can_premult (bmp->format)) if (!_cogl_bitmap_fallback_can_premult (bmp->format))
return FALSE; return FALSE;
bpp = _cogl_get_format_bpp (bmp->format);
/* Initialize destination bitmap */
*dst_bmp = *bmp;
dst_bmp->format |= COGL_PREMULT_BIT;
/* Allocate a new buffer to hold converted data */
dst_bmp->data = g_malloc (sizeof(guchar)
* dst_bmp->height
* dst_bmp->rowstride);
for (y = 0; y < bmp->height; y++) for (y = 0; y < bmp->height; y++)
{ {
src = (guchar*)bmp->data + y * bmp->rowstride; p = (guchar*) bmp->data + y * bmp->rowstride;
dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride;
if (bmp->format & COGL_AFIRST_BIT) if (bmp->format & COGL_AFIRST_BIT)
{ {
for (x = 0; x < bmp->width; x++) for (x = 0; x < bmp->width; x++)
{ {
_cogl_premult_alpha_first (src, dst); _cogl_premult_alpha_first (p);
src += bpp; p += 4;
dst += bpp; }
} }
}
else else
{ {
for (x = 0; x < bmp->width; x++) for (x = 0; x < bmp->width; x++)
{ {
_cogl_premult_alpha_last (src, dst); _cogl_premult_alpha_last (p);
src += bpp; p += 4;
dst += bpp; }
} }
}
} }
return TRUE; return TRUE;

View File

@ -64,15 +64,13 @@ _cogl_bitmap_convert (const CoglBitmap *bmp,
} }
gboolean gboolean
_cogl_bitmap_unpremult (const CoglBitmap *bmp, _cogl_bitmap_unpremult (CoglBitmap *dst_bmp)
CoglBitmap *dst_bmp)
{ {
return FALSE; return FALSE;
} }
gboolean gboolean
_cogl_bitmap_premult (const CoglBitmap *bmp, _cogl_bitmap_premult (CoglBitmap *dst_bmp)
CoglBitmap *dst_bmp)
{ {
return FALSE; return FALSE;
} }

View File

@ -68,20 +68,16 @@ _cogl_bitmap_fallback_convert (const CoglBitmap *bmp,
CoglPixelFormat dst_format); CoglPixelFormat dst_format);
gboolean gboolean
_cogl_bitmap_unpremult (const CoglBitmap *bmp, _cogl_bitmap_unpremult (CoglBitmap *dst_bmp);
CoglBitmap *dst_bmp);
gboolean gboolean
_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp, _cogl_bitmap_fallback_unpremult (CoglBitmap *dst_bmp);
CoglBitmap *dst_bmp);
gboolean gboolean
_cogl_bitmap_premult (const CoglBitmap *bmp, _cogl_bitmap_premult (CoglBitmap *dst_bmp);
CoglBitmap *dst_bmp);
gboolean gboolean
_cogl_bitmap_fallback_premult (const CoglBitmap *bmp, _cogl_bitmap_fallback_premult (CoglBitmap *dst_bmp);
CoglBitmap *dst_bmp);
gboolean gboolean
_cogl_bitmap_from_file (CoglBitmap *bmp, _cogl_bitmap_from_file (CoglBitmap *bmp,
@ -93,9 +89,13 @@ _cogl_bitmap_fallback_from_file (CoglBitmap *bmp,
const gchar *filename); const gchar *filename);
gboolean gboolean
_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp, _cogl_bitmap_convert_premult_status (CoglBitmap *bmp,
CoglBitmap *dst_bmp, CoglPixelFormat dst_format);
CoglPixelFormat dst_format);
gboolean
_cogl_bitmap_convert_format_and_premult (const CoglBitmap *bmp,
CoglBitmap *dst_bmp,
CoglPixelFormat dst_format);
void void
_cogl_bitmap_copy_subregion (CoglBitmap *src, _cogl_bitmap_copy_subregion (CoglBitmap *src,

View File

@ -61,84 +61,58 @@ _cogl_get_format_bpp (CoglPixelFormat format)
} }
gboolean gboolean
_cogl_bitmap_convert_and_premult (const CoglBitmap *bmp, _cogl_bitmap_convert_premult_status (CoglBitmap *bmp,
CoglBitmap *dst_bmp, CoglPixelFormat dst_format)
CoglPixelFormat dst_format)
{ {
CoglBitmap tmp_bmp = *bmp; /* Do we need to unpremultiply? */
CoglBitmap new_bmp = *bmp; if ((bmp->format & COGL_PREMULT_BIT) > 0 &&
gboolean new_bmp_owner = FALSE; (dst_format & COGL_PREMULT_BIT) == 0)
/* Try unpremultiplying using imaging library */
return (_cogl_bitmap_unpremult (bmp)
/* ... or try fallback */
|| _cogl_bitmap_fallback_unpremult (bmp));
/* Do we need to premultiply? */
if ((bmp->format & COGL_PREMULT_BIT) == 0 &&
(dst_format & COGL_PREMULT_BIT) > 0)
/* Try premultiplying using imaging library */
return (_cogl_bitmap_premult (bmp)
/* ... or try fallback */
|| _cogl_bitmap_fallback_premult (bmp));
return TRUE;
}
gboolean
_cogl_bitmap_convert_format_and_premult (const CoglBitmap *bmp,
CoglBitmap *dst_bmp,
CoglPixelFormat dst_format)
{
/* Is base format different (not considering premult status)? */ /* Is base format different (not considering premult status)? */
if ((bmp->format & COGL_UNPREMULT_MASK) != if ((bmp->format & COGL_UNPREMULT_MASK) !=
(dst_format & COGL_UNPREMULT_MASK)) (dst_format & COGL_UNPREMULT_MASK))
{ {
/* Try converting using imaging library */ /* Try converting using imaging library */
if (!_cogl_bitmap_convert (&new_bmp, &tmp_bmp, dst_format)) if (!_cogl_bitmap_convert (bmp, dst_bmp, dst_format))
{ {
/* ... or try fallback */ /* ... or try fallback */
if (!_cogl_bitmap_fallback_convert (&new_bmp, &tmp_bmp, dst_format)) if (!_cogl_bitmap_fallback_convert (bmp, dst_bmp, dst_format))
return FALSE; return FALSE;
} }
/* Update bitmap with new data */
new_bmp = tmp_bmp;
new_bmp_owner = TRUE;
} }
else
/* Do we need to unpremultiply */
if ((bmp->format & COGL_PREMULT_BIT) > 0 &&
(dst_format & COGL_PREMULT_BIT) == 0)
{ {
/* Try unpremultiplying using imaging library */ /* Copy the bitmap so that we can premultiply in-place */
if (!_cogl_bitmap_unpremult (&new_bmp, &tmp_bmp)) *dst_bmp = *bmp;
{ dst_bmp->data = g_memdup (bmp->data, bmp->rowstride * bmp->height);
/* ... or try fallback */
if (!_cogl_bitmap_fallback_unpremult (&new_bmp, &tmp_bmp))
{
if (new_bmp_owner)
g_free (new_bmp.data);
return FALSE;
}
}
/* Update bitmap with new data */
if (new_bmp_owner)
g_free (new_bmp.data);
new_bmp = tmp_bmp;
new_bmp_owner = TRUE;
} }
/* Do we need to premultiply */ if (!_cogl_bitmap_convert_premult_status (dst_bmp, dst_format))
if ((bmp->format & COGL_PREMULT_BIT) == 0 &&
(dst_format & COGL_PREMULT_BIT) > 0)
{ {
/* Try premultiplying using imaging library */ g_free (dst_bmp->data);
if (!_cogl_bitmap_premult (&new_bmp, &tmp_bmp)) return FALSE;
{
/* ... or try fallback */
if (!_cogl_bitmap_fallback_premult (&new_bmp, &tmp_bmp))
{
if (new_bmp_owner)
g_free (new_bmp.data);
return FALSE;
}
}
/* Update bitmap with new data */
if (new_bmp_owner)
g_free (new_bmp.data);
new_bmp = tmp_bmp;
new_bmp_owner = TRUE;
} }
/* Output new bitmap info */
*dst_bmp = new_bmp;
return TRUE; return TRUE;
} }

View File

@ -1433,9 +1433,9 @@ _cogl_texture_2d_sliced_set_region (CoglTexture *tex,
if (closest_format != format) if (closest_format != format)
{ {
/* Convert to required format */ /* Convert to required format */
success = _cogl_bitmap_convert_and_premult (&source_bmp, success = _cogl_bitmap_convert_format_and_premult (&source_bmp,
&temp_bmp, &temp_bmp,
closest_format); closest_format);
/* Swap bitmaps if succeeded */ /* Swap bitmaps if succeeded */
if (!success) return FALSE; if (!success) return FALSE;
@ -1635,9 +1635,9 @@ _cogl_texture_2d_sliced_get_data (CoglTexture *tex,
if (closest_format != format) if (closest_format != format)
{ {
/* Convert to requested format */ /* Convert to requested format */
success = _cogl_bitmap_convert_and_premult (&target_bmp, success = _cogl_bitmap_convert_format_and_premult (&target_bmp,
&new_bmp, &new_bmp,
format); format);
/* Free intermediate data and return if failed */ /* Free intermediate data and return if failed */
g_free (target_bmp.data); g_free (target_bmp.data);

View File

@ -466,9 +466,9 @@ _cogl_texture_2d_set_region (CoglTexture *tex,
if (closest_format != format) if (closest_format != format)
{ {
/* Convert to required format */ /* Convert to required format */
success = _cogl_bitmap_convert_and_premult (&source_bmp, success = _cogl_bitmap_convert_format_and_premult (&source_bmp,
&temp_bmp, &temp_bmp,
closest_format); closest_format);
/* Swap bitmaps if succeeded */ /* Swap bitmaps if succeeded */
if (!success) return FALSE; if (!success) return FALSE;
@ -570,9 +570,9 @@ _cogl_texture_2d_get_data (CoglTexture *tex,
if (closest_format != format) if (closest_format != format)
{ {
/* Convert to requested format */ /* Convert to requested format */
success = _cogl_bitmap_convert_and_premult (&target_bmp, success = _cogl_bitmap_convert_format_and_premult (&target_bmp,
&new_bmp, &new_bmp,
format); format);
/* Free intermediate data and return if failed */ /* Free intermediate data and return if failed */
g_free (target_bmp.data); g_free (target_bmp.data);

View File

@ -193,9 +193,9 @@ _cogl_texture_upload_data_convert (CoglTextureUploadData *data,
/* Convert to internal format */ /* Convert to internal format */
if (internal_format != data->bitmap.format) if (internal_format != data->bitmap.format)
{ {
success = _cogl_bitmap_convert_and_premult (&data->bitmap, success = _cogl_bitmap_convert_format_and_premult (&data->bitmap,
&new_bitmap, &new_bitmap,
internal_format); internal_format);
if (!success) if (!success)
return FALSE; return FALSE;