cogl-atlas-texture: Fix premultiplied texture formats
authorNeil Roberts <neil@linux.intel.com>
Mon, 18 Jan 2010 10:53:00 +0000 (10:53 +0000)
committerNeil Roberts <neil@linux.intel.com>
Mon, 18 Jan 2010 10:53:00 +0000 (10:53 +0000)
When uploading texture data it was just calling cogl_texture_set_data
on the large texture. This would attempt to convert the data to the
format of the large texture. All of the textures with alpha channels
are stored together regardless of whether they are premultiplied so
this was causing premultiplied textures to be unpremultiplied
again. It now just uploads the data ignoring the premult bit of the
format so that it only gets converted once.

clutter/cogl/cogl/cogl-atlas-texture.c

index 4bbff22..1400919 100644 (file)
@@ -420,6 +420,90 @@ _cogl_atlas_texture_ensure_non_quad_rendering (CoglTexture *tex)
 }
 
 static gboolean
+_cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex,
+                                            int             src_x,
+                                            int             src_y,
+                                            int             dst_x,
+                                            int             dst_y,
+                                            unsigned int    dst_width,
+                                            unsigned int    dst_height,
+                                            int             width,
+                                            int             height,
+                                            CoglPixelFormat format,
+                                            unsigned int    rowstride,
+                                            const guint8   *data)
+{
+  CoglHandle big_texture;
+
+  _COGL_GET_CONTEXT (ctx, FALSE);
+
+  big_texture = ((atlas_tex->format & COGL_A_BIT) ?
+                 ctx->atlas_alpha_texture : ctx->atlas_no_alpha_texture);
+
+  /* Copy the central data */
+  if (!cogl_texture_set_region (big_texture,
+                                src_x, src_y,
+                                dst_x + atlas_tex->rectangle.x + 1,
+                                dst_y + atlas_tex->rectangle.y + 1,
+                                dst_width,
+                                dst_height,
+                                width, height,
+                                format,
+                                rowstride,
+                                data))
+    return FALSE;
+
+  /* Update the left edge pixels */
+  if (dst_x == 0 &&
+      !cogl_texture_set_region (big_texture,
+                                src_x, src_y,
+                                atlas_tex->rectangle.x,
+                                dst_y + atlas_tex->rectangle.y + 1,
+                                1, dst_height,
+                                width, height,
+                                format, rowstride,
+                                data))
+    return FALSE;
+  /* Update the right edge pixels */
+  if (dst_x + dst_width == atlas_tex->rectangle.width - 2 &&
+      !cogl_texture_set_region (big_texture,
+                                src_x + dst_width - 1, src_y,
+                                atlas_tex->rectangle.x +
+                                atlas_tex->rectangle.width - 1,
+                                dst_y + atlas_tex->rectangle.y + 1,
+                                1, dst_height,
+                                width, height,
+                                format, rowstride,
+                                data))
+    return FALSE;
+  /* Update the top edge pixels */
+  if (dst_y == 0 &&
+      !cogl_texture_set_region (big_texture,
+                                src_x, src_y,
+                                dst_x + atlas_tex->rectangle.x + 1,
+                                atlas_tex->rectangle.y,
+                                dst_width, 1,
+                                width, height,
+                                format, rowstride,
+                                data))
+    return FALSE;
+  /* Update the bottom edge pixels */
+  if (dst_y + dst_height == atlas_tex->rectangle.height - 2 &&
+      !cogl_texture_set_region (big_texture,
+                                src_x, src_y + dst_height - 1,
+                                dst_x + atlas_tex->rectangle.x + 1,
+                                atlas_tex->rectangle.y +
+                                atlas_tex->rectangle.height - 1,
+                                dst_width, 1,
+                                width, height,
+                                format, rowstride,
+                                data))
+    return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
 _cogl_atlas_texture_set_region (CoglTexture    *tex,
                                 int             src_x,
                                 int             src_y,
@@ -439,74 +523,71 @@ _cogl_atlas_texture_set_region (CoglTexture    *tex,
      pixels to the border */
   if (atlas_tex->in_atlas)
     {
-      CoglHandle big_texture;
+      gint             bpp;
+      CoglBitmap       source_bmp;
+      CoglBitmap       temp_bmp;
+      gboolean         source_bmp_owner = FALSE;
+      CoglPixelFormat  closest_format;
+      GLenum           closest_gl_format;
+      GLenum           closest_gl_type;
+      gboolean         success;
+
+      /* Check for valid format */
+      if (format == COGL_PIXEL_FORMAT_ANY)
+        return FALSE;
 
-      _COGL_GET_CONTEXT (ctx, FALSE);
+      /* Shortcut out early if the image is empty */
+      if (width == 0 || height == 0)
+        return TRUE;
 
-      big_texture = ((atlas_tex->format & COGL_A_BIT) ?
-                     ctx->atlas_alpha_texture : ctx->atlas_no_alpha_texture);
+      /* Init source bitmap */
+      source_bmp.width = width;
+      source_bmp.height = height;
+      source_bmp.format = format;
+      source_bmp.data = (guchar*) data;
 
-      /* Copy the central data */
-      if (!cogl_texture_set_region (big_texture,
-                                    src_x, src_y,
-                                    dst_x + atlas_tex->rectangle.x + 1,
-                                    dst_y + atlas_tex->rectangle.y + 1,
-                                    dst_width,
-                                    dst_height,
-                                    width, height,
-                                    format,
-                                    rowstride,
-                                    data))
-        return FALSE;
+      /* Rowstride from width if none specified */
+      bpp = _cogl_get_format_bpp (format);
+      source_bmp.rowstride = (rowstride == 0) ? width * bpp : rowstride;
 
-      /* Update the left edge pixels */
-      if (dst_x == 0 &&
-          !cogl_texture_set_region (big_texture,
-                                    src_x, src_y,
-                                    atlas_tex->rectangle.x,
-                                    dst_y + atlas_tex->rectangle.y + 1,
-                                    1, dst_height,
-                                    width, height,
-                                    format, rowstride,
-                                    data))
-        return FALSE;
-      /* Update the right edge pixels */
-      if (dst_x + dst_width == atlas_tex->rectangle.width - 2 &&
-          !cogl_texture_set_region (big_texture,
-                                    src_x + dst_width - 1, src_y,
-                                    atlas_tex->rectangle.x +
-                                    atlas_tex->rectangle.width - 1,
-                                    dst_y + atlas_tex->rectangle.y + 1,
-                                    1, dst_height,
-                                    width, height,
-                                    format, rowstride,
-                                    data))
-        return FALSE;
-      /* Update the top edge pixels */
-      if (dst_y == 0 &&
-          !cogl_texture_set_region (big_texture,
-                                    src_x, src_y,
-                                    dst_x + atlas_tex->rectangle.x + 1,
-                                    atlas_tex->rectangle.y,
-                                    dst_width, 1,
-                                    width, height,
-                                    format, rowstride,
-                                    data))
-        return FALSE;
-      /* Update the bottom edge pixels */
-      if (dst_y + dst_height == atlas_tex->rectangle.height - 2 &&
-          !cogl_texture_set_region (big_texture,
-                                    src_x, src_y + dst_height - 1,
-                                    dst_x + atlas_tex->rectangle.x + 1,
-                                    atlas_tex->rectangle.y +
-                                    atlas_tex->rectangle.height - 1,
-                                    dst_width, 1,
-                                    width, height,
-                                    format, rowstride,
-                                    data))
-        return FALSE;
+      /* Find closest format to internal that's supported by GL */
+      closest_format = _cogl_pixel_format_to_gl (atlas_tex->format,
+                                                 NULL, /* don't need */
+                                                 &closest_gl_format,
+                                                 &closest_gl_type);
 
-      return TRUE;
+      /* If no direct match, convert */
+      if (closest_format != format)
+        {
+          /* Convert to required format */
+          success = _cogl_bitmap_convert_and_premult (&source_bmp,
+                                                      &temp_bmp,
+                                                      closest_format);
+
+          /* Swap bitmaps if succeeded */
+          if (!success) return FALSE;
+          source_bmp = temp_bmp;
+          source_bmp_owner = TRUE;
+        }
+
+      /* Upload the data ignoring the premult bit */
+      success =
+        _cogl_atlas_texture_set_region_with_border (atlas_tex,
+                                                    src_x, src_y,
+                                                    dst_x, dst_y,
+                                                    dst_width, dst_height,
+                                                    source_bmp.width,
+                                                    source_bmp.height,
+                                                    source_bmp.format &
+                                                    ~COGL_PREMULT_BIT,
+                                                    source_bmp.rowstride,
+                                                    source_bmp.data);
+
+      /* Free data if owner */
+      if (source_bmp_owner)
+        g_free (source_bmp.data);
+
+      return success;
     }
   else
     /* Otherwise we can just forward on to the sub texture */
@@ -962,17 +1043,20 @@ _cogl_atlas_texture_new_from_bitmap (CoglHandle       bmp_handle,
                                             &atlas_tex->rectangle);
 
   /* Defer to set_region so that we can share the code for copying the
-     edge pixels to the border */
-  _cogl_atlas_texture_set_region (COGL_TEXTURE (atlas_tex),
-                                  0, 0,
-                                  0, 0,
-                                  upload_data.bitmap.width,
-                                  upload_data.bitmap.height,
-                                  upload_data.bitmap.width,
-                                  upload_data.bitmap.height,
-                                  upload_data.bitmap.format,
-                                  upload_data.bitmap.rowstride,
-                                  upload_data.bitmap.data);
+     edge pixels to the border. We don't want to pass the actual
+     format of the converted texture because otherwise it will get
+     unpremultiplied. */
+  _cogl_atlas_texture_set_region_with_border (atlas_tex,
+                                              0, 0,
+                                              0, 0,
+                                              upload_data.bitmap.width,
+                                              upload_data.bitmap.height,
+                                              upload_data.bitmap.width,
+                                              upload_data.bitmap.height,
+                                              upload_data.bitmap.format &
+                                              ~COGL_PREMULT_BIT,
+                                              upload_data.bitmap.rowstride,
+                                              upload_data.bitmap.data);
 
   return _cogl_atlas_texture_handle_new (atlas_tex);
 }