[cairo] Applying to a surface the alpha of other

Krzysztof Kosiński tweenk.pl at gmail.com
Tue Oct 16 15:48:05 PDT 2012


2012/10/16 Carlos López González <genetita at gmail.com>:
> Hi,
> unfortunately the proposed solution doesn't work. When a ARGB32 color
> is painted in a RGB24 surface, the colors are multiplied by the alpha.
> Say I have a ARGB color:
> ARGB(0.8, 1.0, 0.5, 0.5)
> once painted to a RGB24 surface it becomes:
> RGB(0.8, 0.4, 0.4)
>
> Later if I mask back this color thru a 0.6 alpha mask, then it keeps
> the RGB values and keeps the alpha but the colors are washed out:
>
> ARGB(0.6, 0.8, 0.4, 0.4)
>
> When my desired result is
> ARGB(0.6, 1.0, 0.5, 0.5) that is keep the colors of one surface and
> apply the alpha of other.
>
> Any other idea?

For an image surface, you can manipulate the pixels directly with code
like this:

cairo_surface_flush(s);
int w = cairo_image_surface_get_width(s);
int h = cairo_image_surface_get_height(s);
int stride = cairo_image_surface_get_stride(s);
char *data = cairo_image_surface_get_data(s);

for (int y = 0; y < h; ++y) {
  unsigned *row = data + y * stride;
  for (int x = 0; x < w; ++x) {
    unsigned px = row[x];
    unsigned a = (px & 0xff000000) >> 24;
    unsigned r = (px & 0x00ff0000) >> 16;
    unsigned g = (px & 0x0000ff00) >> 8;
    unsigned b = (px & 0x000000ff);

    // un-premultiply color values
    if (a != 0) {
      r = (255 * r + a/2) / a;
      g = (255 * g + a/2) / a;
      b = (255 * g + a/2) / a;
    }

    // assemble the resulting pixel from components
    row[x] = 0xff000000 | (r << 16) | (g << 8) | b;
  }
}

cairo_surface_mark_dirty(s);

Note that since Cairo uses premultiplied alpha compositing, you will
lose a lot of precision precision for low alpha values, which may look
very ugly. This could be solved with a floating point surface format,
but Cairo doesn't have one right now.

Regards, Krzysztof


More information about the cairo mailing list