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

从Gtk2Hs迁移至gi-gtk(GTK3):如何在GLArea上用Cairo绘图?

在GTK3的GLArea上用Cairo绘图的解决方案

在GTK3中,GLArea的渲染模型和GTK2的GLDrawingArea完全不同,原来依赖DrawWindow的方式不再适用。要在GLArea上结合Cairo绘图,需要通过GDK的Cairo-OpenGL桥接接口创建关联OpenGL上下文的Cairo Surface,具体步骤和示例如下:

核心原理

GLArea的渲染依赖OpenGL上下文,要让Cairo在上面绘图,必须创建一个绑定到当前OpenGL帧缓冲区的Cairo GL Surface,通过这个Surface调用Cairo API完成绘制后,再将内容同步到GLArea的OpenGL上下文。

C语言示例代码

#include <gtk/gtk.h>
#include <cairo/cairo.h>
#include <gdk/gdkgl.h>

static gboolean on_glarea_render(GtkGLArea *area, GdkGLContext *context) {
    // 1. 先完成OpenGL基础渲染(如不需要可跳过)
    glClearColor(0.1, 0.1, 0.1, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    // 2. 创建Cairo GL Surface,绑定到当前OpenGL帧缓冲区
    cairo_surface_t *surface = gdk_cairo_gl_surface_create(context, 
                                                           gtk_gl_area_get_width(area), 
                                                           gtk_gl_area_get_height(area));
    if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
        g_warning("Failed to create Cairo GL surface");
        cairo_surface_destroy(surface);
        return TRUE;
    }

    // 3. 创建Cairo上下文并执行绘图
    cairo_t *cr = cairo_create(surface);
    // 绘制红色矩形示例
    cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
    cairo_rectangle(cr, 50, 50, 200, 150);
    cairo_fill(cr);

    // 4. 清理Cairo资源
    cairo_destroy(cr);
    cairo_surface_destroy(surface);

    return TRUE;
}

int main(int argc, char *argv[]) {
    gtk_init(&argc, &argv);

    // 检查系统是否支持OpenGL
    if (!gdk_display_gl_context_is_supported(gdk_display_get_default())) {
        g_error("OpenGL not supported");
        return 1;
    }

    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_default_size(GTK_WINDOW(window), 300, 250);
    g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);

    GtkWidget *glarea = gtk_gl_area_new();
    gtk_container_add(GTK_CONTAINER(window), glarea);
    // 绑定render信号处理函数
    g_signal_connect(glarea, "render", G_CALLBACK(on_glarea_render), NULL);

    gtk_widget_show_all(window);
    gtk_main();

    return 0;
}

Haskell (gi-gtk) 对应实现思路

在Haskell的gi-gtk中,需要调用对应的GI绑定函数完成流程:

  1. 绑定GLArea的render信号,处理函数接收GLArea和GLContext参数
  2. 使用GI.Gdk.Functions.cairoGlSurfaceCreate创建Cairo GL Surface
  3. 通过GI.Cairo.Functions.create创建Cairo上下文
  4. 调用Cairo绘图函数完成绘制
  5. 销毁上下文和Surface,完成渲染

关键代码片段示例:

import qualified GI.Gtk as Gtk
import qualified GI.Gdk as Gdk
import qualified GI.Cairo as Cairo
import Control.Monad.IO.Class (liftIO)
import Graphics.Rendering.OpenGL (glClearColor, glClear, ColorBufferBit(..))

onRender :: Gtk.GLArea -> Gdk.GLContext -> IO Bool
onRender area context = do
  -- 基础OpenGL清屏操作
  liftIO $ glClearColor 0.1 0.1 0.1 1.0
  liftIO $ glClear [ColorBufferBit]

  -- 创建Cairo GL Surface
  width <- Gtk.widgetGetWidth area
  height <- Gtk.widgetGetHeight area
  surface <- Gdk.cairoGlSurfaceCreate context width height
  status <- Cairo.surfaceStatus surface
  if status /= Cairo.StatusSuccess
    then do
      putStrLn "Failed to create Cairo GL surface"
      Cairo.surfaceDestroy surface
      return True
    else do
      -- 创建Cairo上下文并绘制红色矩形
      cr <- Cairo.create surface
      Cairo.setSourceRgb cr 1.0 0.0 0.0
      Cairo.rectangle cr 50 50 200 150
      Cairo.fill cr

      -- 清理资源
      Cairo.destroy cr
      Cairo.surfaceDestroy surface
      return True

-- 初始化时绑定信号
initGLArea :: IO Gtk.GLArea
initGLArea = do
  glarea <- Gtk.glAreaNew
  Gtk.on glarea #render onRender
  return glarea

注意事项

  • 必须确保系统支持OpenGL和Cairo GL扩展,否则cairoGlSurfaceCreate会失败
  • 在render信号处理函数中,OpenGL和Cairo操作要按顺序执行,避免上下文冲突
  • 如果需要混合OpenGL渲染和Cairo绘图,Cairo绘制内容会覆盖在OpenGL渲染结果之上

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 11:25:03