绘画时不会反映对图像表面的影响

时间:2014-03-20 17:34:44

标签: c image gtk cairo

我有一个小代码片段,它从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 - 实际结果
  • bg.png - 源图像,由于stackoverflow限制而被省略,它只是白色背景上的黑色圆角矩形。预期结果是黑色半透明矩形和完全透明的字段,而不是白色,如demo.png上的这些字段。

1 个答案:

答案 0 :(得分:4)

将alpha设置为0表示颜色完全透明。由于cairo使用预乘的alpha,因此必须将像素设置为0,否则颜色分量的值可能高于alpha通道。我认为cairo会对那些超级发光的像素感到窒息。

所以代替这段代码: 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; } 虽然我们在这里:

  • (z & 0xffffff) == 0xffffff检查绿色,蓝色和Alpha通道是否全部为100%并忽略红色通道?你确定这真的是你想要的吗? z == 0xffffffff将是不透明的白色。
  • 如果您使用unsigned int来访问像素数据,那么最好不要使用uint32_t。便携!
  • 您的代码假定cairo_image_surface_create_from_png()始终为您提供格式为ARGB32的图像表面。我认为这不一定总是正确的,例如RGB24也是可能的。

我想我会这样做: for (y = 0; y < height; y++) { uint32_t row = (uint32_t *) &ptr[y * stride]; for (x = 0; x < width; x++) { uint32_t px = row[x]; if (is_expected_color(px)) row[x] = 0; } }