如何在GTK4的Cairo Surface上实现CSS径向渐变绘制并保存PNG?
GTK4 保存GtkDrawingArea的CSS渐变内容为PNG的问题修复
原代码的核心问题分析
- CSS Provider添加时机错误:在
draw_function中每次绘制都重复添加CSS Provider,既冗余又不符合GTK样式管理逻辑,应在widget初始化阶段一次性设置样式。 - 点击事件中的快照逻辑错误:
- 未初始化
graphene_rect_t就传入gsk_cairo_node_new,导致无效的矩形区域。 - 错误混用
GdkSnapshot和GskRenderNode,正确保存方式是直接创建cairo surface,将widget的paintable绘制到该surface。
- 未初始化
- 变量声明语法错误:手势变量声明时缺少变量名,导致编译失败。
修正后的完整代码
#include <gtk/gtk.h> // 初始化CSS样式,在widget创建时调用一次 static void setup_darea_style(GtkWidget *darea) { GtkCssProvider* provider = gtk_css_provider_new(); gtk_css_provider_load_from_data(provider, "#darea { border-radius: 50%;" " background: radial-gradient(#FFFFFF, #000000); " "}", -1); gtk_style_context_add_provider(gtk_widget_get_style_context(darea), GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); g_object_unref(provider); } /*save as png on click*/ void click_event(GtkEventController *gesture, gdouble x, gdouble y, gpointer user_data) { GtkWidget *darea = GTK_WIDGET(user_data); int width = gtk_widget_get_allocated_width(darea); int height = gtk_widget_get_allocated_height(darea); // 创建对应尺寸的cairo surface cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); cairo_t *cr = cairo_create(surface); // 将widget的paintable绘制到cairo上下文 GdkPaintable *paintable = gtk_widget_paintable_new(darea); gdk_paintable_draw(paintable, cr, 0, 0, width, height); g_object_unref(paintable); // 保存为PNG并清理资源 cairo_surface_write_to_png(surface, "blur.png"); cairo_destroy(cr); cairo_surface_destroy(surface); } static void activate(GtkApplication* app, gpointer user_data) { GtkWidget *window = gtk_application_window_new(app); gtk_window_set_title(GTK_WINDOW(window), "Window"); gtk_window_set_default_size(GTK_WINDOW(window), 350, 350); GtkWidget *darea = gtk_drawing_area_new(); gtk_widget_set_name(darea, "darea"); gtk_drawing_area_set_content_width(GTK_DRAWING_AREA(darea), 350); gtk_drawing_area_set_content_height(GTK_DRAWING_AREA(darea), 350); // 初始化样式(移除原draw_func,因为CSS已经处理了背景) setup_darea_style(darea); gtk_window_set_child(GTK_WINDOW(window), darea); // 创建点击手势(修复变量声明错误) GtkGesture *gesture = gtk_gesture_click_new(); gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(gesture), 0); gtk_widget_add_controller(darea, GTK_EVENT_CONTROLLER(gesture)); g_signal_connect(gesture, "pressed", G_CALLBACK(click_event), darea); gtk_window_present(GTK_WINDOW(window)); } int main(int argc, char **argv) { GtkApplication *app = gtk_application_new("org.gtk.example", G_APPLICATION_DEFAULT_FLAGS); g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); int status = g_application_run(G_APPLICATION(app), argc, argv); g_object_unref(app); return status; }
关键修正点说明
- 样式初始化优化:将CSS Provider的添加移到单独的
setup_darea_style函数,在widget创建时调用一次,避免重复添加。同时移除原draw_function,因为CSS已负责绘制背景,无需在draw回调中额外处理。 - 点击事件逻辑重构:直接创建对应widget尺寸的cairo image surface,通过
gdk_paintable_draw将widget内容绘制到该surface,再保存为PNG,逻辑简洁且避免了原代码中的无效指针问题。 - 修复语法错误:修正手势变量的声明问题,确保编译通过。
内容的提问来源于stack exchange,提问作者dibyendu
相关产品推荐
相关产品推荐

