這一篇講述有關透明的一些基本知識,並提供兩個有趣的透明效果。
透明是透過某種材質的可見度。理解透明最簡單的方式就是想像一下玻璃或者水。從技術上講,光線可以穿過玻璃,因此我們可以看到玻璃之後的物體。
在計算機圖形學中,可以使用alpha 混合方式來實現透明效果。Alpha 混合,是通過將圖像與背景組合實現部分透明的視覺效果。混合過程中使用了一種叫做 alpha 通道的東西。Alpha 通道在圖形文件格式中是一個 8 位的層,用於表示圖片的透明性。每個像素所包含的這個各額外的 8 位數字提供了一個蒙板,可以表達 256 個層次的透明度。
透明的矩形
第一個例子,繪製了 10 個不同透明程度的矩形。
#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 參數,用於提供透明色支持。
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
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 網站的介紹。
出於對程序運行效率的考慮,圖像外觀的創建在主函數中進行。
將載入的圖片設置為源 (source)。
調用函數 cairo_paint_with_alpha () 來實現圖片淡出效果,該函數使用透明度作為蒙板。
在每次響應 expose 事件的處理過程中,進行透明度的值遞減。
alpha = 1;
當透明度為 0 時,意味著圖片是完全透明的。這時,再將透明度置為 1,即將圖片復原為不透明狀態。這樣做的目的是讓圖片週而復始、痛不欲生的淡出著。
創建一個計時器,其回調函數為 time_handler (),每隔 50ms 便會被調用一次。
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 時,計時器便停止工作。
return FALSE;
這兩行代碼看上去有點多餘。但是考慮在計時器的時間間隔很小的情況下 (譬如 5ms),如果關閉窗口,這時進程尚未完全銷毀,不過窗口已經被 destroy 了。這時,這兩行代碼可以防止計時器回調函數繼續工作而引起的程序崩潰。
沒有留言:
張貼留言