Cairo图形库如何在两个Xlib surface间拷贝传输区域数据
Cairo实现X11窗口区域拷贝的方案
首选方案:纯Cairo直接拷贝(无中转,性能最优)
不需要借助image surface做中转,Cairo原生支持直接在两个Xlib surface之间做区域拷贝,会优先走X11的硬件加速拷贝路径,没有CPU侧像素搬运的开销:
- 基于目标Xlib surface创建Cairo绘图上下文
- 设置源surface为待拷贝的源Xlib surface,通过坐标偏移对齐源和目标的拷贝位置
- 限定绘制区域为需要拷贝的矩形,执行填充完成拷贝
- 提交绘制指令确保内容显示
参考代码:
// 为目标surface创建绘图上下文 cairo_t *cr = cairo_create(target_xlib_surface); // 配置源和坐标偏移:让源的(xs, ys)点对齐目标的(xd, yd)点 cairo_set_source_surface(cr, source_xlib_surface, xd - xs, yd - ys); // 仅绘制目标位置、指定尺寸的矩形,避免多余拷贝 cairo_rectangle(cr, xd, yd, ws, hs); cairo_fill(cr); // 提交绘制指令到X服务,确保内容可见 cairo_surface_flush(target_xlib_surface); // 释放上下文 cairo_destroy(cr);
方案二:手动创建image surface中转
如果需要在拷贝过程中对像素做CPU侧处理(比如读取像素值、添加特效),可以通过image surface做中转,流程分为两步:
- 将源surface的指定区域绘制到临时image surface
- 将image surface的内容绘制到目标surface的指定位置
参考代码:
// 创建和待拷贝区域同尺寸的临时image surface cairo_surface_t *tmp_img = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, ws, hs); cairo_t *cr = cairo_create(tmp_img); // 偏移坐标,让源的(xs, ys)对齐临时surface的(0,0)原点 cairo_set_source_surface(cr, source_xlib_surface, -xs, -ys); cairo_paint(cr); cairo_destroy(cr); cairo_surface_flush(tmp_img); // 此处可以对tmp_img的像素数据做自定义CPU处理 // unsigned char *pixel_data = cairo_image_surface_get_data(tmp_img); // 将临时surface绘制到目标位置 cr = cairo_create(target_xlib_surface); cairo_set_source_surface(cr, tmp_img, xd, yd); cairo_paint(cr); cairo_destroy(cr); cairo_surface_flush(target_xlib_surface); // 释放临时资源 cairo_surface_destroy(tmp_img);
该方案会触发X服务端到CPU内存、CPU内存到X服务端两次像素拷贝,性能远低于直接拷贝方案,无特殊像素处理需求不建议使用。
方案三:通过cairo_surface_map_to_image()映射拷贝
该接口可以直接将Xlib surface的指定区域映射为可读写的image surface,不需要手动创建临时surface,本质还是CPU侧拷贝,性能和方案二接近,适合需要临时读写surface像素的场景:
参考代码:
// 定义源、目标的待操作区域 cairo_rectangle_int_t src_region = {.x = xs, .y = ys, .width = ws, .height = hs}; cairo_rectangle_int_t dst_region = {.x = xd, .y = yd, .width = ws, .height = hs}; // 映射两个区域为image surface cairo_surface_t *mapped_src = cairo_surface_map_to_image(source_xlib_surface, &src_region); cairo_surface_t *mapped_dst = cairo_surface_map_to_image(target_xlib_surface, &dst_region); // 在两个映射出的image surface之间完成拷贝 cairo_t *cr = cairo_create(mapped_dst); cairo_set_source_surface(cr, mapped_src, 0, 0); cairo_paint(cr); cairo_destroy(cr); // 必须执行unmap,修改才会同步回原Xlib surface cairo_surface_unmap_image(target_xlib_surface, mapped_dst); cairo_surface_unmap_image(source_xlib_surface, mapped_src); cairo_surface_flush(target_xlib_surface);
关于X11原生接口的选择
如果你的业务逻辑完全不涉及Cairo绘制,只需要做窗口内容拷贝,直接调用X11原生的XCopyArea()性能最高。但如果你已经基于Cairo实现了其他绘制逻辑,优先选第一种纯Cairo直接拷贝方案即可,混用X11原生接口可能导致Cairo和X服务的状态不同步,出现显示异常。
注意:X11默认不保存窗口被遮挡区域的像素内容,如果源窗口被其他窗口遮挡、或者超出屏幕边界,拷贝得到的内容会是未定义的无效数据,这是X11的窗口机制决定的,和Cairo无关。
内容的提问来源于stack exchange,提问作者Ralf
相关产品推荐
相关产品推荐

