Re: Applying to a surface the alpha of other

Krzysztof Kosiński <[email protected]>
Newsgroups gmane.comp.lib.cairo
Message-ID <CAFLw2WS_DMphgH=aBVwKgTEBbDh+=kgxdvYYOfBOYr_OuSEFwA@mail.gmail.com>
2012/10/17 Carlos López González <[email protected]>:
> 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
-- 
cairo mailing list
[email protected]
http://lists.cairographics.org/mailman/listinfo/cairo
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.