[cairo] Applying to a surface the alpha of other

Carlos López González genetita at gmail.com
Tue Oct 16 22:48:13 PDT 2012

```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;
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?

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.

In any case thank you very much.

Regards

2012/10/17 Krzysztof Kosiński <tweenk.pl at gmail.com>:
> 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

--
Carlos
http://synfig.org
```