我有一个小代码片段,它从PNG文件加载图像,然后通过使特定颜色透明(将该颜色的alpha设置为0)来修改内存中的图像数据。下面是代码本身:

static gboolean expose (GtkWidget *widget, GdkEventExpose *event, gpointer userdata)
{
    int width, height, stride, x, y;
    cairo_t *cr = gdk_cairo_create(widget->window);
    cairo_surface_t* image;
    char* ptr;

    if (supports_alpha)
        cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0); /* transparent */
    else
        cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* opaque white */

    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
    cairo_paint (cr);

    image = cairo_image_surface_create_from_png ("bg.png");
    width = cairo_image_surface_get_width (image);
    height = cairo_image_surface_get_height (image);
    stride = cairo_image_surface_get_stride (image);
    cairo_surface_flush (image);

    ptr = (unsigned char*)malloc (stride * height);
    memcpy (ptr, cairo_image_surface_get_data (image), stride * height);
    cairo_surface_destroy (image);

    image = cairo_image_surface_create_for_data (ptr, CAIRO_FORMAT_ARGB32, width, height, stride);
    cairo_surface_flush (image);

    for (y = 0; y < height; y++) {
        for (x = 0; x < width; x++) {
            char alpha = 0;
            unsigned int z = *((unsigned int*)&ptr [y * stride + x * 4]);

            if ((z & 0xffffff) == 0xffffff) {
                z = (z & ~0xff000000) | (alpha & 0xff000000);
                *((unsigned int*) &ptr [y * stride + x * 4]) = z;
                    }
        }
    }

    cairo_surface_mark_dirty (image);
    cairo_surface_write_to_png (image, "image.png");

    gtk_widget_set_size_request (GTK_OBJECT (window), width, height);
    gtk_window_set_resizable (GTK_OBJECT (window), FALSE);

    cairo_set_source_surface (cr, image, 0, 0);
    cairo_paint_with_alpha (cr, 0.9);
    cairo_destroy (cr);
    cairo_surface_destroy (image);
    free (ptr);

    return FALSE;
}

当我将修改后的数据转储到PNG时,透明实际上就存在了。但是,当相同的数据用作绘制的源曲面时,就没有透明度。怎么了?
附件:
image.png-修改后的数据转储到文件中,以便进行调试,
demo.png-实际结果
png-源图像,由于stackoverflow限制而被忽略,它只是白色背景上的黑色圆形矩形。预期的结果是黑色的半透明矩形和完全透明的字段,而不是像demo.png中那样的白色字段。

最佳答案

将alpha设置为0意味着颜色完全透明。由于cairo使用预乘alpha,因此必须将像素设置为0,否则颜色组件的值可能高于alpha通道。我觉得开罗被那些超级发光的像素呛住了。
因此,不要使用此代码:
if ((z & 0xffffff) == 0xffffff) { z = (z & ~0xff000000) | (alpha & 0xff000000); *((unsigned int*) &ptr [y * stride + x * 4]) = z;}
您应该尝试以下操作:
if ((z & 0xffffff) == 0xffffff) { *((unsigned int*) &ptr [y * stride + x * 4]) = 0;}
当我们在做的时候:
不检查绿色、蓝色和alpha通道是否都是100%并且忽略红色通道吗?你确定那是你真正想要的吗?(z & 0xffffff) == 0xffffff将是不透明的白色。
与其使用z == 0xffffffff,不如使用unsigned int来访问像素数据。便携性!
您的代码假设uint32_t总是为您提供格式为ARGB32的图像表面。我认为这不一定总是正确的,例如RGB24也是可能的。
我想我应该这样做:
cairo_image_surface_create_from_png()

10-06 07:05