Bug 1406 - Handling of premultiplication in clutter

Merge branch 'premultiplication'

[cogl-texture docs] Improves the documentation of the internal_format args
[test-premult] Adds a unit test for texture upload premultiplication semantics
[fog] Document that fogging only works with opaque or unmultipled colors
[test-blend-strings] Explicitly request RGBA_888 tex format for test textures
[premultiplication] Be more conservative with what data gets premultiplied
[bitmap] Fixes _cogl_bitmap_fallback_unpremult
[cogl-bitmap] Fix minor copy and paste error in _cogl_bitmap_fallback_premult
Avoid unnecesary unpremultiplication when saving to local data
Don't unpremultiply Cairo data
Default to a blend function that expects premultiplied colors
Implement premultiplication for CoglBitmap
Use correct texture format for pixmap textures and FBO's
Add cogl_color_premultiply()
This commit is contained in:
Robert Bragg 2009-06-11 15:16:27 +01:00
commit 96827db740
26 changed files with 705 additions and 138 deletions

View File

@ -292,14 +292,13 @@ clutter_cairo_texture_surface_resize_internal (ClutterCairoTexture *cairo)
cairo,
clutter_cairo_texture_surface_destroy);
/* The texture data will be all zeroes so we can use it to create a
* blank Cogl texture even though its in a different format
/* Create a blank Cogl texture
*/
clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (cairo),
priv->cr_surface_data,
TRUE, priv->width, priv->height,
priv->rowstride,
4, 0, NULL);
4, CLUTTER_TEXTURE_RGB_FLAG_PREMULT, NULL);
}
static void
@ -451,9 +450,7 @@ clutter_cairo_texture_context_destroy (void *data)
gint cairo_width, cairo_height, cairo_rowstride;
gint surface_width, surface_height;
guchar *pixbuf_data, *dst, *cairo_data;
guint *src, pixbuf_rowstride;
gint x, y;
guchar *cairo_data;
priv = CLUTTER_CAIRO_TEXTURE_GET_PRIVATE (cairo);
@ -473,56 +470,19 @@ clutter_cairo_texture_context_destroy (void *data)
}
cairo_rowstride = priv->rowstride;
cairo_data = priv->cr_surface_data;
pixbuf_data = g_malloc (cairo_width * cairo_height * 4);
pixbuf_rowstride = cairo_width * 4;
/* BAH BAH BAH ! un-pre-multiply alpha...
*
* FIXME: Need to figure out if GL has a premult texture
* format, or we need to change the order of the
* paint sequence in Clutter. or go back to battling
* glitz (ugh).
*
* in theory, this could be moved to a shader, but apparently
* the performance gain is not really worth it.
*/
for (y = 0; y < cairo_height; y++)
{
src = (unsigned int *) (cairo_data
+ ((y + ctxt->rect.y) * cairo_rowstride)
+ (ctxt->rect.x * 4));
dst = pixbuf_data + y * pixbuf_rowstride;
for (x = 0; x < cairo_width; x++)
{
guchar alpha = (*src >> 24) & 0xff;
if (alpha == 0)
dst[0] = dst[1] = dst[2] = dst[3] = alpha;
else
{
dst[0] = (((*src >> 16) & 0xff) * 255 ) / alpha;
dst[1] = (((*src >> 8) & 0xff) * 255 ) / alpha;
dst[2] = (((*src >> 0) & 0xff) * 255 ) / alpha;
dst[3] = alpha;
}
dst += 4;
src++;
}
}
cairo_data = (priv->cr_surface_data
+ ctxt->rect.y * cairo_rowstride
+ ctxt->rect.x * 4);
clutter_texture_set_area_from_rgb_data (CLUTTER_TEXTURE (cairo),
pixbuf_data,
cairo_data,
TRUE,
ctxt->rect.x,
ctxt->rect.y,
cairo_width, cairo_height,
pixbuf_rowstride,
4, 0, NULL);
cairo_rowstride,
4, CLUTTER_TEXTURE_RGB_FLAG_PREMULT, NULL);
g_free (pixbuf_data);
g_free (ctxt);
if (CLUTTER_ACTOR_IS_VISIBLE (cairo))

View File

@ -1788,6 +1788,16 @@ clutter_stage_set_use_fog (ClutterStage *stage,
* }
* ]|
*
* Note: The fogging functions only work correctly when the visible actors use
* unmultiplied alpha colors. By default Cogl will premultiply textures
* and cogl_set_source_color will premultiply colors, so unless you
* explicitly load your textures requesting an unmultiplied
* internal_format and use cogl_material_set_color you can only use
* fogging with fully opaque actors.
*
* We can look to improve this in the future when we can depend on
* fragment shaders.
*
* Since: 0.6
*/
void

View File

@ -296,7 +296,7 @@ clutter_texture_realize (ClutterActor *actor)
tex = cogl_texture_new_with_size (priv->width,
priv->height,
flags,
COGL_PIXEL_FORMAT_RGBA_8888);
COGL_PIXEL_FORMAT_RGBA_8888_PRE);
cogl_material_set_layer (priv->material, 0, tex);
@ -608,7 +608,8 @@ clutter_texture_paint (ClutterActor *self)
clutter_actor_get_name (self) ? clutter_actor_get_name (self)
: "unknown");
cogl_material_set_color4ub (priv->material, 0xff, 0xff, 0xff, paint_opacity);
cogl_material_set_color4ub (priv->material,
paint_opacity, paint_opacity, paint_opacity, paint_opacity);
clutter_actor_get_allocation_box (self, &box);
@ -1224,7 +1225,7 @@ clutter_texture_save_to_local_data (ClutterTexture *texture)
if (cogl_texture_get_data (cogl_texture,
priv->local_data_has_alpha
? COGL_PIXEL_FORMAT_RGBA_8888
? COGL_PIXEL_FORMAT_RGBA_8888_PRE
: COGL_PIXEL_FORMAT_RGB_888,
priv->local_data_rowstride,
priv->local_data) == 0)
@ -1251,7 +1252,7 @@ clutter_texture_load_from_local_data (ClutterTexture *texture)
priv->local_data_height,
priv->local_data_rowstride,
priv->local_data_has_alpha ? 4: 3,
0, NULL);
CLUTTER_TEXTURE_RGB_FLAG_PREMULT, NULL);
g_free (priv->local_data);
priv->local_data = NULL;
@ -2243,7 +2244,7 @@ on_fbo_source_size_change (GObject *object,
tex = cogl_texture_new_with_size (MAX (priv->width, 1),
MAX (priv->height, 1),
flags,
COGL_PIXEL_FORMAT_RGBA_8888);
COGL_PIXEL_FORMAT_RGBA_8888_PRE);
cogl_material_set_layer (priv->material, 0, tex);

View File

@ -228,6 +228,18 @@ float cogl_color_get_blue (const CoglColor *color);
*/
float cogl_color_get_alpha (const CoglColor *color);
/**
* cogl_color_premultiply:
* @color: the color to premultiply
*
* Converts a non-premultiplied color to a pre-multiplied color. For
* example, semi-transparent red is (1.0, 0, 0, 0.5) when non-premultiplied
* and (0.5, 0, 0, 0.5) when premultiplied.
*
* Since: 1.0
*/
void cogl_color_premultiply (CoglColor *color);
G_END_DECLS
#endif /* __COGL_COLOR_H__ */

View File

@ -138,6 +138,11 @@ gboolean cogl_is_material (CoglHandle handle);
*
* This is the basic color of the material, used when no lighting is enabled.
*
* Note that if you don't add any layers to the material then the color
* will be blended unmodified with the destination; the default blend
* expects premultiplied colors: for example, use (0.5, 0.0, 0.0, 0.5) for
* semi-transparent red. See cogl_color_premultiply().
*
* The default value is (1.0, 1.0, 1.0, 1.0)
*
* Since 1.0
@ -475,6 +480,11 @@ void cogl_material_set_alpha_test_function (CoglHandle material,
* </programlisting>
* </section>
*
* The default blend string is:
* "RGBA = ADD (SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))"
* That gives normal alpha-blending when the calculated color for the material
* is in premultiplied form.
*
* Returns: TRUE if the blend string was successfully parsed, and the described
* blending is supported by the underlying driver/hardware. If there
* was an error, it returns FALSE.

View File

@ -68,7 +68,13 @@ CoglHandle cogl_texture_new_with_size (guint width,
* @filename: the file to load
* @flags: Optional flags for the texture, or %COGL_TEXTURE_NONE
* @internal_format: the #CoglPixelFormat to use for the GPU storage of the
* texture
* texture. If COGL_PIXEL_FORMAT_ANY is given then a premultiplied
* format similar to the format of the source data will be used. The
* default blending equations of Cogl expect premultiplied color data;
* the main use of passing a non-premultiplied format here is if you
* have non-premultiplied source data and are going to adjust the blend
* mode (see cogl_material_set_blend()) or use the data for something
* other than straight blending.
* @error: return location for a #GError or %NULL
*
* Creates a COGL texture from an image file.
@ -90,7 +96,13 @@ CoglHandle cogl_texture_new_from_file (const gchar *filename,
* @flags: Optional flags for the texture, or %COGL_TEXTURE_NONE
* @format: the #CoglPixelFormat the buffer is stored in in RAM
* @internal_format: the #CoglPixelFormat that will be used for storing
* the buffer on the GPU
* the buffer on the GPU. If COGL_PIXEL_FORMAT_ANY is given then a
* premultiplied format similar to the format of the source data will
* be used. The default blending equations of Cogl expect premultiplied
* color data; the main use of passing a non-premultiplied format here
* is if you have non-premultiplied source data and are going to adjust
* the blend mode (see cogl_material_set_blend()) or use the data for
* something other than straight blending.
* @rowstride: the memory offset in bytes between the starts of
* scanlines in @data
* @data: pointer the memory region where the source buffer resides

View File

@ -386,13 +386,22 @@ gboolean cogl_get_backface_culling_enabled (void);
* @z_near: Position along z-axis where no fogging should be applied
* @z_far: Position along z-axes where full fogging should be applied
*
* Enables fogging. Fogging causes vertices that are further away from
* the eye to be rendered with a different color. The color is determined
* according to the chosen fog mode; at it's simplest the color is
* linearly interpolated so that vertices at @z_near are drawn fully
* with their original color and vertices at @z_far are drawn fully
* with @fog_color. Fogging will remain enabled until you call
* cogl_disable_fog().
* Enables fogging. Fogging causes vertices that are further away from the eye
* to be rendered with a different color. The color is determined according to
* the chosen fog mode; at it's simplest the color is linearly interpolated so
* that vertices at @z_near are drawn fully with their original color and
* vertices at @z_far are drawn fully with @fog_color. Fogging will remain
* enabled until you call cogl_disable_fog().
*
* Note: The fogging functions only work correctly when primitives use
* unmultiplied alpha colors. By default Cogl will premultiply textures
* and cogl_set_source_color will premultiply colors, so unless you
* explicitly load your textures requesting an unmultiplied
* internal_format and use cogl_material_set_color you can only use
* fogging with fully opaque primitives.
*
* We can look to improve this in the future when we can depend on
* fragment shaders.
*/
void cogl_set_fog (const CoglColor *fog_color,
CoglFogMode mode,
@ -455,8 +464,13 @@ void cogl_set_source (CoglHandle material);
* cogl_set_source_color:
* @color: a #CoglColor
*
* Sets the source color using normalized values for each component.
* This color will be used for any subsequent drawing operation.
* This is a convenience function for creating a solid fill source material
* from the given color. This color will be used for any subsequent drawing
* operation.
*
* The color will be premultiplied by Cogl, so the color should be
* non-premultiplied. For example: use (1.0, 0.0, 0.0, 0.5) for
* semi-transparent red.
*
* See also cogl_set_source_color4ub() and cogl_set_source_color4f()
* if you already have the color components.

View File

@ -163,9 +163,9 @@ _cogl_unpremult_alpha_last (const guchar *src, guchar *dst)
{
guchar alpha = src[3];
dst[0] = ((((gulong) src[0] >> 16) & 0xff) * 255 ) / alpha;
dst[1] = ((((gulong) src[1] >> 8) & 0xff) * 255 ) / alpha;
dst[2] = ((((gulong) src[2] >> 0) & 0xff) * 255 ) / alpha;
dst[0] = (src[0] * 255) / alpha;
dst[1] = (src[1] * 255) / alpha;
dst[2] = (src[2] * 255) / alpha;
dst[3] = alpha;
}
@ -175,11 +175,46 @@ _cogl_unpremult_alpha_first (const guchar *src, guchar *dst)
guchar alpha = src[0];
dst[0] = alpha;
dst[1] = ((((gulong) src[1] >> 16) & 0xff) * 255 ) / alpha;
dst[2] = ((((gulong) src[2] >> 8) & 0xff) * 255 ) / alpha;
dst[3] = ((((gulong) src[3] >> 0) & 0xff) * 255 ) / alpha;
dst[1] = (src[1] * 255) / alpha;
dst[2] = (src[2] * 255) / alpha;
dst[3] = (src[3] * 255) / alpha;
}
/* No division form of floor((c*a + 128)/255) (I first encountered
* this in the RENDER implementation in the X server.) Being exact
* 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
inline static void
_cogl_premult_alpha_last (const guchar *src, guchar *dst)
{
guchar alpha = src[3];
/* Using a separate temporary per component has given slightly better
* code generation with GCC in the past; it shouldn't do any worse in
* any case.
*/
guint t1, t2, t3;
MULT(dst[0], src[0], alpha, t1);
MULT(dst[1], src[1], alpha, t2);
MULT(dst[2], src[2], alpha, t3);
dst[3] = alpha;
}
inline static void
_cogl_premult_alpha_first (const guchar *src, guchar *dst)
{
guchar alpha = src[0];
guint t1, t2, t3;
dst[0] = alpha;
MULT(dst[1], src[1], alpha, t1);
MULT(dst[2], src[2], alpha, t2);
MULT(dst[3], src[3], alpha, t3);
}
#undef MULT
gboolean
_cogl_bitmap_fallback_can_convert (CoglPixelFormat src, CoglPixelFormat dst)
{
@ -211,6 +246,12 @@ _cogl_bitmap_fallback_can_unpremult (CoglPixelFormat format)
return ((format & COGL_UNORDERED_MASK) == COGL_PIXEL_FORMAT_32);
}
gboolean
_cogl_bitmap_fallback_can_premult (CoglPixelFormat format)
{
return ((format & COGL_UNORDERED_MASK) == COGL_PIXEL_FORMAT_32);
}
gboolean
_cogl_bitmap_fallback_convert (const CoglBitmap *bmp,
CoglBitmap *dst_bmp,
@ -324,34 +365,86 @@ _cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp,
* dst_bmp->height
* dst_bmp->rowstride);
/* FIXME: Optimize */
for (y = 0; y < bmp->height; y++)
{
src = (guchar*)bmp->data + y * bmp->rowstride;
dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride;
for (x = 0; x < bmp->width; x++)
{
/* FIXME: Would be nice to at least remove this inner
* branching, but not sure it can be done without
* rewriting of the whole loop */
if (bmp->format & COGL_AFIRST_BIT)
{
if (src[0] == 0)
_cogl_unpremult_alpha_0 (src, dst);
else
_cogl_unpremult_alpha_first (src, dst);
}
else
{
if (src[3] == 0)
_cogl_unpremult_alpha_0 (src, dst);
else
_cogl_unpremult_alpha_last (src, dst);
}
if (bmp->format & COGL_AFIRST_BIT)
{
for (x = 0; x < bmp->width; x++)
{
if (src[0] == 0)
_cogl_unpremult_alpha_0 (src, dst);
else
_cogl_unpremult_alpha_first (src, dst);
src += bpp;
dst += bpp;
}
}
else
{
for (x = 0; x < bmp->width; x++)
{
if (src[0] == 0)
_cogl_unpremult_alpha_0 (src, dst);
else
_cogl_unpremult_alpha_last (src, dst);
src += bpp;
dst += bpp;
}
}
}
src += bpp;
dst += bpp;
return TRUE;
}
gboolean
_cogl_bitmap_fallback_premult (const CoglBitmap *bmp,
CoglBitmap *dst_bmp)
{
guchar *src;
guchar *dst;
gint bpp;
gint x,y;
/* Make sure format supported for un-premultiplication */
if (!_cogl_bitmap_fallback_can_premult (bmp->format))
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++)
{
src = (guchar*)bmp->data + y * bmp->rowstride;
dst = (guchar*)dst_bmp->data + y * dst_bmp->rowstride;
if (bmp->format & COGL_AFIRST_BIT)
{
for (x = 0; x < bmp->width; x++)
{
_cogl_premult_alpha_first (src, dst);
src += bpp;
dst += bpp;
}
}
else
{
for (x = 0; x < bmp->width; x++)
{
_cogl_premult_alpha_last (src, dst);
src += bpp;
dst += bpp;
}
}
}

View File

@ -49,6 +49,12 @@ _cogl_bitmap_can_unpremult (CoglPixelFormat format)
return FALSE;
}
gboolean
_cogl_bitmap_can_premult (CoglPixelFormat format)
{
return FALSE;
}
gboolean
_cogl_bitmap_convert (const CoglBitmap *bmp,
CoglBitmap *dst_bmp,
@ -64,6 +70,13 @@ _cogl_bitmap_unpremult (const CoglBitmap *bmp,
return FALSE;
}
gboolean
_cogl_bitmap_premult (const CoglBitmap *bmp,
CoglBitmap *dst_bmp)
{
return FALSE;
}
#ifdef USE_QUARTZ
/* lacking GdkPixbuf and other useful GError domains, define one of our own */

View File

@ -52,6 +52,12 @@ _cogl_bitmap_can_unpremult (CoglPixelFormat format);
gboolean
_cogl_bitmap_fallback_can_unpremult (CoglPixelFormat format);
gboolean
_cogl_bitmap_can_premult (CoglPixelFormat format);
gboolean
_cogl_bitmap_fallback_can_premult (CoglPixelFormat format);
gboolean
_cogl_bitmap_convert (const CoglBitmap *bmp,
CoglBitmap *dst_bmp,
@ -69,6 +75,14 @@ gboolean
_cogl_bitmap_fallback_unpremult (const CoglBitmap *bmp,
CoglBitmap *dst_bmp);
gboolean
_cogl_bitmap_premult (const CoglBitmap *bmp,
CoglBitmap *dst_bmp);
gboolean
_cogl_bitmap_fallback_premult (const CoglBitmap *bmp,
CoglBitmap *dst_bmp);
gboolean
_cogl_bitmap_from_file (CoglBitmap *bmp,
const gchar *filename,

View File

@ -87,8 +87,8 @@ _cogl_bitmap_convert_and_premult (const CoglBitmap *bmp,
}
/* Do we need to unpremultiply */
if ((bmp->format & COGL_PREMULT_BIT) == 0 &&
(dst_format & COGL_PREMULT_BIT) > 0)
if ((bmp->format & COGL_PREMULT_BIT) > 0 &&
(dst_format & COGL_PREMULT_BIT) == 0)
{
/* Try unpremultiplying using imaging library */
if (!_cogl_bitmap_unpremult (&new_bmp, &tmp_bmp))
@ -112,14 +112,28 @@ _cogl_bitmap_convert_and_premult (const CoglBitmap *bmp,
}
/* Do we need to premultiply */
if ((bmp->format & COGL_PREMULT_BIT) > 0 &&
(dst_format & COGL_PREMULT_BIT) == 0)
if ((bmp->format & COGL_PREMULT_BIT) == 0 &&
(dst_format & COGL_PREMULT_BIT) > 0)
{
/* FIXME: implement premultiplication */
/* Try premultiplying using imaging library */
if (!_cogl_bitmap_premult (&new_bmp, &tmp_bmp))
{
/* ... 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);
return FALSE;
new_bmp = tmp_bmp;
new_bmp_owner = TRUE;
}
/* Output new bitmap info */

View File

@ -153,6 +153,14 @@ cogl_color_get_alpha (const CoglColor *color)
return ((float) color->alpha / 255.0);
}
void
cogl_color_premultiply (CoglColor *color)
{
color->red = (color->red * color->alpha + 128) / 255;
color->green = (color->green * color->alpha + 128) / 255;
color->blue = (color->blue * color->alpha + 128) / 255;
}
void
cogl_set_source_color4ub (guint8 red,
guint8 green,

View File

@ -109,7 +109,7 @@ cogl_material_new (void)
material->blend_constant[2] = 0;
material->blend_constant[3] = 0;
#endif
material->blend_src_factor_rgb = GL_SRC_ALPHA;
material->blend_src_factor_rgb = GL_ONE;
material->blend_dst_factor_rgb = GL_ONE_MINUS_SRC_ALPHA;
material->flags |= COGL_MATERIAL_FLAG_DEFAULT_BLEND_FUNC;

View File

@ -250,12 +250,17 @@ cogl_get_backface_culling_enabled (void)
void
cogl_set_source_color (const CoglColor *color)
{
CoglColor premultiplied;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
/* In case cogl_set_source_texture was previously used... */
cogl_material_remove_layer (ctx->default_material, 0);
cogl_material_set_color (ctx->default_material, color);
premultiplied = *color;
cogl_color_premultiply (&premultiplied);
cogl_material_set_color (ctx->default_material, &premultiplied);
cogl_set_source (ctx->default_material);
}

View File

@ -1085,18 +1085,12 @@ _cogl_pixel_format_to_gl (CoglPixelFormat format,
GLenum glformat = 0;
GLenum gltype = 0;
/* No premultiplied formats accepted by GL
* (FIXME: latest hardware?) */
/* FIXME: check YUV support */
if (format & COGL_PREMULT_BIT)
format = (format & COGL_UNPREMULT_MASK);
/* Everything else accepted
* (FIXME: check YUV support) */
required_format = format;
/* Find GL equivalents */
switch (format)
switch (format & COGL_UNPREMULT_MASK)
{
case COGL_PIXEL_FORMAT_A_8:
glintformat = GL_ALPHA;
@ -1195,9 +1189,15 @@ _cogl_texture_bitmap_prepare (CoglTexture *tex,
CoglPixelFormat new_data_format;
gboolean success;
/* Was there any internal conversion requested? */
/* Was there any internal conversion requested?
* By default Cogl will use a premultiplied internal format. Later we will
* add control over this. */
if (internal_format == COGL_PIXEL_FORMAT_ANY)
internal_format = tex->bitmap.format;
{
if ((tex->bitmap.format & COGL_A_BIT) &&
tex->bitmap.format != COGL_PIXEL_FORMAT_A_8)
internal_format = tex->bitmap.format | COGL_PREMULT_BIT;
}
/* Find closest format accepted by GL */
new_data_format = _cogl_pixel_format_to_gl (internal_format,

View File

@ -1184,18 +1184,12 @@ _cogl_pixel_format_to_gl (CoglPixelFormat format,
GLenum glformat = 0;
GLenum gltype = 0;
/* No premultiplied formats accepted by GL
* (FIXME: latest hardware?) */
/* FIXME: check YUV support */
if (format & COGL_PREMULT_BIT)
format = (format & COGL_UNPREMULT_MASK);
/* Everything else accepted
* (FIXME: check YUV support) */
required_format = format;
/* Find GL equivalents */
switch (format)
switch (format & COGL_UNPREMULT_MASK)
{
case COGL_PIXEL_FORMAT_A_8:
glintformat = GL_ALPHA;
@ -1226,6 +1220,7 @@ _cogl_pixel_format_to_gl (CoglPixelFormat format,
glformat = GL_RGBA;
gltype = GL_UNSIGNED_BYTE;
required_format = COGL_PIXEL_FORMAT_RGBA_8888;
required_format |= (format & COGL_PREMULT_BIT);
break;
/* The following three types of channel ordering
@ -1270,9 +1265,15 @@ _cogl_texture_bitmap_prepare (CoglTexture *tex,
CoglPixelFormat new_data_format;
gboolean success;
/* Was there any internal conversion requested? */
/* Was there any internal conversion requested?
* By default Cogl will use a premultiplied internal format. Later we will
* add control over this. */
if (internal_format == COGL_PIXEL_FORMAT_ANY)
internal_format = tex->bitmap.format;
{
if ((tex->bitmap.format & COGL_A_BIT) &&
tex->bitmap.format != COGL_PIXEL_FORMAT_A_8)
internal_format = tex->bitmap.format | COGL_PREMULT_BIT;
}
/* Find closest format accepted by GL */
new_data_format = _cogl_pixel_format_to_gl (internal_format,

View File

@ -359,14 +359,16 @@ create_cogl_texture (ClutterTexture *texture,
handle = cogl_texture_new_from_foreign (tex, CGL_TEXTURE_RECTANGLE_ARB,
width, height,
0, 0,
cogl_format | COGL_BGR_BIT);
cogl_format | COGL_BGR_BIT |
COGL_PREMULT_BIT);
}
else
{
handle
= cogl_texture_new_with_size (width, height,
COGL_TEXTURE_NO_SLICING,
cogl_format | COGL_BGR_BIT);
cogl_format | COGL_BGR_BIT |
COGL_PREMULT_BIT);
using_rectangle = FALSE;
}

View File

@ -239,8 +239,10 @@ _cogl_pango_display_list_render_texture (CoglHandle material,
const CoglColor *color,
CoglPangoDisplayListNode *node)
{
CoglColor premult_color = *color;
cogl_material_set_layer (material, 0, node->d.texture.texture);
cogl_material_set_color (material, color);
cogl_color_premultiply (&premult_color);
cogl_material_set_color (material, &premult_color);
cogl_set_source (material);
if (node->d.texture.vertex_buffer == COGL_INVALID_HANDLE)
@ -311,6 +313,7 @@ _cogl_pango_display_list_render (CoglPangoDisplayList *dl,
cogl_color_get_alpha_byte (color));
else
draw_color = *color;
cogl_color_premultiply (&draw_color);
switch (node->type)
{

View File

@ -124,16 +124,20 @@ cogl_pango_renderer_init (CoglPangoRenderer *priv)
/* The default combine mode of materials is to modulate (A x B) the texture
* RGBA channels with the RGBA channels of the previous layer (which in our
* case is just the solid font color)
* case is just the font color)
*
* Since our glyph cache textures are component alpha textures, and so the
* RGB channels are defined as (0, 0, 0) we don't want to modulate the RGB
* channels, instead we want to simply replace them with our solid font
* color...
* Since the RGB for an alpha texture is defined as 0, this gives us:
*
* result.rgb = color.rgb * 0
* result.a = color.a * texture.a
*
* What we want is premultiplied rgba values:
*
* result.rgba = color.rgb * texture.a
* result.a = color.a * texture.a
*/
cogl_material_set_layer_combine (priv->glyph_material, 0, /* layer */
"RGB = REPLACE (PREVIOUS)"
"A = MODULATE (PREVIOUS, TEXTURE)",
"RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
NULL);
priv->solid_material = cogl_material_new ();

View File

@ -871,7 +871,8 @@ clutter_x11_texture_pixmap_update_area_real (ClutterX11TexturePixmap *texture,
width, height,
bytes_per_line,
4,
CLUTTER_TEXTURE_RGB_FLAG_BGR,
CLUTTER_TEXTURE_RGB_FLAG_BGR |
CLUTTER_TEXTURE_RGB_FLAG_PREMULT,
&error);
else
clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (texture),
@ -880,7 +881,8 @@ clutter_x11_texture_pixmap_update_area_real (ClutterX11TexturePixmap *texture,
width, height,
bytes_per_line,
4,
CLUTTER_TEXTURE_RGB_FLAG_BGR,
CLUTTER_TEXTURE_RGB_FLAG_BGR |
CLUTTER_TEXTURE_RGB_FLAG_PREMULT,
&error);

View File

@ -29,6 +29,7 @@ test_conformance_SOURCES = \
test-blend-strings.c \
test-color.c \
test-clutter-units.c \
test-premult.c \
$(NULL)
# For convenience, this provides a way to easily run individual unit tests:

View File

@ -172,11 +172,13 @@ make_texture (guint32 color)
*(--p) = r;
}
/* Note: we don't use COGL_PIXEL_FORMAT_ANY for the internal format here
* since we don't want to allow Cogl to premultiply our data. */
tex = cogl_texture_new_from_data (QUAD_WIDTH,
QUAD_WIDTH,
COGL_TEXTURE_NONE,
COGL_PIXEL_FORMAT_RGBA_8888,
COGL_PIXEL_FORMAT_ANY,
COGL_PIXEL_FORMAT_RGBA_8888,
QUAD_WIDTH * 4,
tex_data);

View File

@ -136,6 +136,7 @@ main (int argc, char **argv)
TEST_CONFORM_SIMPLE ("/texture", test_backface_culling);
TEST_CONFORM_SIMPLE ("/texture", test_npot_texture);
TEST_CONFORM_SIMPLE ("/texture", test_premult);
TEST_CONFORM_SIMPLE ("/path", test_path);

View File

@ -0,0 +1,388 @@
#include <clutter/clutter.h>
#include <cogl/cogl.h>
#include <string.h>
#include "test-conform-common.h"
static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff };
#define QUAD_WIDTH 20
#define RED 0
#define GREEN 1
#define BLUE 2
#define ALPHA 3
#define MASK_RED(COLOR) ((COLOR & 0xff000000) >> 24);
#define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16);
#define MASK_BLUE(COLOR) ((COLOR & 0xff00) >> 8);
#define MASK_ALPHA(COLOR) (COLOR & 0xff);
#define SKIP_FRAMES 2
typedef struct _TestState
{
guint frame;
ClutterGeometry stage_geom;
CoglHandle passthrough_material;
} TestState;
static void
check_pixel (GLubyte *pixel, guint32 color)
{
guint8 r = MASK_RED (color);
guint8 g = MASK_GREEN (color);
guint8 b = MASK_BLUE (color);
guint8 a = MASK_ALPHA (color);
if (g_test_verbose ())
g_print (" expected = %x, %x, %x, %x\n",
r, g, b, a);
/* FIXME - allow for hardware in-precision */
g_assert (pixel[RED] == r);
g_assert (pixel[GREEN] == g);
g_assert (pixel[BLUE] == b);
/* FIXME
* We ignore the alpha, since we don't know if our render target is
* RGB or RGBA */
/* g_assert (pixel[ALPHA] == a); */
}
static guchar *
gen_tex_data (guint32 color)
{
guchar *tex_data, *p;
guint8 r = MASK_RED (color);
guint8 g = MASK_GREEN (color);
guint8 b = MASK_BLUE (color);
guint8 a = MASK_ALPHA (color);
tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 4);
for (p = tex_data + QUAD_WIDTH * QUAD_WIDTH * 4; p > tex_data;)
{
*(--p) = a;
*(--p) = b;
*(--p) = g;
*(--p) = r;
}
return tex_data;
}
static CoglHandle
make_texture (guint32 color,
CoglPixelFormat src_format,
CoglPixelFormat internal_format)
{
CoglHandle tex;
guchar *tex_data = gen_tex_data (color);
tex = cogl_texture_new_from_data (QUAD_WIDTH,
QUAD_WIDTH,
COGL_TEXTURE_NONE,
src_format,
internal_format,
QUAD_WIDTH * 4,
tex_data);
g_free (tex_data);
return tex;
}
static void
check_texture (TestState *state,
int x,
int y,
CoglHandle tex,
guint32 expected_result)
{
GLubyte pixel[4];
GLint y_off;
GLint x_off;
cogl_material_set_layer (state->passthrough_material, 0, tex);
cogl_set_source (state->passthrough_material);
cogl_rectangle (x * QUAD_WIDTH,
y * QUAD_WIDTH,
x * QUAD_WIDTH + QUAD_WIDTH,
y * QUAD_WIDTH + QUAD_WIDTH);
/* See what we got... */
/* NB: glReadPixels is done in GL screen space so y = 0 is at the bottom */
y_off = state->stage_geom.height - y * QUAD_WIDTH - (QUAD_WIDTH / 2);
x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2);
/* XXX:
* We haven't always had good luck with GL drivers implementing glReadPixels
* reliably and skipping the first two frames improves our chances... */
if (state->frame <= SKIP_FRAMES)
return;
glReadPixels (x_off, y_off, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
if (g_test_verbose ())
{
g_print ("check texture (%d, %d):\n", x, y);
g_print (" result = %02x, %02x, %02x, %02x\n",
pixel[RED], pixel[GREEN], pixel[BLUE], pixel[ALPHA]);
}
check_pixel (pixel, expected_result);
}
static void
on_paint (ClutterActor *actor, TestState *state)
{
int frame_num;
CoglHandle tex;
guchar *tex_data;
/* If the user explicitly specifies an unmultiplied internal format then
* Cogl shouldn't automatically premultiply the given texture data... */
if (state->frame > SKIP_FRAMES && g_test_verbose ())
g_print ("make_texture (0xff00ff80, "
"src = RGBA_8888, internal = RGBA_8888)\n");
tex = make_texture (0xff00ff80,
COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
COGL_PIXEL_FORMAT_RGBA_8888); /* internal format */
check_texture (state, 0, 0, /* position */
tex,
0xff00ff80); /* expected */
/* If the user explicitly requests a premultiplied internal format and
* gives unmultiplied src data then Cogl should always premultiply that
* for us */
if (state->frame > SKIP_FRAMES && g_test_verbose ())
g_print ("make_texture (0xff00ff80, "
"src = RGBA_8888, internal = RGBA_8888_PRE)\n");
tex = make_texture (0xff00ff80,
COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
COGL_PIXEL_FORMAT_RGBA_8888_PRE); /* internal format */
check_texture (state, 1, 0, /* position */
tex,
0x80008080); /* expected */
/* If the user gives COGL_PIXEL_FORMAT_ANY for the internal format then
* by default Cogl should premultiply the given texture data...
* (In the future there will be additional Cogl API to control this
* behaviour) */
if (state->frame > SKIP_FRAMES && g_test_verbose ())
g_print ("make_texture (0xff00ff80, "
"src = RGBA_8888, internal = ANY)\n");
tex = make_texture (0xff00ff80,
COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
COGL_PIXEL_FORMAT_ANY); /* internal format */
check_texture (state, 2, 0, /* position */
tex,
0x80008080); /* expected */
/* If the user requests a premultiplied internal texture format and supplies
* premultiplied source data, Cogl should never modify that source data...
*/
if (state->frame > SKIP_FRAMES && g_test_verbose ())
g_print ("make_texture (0x80008080, "
"src = RGBA_8888_PRE, "
"internal = RGBA_8888_PRE)\n");
tex = make_texture (0x80008080,
COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
COGL_PIXEL_FORMAT_RGBA_8888_PRE); /* internal format */
check_texture (state, 3, 0, /* position */
tex,
0x80008080); /* expected */
/* If the user requests an unmultiplied internal texture format, but
* supplies premultiplied source data, then Cogl should always
* un-premultiply the source data... */
if (state->frame > SKIP_FRAMES && g_test_verbose ())
g_print ("make_texture (0x80008080, "
"src = RGBA_8888_PRE, internal = RGBA_8888)\n");
tex = make_texture (0x80008080,
COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
COGL_PIXEL_FORMAT_RGBA_8888); /* internal format */
check_texture (state, 4, 0, /* position */
tex,
0xff00ff80); /* expected */
/* If the user allows any internal texture format and provides premultipled
* source data then by default Cogl shouldn't modify the source data...
* (In the future there will be additional Cogl API to control this
* behaviour) */
if (state->frame > SKIP_FRAMES && g_test_verbose ())
g_print ("make_texture (0x80008080, "
"src = RGBA_8888_PRE, internal = ANY)\n");
tex = make_texture (0x80008080,
COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
COGL_PIXEL_FORMAT_ANY); /* internal format */
check_texture (state, 5, 0, /* position */
tex,
0x80008080); /* expected */
/*
* Test cogl_texture_set_region() ....
*/
if (state->frame > SKIP_FRAMES && g_test_verbose ())
g_print ("make_texture (0xDEADBEEF, "
"src = RGBA_8888, internal = RGBA_8888)\n");
tex = make_texture (0xDEADBEEF,
COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
COGL_PIXEL_FORMAT_RGBA_8888); /* internal format */
if (state->frame > SKIP_FRAMES && g_test_verbose ())
g_print ("set_region (0xff00ff80, RGBA_8888)\n");
tex_data = gen_tex_data (0xff00ff80);
cogl_texture_set_region (tex,
0, 0, /* src x, y */
0, 0, /* dst x, y */
QUAD_WIDTH, QUAD_WIDTH, /* dst width, height */
QUAD_WIDTH, QUAD_WIDTH, /* src width, height */
COGL_PIXEL_FORMAT_RGBA_8888,
0, /* auto compute row stride */
tex_data);
check_texture (state, 6, 0, /* position */
tex,
0xff00ff80); /* expected */
/* Updating a texture region for an unmultiplied texture using premultiplied
* region data should result in Cogl unmultiplying the given region data...
*/
if (state->frame > SKIP_FRAMES && g_test_verbose ())
g_print ("make_texture (0xDEADBEEF, "
"src = RGBA_8888, internal = RGBA_8888)\n");
tex = make_texture (0xDEADBEEF,
COGL_PIXEL_FORMAT_RGBA_8888, /* src format */
COGL_PIXEL_FORMAT_RGBA_8888); /* internal format */
if (state->frame > SKIP_FRAMES && g_test_verbose ())
g_print ("set_region (0x80008080, RGBA_8888_PRE)\n");
tex_data = gen_tex_data (0x80008080);
cogl_texture_set_region (tex,
0, 0, /* src x, y */
0, 0, /* dst x, y */
QUAD_WIDTH, QUAD_WIDTH, /* dst width, height */
QUAD_WIDTH, QUAD_WIDTH, /* src width, height */
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
0, /* auto compute row stride */
tex_data);
check_texture (state, 7, 0, /* position */
tex,
0xff00ff80); /* expected */
if (state->frame > SKIP_FRAMES && g_test_verbose ())
g_print ("make_texture (0xDEADBEEF, "
"src = RGBA_8888_PRE, "
"internal = RGBA_8888_PRE)\n");
tex = make_texture (0xDEADBEEF,
COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
COGL_PIXEL_FORMAT_RGBA_8888_PRE); /* internal format */
if (state->frame > SKIP_FRAMES && g_test_verbose ())
g_print ("set_region (0x80008080, RGBA_8888_PRE)\n");
tex_data = gen_tex_data (0x80008080);
cogl_texture_set_region (tex,
0, 0, /* src x, y */
0, 0, /* dst x, y */
QUAD_WIDTH, QUAD_WIDTH, /* dst width, height */
QUAD_WIDTH, QUAD_WIDTH, /* src width, height */
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
0, /* auto compute row stride */
tex_data);
check_texture (state, 8, 0, /* position */
tex,
0x80008080); /* expected */
/* Updating a texture region for a premultiplied texture using unmultiplied
* region data should result in Cogl premultiplying the given region data...
*/
if (state->frame > SKIP_FRAMES && g_test_verbose ())
g_print ("make_texture (0xDEADBEEF, "
"src = RGBA_8888_PRE, "
"internal = RGBA_8888_PRE)\n");
tex = make_texture (0xDEADBEEF,
COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */
COGL_PIXEL_FORMAT_RGBA_8888_PRE); /* internal format */
if (state->frame > SKIP_FRAMES && g_test_verbose ())
g_print ("set_region (0xff00ff80, RGBA_8888)\n");
tex_data = gen_tex_data (0xff00ff80);
cogl_texture_set_region (tex,
0, 0, /* src x, y */
0, 0, /* dst x, y */
QUAD_WIDTH, QUAD_WIDTH, /* dst width, height */
QUAD_WIDTH, QUAD_WIDTH, /* src width, height */
COGL_PIXEL_FORMAT_RGBA_8888,
0, /* auto compute row stride */
tex_data);
check_texture (state, 9, 0, /* position */
tex,
0x80008080); /* expected */
/* XXX: Experiments have shown that for some buggy drivers, when using
* glReadPixels there is some kind of race, so we delay our test for a
* few frames and a few seconds:
*/
frame_num = state->frame++;
if (frame_num < SKIP_FRAMES)
g_usleep (G_USEC_PER_SEC);
/* Comment this out if you want visual feedback for what this test paints */
#if 1
if (frame_num > SKIP_FRAMES)
clutter_main_quit ();
#endif
}
static gboolean
queue_redraw (gpointer stage)
{
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
return TRUE;
}
void
test_premult (TestConformSimpleFixture *fixture,
gconstpointer data)
{
TestState state;
ClutterActor *stage;
ClutterActor *group;
guint idle_source;
state.frame = 0;
state.passthrough_material = cogl_material_new ();
cogl_material_set_blend (state.passthrough_material,
"RGBA = ADD (SRC_COLOR, 0)", NULL);
cogl_material_set_layer_combine (state.passthrough_material, 0,
"RGBA = REPLACE (TEXTURE)", NULL);
stage = clutter_stage_get_default ();
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
clutter_actor_get_geometry (stage, &state.stage_geom);
group = clutter_group_new ();
clutter_container_add_actor (CLUTTER_CONTAINER (stage), group);
/* We force continuous redrawing of the stage, since we need to skip
* the first few frames, and we wont be doing anything else that
* will trigger redrawing. */
idle_source = g_idle_add (queue_redraw, stage);
g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state);
clutter_actor_show_all (stage);
clutter_main ();
g_source_remove (idle_source);
if (g_test_verbose ())
g_print ("OK\n");
}

View File

@ -106,7 +106,7 @@ test_coglbox_paint(ClutterActor *self)
cogl_set_draw_buffer (COGL_WINDOW_BUFFER, 0);
material = cogl_material_new ();
cogl_material_set_color4ub (material, 0xff, 0xff, 0xff, 0x88);
cogl_material_set_color4ub (material, 0x88, 0x88, 0x88, 0x88);
cogl_material_set_layer (material, 0, priv->texture_id);
cogl_set_source (material);
cogl_rectangle_with_texture_coords (100, 100,

View File

@ -119,15 +119,12 @@ test_depth_main (int argc, char *argv[])
ClutterActor *group, *hand, *label, *rect, *janus, *box;
ClutterColor stage_color = { 0xcc, 0xcc, 0xcc, 0xff };
ClutterColor rect_color = { 0, 0, 0, 0x88 };
ClutterFog stage_fog = { 10.0, -50.0 };
GError *error;
clutter_init (&argc, &argv);
stage = clutter_stage_get_default ();
clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color);
clutter_stage_set_fog (CLUTTER_STAGE (stage), &stage_fog);
clutter_stage_set_use_fog (CLUTTER_STAGE (stage), TRUE);
g_signal_connect (stage,
"button-press-event", G_CALLBACK (clutter_main_quit),