Default to a blend function that expects premultiplied colors
authorOwen W. Taylor <otaylor@fishsoup.net>
Sat, 9 May 2009 18:39:01 +0000 (14:39 -0400)
committerRobert Bragg <robert@linux.intel.com>
Thu, 11 Jun 2009 13:17:52 +0000 (14:17 +0100)
Many operations, like mixing two textures together or alpha-blending
onto a destination with alpha, are done most logically if texture data
is in premultiplied form. We also have many sources of premultiplied
texture data, like X pixmaps, FBOs, cairo surfaces. Rather than trying
to work with two different types of texture data, simplify things by
always premultiplying texture data before uploading to GL.

Because the default blend function is changed to accommodate this,
uses of pure-color CoglMaterial need to be adapted to add
premultiplication.

gl/cogl-texture.c gles/cogl-texture.c: Always premultiply
  non-premultiplied texture data before uploading to GL.

cogl-material.c cogl-material.h: Switch the default blend functions
  to ONE, ONE_MINUS_SRC_ALPHA so they work correctly with premultiplied
  data.

cogl.c: Make cogl_set_source_color() premultiply the color.

cogl.h.in color-material.h: Add some documentation about
  premultiplication and its interaction with color values.

cogl-pango-render.c clutter-texture.c tests/interactive/test-cogl-offscreen.c:
  Use premultiplied colors.

http://bugzilla.openedhand.com/show_bug.cgi?id=1406

Signed-off-by: Robert Bragg <robert@linux.intel.com>
clutter/clutter-texture.c
clutter/cogl/cogl-material.h
clutter/cogl/cogl.h.in
clutter/cogl/common/cogl-material.c
clutter/cogl/common/cogl.c
clutter/cogl/gl/cogl-texture.c
clutter/cogl/gles/cogl-texture.c
clutter/pango/cogl-pango-display-list.c
clutter/pango/cogl-pango-render.c
tests/interactive/test-cogl-offscreen.c

index 5a31cff..b8fb002 100644 (file)
@@ -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);
 
index 510ce82..44a8375 100644 (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.
index a468cf7..64d57fc 100644 (file)
@@ -455,8 +455,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.
index 831fa30..7e3935b 100644 (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;
 
index 3363039..5857db2 100644 (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);
 }
 
index 3af78f9..2ee5061 100644 (file)
@@ -1085,18 +1085,20 @@ _cogl_pixel_format_to_gl (CoglPixelFormat  format,
   GLenum          glformat = 0;
   GLenum          gltype = 0;
 
-  /* No premultiplied formats accepted  by GL
-   * (FIXME: latest hardware?) */
-
-  if (format & COGL_PREMULT_BIT)
-    format = (format & COGL_UNPREMULT_MASK);
-
-  /* Everything else accepted
-   * (FIXME: check YUV support) */
-  required_format = format;
+  /* If PREMULT_BIT isn't specified, that means that we premultiply
+   * textures with alpha before uploading to GL; once we are in GL land,
+   * everything is premultiplied.
+   *
+   * Everything else accepted (FIXME: check YUV support)
+   */
+  if ((format & COGL_A_BIT) != 0 &&
+      format != COGL_PIXEL_FORMAT_A_8)
+    required_format = format | COGL_PREMULT_BIT;
+  else
+    required_format = format;
 
   /* Find GL equivalents */
-  switch (format)
+  switch (format & COGL_UNPREMULT_MASK)
     {
     case COGL_PIXEL_FORMAT_A_8:
       glintformat = GL_ALPHA;
index ae3e437..f9b6e0b 100644 (file)
@@ -1184,18 +1184,20 @@ _cogl_pixel_format_to_gl (CoglPixelFormat  format,
   GLenum          glformat = 0;
   GLenum          gltype = 0;
 
-  /* No premultiplied formats accepted  by GL
-   * (FIXME: latest hardware?) */
-
-  if (format & COGL_PREMULT_BIT)
-    format = (format & COGL_UNPREMULT_MASK);
-
-  /* Everything else accepted
-   * (FIXME: check YUV support) */
-  required_format = format;
+  /* If PREMULT_BIT isn't specified, that means that we premultiply
+   * textures with alpha before uploading to GL; once we are in GL land,
+   * everything is premultiplied.
+   *
+   * Everything else accepted (FIXME: check YUV support)
+   */
+  if ((format & COGL_A_BIT) != 0 &&
+      format != COGL_PIXEL_FORMAT_A_8)
+    required_format = format | COGL_PREMULT_BIT;
+  else
+    required_format = format;
 
   /* Find GL equivalents */
-  switch (format)
+  switch (format & COGL_UNPREMULT_MASK)
     {
     case COGL_PIXEL_FORMAT_A_8:
       glintformat = GL_ALPHA;
@@ -1226,6 +1228,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
index 1e16a76..6a3b29c 100644 (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)
         {
index ffa224e..c98ddca 100644 (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 ();
index 8b9d8e8..2aa5e79 100644 (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,