這一部分講述如何繪製一些簡單的圖元,包括直線、填充與筆畫操作、虛線、線端(Cap)與線的交合等圖形的繪製方法。
直線段
直線段是非常基礎的矢量圖形對象。畫一條直線段,需要調用兩個函數:cairo_move_to()
函數,用於設置線段起點;cairo_line_to()
用於設定線段終點。
#include
double coordx[100];
double coordy[100];
int count = 0;
static gboolean
on_expose_event(GtkWidget *widget,
GdkEventExpose *event,
gpointer data)
{
cairo_t *cr;
cr = gdk_cairo_create(widget->window);
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_set_line_width (cr, 0.5);
int i, j;
for ( i = 0; i <= count - 1; i++ ) {
for ( j = 0; j <= count -1; j++ ) {
cairo_move_to(cr, coordx[i], coordy[i]);
cairo_line_to(cr, coordx[j], coordy[j]);
}
}
count = 0;
cairo_stroke(cr);
cairo_destroy(cr);
return FALSE;
}
gboolean clicked(GtkWidget *widget, GdkEventButton *event,
gpointer user_data)
{
if (event->button == 1) {
coordx[count] = event->x;
coordy[count++] = event->y;
}
if (event->button == 3) {
gtk_widget_queue_draw(widget);
}
return TRUE;
}
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_BUTTON_PRESS_MASK);
g_signal_connect(window, "expose-event",
G_CALLBACK(on_expose_event), NULL);
g_signal_connect(window, "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(window, "button-press-event",
G_CALLBACK(clicked), NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_title(GTK_WINDOW(window), "lines");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
gtk_widget_set_app_paintable(window, TRUE);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
該示例會創建一個支持鼠標交互繪製直線段的 GTK+ 窗口。在窗口中使用鼠標左鍵隨便點幾下,每一次點擊時,光標位置的坐標都會被記入長度為 100 的數組;然後點擊鼠標右鍵,所有由鼠標左鍵點擊所得到的點會被彼此連接形成直線段;在窗口中再次點擊鼠標右鍵時,會對窗口繪圖區域進行清除。
下面對該示例程序代碼進行分析:
cairo_set_line_width (cr, 0.5);
設置顏色為黑色,線寬為 0.5pt 為參數,繪製直線段。
for ( i = 0; i <= count - 1; i++ ) {
for ( j = 0; j <= count -1; j++ ) {
cairo_move_to(cr, coordx[i], coordy[i]);
cairo_line_to(cr, coordx[j], coordy[j]);
}
}
用 cairo_move_to() 和 cairo_line_to() 函數在 cr 中定義繪圖路徑 (path),連接 coordx[] 和 coordy[] 所記錄的每個點。
cairo_stroke() 函數會將 cr 中的路徑繪製出來。
G_CALLBACK(clicked), NULL);
設定 button-press-event
事件的回調函數為 clicked ()
。
coordx[count] = event->x;
coordy[count++] = event->y;
}
在 clicked ()
函數中,當鼠標左鍵點擊事件發生時,講光標所在位置的 x 和 y 坐標分別記入數組 coordx 和
coordy
。
gtk_widget_queue_draw(widget);
}
在 clicked ()
函數中,當鼠標右鍵單擊時,調用 gtk_widget_queue_draw () 函數重繪窗口區域。
描繪 (Stroke) 與填充 (Fill)
描繪 (Stroke) 可以繪製形狀的輪廓,填充 (Fill) 則用於向形狀內部灌注顏色。
#include
#include
static gboolean
on_expose_event (GtkWidget * widget,
GdkEventExpose * event, gpointer data)
{
cairo_t *cr;
cr = gdk_cairo_create (widget->window);
int width, height;
gtk_window_get_size (GTK_WINDOW (widget), &width, &height);
cairo_set_line_width (cr, 9);
cairo_set_source_rgb (cr, 0.69, 0.19, 0);
cairo_arc (cr, width / 2, height / 2,
(width < style="color: rgb(102, 204, 102);">) / 2 - 10, 0,
2 * M_PI);
cairo_stroke_preserve (cr);
cairo_set_source_rgb (cr, 0.3, 0.4, 0.6);
cairo_fill (cr);
cairo_destroy (cr);
return FALSE;
}
int
main (int argc, char *argv[])
{
GtkWidget *window;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (G_OBJECT (window), "expose-event",
G_CALLBACK (on_expose_event), NULL);
g_signal_connect (G_OBJECT (window), "destroy",
G_CALLBACK (gtk_main_quit), NULL);
gtk_window_set_position (GTK_WINDOW (window),
GTK_WIN_POS_CENTER);
gtk_window_set_default_size (GTK_WINDOW (window), 200, 150);
gtk_widget_set_app_paintable (window, TRUE);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
這個示例繪製一個內部填充灰色的圓。
下面對代碼進行解析:
之所以引入這個頭文件,是因為程序中使用了圓周率常量 M_PI。
gtk_window_get_size (GTK_WINDOW (widget), &width, &height);
獲取窗口的寬度與高度尺寸。程序中將使用這些值作為繪製圓形的參考尺寸,以實現窗口尺寸變化時,所繪製的圓的尺寸也會相應變化。
cairo_arc (cr, width / 2, height / 2,
(width < style="color: rgb(102, 204, 102);">) / 2 - 10, 0,
2 * M_PI);
cairo_stroke_preserve (cr);
描繪圓的輪廓。這裡要注意一下 cairo_stroke_preserve () 函數與 cairo_stroke () 函數的區別(最好的辦法是用後者替換一下前者,看看程序執行效果)。cairo_stroke_preserve () 函數會將它繪製的路徑依然保存在 cairo 環境中,而 cairo_stroke () 所繪製的路徑,在繪製完成後,就從 cairo的環境中清除了。
cairo_fill (cr);
對使用 cairo_stroke_preserve () 函數繪製的路徑進行藍色填充。
沒有留言:
張貼留言