2010年11月16日 星期二

「轉貼」Cairo 圖形指南 (9) —— 透明

「等待」的演示

下面的示例,使用透明效果製作了一個「等待」狀態的演示,是通過繪製 8 條依次消隱淡出的線,模擬一條線的運動過程。像這樣的效果通常被用於通知用戶耐心地等待一個隱藏在屏幕之後漫長的任務的完成。在網上看視頻時,經常可以看到類似的「等待」。

#include
#include
#include

static gushort count = 0;

static gboolean
on_expose_event (GtkWidget * widget,
GdkEventExpose * event, gpointer data)
{
cairo_t *cr;

cr = gdk_cairo_create (widget->window);

static gdouble const trs[8][8] = {
{0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0},
{1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9},
{0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8},
{0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65},
{0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5},
{0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3},
{0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15},
{0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0,}
};


gint width, height;
gtk_window_get_size (GTK_WINDOW (widget), &width, &height);

cairo_translate (cr, width / 2, height / 2);

gint i = 0;
for (i = 0; i < 8; i++) {
cairo_set_line_width (cr, 3);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
cairo_set_source_rgba (cr, 0, 0, 0, trs[count % 8][i]);

cairo_move_to (cr, 0.0, -10.0);
cairo_line_to (cr, 0.0, -40.0);
cairo_rotate (cr, M_PI / 4);

cairo_stroke (cr);
}

cairo_destroy (cr);

return FALSE;
}

static gboolean
time_handler (GtkWidget * widget)
{
count += 1;
gtk_widget_queue_draw (widget);
return TRUE;
}

int
main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;

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), 250, 150);

gtk_window_set_title (GTK_WINDOW (window), "waiting demo");

gtk_widget_set_app_paintable (window, TRUE);
gtk_widget_show_all (window);
g_timeout_add (100, (GSourceFunc) time_handler,
(gpointer) window);

gtk_main ();

return 0;
}

方法比較笨,就是繪製 8 條不同透明度的線。


static gdouble const trs[8][8] = {
{0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0},
{1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9},
{0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8},
{0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65},
{0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5},
{0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3},
{0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15},
{0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0,}
};

這 8 條線的透明度就是這樣被活生生地定義出來的,每一行表示一條線的透明度候選集。


cairo_set_line_width (cr, 3);
cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);

設置線寬與線帽。


cairo_set_source_rgba (cr, 0, 0, 0, trs[count % 8][i]);

每條線的透明度就是這樣活生生地變化出來的。


cairo_move_to (cr, 0.0, -10.0);
cairo_line_to (cr, 0.0, -40.0);
cairo_rotate (cr, M_PI / 4);

這裡有個從未講過的技巧,就是 cairo 的旋轉變換。每次都在前一次旋轉的基礎上繼續逆時針旋轉 $\frac{\pi}{4}$角度。這樣,8 次循環之後,恰好繞了一圈。


g_timeout_add (100, (GSourceFunc) time_handler, (gpointer) window);

又是計時器。


「轉貼」Cairo 圖形指南 (8) —— 透明

這一篇講述有關透明的一些基本知識,並提供兩個有趣的透明效果。

透明是透過某種材質的可見度。理解透明最簡單的方式就是想像一下玻璃或者水。從技術上講,光線可以穿過玻璃,因此我們可以看到玻璃之後的物體。

在計算機圖形學中,可以使用alpha 混合方式來實現透明效果。Alpha 混合,是通過將圖像與背景組合實現部分透明的視覺效果。混合過程中使用了一種叫做 alpha 通道的東西。Alpha 通道在圖形文件格式中是一個 8 位的層,用於表示圖片的透明性。每個像素所包含的這個各額外的 8 位數字提供了一個蒙板,可以表達 256 個層次的透明度。

透明的矩形

第一個例子,繪製了 10 個不同透明程度的矩形。


#include
#include

static gboolean
on_expose_event (GtkWidget * widget,
GdkEventExpose * event, gpointer data)
{
cairo_t *cr;

cr = gdk_cairo_create (widget->window);

gint i;
for (i = 1; i <= 10; i++) {
cairo_set_source_rgba (cr, 0, 0, 1, i * 0.1);
cairo_rectangle (cr, 50 * i, 20, 40, 40);
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), 590, 90);
gtk_window_set_title (GTK_WINDOW (window), "transparency");

gtk_widget_set_app_paintable (window, TRUE);
gtk_widget_show_all (window);

gtk_main ();

return 0;
}

cairo_set_source_rgba () 函數有一個可選的 alpha 參數,用於提供透明色支持。


gint i;
for (i = 1; i <= 10; i++) {
cairo_set_source_rgba (cr, 0, 0, 1, i * 0.1);
cairo_rectangle (cr, 50 * i, 20, 40, 40);
cairo_fill (cr);
}

這段代碼創建 10 個矩形,其 alpha 值從 0.1 遞增到 1。



淡出的效果

在下一個示例中,實現對一幅圖片的淡出處理。這幅圖片會逐漸變得越來越透明直至看不見。


#include
#include

cairo_surface_t *image;

static gboolean
on_expose_event (GtkWidget * widget,
GdkEventExpose * event, gpointer data)
{
cairo_t *cr;

cr = gdk_cairo_create (widget->window);

static double alpha = 1;
double const delta = 0.01;

cairo_set_source_surface (cr, image, 10, 10);
cairo_paint_with_alpha (cr, alpha);

alpha -= delta;

if (alpha <= 0)
timer = FALSE;

cairo_destroy (cr);

return FALSE;
}

static gboolean
time_handler (GtkWidget * widget)
{
if (widget->window == NULL)
return FALSE;

gtk_widget_queue_draw (widget);
return TRUE;
}

int
main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;

gint width, height;

image = cairo_image_surface_create_from_png ("tuz.png");
width = cairo_image_surface_get_width (image);
height = cairo_image_surface_get_height (image);

gtk_init (&argc, &argv);

window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

darea = gtk_drawing_area_new ();
gtk_container_add (GTK_CONTAINER (window), darea);

g_signal_connect (darea, "expose-event",
G_CALLBACK (on_expose_event), NULL);
g_signal_connect (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), width + 20,
height + 20);

gtk_window_set_title (GTK_WINDOW (window), "fade out");
g_timeout_add (50, (GSourceFunc) time_handler,
(gpointer) window);
gtk_widget_show_all (window);

gtk_main ();

cairo_surface_destroy (image);

return 0;
}

在這一示例中,顯示了一幅漸漸淡出的圖片。所使用的圖片是 Linux kernel 的新 Logo,詳見 LinuxToy 網站的介紹


image = cairo_image_surface_create_from_png ("tuz.png");

出於對程序運行效率的考慮,圖像外觀的創建在主函數中進行。


cairo_set_source_surface (cr, image, 10, 10);

將載入的圖片設置為源 (source)。


cairo_paint_with_alpha (cr, alpha);

調用函數 cairo_paint_with_alpha () 來實現圖片淡出效果,該函數使用透明度作為蒙板。


alpha -= delta;

在每次響應 expose 事件的處理過程中,進行透明度的值遞減。


if (alpha <= 0)
alpha = 1;

當透明度為 0 時,意味著圖片是完全透明的。這時,再將透明度置為 1,即將圖片復原為不透明狀態。這樣做的目的是讓圖片週而復始、痛不欲生的淡出著。


g_timeout_add (50, (GSourceFunc) time_handler, (gpointer) window);

創建一個計時器,其回調函數為 time_handler (),每隔 50ms 便會被調用一次。


static gboolean
time_handler (GtkWidget * widget)
{
if (widget->window == NULL)
return FALSE;

gtk_widget_queue_draw (widget);
return TRUE;
}

time_handler () 函數的實現,它的主要作用是調用 gtk_widget_queue_draw () 函數,實現窗口的重繪 (發出 expose 事件)。當該函數返回值為 FALSE 時,計時器便停止工作。


if (widget->window == NULL)
return FALSE;

這兩行代碼看上去有點多餘。但是考慮在計時器的時間間隔很小的情況下 (譬如 5ms),如果關閉窗口,這時進程尚未完全銷毀,不過窗口已經被 destroy 了。這時,這兩行代碼可以防止計時器回調函數繼續工作而引起的程序崩潰。


「轉貼」Cairo 圖形指南 (7) —— 形狀與填充

圖案 (Pattern)

所謂圖案填充,就是將圖片填充到形狀內部。

#include
#include
#include

cairo_surface_t *surface1;
cairo_surface_t *surface2;
cairo_surface_t *surface3;
cairo_surface_t *surface4;

static void
create_surfaces ()
{
surface1 = cairo_image_surface_create_from_png ("blueweb.png");
surface2 = cairo_image_surface_create_from_png ("maple.png");
surface3 = cairo_image_surface_create_from_png ("crack.png");
surface4 =
cairo_image_surface_create_from_png ("chocolate.png");
}

static void
destroy_surfaces ()
{
g_print ("destroying surfaces");
cairo_surface_destroy (surface1);
cairo_surface_destroy (surface2);
cairo_surface_destroy (surface3);
cairo_surface_destroy (surface4);
}

static gboolean
on_expose_event (GtkWidget * widget,
GdkEventExpose * event, gpointer data)
{
cairo_t *cr;

cairo_pattern_t *pattern1;
cairo_pattern_t *pattern2;
cairo_pattern_t *pattern3;
cairo_pattern_t *pattern4;

cr = gdk_cairo_create (widget->window);

int width, height;
gtk_window_get_size (GTK_WINDOW (widget), &width, &height);


pattern1 = cairo_pattern_create_for_surface (surface1);
pattern2 = cairo_pattern_create_for_surface (surface2);
pattern3 = cairo_pattern_create_for_surface (surface3);
pattern4 = cairo_pattern_create_for_surface (surface4);


cairo_set_source (cr, pattern1);
cairo_pattern_set_extend (cairo_get_source (cr),
CAIRO_EXTEND_REPEAT);
cairo_rectangle (cr, 20, 20, 100, 100);
cairo_fill (cr);

cairo_set_source (cr, pattern2);
cairo_pattern_set_extend (cairo_get_source (cr),
CAIRO_EXTEND_REPEAT);
cairo_arc (cr, 200, 70, 50, 0, 2 * M_PI);
cairo_fill (cr);

cairo_set_source (cr, pattern3);
cairo_pattern_set_extend (cairo_get_source (cr),
CAIRO_EXTEND_REPEAT);
cairo_rectangle (cr, 20, 140, 100, 100);
cairo_fill (cr);

cairo_set_source (cr, pattern4);
cairo_pattern_set_extend (cairo_get_source (cr),
CAIRO_EXTEND_REPEAT);
cairo_rectangle (cr, 150, 140, 100, 100);
cairo_fill (cr);

cairo_pattern_destroy (pattern1);
cairo_pattern_destroy (pattern2);
cairo_pattern_destroy (pattern3);
cairo_pattern_destroy (pattern4);

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);

create_surfaces ();

gtk_window_set_position (GTK_WINDOW (window),
GTK_WIN_POS_CENTER);
gtk_window_set_default_size (GTK_WINDOW (window), 270, 260);
gtk_window_set_title (GTK_WINDOW (window), "patterns");

gtk_widget_set_app_paintable (window, TRUE);
gtk_widget_show_all (window);

gtk_main ();

destroy_surfaces ();

return 0;
}

該示例,載入 4 張圖片,分別填充至三個矩形與一個圓形內部區域。所使用的 4 幅圖,均採用 GIMP 製作。程序中,圖片的外觀 (surface) 實在 on_expose_event () 函數中創建的,這並不是很妥當,因為窗口每次被重繪時,都需要從硬盤中讀取圖片。


pattern1 = cairo_pattern_create_for_surface (surface1);

由圖片外觀創建一個圖案。


cairo_set_source (cr, pattern1);
cairo_pattern_set_extend (cairo_get_source (cr),
CAIRO_EXTEND_REPEAT);
cairo_rectangle (cr, 20, 20, 100, 100);
cairo_fill (cr);

這裡,繪製第一個矩形。cairo_set_source () 函數通知 Cairo 環境,讓它使用一份圖案作為源 (source)。圖片所形成的圖案或許並不適合於形狀,當使用 cairo_pattern_set_extend () 函數講圖案填充模式設為 CAIRO_EXTEND_REPEAT 時,可以讓圖案像瓦片那樣填充於形狀內部。cairo_rectangle () 函數創建一個矩形路徑,cairo_fill () 函數將已經準備好的圖案填充到矩形路徑所構成的封閉區域中。



漸變 (Gradient)

在計算機圖形學中,漸變是形狀由明到暗或者從一種顏色向另一種顏色的平滑過度。在 2D 繪圖與渲染程序中,漸變通常被用於創造多彩的背景與一些特效,比如光影的仿真。

#include
#include

static gboolean
on_expose_event (GtkWidget * widget,
GdkEventExpose * event, gpointer data)
{
cairo_t *cr;
cairo_pattern_t *pat1;
cairo_pattern_t *pat2;
cairo_pattern_t *pat3;

cr = gdk_cairo_create (widget->window);

pat1 = cairo_pattern_create_linear (0.0, 0.0, 350.0, 350.0);

gdouble j;
gint count = 1;
for (j = 0.1; j < 1; j += 0.1) {
if ((count % 2)) {
cairo_pattern_add_color_stop_rgb (pat1, j, 0, 0,
0);
} else {
cairo_pattern_add_color_stop_rgb (pat1, j, 1, 0,
0);
}
count++;
}

cairo_rectangle (cr, 20, 20, 300, 100);
cairo_set_source (cr, pat1);
cairo_fill (cr);


pat2 = cairo_pattern_create_linear (0.0, 0.0, 350.0, 0.0);

gdouble i;
count = 1;
for (i = 0.05; i < 0.95; i += 0.025) {
if ((count % 2)) {
cairo_pattern_add_color_stop_rgb (pat2, i, 0, 0,
0);
} else {
cairo_pattern_add_color_stop_rgb (pat2, i, 0, 0,
1);
}
count++;
}

cairo_rectangle (cr, 20, 140, 300, 100);
cairo_set_source (cr, pat2);
cairo_fill (cr);


pat3 = cairo_pattern_create_linear (20.0, 260.0, 20.0, 360.0);

cairo_pattern_add_color_stop_rgb (pat3, 0.1, 0, 0, 0);
cairo_pattern_add_color_stop_rgb (pat3, 0.5, 1, 1, 0);
cairo_pattern_add_color_stop_rgb (pat3, 0.9, 0, 0, 0);

cairo_rectangle (cr, 20, 260, 300, 100);
cairo_set_source (cr, pat3);
cairo_fill (cr);

cairo_pattern_destroy (pat1);
cairo_pattern_destroy (pat2);
cairo_pattern_destroy (pat3);

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), 340, 390);
gtk_window_set_title (GTK_WINDOW (window), "gradients");

gtk_widget_set_app_paintable (window, TRUE);
gtk_widget_show_all (window);

gtk_main ();

return 0;
}

在這一示例程序中,我們繪製了三個具有不同漸變風格的矩形。


pat3 = cairo_pattern_create_linear (20.0, 260.0, 20.0, 360.0);

這裡,創建了一個線性漸變圖案。參數設定了繪製漸變方向的直線,在示例中,它是一條豎線。


cairo_pattern_add_color_stop_rgb (pat3, 0.1, 0, 0, 0);
cairo_pattern_add_color_stop_rgb (pat3, 0.5, 1, 1, 0);
cairo_pattern_add_color_stop_rgb (pat3, 0.9, 0, 0, 0);

定義了漸變圖案的斷點。在示例中,漸變圖案表現為黑色與黃色的過渡。通過添加兩個黑色斷點和一個黃色斷點,就可以構成一個水平方向的漸變圖案,顏色 的變化方向則是沿豎直方向。漸變圖案從矩形的上端至下端,開始是黑色,到 1/10 寬度時,黑色便停止了,然後就是由黑色向黃色的漸變渲染;到達矩形中部時,黃色達到飽和狀態。黃色斷點會在 9/10 寬度處終止,最後的 1/10 又是黑色。


「轉貼」Cairo 圖形指南 (6) —— 形狀與填充

這一部分,講述一些基本的以及較為高級的形狀繪製及其純色 (solid color)、圖案 (pattern) 與漸變 (gradient) 填充方法。

基本形狀

Cairo 提供了幾個用於繪製基本形狀的函數。

#include
#include
#include

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, 1);

cairo_rectangle (cr, 20, 20, 120, 80);
cairo_rectangle (cr, 180, 20, 80, 80);
cairo_stroke_preserve (cr);
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_fill (cr);

cairo_set_source_rgb (cr, 0, 0, 0);
cairo_arc (cr, 330, 60, 40, 0, 2 * M_PI);
cairo_stroke_preserve (cr);
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_fill (cr);

cairo_set_source_rgb (cr, 0, 0, 0);
cairo_arc (cr, 90, 160, 40, M_PI / 4, M_PI);
cairo_close_path (cr);
cairo_stroke_preserve (cr);
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_fill (cr);

cairo_set_source_rgb (cr, 0, 0, 0);
cairo_translate (cr, 220, 180);
cairo_scale (cr, 1, 0.7);
cairo_arc (cr, 0, 0, 50, 0, 2 * M_PI);
cairo_stroke_preserve (cr);
cairo_set_source_rgb (cr, 1, 1, 1);
cairo_fill (cr);

cairo_destroy (cr);

return FALSE;
}


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

GtkWidget *window;
GtkWidget *darea;

gtk_init (&argc, &argv);

window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

darea = gtk_drawing_area_new ();
gtk_container_add (GTK_CONTAINER (window), darea);

g_signal_connect (darea, "expose-event",
G_CALLBACK (on_expose_event), NULL);
g_signal_connect (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), 390, 240);

gtk_widget_show_all (window);

gtk_main ();

return 0;
}

這個示例,繪製了矩形、正方形、圓、圓弧和橢圓。

下面對關鍵代碼簡單分析:


cairo_rectangle (cr, 20, 20, 120, 80);
cairo_rectangle (cr, 180, 20, 80, 80);

繪製矩形與正方形。正方形在 cairo 中是矩形的一種特例。


cairo_arc (cr, 330, 60, 40, 0, 2 * M_PI);

畫了一個圓,圓心為 (330, 60)px,半徑為 40px。Cairo 所謂的圓,其實是起始角為 0 度,終止角為 360 度的弧線。


cairo_scale (cr, 1, 0.7);
cairo_arc (cr, 0, 0, 50, 0, 2 * M_PI);

畫橢圓的方法也與畫圓類似,只是需要先設定長軸與短軸的比例,在本例中為 1:0.7。



複雜的圖形

複雜的圖形是由簡單的圖形拼湊出來的,譬如下面這個繪製圓角矩形的程序。

#include
#include
#include

static void
draw_round_rectangle (cairo_t * cr,
double x, double y,
double width, double height, double r)
{
cairo_move_to (cr, x + r, y);
cairo_line_to (cr, x + width - r, y);

cairo_move_to (cr, x + width, y + r);
cairo_line_to (cr, x + width, y + height - r);

cairo_move_to (cr, x + width - r, y + height);
cairo_line_to (cr, x + r, y + height);

cairo_move_to (cr, x, y + height - r);
cairo_line_to (cr, x, y + r);

cairo_arc (cr, x + r, y + r, r, M_PI, 3 * M_PI / 2.0);
cairo_arc (cr, x + width - r, y + r, r, 3 * M_PI / 2, 2 * M_PI);
cairo_arc (cr, x + width - r, y + height - r, r, 0, M_PI / 2);
cairo_arc (cr, x + r, y + height - r, r, M_PI / 2, M_PI);
}

static gboolean
on_expose_event (GtkWidget * widget,
GdkEventExpose * event, gpointer data)
{
cairo_t *cr;
int width, height;

double w, h, x, y, r;

gtk_window_get_size (GTK_WINDOW (widget), &width, &height);

x = width / 5.0;
y = height / 5.0;
w = 3 * width / 5.0;
h = 3 * height / 5.0;
r = h / 4.0;

cr = gdk_cairo_create (widget->window);

cairo_set_source_rgb (cr, 0.8, 0.4, 0);
cairo_set_line_width (cr, 6);

draw_round_rectangle (cr, x, y, w, h, r);
cairo_stroke_preserve (cr);
cairo_set_source_rgb (cr, 0.8, 0.8, 0.2);
cairo_fill (cr);

cairo_destroy (cr);

g_print ("test\n");

return FALSE;
}

static gboolean
on_configure_event (GtkWidget * widget,
GdkEventConfigure * event, gpointer data)
{
gdk_window_invalidate_rect (widget->window,
&widget->allocation,
FALSE);
return FALSE;
}

int
main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *darea;

gtk_init (&argc, &argv);

window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

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(G_OBJECT(window), "configure-event",
G_CALLBACK(on_configure_event), NULL);


gtk_window_set_position (GTK_WINDOW (window),
GTK_WIN_POS_CENTER);
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;
}

註:因為 "The cairo graphics tutorial" 在這一部分所提供的示例程序不具代表性,因此寫了這個程序。

該示例程序繪製了一個可跟隨窗口尺寸進行縮放變化的圓角矩形。

自定義的 draw_round_rectangle () 函數利用 Cairo 提供的基本圖元函數,利用直線段與圓弧拼湊出圓角矩形。on_configure_event () 函數用於響應窗口尺寸變化事件,在其中調用 gdk_window_invalidate_rect () 函數讓窗口繪圖區域失效,並產生窗口重繪製事件(即 expose 事件)。



填充 (Fill)

雖然上一篇已經講述了一些有關填充的知識,但這裡所講述的內容是與形狀相關的。填充可分為三種類型:純色、圖案、漸變。

純色 (Solid color)

對象的顏色是採用紅 (R)、綠 (G)、藍 (B) 三原色描述的,Cairo 的 RGB 取值是從 0 到 1 的雙精浮點數。

#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_source_rgb (cr, 0.5, 0.5, 1);
cairo_rectangle (cr, 20, 20, 100, 100);
cairo_fill (cr);

cairo_set_source_rgb (cr, 0.6, 0.6, 0.6);
cairo_rectangle (cr, 150, 20, 100, 100);
cairo_fill (cr);

cairo_set_source_rgb (cr, 0, 0.3, 0);
cairo_rectangle (cr, 20, 140, 100, 100);
cairo_fill (cr);

cairo_set_source_rgb (cr, 1, 0, 0.5);
cairo_rectangle (cr, 150, 140, 100, 100);
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), 270, 260);
gtk_window_set_title (GTK_WINDOW (window), "colors");

gtk_widget_set_app_paintable (window, TRUE);
gtk_widget_show_all (window);

gtk_main ();

return 0;
}

該示例繪製了 4 個正方形,分別採用四種不同顏色進行填充。這個例子,由於很簡單,就不再像原作者那樣自作多情的分析了。