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

如何在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;
}

关键修正点说明

  1. 样式初始化优化:将CSS Provider的添加移到单独的setup_darea_style函数,在widget创建时调用一次,避免重复添加。同时移除原draw_function,因为CSS已负责绘制背景,无需在draw回调中额外处理。
  2. 点击事件逻辑重构:直接创建对应widget尺寸的cairo image surface,通过gdk_paintable_draw将widget内容绘制到该surface,再保存为PNG,逻辑简洁且避免了原代码中的无效指针问题。
  3. 修复语法错误:修正手势变量的声明问题,确保编译通过。

内容的提问来源于stack exchange,提问作者dibyendu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 14:32:20