[cairo] Applying to a surface the alpha of other

Krzysztof Kosiński tweenk.pl at gmail.com
Wed Oct 17 06:54:16 PDT 2012


2012/10/17 Carlos López González <genetita at gmail.com>:
> Hi Krzysztof,
> thank you for the code sample to perform the bit operations on the
> pixel. I already was aware about how to manipulate them, thanks to the
> help of this cairo mailing list.
>
> I guess that the reason to sum a/2 is to do a rounded up operation on
> the result. Is there any kind of convention on that? If I were done
> the code my self I would just do:
>  r = (255 * r ) / a;

This division would round up if the code was:

r = (255 * r + a) / a;

Adding a/2 rounds to the nearest value, which is most likely what you want.

> So I guess that the internals of Cairo does the rounding up operation
> when receive a color by its components. Is that true or is there
> anything else hidden on that code?

AFAIK Cairo rounds to the nearest integer value, e.g. color_integer =
round(color_double * 255)

> Regarding to my problem, in case I would use direct pixel manipulation
> I would do all the operations in that level so instead of set the
> alpha result to 0xff000000 I would use the value from the other
> surface, that is what I want.

If the other surface has exactly the same dimensions and stride, you
can just obtain a second data pointer:

cairo_surface_flush(s);
cairo_surface_flush(as);
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);
char *adata = cairo_image_surface_get_data(as);

for (int y = 0; y < h; ++y) {
  unsigned *row = data + y * stride;
  unsigned *arow = adata + 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] = (arow[x] & 0xff000000) | (r << 16) | (g << 8) | b;
  }
}

cairo_surface_mark_dirty(s);

However, if the alpha surface can have different dimensions, the code
gets complicated and it's easier to just use a mask operation.

cairo_surface_t *result = cairo_surface_create_similar(
  s, CAIRO_CONTENT_COLOR_ALPHA,
  cairo_image_surface_get_width(s),
  cairo_image_surface_get_height(s));

cairo_t *cr = cairo_create(result);
// you might need to pass different x and y values
// to the calls below to align the alpha and color surfaces properly
cairo_set_source_surface(cr, s, 0, 0);
cairo_mask_surface(cr, as, 0, 0);
cairo_paint(cr);
cairo_destroy(cr);

Regards, Krzysztof


More information about the cairo mailing list