矩形选择
本来是灵机一动,考虑在 gtkglext widget 上使用 cairo 在窗口中动态绘制一个矩形框表示所选的 OpenGL 三空间中的局部区域,然后将这个矩形框映射到 OpenGL 三维空间中得到三维选区,并将 OpenGL 的视景体调整为这个三维选区,从而实现 OpenGL 图形的放大效果。可惜,这个灵机一动没成功,cairo 所绘制的矩形框被 gtkglext 的双缓冲区里的图形淹没了。
尽管如此,我还是要将这个 cairo 效果拿出来展展,用于揭示虽然我一直都喜欢 gtk+,但是这实际上是我第一个具有现实意义的 gtk+ 程序 :)
为了简单起见,程序写的很难看,还借用了当初我翻译的那个『cairo 图形指南』里的一份示例:
#include <gtk/gtk.h> typedef struct _Rectangle Rectangle; struct _Rectangle { gdouble v1[2]; gdouble v2[2]; }; Rectangle rect = { {0.0, 0.0}, {0.0, 0.0} }; static gboolean expose (GtkWidget * widget, cairo_t *cr, gpointer data) { /* 这段代码就是为了在窗口区域绘制一些文字,主要是为了显摆矩形选框的 */ cairo_set_source_rgb (cr, 0.1, 0.1, 0.1); cairo_select_font_face (cr, "Bitstream Vera Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD); cairo_set_font_size (cr, 13); cairo_move_to (cr, 20, 30); cairo_show_text (cr, "Most relationships seem so transitory"); cairo_move_to (cr, 20, 60); cairo_show_text (cr, "They're all good but not the permanent one"); cairo_move_to (cr, 20, 120); cairo_show_text (cr, "Who doesn't long for someone to hold"); cairo_move_to (cr, 20, 150); cairo_show_text (cr, "Who knows how to love you without being told"); cairo_move_to (cr, 20, 180); cairo_show_text (cr, "Somebody tell me why I'm on my own"); cairo_move_to (cr, 20, 210); cairo_show_text (cr, "If there's a soulmate for everyone"); /* 绘制矩形选框 */ cairo_rectangle (cr, rect.v1[0], rect.v1[1], rect.v2[0] - rect.v1[0], rect.v2[1] - rect.v1[1]); cairo_set_line_width (cr, 2); cairo_set_source_rgba (cr, 0.6, 0.4, 0.2, 1.0); cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND); cairo_stroke_preserve (cr); cairo_set_source_rgba (cr, 0.6, 0.4, 0.2, 0.5); cairo_fill (cr); return FALSE; } static gboolean clicked (GtkWidget * widget, GdkEventButton * event, gpointer user_data) { if (event->button == 1) { rect.v1[0] = event->x; rect.v1[1] = event->y; } return FALSE; } static gboolean motion (GtkWidget * widget, GdkEventMotion * event, gpointer data) { if (event->state & GDK_BUTTON1_MASK) { rect.v2[0] = event->x; rect.v2[1] = event->y; gtk_widget_queue_draw (widget); } return FALSE; } static gboolean release (GtkWidget * widget, GdkEventMotion * event, gpointer data) { rect.v1[0] = rect.v1[1] = rect.v2[0] = rect.v2[1] = 0.0; gtk_widget_queue_draw (widget); return FALSE; } int main (int argc, char *argv[]) { GtkWidget *window; gtk_init (&argc, &argv); window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_widget_add_events (window, GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_SCROLL_MASK | GDK_BUTTON_RELEASE_MASK); g_signal_connect (window, "draw", G_CALLBACK (expose), NULL); g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL); g_signal_connect (window, "button-press-event", G_CALLBACK (clicked), NULL); g_signal_connect (window, "motion_notify_event", G_CALLBACK (motion), NULL); g_signal_connect (window, "button-release-event", G_CALLBACK (release), NULL); gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER); gtk_window_set_title (GTK_WINDOW (window), "test"); gtk_window_set_default_size (GTK_WINDOW (window), 420, 250); gtk_widget_set_app_paintable (window, TRUE); gtk_widget_show_all (window); gtk_main (); return 0; }
用这个命令可以编译这个程序(前提是安装了 gcc 和 gtk+):
$ gcc `pkg-config --cflags --libs gtk+-3.0` test.c -o test
注意,这里我使用的是 gtk+ 3.0。所有的窗口图形绘制工作均交给了 cairo。
由于 cairo 不给力,所以我不得不暗自将 gtk+ 窗口中的鼠标位置映射到 gtkglext 所营造的 OpenGL 场景中,从而构造一个三维平面来实现与上例相似的效果 \footnote{代码很繁琐,不贴了,也别向我要 :) }:
转载时,希望不要链接文中图片,另外请保留本文原始出处:http://garfileo.is-programmer.com