I have a problem with displaying alpha transparency using GTK and Cairo. I try to display this image 1
If I do the alpha blending my self, everything works.
If I pass the alpha values directly to Cairo, the shadow seems to render fine, but the glow effect is corrupted.
Is this a bug in Cairo 1.14.2, or am I missing something?
//Need deprecated API to get background color
GdkColor color = gtk_widget_get_style(widget)->bg[GTK_STATE_NORMAL];
Pixel color_blend
{
uint8_t(255*color.red/65535.0f)
,uint8_t(255*color.green/65535.0f)
,uint8_t(255*color.blue/65535.0f)
,255
};
while(ptr!=ptr_end)
{
// TODO: Interpolate
auto row_src=size_t(row*factor);
auto col_src=size_t(col*factor);
auto alpha=ptr_src[row_src*width_in + col_src].v3/255.0f;
*ptr=
{
// Using manual alpha blend works
uint8_t(alpha*ptr_src[row_src*width_in + col_src].v2 + (1-alpha)*color_blend.v2)
,uint8_t(alpha*ptr_src[row_src*width_in + col_src].v1 + (1-alpha)*color_blend.v1)
,uint8_t(alpha*ptr_src[row_src*width_in + col_src].v0 + (1-alpha)*color_blend.v0)
,255
/* This appears to be broken
ptr_src[row_src*width_in + col_src].v2
,ptr_src[row_src*width_in + col_src].v1
,ptr_src[row_src*width_in + col_src].v0
,ptr_src[row_src*width_in + col_src].v3*/
};
++col;
if(col==width_out)
{
col=0;
++row;
}
++ptr;
}
I push the pixels using
auto surface=cairo_image_surface_create_for_data((uint8_t*)pixels.begin(),CAIRO_FORMAT_ARGB32,width_out,height_out,width_out*sizeof(Pixel));
cairo_set_source_surface(cr, surface, 0.5*(width-width_out), 0.0);
cairo_paint(cr);
cairo_surface_destroy(surface);
Explicitly setting the operator to CAIRO_OPERATOR_OVER does not help, the result is still the same.
As you mention in your comment above, your pixel values are wrong. You need to use pre-multiplied alpha. Coming back to my example from the question (and ignoring endianness), fully red with 50% transparency is 0x7f << 24 | 0x7f
in Cairo. Pixels with invalid values (some color component is larger than the alpha value) produce undefined results and your 0xff << 24 | 0x7f
falls into this category.
See http://www.cairographics.org/manual/cairo-Image-Surfaces.html#cairo-format-t:
Pre-multiplied alpha is used. (That is, 50% transparent red is 0x80800000, not 0x80ff0000.)
P.S.: In my opinion, the correct way to access pixel data is via a uint32_t
and shifting, e.g. uint32_t pixel = (r << 24) | (g << 16) | (b << 8) | a;
. That way you don't have to worry about endianness at all.
P.P.S.: For OVER and a fully-opaque target, the formula Cairo uses simplifies to source_color + target_color * (1 - source_alpha)
while your code uses source_color * source_alpha + target_color * (1 - source_alpha)
. See http://www.cairographics.org/operators/. These two formulas clearly are not equivalent.
Edit: Ok, perhaps they are equivalent when using pre-multiplie alpha. Sorry for the confusion there.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With