Cairo在将渲染输出到Xlib表面时,透明区域会显示垃圾像素吗?
我正在尝试使用Cairo在一个窗口中渲染一张基本图像。我先把窗口设好,然后像往常一样把图像绘制到窗口上:
#include <threads.h>
#include <time.h>
#include <X11/Xlib.h>
#include <cairo.h>
#include <cairo-xlib.h>
constexpr int start_width = 192;
constexpr int start_height = 192;
Display *_Atomic display = nullptr;
_Atomic bool have_been_killed = false;
constexpr double button_size = 192;
constexpr double button_thickness = 8;
constexpr cairo_rectangle_t button_extents = {.x = 0, .y = 0, .width = button_size, .height = button_size};
cairo_surface_t *drawButton() {
cairo_surface_t *surface = cairo_recording_surface_create(CAIRO_CONTENT_COLOR_ALPHA, &button_extents);
cairo_status_t status = cairo_surface_status(surface);
if (status != CAIRO_STATUS_SUCCESS) {
return surface;
}
cairo_t *ctx = cairo_create(surface);
status = cairo_status(ctx);
if (status != CAIRO_STATUS_SUCCESS) {
goto done;
}
cairo_set_source_rgba(ctx, 0.306, 0.518, 0.518, 0.506);
constexpr double lt = button_thickness / 2;
constexpr double rt = button_size - button_thickness / 2;
constexpr double tp = lt;
constexpr double bm = rt;
constexpr double mx = button_size / 2;
constexpr double my = mx;
cairo_set_line_width(ctx, button_thickness);
cairo_move_to(ctx, lt, my);
cairo_curve_to(ctx, lt, tp, lt, tp, mx, tp);
cairo_curve_to(ctx, rt, tp, rt, tp, rt, my);
cairo_curve_to(ctx, rt, bm, rt, bm, mx, bm);
cairo_curve_to(ctx, lt, bm, lt, bm, lt, my);
cairo_close_path(ctx);
cairo_stroke(ctx);
done:
cairo_destroy(ctx);
return surface;
}
int guiThread(void *data);
int main() {
display = XOpenDisplay(nullptr);
if (!display) {
return 1;
}
const Window root_window = XDefaultRootWindow(display);
if (!root_window) {
return 1;
}
Window main_window = XCreateWindow(
display, root_window,
100, 100, start_width, start_height, 0,
CopyFromParent, CopyFromParent, CopyFromParent,
0, nullptr
);
XWindowAttributes main_window_attributes;
if (!XGetWindowAttributes(display, main_window, &main_window_attributes)) {
return 1;
}
Atom WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", true);
if(!XSetWMProtocols(display, main_window, &WM_DELETE_WINDOW, 1)) {
return 1;
}
cairo_surface_t *surface = cairo_xlib_surface_create(display, main_window, main_window_attributes.visual, start_width, start_height);
cairo_status_t status = cairo_surface_status(surface);
if (status != CAIRO_STATUS_SUCCESS) {
return 1;
}
thrd_t gui_thread;
if (thrd_create(&gui_thread, guiThread, surface) != thrd_success) {
return 1;
}
XMapWindow(display, main_window);
XFlush(display);
while (!have_been_killed) {
XEvent event;
XNextEvent(display, &event);
switch (event.type) {
default:
if (event.xany.window != main_window) {
continue;
}
case KeymapNotify:
case GenericEvent:
}
switch (event.type) {
case ClientMessage: {
XClientMessageEvent e = event.xclient;
if ((Atom)e.data.l[0] == WM_DELETE_WINDOW) {
have_been_killed = true;
}
break;
}
default:
}
}
thrd_join(gui_thread, nullptr);
XCloseDisplay(display);
return 0;
}
int guiThread(void *data) {
cairo_surface_t *surface = data;
cairo_surface_t *button = drawButton();
cairo_t *ctx = cairo_create(surface);
while (!have_been_killed) {
cairo_set_source_rgb(ctx, 0, 0, 0);
cairo_paint(ctx);
cairo_set_source_surface(ctx, button, 0, 0);
cairo_paint(ctx);
cairo_surface_flush(surface);
XFlush(display);
thrd_sleep(&(struct timespec){.tv_nsec = 10'000'000}, nullptr);
}
cairo_destroy(ctx);
}
然而,在透明区域里,本应显示透明的地方,却绘制出垃圾像素(癫痫警告):
https://i.imgur.com/WKCd1Kg.mp4
此外,尽管垃圾像素通常来自读取不应该读取的内存,但使用 -fsanitize=address 编译并不会暴露任何问题。若把绘制目标改成像SVG表面这样的表面而不是窗口,它也不会这样;若改用图像表面而不是记录表面来绘制,也不会这样。
我希望这不会因为展示这些垃圾像素而暴露我的身份信息。为什么会这样?我应该如何解决?
解决方案
原来你并没有覆盖所有像素。你很可能需要这样做:
// in your button drawing code
cairo_t *b = cairo_create(btn);
cairo_set_source_rgb(b, 0, 0, 0);
cairo_rectangle(b, 0, 0, WIDTH, HEIGHT); // create the background!
cairo_fill(b); // fill it!
cairo_set_source_rgba(b, 0.306, 0.518, 0.518, 0.506);
// continue drawing button
// in your x11 paint code
/* paint */
cairo_set_source_surface(ctx, btn, 0, 0);
cairo_paint(ctx);
// That's it
我还建议在Expose事件到来时再进行绘制,而不是每隔X 毫秒(那会浪费资源)。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。