开罗的 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 时是等效的。抱歉造成混乱。