开罗的 Alpha 透明度
Alpha transparency in Cairo
我在使用 GTK 和 Cairo 显示 alpha 透明度时遇到问题。我尝试显示这张图片 1
如果我对自己进行 alpha 混合,一切正常。
如果我将 alpha 值直接传递给 Cairo,阴影似乎渲染良好,但发光效果已损坏。
这是 Cairo 1.14.2 中的错误,还是我遗漏了什么?
//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;
}
我使用
推像素
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);
将运算符显式设置为 CAIRO_OPERATOR_OVER 没有帮助,结果还是一样。
正如您在上面的评论中提到的,您的像素值是错误的。您需要使用预乘 alpha。回到我的问题示例(并忽略字节顺序),在开罗 0x7f << 24 | 0x7f
具有 50% 透明度的全红色。具有无效值的像素(某些颜色分量大于 alpha 值)会产生未定义的结果,您的 0xff << 24 | 0x7f
属于此类。
参见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.: 在我看来,访问像素数据的正确方法是通过 uint32_t
和移位,例如uint32_t pixel = (r << 24) | (g << 16) | (b << 8) | a;
。这样你就完全不用担心字节顺序了。
P.P.S.:对于 OVER 和完全不透明的目标,Cairo 使用的公式简化为 source_color + target_color * (1 - source_alpha)
,而您的代码使用 source_color * source_alpha + target_color * (1 - source_alpha)
。参见 http://www.cairographics.org/operators/。这两个公式显然不等价。
编辑:好的,也许它们在使用预乘 alpha 时是等效的。抱歉造成混乱。
我在使用 GTK 和 Cairo 显示 alpha 透明度时遇到问题。我尝试显示这张图片 1
如果我对自己进行 alpha 混合,一切正常。
如果我将 alpha 值直接传递给 Cairo,阴影似乎渲染良好,但发光效果已损坏。
这是 Cairo 1.14.2 中的错误,还是我遗漏了什么?
//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;
}
我使用
推像素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);
将运算符显式设置为 CAIRO_OPERATOR_OVER 没有帮助,结果还是一样。
正如您在上面的评论中提到的,您的像素值是错误的。您需要使用预乘 alpha。回到我的问题示例(并忽略字节顺序),在开罗 0x7f << 24 | 0x7f
具有 50% 透明度的全红色。具有无效值的像素(某些颜色分量大于 alpha 值)会产生未定义的结果,您的 0xff << 24 | 0x7f
属于此类。
参见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.: 在我看来,访问像素数据的正确方法是通过 uint32_t
和移位,例如uint32_t pixel = (r << 24) | (g << 16) | (b << 8) | a;
。这样你就完全不用担心字节顺序了。
P.P.S.:对于 OVER 和完全不透明的目标,Cairo 使用的公式简化为 source_color + target_color * (1 - source_alpha)
,而您的代码使用 source_color * source_alpha + target_color * (1 - source_alpha)
。参见 http://www.cairographics.org/operators/。这两个公式显然不等价。
编辑:好的,也许它们在使用预乘 alpha 时是等效的。抱歉造成混乱。