如何清除Cairo填充图形?实时鼠标选框绘制叠加问题求助
解决Cairo绘制选框时图形叠加的问题
你的问题核心是每次鼠标移动都直接在根窗口表面绘制新矩形,且没有清除之前的绘制内容,同时重复创建surface和上下文导致资源浪费,最终造成图形叠加。以下是修复方案:
问题根源
- 每次
MotionNotify事件都重新创建cairo_surface_t和cairo_t,旧的绘制内容会保留在根窗口上 - 没有处理选框坐标的正负值(比如鼠标向左/向上拖动时,宽度/高度会为负)
- 缺少背景恢复机制,无法清除上一帧的选框
修复后的代码实现
首先在全局或初始化区域声明必要变量:
#include <X11/Xlib.h> #include <cairo.h> #include <cairo-xlib.h> #include <stdlib.h> #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) Display *xdisplay; Window xroot; cairo_surface_t *surface = NULL; cairo_t *cr = NULL; Pixmap bg_pixmap = None; int old_x1, old_y1, old_x2, old_y2; bool isPressed = false; int init_x, init_y;
初始化时创建一次surface和上下文(不要在事件循环里重复创建):
xdisplay = XOpenDisplay(NULL); xroot = DefaultRootWindow(xdisplay); int scr = DefaultScreen(xdisplay); surface = cairo_xlib_surface_create(xdisplay, xroot, DefaultVisual(xdisplay, scr), DisplayWidth(xdisplay, scr), DisplayHeight(xdisplay, scr)); cr = cairo_create(surface);
修改鼠标事件处理逻辑:
// 处理鼠标按下事件(记录初始坐标) if (xevent.type == ButtonPress && xevent.xbutton.button == Button1) { isPressed = true; init_x = xevent.xbutton.x_root; init_y = xevent.xbutton.y_root; old_x1 = old_x2 = init_x; old_y1 = old_y2 = init_y; } // 处理鼠标移动事件(绘制选框) if (xevent.type == MotionNotify && isPressed == true) { int tmp_x = xevent.xmotion.x_root; int tmp_y = xevent.xmotion.y_root; // 计算选框的有效坐标(确保宽高为正) int x1 = MIN(init_x, tmp_x); int y1 = MIN(init_y, tmp_y); int x2 = MAX(init_x, tmp_x); int y2 = MAX(init_y, tmp_y); // 清除上一次的选框:恢复保存的背景 if (bg_pixmap != None) { XCopyArea(xdisplay, bg_pixmap, xroot, DefaultGC(xdisplay, scr), 0, 0, old_x2 - old_x1, old_y2 - old_y1, old_x1, old_y1); XFreePixmap(xdisplay, bg_pixmap); bg_pixmap = None; } // 保存当前选框区域的背景到Pixmap bg_pixmap = XCreatePixmap(xdisplay, xroot, x2 - x1, y2 - y1, DefaultDepth(xdisplay, scr)); XCopyArea(xdisplay, xroot, bg_pixmap, DefaultGC(xdisplay, scr), x1, y1, x2 - x1, y2 - y1, 0, 0); // 绘制新选框 cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1); cairo_set_source_rgba(cr, 0.3, 0.4, 0.6, 0.1); cairo_fill(cr); cairo_surface_flush(surface); // 强制刷新到屏幕 // 保存当前选框坐标,用于下次清除 old_x1 = x1; old_y1 = y1; old_x2 = x2; old_y2 = y2; } // 处理鼠标释放事件(清理资源) if (xevent.type == ButtonRelease && xevent.xbutton.button == Button1) { isPressed = false; if (bg_pixmap != None) { XCopyArea(xdisplay, bg_pixmap, xroot, DefaultGC(xdisplay, scr), 0, 0, old_x2 - old_x1, old_y2 - old_y1, old_x1, old_y1); XFreePixmap(xdisplay, bg_pixmap); bg_pixmap = None; } // 这里可以添加选框完成后的逻辑,比如获取最终选框坐标 }
程序退出时清理资源:
cairo_destroy(cr); cairo_surface_destroy(surface); XCloseDisplay(xdisplay);
关键改进点
- 复用上下文和surface:只初始化一次,避免重复创建导致的资源泄漏和绘制叠加
- 背景恢复机制:用
XCopyArea保存选框区域的原始背景,绘制新选框前先恢复旧背景,彻底清除上一帧的选框 - 坐标合法性处理:通过
MIN/MAX确保选框的宽高为正,避免绘制异常
内容的提问来源于stack exchange,提问作者XoDefender
相关产品推荐
相关产品推荐

