網誌存檔

2010年11月16日 星期二

「轉貼」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 了。這時,這兩行代碼可以防止計時器回調函數繼續工作而引起的程序崩潰。


沒有留言:

張貼留言