You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在X11中使用XCB绘制带透明色的图像?

解决XCB绘制PNG透明失效的问题

xcb_copy_area本身不支持直接处理alpha通道,直接复制带透明的Pixmap会忽略透明效果。以下提供两种可行的实现方案:

方案一:使用蒙版实现完全透明

这种方法适合仅需完全透明/不透明的场景,通过创建Alpha蒙版Pixmap,将其设置为GC的裁剪遮罩,过滤透明区域。

步骤说明

  1. 从PNG数据中分离RGB颜色通道和Alpha通道数据
  2. 创建两个Pixmap:一个存储RGB颜色,一个存储Alpha蒙版(8位深度)
  3. 将Alpha蒙版绑定到GC的clip_mask属性,指定裁剪原点后执行复制

修改后的代码示例

// load PNG(保留原有加载代码)
int w, h;
int comp;
uint8_t* data = stbi_load_from_memory((stbi_uc const *) df.data.data(), (int) df.data.length(),
                                      &w, &h, &comp, 4);

// organize pixels(保留原有像素格式转换代码)
unsigned* dp = (unsigned*) data;
for(size_t i = 0, len = w * h; i < len; i++) {
    dp[i] = dp[i] & 0xff00ff00 | ((dp[i] >> 16) & 0xFF) | ((dp[i] << 16) & 0xFF0000);
}

// -------------------------- 新增蒙版处理逻辑 --------------------------
// 分离RGB颜色数据和Alpha通道数据
uint8_t* rgb_data = (uint8_t*)malloc(w * h * 3);
uint8_t* alpha_data = (uint8_t*)malloc(w * h);
for (size_t i = 0; i < w * h; ++i) {
    uint32_t pixel = dp[i];
    // 提取BGRA中的B/G/R通道(匹配之前的格式转换)
    rgb_data[i*3 + 0] = (pixel >> 16) & 0xFF; // B
    rgb_data[i*3 + 1] = (pixel >> 8) & 0xFF;  // G
    rgb_data[i*3 + 2] = pixel & 0xFF;         // R
    alpha_data[i] = (pixel >> 24) & 0xFF;     // A
}

// 创建颜色Pixmap(使用屏幕根窗口深度)
xcb_pixmap_t color_pix = xcb_generate_id(dpy_);
xcb_create_pixmap(dpy_, scr_->root_depth, color_pix, scr_->root, w, h);

// 创建Alpha蒙版Pixmap(8位灰度深度)
xcb_pixmap_t alpha_pix = xcb_generate_id(dpy_);
xcb_create_pixmap(dpy_, 8, alpha_pix, scr_->root, w, h);

// 将RGB数据写入颜色Pixmap
xcb_gcontext_t gc_color = xcb_generate_id(dpy_);
xcb_create_gc(dpy_, gc_color, color_pix, 0, nullptr);
xcb_image_t* color_image = xcb_image_create_native(dpy_, w, h, XCB_IMAGE_FORMAT_Z_PIXMAP, scr_->root_depth, rgb_data, w*h*3, rgb_data);
xcb_image_put(dpy_, color_pix, gc_color, color_image, 0, 0, 0);
xcb_image_destroy(color_image);
xcb_free_gc(dpy_, gc_color);

// 将Alpha数据写入蒙版Pixmap
xcb_gcontext_t gc_alpha = xcb_generate_id(dpy_);
xcb_create_gc(dpy_, gc_alpha, alpha_pix, 0, nullptr);
xcb_image_t* alpha_image = xcb_image_create_native(dpy_, w, h, XCB_IMAGE_FORMAT_Z_PIXMAP, 8, alpha_data, w*h, alpha_data);
xcb_image_put(dpy_, alpha_pix, gc_alpha, alpha_image, 0, 0, 0);
xcb_image_destroy(alpha_image);
xcb_free_gc(dpy_, gc_alpha);

// 创建带蒙版的GC,设置裁剪遮罩和原点
xcb_gcontext_t gc_masked = xcb_generate_id(dpy_);
uint32_t gc_vals[] = {
    XCB_GC_CLIP_MASK, alpha_pix,
    XCB_GC_CLIP_ORIGIN_X, 50,
    XCB_GC_CLIP_ORIGIN_Y, 50
};
xcb_create_gc(dpy_, gc_masked, id, sizeof(gc_vals)/sizeof(gc_vals[0]), gc_vals);

// 应用蒙版复制Pixmap到窗口
xcb_copy_area(dpy_, color_pix, id, gc_masked, 0, 0, 50, 50, w, h);
xcb_flush(dpy_);

// 清理资源
xcb_free_gc(dpy_, gc_masked);
xcb_free_pixmap(dpy_, color_pix);
xcb_free_pixmap(dpy_, alpha_pix);
free(rgb_data);
free(alpha_data);
free(data); // 释放stbi加载的内存

方案二:使用XRender扩展实现半透明

如果需要支持半透明(alpha通道中间值),必须使用XRender扩展的复合绘制功能,这是X11处理alpha混合的标准方式。

步骤说明

  1. 将PNG的BGRA数据写入32位深度的Pixmap
  2. 查询X服务器支持的ARGB32格式,创建对应的Picture对象
  3. 使用xcb_render_composite以OVER模式绘制,自动处理alpha混合

代码示例

// load PNG(保留原有加载和格式转换代码)
int w, h;
int comp;
uint8_t* data = stbi_load_from_memory((stbi_uc const *) df.data.data(), (int) df.data.length(),
                                      &w, &h, &comp, 4);

unsigned* dp = (unsigned*) data;
for(size_t i = 0, len = w * h; i < len; i++) {
    dp[i] = dp[i] & 0xff00ff00 | ((dp[i] >> 16) & 0xFF) | ((dp[i] << 16) & 0xFF0000);
}
uint8_t* bgra_data = (uint8_t*)dp;

// -------------------------- XRender绘制逻辑 --------------------------
// 创建32位深度的Pixmap存储BGRA数据
xcb_pixmap_t src_pix = xcb_generate_id(dpy_);
xcb_create_pixmap(dpy_, 32, src_pix, scr_->root, w, h);

xcb_gcontext_t gc = xcb_generate_id(dpy_);
xcb_create_gc(dpy_, gc, src_pix, 0, nullptr);
xcb_image_t* image = xcb_image_create_native(dpy_, w, h, XCB_IMAGE_FORMAT_Z_PIXMAP, 32, bgra_data, w*h*4, bgra_data);
xcb_image_put(dpy_, src_pix, gc, image, 0, 0, 0);
xcb_image_destroy(image);
xcb_free_gc(dpy_, gc);

// 查询X服务器支持的ARGB32格式
xcb_render_pictformat_t argb_fmt = XCB_RENDER_PICT_FORMAT_NONE;
xcb_render_query_pict_formats_reply_t* formats = xcb_render_query_pict_formats_reply(dpy_, xcb_render_query_pict_formats(dpy_), nullptr);
xcb_render_pictformat_iterator_t fmt_iter = xcb_render_query_pict_formats_formats_iterator(formats);
for (; fmt_iter.rem; xcb_render_pictformat_next(&fmt_iter)) {
    xcb_render_pictformat_info_t* info = xcb_render_query_pict_format_reply(dpy_, xcb_render_query_pict_format(dpy_, fmt_iter.data->id), nullptr);
    if (info && info->type == XCB_RENDER_PICT_TYPE_DIRECT && info->depth == 32) {
        // 匹配ARGB32格式(根据像素格式调整mask)
        if (info->direct.alpha_mask == 0xFF000000 &&
            info->direct.red_mask == 0x00FF0000 &&
            info->direct.green_mask == 0x0000FF00 &&
            info->direct.blue_mask == 0x000000FF) {
            argb_fmt = fmt_iter.data->id;
            free(info);
            break;
        }
    }
    free(info);
}
free(formats);

if (argb_fmt == XCB_RENDER_PICT_FORMAT_NONE) {
    // 处理格式不支持的情况
    xcb_free_pixmap(dpy_, src_pix);
    free(data);
    return;
}

// 创建源Picture和目标窗口Picture
xcb_render_picture_t src_pic = xcb_generate_id(dpy_);
uint32_t src_pic_vals[] = { XCB_RENDER_CP_PICT_FORMAT, argb_fmt };
xcb_render_create_picture(dpy_, src_pic, src_pix, argb_fmt, sizeof(src_pic_vals)/sizeof(src_pic_vals[0]), src_pic_vals);

xcb_render_picture_t dst_pic = xcb_generate_id(dpy_);
xcb_render_create_picture(dpy_, dst_pic, id, XCB_RENDER_PICT_FORMAT_NONE, 0, nullptr);

// 使用OVER模式复合绘制,自动处理alpha混合
xcb_render_composite(dpy_, XCB_RENDER_PICT_OP_OVER, src_pic, XCB_NONE, dst_pic,
                     0, 0, 0, 0, 50, 50, w, h);
xcb_flush(dpy_);

// 清理资源
xcb_render_free_picture(dpy_, src_pic);
xcb_render_free_picture(dpy_, dst_pic);
xcb_free_pixmap(dpy_, src_pix);
free(data);

注意事项

  • 使用XRender前需确保X服务器支持该扩展(大部分现代桌面环境都支持)
  • 像素格式的mask需要和PNG数据格式对应,若转换的是BGRA,需调整mask匹配

内容的提问来源于stack exchange,提问作者André Wagner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 17:44:59