網誌存檔

2010年11月18日 星期四

「轉貼來源-http://xy0811.spaces.live.com/default.aspx」xserver

任何一個X應用程序,包括WM在內都是XClient; XClient不負責任何圖形相關的具體操作,只和xserver交互,由xserver負責完成其圖形顯示任務,這種交互一般來說是通過在底層調用xlib實現的; Xclient的核心任務是計算和數據處理。

對XServer的理解

1. XServer/XClient通過X協議交互, 而X協議就是一套基於socket的通信
2. XServer啟動時初始化socket, 並進入Select循環,
等socket連接(多個連接時形成client隊列), 鍵鼠等
3. XClient啟動時連接該socket, 與XServer建立起一個通道,
然後任何圖形相關的操作都通過XServer實現
4. 好處在於: 多個XClient同時運行時, 在XServer之上可做互斥, 共享, 疊加等


linux的一個瀏覽器是這樣實現的:
1. XServer: 圖形顯示, 根據XClient的控制顯示(見nano-X)
2. Xlib: 對XServer提供功能的封裝, 供XClient調用,
底層通過socket和XServer交互(見xcblib)
3. WindowManager: 控制各應用窗口之間關係
4. gtk庫: 控件, 及消息處理
5. 瀏覽器程序

2010年11月17日 星期三

「轉貼來源-http://xy0811.spaces.live.com/default.aspx」Linux的X窗口系統結構說明

一、說明
X Server為中心,簡要地分為四層,如圖所示

二、X方式與Framebuffer方式的差異

1. X方式

1) 什麼是X
我們常說的X WindowXX11(X協議的11版本)一般指X協議,或指是基於X協議的X服務端程序(X Server)

2) X ServerX Client

a) X協議指X ServerX Client組成的c/s架構,及其通訊協議的實現

b) X Server有主事件循環,由它來處理用戶輸入、顯示及與X Client通訊

c) X Client即X桌面上運行的普通應用程序
應用程序如果想顯示數據,需要利用X Lib庫建立X窗口(X Client),它通過X協議與X Server通訊,讓X Server完成顯示;X Server會把接到鍵盤鼠標事件,傳給焦點所在的X Client處理

2. Framebuffer方式
Framebuffer
方式相對簡單,它由內核直接支持,通常用於嵌入式系統。應用程序可以得到屏幕顯示區域的指針,然後對其寫數據來進行顯示,其中窗口的概念不強,它自身不帶窗口管理,需要應用軟件自己管理窗口。比如qtopia基本於framebuffer顯示時,qtopia自身實現了窗口管理功能

3. 應用程序的顯示方式
很多應用程序同時支持XFramebuffer兩種模式顯示,如gtkqt等,以下我們只討論它基本於X實現的部分

三、分層

1. 應用層(Application)
指X Window上運行所有帶圖形界面的應用程序,每個窗口都是一個X Client

1) X Lib app
直接使用X函數的應用程序,這種程序一般界面簡單,比較底層,比如很多窗口管理器直接寫在這層

2) Gtk app
gtk
又分為gtkgdk兩層,

gtk
為控件及主循環的實現
gdk
相對底層,控制底層繪圖部分,它支持framebufferX11DirectFb
應用程序可調用gtk函數,也可直接調用gdk函數
gtk
常和cairo一起實現二維特效

3) Qt app
Qt
也通過調用X Lib實現圖形界面

Qt
的優勢在於它是C++實現的,使用起來程序結構更好,也有較成熟的嵌入式版本

4) SDL app
SDL
也通過調用X Lib實現圖形界面

SDL
更底層,代碼少,沒有控件,但做特殊效果很好用

2. 窗口管理器層(Window manager簡稱wm)
Window manager
是特殊的X Client,也通過X Lib庫與X Server交互,與一般應用不同的是:它負責控制各個窗口的動作,及操作主窗口

WM
的功能分為管理(manager)和工具(tools)

1) Manager
負責各個窗口的建立銷毀/顯示隱藏/最大最小化/移動縮放,管理窗口隊列,設置焦點窗口,窗口切換效果等

2) Tools
實現桌面工具條,桌面菜單等基本界面及小工具

3) 具體實現
實現通常有兩種方式:一種是managertools在一個程序中實現,一種是分開兩個程序實現,使用時可以隨意組合

a) qvwm, blackbox等較早期的wm,都是managertools在一個程序中實現的

b) xfcemetacity等是分開的,例如:xfce包含toolsmanager,但不在一個程序中實現,它的managerxfwm,我們可在使用xfce時把xfwm替成metacity(metacity是一個manager,不帶tools)

3. X服務器層(X Server)
主循環控制顯示,讀取設備數據,與X Client通訊,事件循環,並把事件送給焦點窗口

1) 普通X Server
功能完整的X Server,代碼量大,支持全,常見的如XFree86,Xorg

2) Tiny X Server
一般用於嵌入式系統,資源佔用小,代碼少,功能及邏輯相對簡單,如KDriveXvesa/Xchips/Xfbdev/Xi810……

4. 系統底層(System)

1) Kernel
與硬件交互,獲得輸入設備的數據,向顯示設備輸出

2) Dev
文件系統中的設備文件,程序通過對它的讀寫和操作與kernel交互,控制硬件

3) 中間層
在程序和Dev層之間,有時還需要庫或程序處理設備數據,比如觸摸屏就使用libts去除噪點,過濾出更有效的數據

「轉貼來源-http://xy0811.spaces.live.com/default.aspx」Gtk透明窗口全攻略


如圖所示,左邊是普通的ubuntu桌面,右邊是我建立的三個gtk特殊窗口,其中包含了:異形窗口(上),透明窗體(中),不同區域不同透明度的窗口(下)。基本包括了各種特殊窗口的需求,而複雜程度和依賴工具又各有不同,下面是實現的代碼及說明

1. 環境說明
ubuntu 8.04
系統,安裝glib-devcairo

2. 異形窗口

a) 原理
異形窗口利用蒙板(mask)實現,蒙板的數據從圖片獲得,每一點只有透明和不透明兩種狀態,不透明區域顯示該窗口的背景,透明區域顯示其後桌面或其它應用

b) 效果
可實現異形窗口,但不支持半透明,所以弧形邊緣看起來不夠平滑。

c) 依賴
gtk
的基本功能,一般gtk都可以支持

#include

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

{

GtkWidget *window = NULL;

GdkPixbuf *pixbuf = NULL;

GdkBitmap *bitmap = NULL;

GdkPixmap *pixmap = NULL;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

gtk_window_set_decorated(GTK_WINDOW(window), FALSE); // 設置無邊框

gtk_widget_set_app_paintable(window, TRUE);

gtk_widget_realize(window);

pixbuf = gdk_pixbuf_new_from_file("demo1.png", NULL); // gdk函數讀取png文件

gdk_pixbuf_render_pixmap_and_mask(pixbuf, &pixmap, &bitmap, 128); // alpha小於128認為透明

gtk_widget_shape_combine_mask(window, bitmap, 0, 0); // 設置透明蒙板

gdk_window_set_back_pixmap(window->window, pixmap, FALSE); // 設置窗口背景

g_object_unref(pixbuf);

g_object_unref(bitmap);

g_object_unref(pixmap);

gtk_widget_show_all(window);

gtk_main();

return TRUE;

}

3. 透明窗體

a) 原理
使用函數gtk_window_set_opacity設置整個窗體的透明度

b) 效果
窗體各處透明度相同

c) 依賴
gtk_window_set_opacity
函數只能在gtk-2.12以上版本中使用

#include

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

{

GtkWidget *window = NULL;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

gtk_window_set_opacity(GTK_WINDOW(window), 0.7); // 設置透明度函數

gtk_widget_show_all(window);

gtk_main();

return TRUE;

}

4. 不同區域不同透明度的窗口

a) 原理
利用cairo庫與gtk相結合,在曝光函數中用cairo向窗口區域繪圖,每一點可以設置不同的透明度,但相對複雜,依賴更多的庫

b) 效果
可設置窗口各個點不同的透明度,過度平滑,效果最好

c) 依賴
gtk
及相應版本的cairo

#include

cairo_surface_t *image = NULL;

static gboolean on_window_expose_event(GtkWidget * widget, // 曝光事件

GdkEventExpose * event, gpointer data)

{

cairo_t *cr;

cr = gdk_cairo_create(widget->window);

cairo_set_source_surface(cr, image, 0, 0);

cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); // 重要

cairo_paint(cr); // 繪圖

cairo_destroy(cr);

return FALSE;

}

gint main(gint argc, gchar ** argv)

{

GtkWidget *window;

GdkScreen *screen;

GdkColormap *colormap;

gtk_init(&argc, &argv);

image = cairo_image_surface_create_from_png("demo3.png"); // cairo函數讀取png文件

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

gtk_window_set_decorated(GTK_WINDOW(window), FALSE); // 設置無邊框

gtk_widget_set_app_paintable(window, TRUE);

gtk_window_resize(GTK_WINDOW(window), 250, 250);

g_signal_connect(G_OBJECT(window), "expose-event",

G_CALLBACK(on_window_expose_event), NULL);

screen = gtk_widget_get_screen(window); // 重要

colormap = gdk_screen_get_rgba_colormap(screen);

gtk_widget_set_colormap(window, colormap);

gtk_widget_show_all(window);

gtk_main();

return TRUE;

}

5. 參考
http://cid-f8aecd2a067a6b17.skydrive.live.com/browse.aspx/.Public?uc=1&isFromRichUpload=1
示例代碼為gtk_demo.tgz

「轉貼來源-http://xy0811.spaces.live.com/default.aspx」使用gdk_text_draw顯示中英文混合字體

一、 說明
我們可以很容易把控件上的字體設成中文,但在gdk層上直接顯示文字,並具按要求字體顯示,相比就比較困難,以下是我測試通過的簡化的例程,僅供說明中文字體的顯示問題。

二、 例程

1. 代碼
#include
#include
#include

gboolean draw_text(gpointer data)
// timeout
的回調函數
{
GtkWidget *drawing_area = (GtkWidget *) data;
// 顯示文字的畫布
GdkFont *font = gdk_fontset_load
("-adobe-helvetica-medium-r-normal--36-*-*-*-*-*-iso8859-1, misc-simsun-medium-r-normal--36-*-*-*-*-*-gb18030.2000-0");
// 設定英文字體用adobe, 中文用宋體(simsun),宋體是我從xp中複製過去使用的,ubuntu自帶中文字體見/usr/share/fonts/X11/misc/fonts.dir中帶gb的行
char str[128] = "test
雙色球"; // 顯示的文字,因為系統設定為zh_CN.UTF8,所以沒有做轉換,如果設成了zh_CN.GB2312的話,可以用gtk_local_to_utf8()轉成UTF8,才能識別;utf8為一個漢字佔三字節,可以通過取字串的長度判定是何種編碼方式

gdk_draw_rectangle(drawing_area->window,
drawing_area->style->white_gc, TRUE, 0, 0, 200, 200);
// 畫矩形填背景
gdk_draw_text(drawing_area->window, font, drawing_area->style->black_gc,
10, 36, str, strlen(str));
// 顯示文字,注意坐標指字的左下角位置
return TRUE;
}

int main(int argc, char *argv[])
{
GtkWidget *window1;
gtk_set_locale();
gtk_init(&argc, &argv);

window1 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_show(window1);
gtk_timeout_add(110, (GtkFunction) draw_text, window1);
// 設置時間回調
gtk_main();
return 0;
}

2. 編譯
all:
gcc main.c -o main `pkg-config --cflags --libs gtk+-2.0`

3. 執行結果

2010年11月16日 星期二

「轉貼」Cairo 學習總結

Cairo將成為Linux 2D 繪圖的未來,相信我,沒錯的。這是一個筆記,另外還有一個cairo粗斜體顯示中文的補丁,這個補丁我永遠也不會向外放,因為,這麼作,就跟firefly和akito的做法一樣,用一個錯誤的方法解決錯誤的問題。

粗體實現,應該在freetype的GetBitmap之前就要完成,這樣,任何基於freetype的東西都不再需要補丁了。

這個文檔會不斷完善,也會跟著cairo的版本升級作修改,我希望最後這個文檔能夠涵蓋cairo編程所有的東西,同時也希望有興趣的能夠一起來寫這個文檔。

這個文檔還沒有清楚的解釋什麼是surface,什麼是path,什麼是pattern,慢慢完善吧,不過如果你用一段時間後,你會慢慢的悟出來。


Cairo真是個好東西,你可以用它畫出所有的東西。





Cairo 編程入門


1,什麼是Cairo。
按照官方的說法:Cairo is a vector graphics library with cross-device output support.
翻譯過來,就是cairo是一個支持多種輸出的向量圖形庫。
具體解釋一下,也就是說,cairo是種畫圖的工具庫,他可以向多種設備上畫圖,比如:
cairo可以輸出到png,可以輸出到pdf,可以輸出到ps,可以輸出到xlib,可以輸出到XCB,可以輸出到win32,以後還要輸出到svg

我們可以展望一下,如果cairo能夠統一linux下所有的畫圖接口,那麼,所見所得可能就會成為顯示。畢竟現在顯示和打印是兩套代碼

2,編譯安裝cairo:
目前的linux操作系統應該都沒有問題,只要注意提供freetype/fontconfig就可以了。
然後,下載:
libpixman
glitz
就可以編譯安裝cairo了。
如果需要svg-cairo就要下載libsvg, libsvg-cairo編譯安裝。
如果需要python綁定,下載pycairo
如果需要gtk綁定,下載gtkcairo
如果需要qt綁定,自己去研究一下吧。不過你要自己寫也行,使用doublebuffer,然後用bitblt就可以了。


3,快速入門:
如果單純的從代碼上理解cairo,可能容易讓人迷惑,這種類似於Postscript編碼風格的東西確實讓人有點混亂,所以,我們需要這麼去理解cairo:
cairo是一個畫筆,你可以為這個畫筆設置顏色、設置字體、設置alpha,也可以用這個畫筆去畫出任何圖形。
就像畫家作畫一樣,你可以用畫布,也可以用宣紙,也可以用其他的材料。cairo這支畫筆,可以在png,ps,pdf或者Xlib上畫東西。

一個比較有代表性的例子如下:

#include <cairo.h>
#include <cairo-png.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{

FILE *file;
file=fopen("a.png","w");//打開一個文件,寫入,文件名為a.png

cairo_t *cr; //聲明一支畫筆
cr=cairo_create();//創建畫筆
cairo_set_target_png(cr,file,CAIRO_FORMAT_ARGB32,400,400);//設置畫布,這裡就是剛剛打開的那個文件,a.png

cairo_set_rgb_color(cr,0,1,0);//設置畫筆顏色,也就是紅,綠,藍,這裡設置成綠色。
cairo_rectangle(cr,10,10,200,200);//畫一個方塊,位置從坐標(10,10)開始,寬200,高200
cairo_fill(cr);//填充,使用的顏色當然是上面設置的顏色。
cairo_move_to(cr,250,200);//將畫筆移動到(250,200)
cairo_select_font (cr, "DongWen--Song",
CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);//為cairo設置一個字體,字體名DongWen--Song,非斜體,非粗體。
cairo_scale_font(cr,60);//縮放字體到60倍
cairo_show_text(cr,"hello world");//畫出一個串
cairo_destroy(cr);//銷毀畫筆
fclose(file);//關閉文件
}

存成t1.c,然後使用下面的命令編譯
gcc -o t1 t1.c -lcairo -I/usr/include/cairo

運行./t1,就生成了一個png圖片文件,自己用工具打開看就可以了。




TIPS:

cairo_stoke(cr);
神來一筆,描線函數,也就是將一個path用線把輪廓描出來。
cairo_fill(cr);
就是填充函數,也就是將一個path用某種顏色填充起來。


比如:
cairo_rectangle(cr,10,10,100,100);
cairo_set_line_width(cr,10)
cairo_set_rgb_color(cr,0,0,1);
cairo_stroke(cr);

這時候就畫了一個方塊。
cairo_move_to(cr,0,0);
cairo_line_to(cr,100,100);
cairo_set_rgb_color(cr,1,0,0);
cairo_stroke(cr);

畫了一條斜線,從0,0到100,100

cairo_set_line_width(cr,LINE_WIDTH);
設置線寬。

cairo_set_line_cap(cr, LINE_CAP);
設置線條兩端的顯示方式
CAIRO_LINE_CAP_ROUND,圓形,圓形的中心就是你定義的兩個端點
CAIRO_LINE_CAP_BUTT,方形,就從你定義的點開始。
CAIRO_LINE_CAP_SQUARE,方形,方形的中心就是你定義的兩個端點。

也就是如果用square或者round,畫出的線條比兩個端點的距離要長一點,一面長半個LINE_WIDTH。

注意,cairo_set_line_cap好像只對線條有效,如果你畫一個rectangle,角永遠是尖的。


cairo_curve_to(cr,x1,y1,x2,y2,x3,y3)
通過三點定義一道弧線。

cairo_arc(cr,x,y,radius,from,to);
畫一個圓,圓心x,y,半徑,然後注意:指定弧度,比如從0,M_PI(在math.h裡)。就可以是一個半圓。
從0到2*M_PI就是一個整圓。其他弧度,自己用M_PI去除就OK了。

注意,這裡畫線的方向是順時針的。

cairo_arc_negative(cr,x,y,radius,from,to);
這個函數功能跟上面的函數一樣,只是逆時針畫。



cairo_rotate(cr,angle);

接受弧度做參數,自己用M_PI去除吧。

旋轉函數,注意,使用時,它是影響全局的,也就是旋轉了之後,後面畫的一切都是旋轉的,所以,在旋轉之前,做一下cairo_save,完成需要的旋轉之後
做一下cairo_restore能方便一點。

cairo_fill(cr);
填充函數,將你前面畫的東西用指定顏色填充起來。
比如:
cairo_arc(cr,100,100,40,0,2*M_PI);//以100,100坐標為圓心畫一個半徑為40的圓(整圓)。
cairo_set_rgb_color(cr,1,0,0);//紅色
cairo_fill(cr);//這時候得到的就是一個紅色的實心圓。

如果是
cairo_arc(cr,100,100,40,0,2*M_PI);
cairo_set_line_width(cr,3);
cairo_set_rgb_color(cr,1,0,0);
cairo_stroke(cr);
這時候就畫了一個空心圓圈。

如果是
cairo_arc(cr,100,100,40,0,M_PI);
然後再作cairo_stroke(cr);
這時候得到的是一個開口的半圓,cairo並不會給你畫上直線。



cairo_scale(cr,x,y);

縮放圖形,謹慎使用,注意使用cairo_save和cairo_restore,這個函數可以在不同的surface上使用。
x指寬度縮放倍數,y指高度縮放的倍數。


字體操作:

cairo_font_scale(cr,num);

將字體縮放多少倍,僅影響字體。

cairo_select_font(cr, char* fontname, cairo_font_slant_t, cairo_font_weight_t );

更改字體函數, fontname就是字體名,比如SimSun, DongWen--Song等。
其中cairo_font_slant_t有
CAIRO_FONT_SLANT_NORMAL//正常
CAIRO_FONT_SLANT_ITALIC//斜體
CAIRO_FONT_SLANT_OBLIQUE//更斜體

cairo_font_weight_t有
CAIRO_FONT_WEIGHT_NORMAL//正常
CAIRO_FONT_WEIGHT_BOLD//粗體

比如我要設置一個粗斜體就是:
cairo_select_font(cr, "Nimbus Sans L", CAIRO_FONT_SLANT_ITALIC, CAIRO_FONT_WEIGHT_BOLD);

注意:中文字體全部沒有粗體,斜體變化,有興趣的可以看看
cairo_ft_font.c中的

cairo_ft_font_create_glyph函數,在FT_Outline_Get_Bitmap拿到bitmap之後
作一下偏移就可以拿到一個fake 的bold,不過,這個是治標不是治本,真正要搞定,還是要修改
freetype,有興趣的自己去看freetype的代碼吧。

也可以通過

cairo_ft_font_create();返回cairo_font_t *
cairo_set_font();接受參數cairo_t *和cairo_font_t *

之類的函數來操作字體,很遺憾,在0.4.0中是無效的。
在這裡我據個例子:
#include <fontconfig.h>

int main()
{
......
FcPattern *pattern;
FcPattern *matched;
FcResult error;
pattern=FcPatternCreate();
FcPatternAddString(pattern,FC_FAMILY,"Nimbus Sans L");
FcPatternAddInteger(pattern,FC_SLANT,FC_SLANT_ITALIC);
FcPatternAddInteger(pattern,FC_WEIGHT,FC_WEIGHT_BOLD);

matched = FcFontMatch(NULL,pattern, &error);
cairo_matrix_t *matrix = cairo_matrix_create();
cairo_font_t * cft = cairo_ft_font_create(matched,matrix);
cairo_set_font(cr,cft);

//可惜,以上代碼無效
......
}

還好,用select_font加上scale_font還能夠滿足要求,所以不這麼寫反而省事了。


Cairo畫筆拷貝操作:
cairo 提供了以下函數操作畫筆:
cairo_create();創建畫筆。
cairo_reference();增加引用計數1。
cairo_destroy();減少引用計數1,如果返回0,所有的資源均被釋放。
cairo_save();保存當前cairo畫筆。
cairo_restore();恢復上一次保存的cairo畫筆,也就是說必須跟cairo_save()配對。

如果僅僅做了一次cairo_save();卻使用了兩次cairo_restore(),那麼就會發生不可預料的效果。

看下面的例子:


cairo_t * cr;
cr = cairo_create(); //聲明畫筆並創建

FILE *file;
file=fopen("c.png","w");

cairo_set_target_png(cr,file,CAIRO_FORMAT_ARGB32,400,400);//設置目標為一個png文件,大小為400x400,色彩32位。

cairo_save(cr);//SAVE_1 保存畫筆,這時候,畫筆是干乾淨淨的,沒有任何操作。

cairo_rectangle(cr,10,10,200,200);
cairo_set_rgb_color(cr,0,0,1);
cairo_set_line_width(cr,4);
cairo_stroke(cr); //畫一個矩形框,藍色

cairo_move_to(cr,60,60);//將畫筆移動到 60x60坐標,注意:畫文字的時候,這個坐標是文字左下角坐標,如果你這時候移動到0,0坐標,
其實你的文字將被畫到400x400的畫布之外。切記。

cairo_select_font(cr,"Nimbus Sans L", 0,0); //選擇正體Nimbus Sans L,請根據你系統的fc-list自己選擇一個字體
cairo_scale_font(cr,60);//設置為60,雖然是scale,但是cairo內部實際上用的是字號
cairo_show_text(cr,"hehe");//顯示hehe,藍色,60號,Nimbus Sans L字體

cairo_restore(cr);//恢復到 SAVE_1,這時候的畫筆是干乾淨淨的。

cairo_save(cr);//SAVE_2 保存一下,沒準以後還會用到這個乾淨的畫筆。

cairo_move_to(cr,70,70);//移動到70,70
cairo_set_rgb_color(cr,1,0,0);
cairo_scale_font(cr,60);

cairo_show_text(cr,"hehe");//顯示文本hehe,紅色,60號,默認字體,如果上面不作restore,而你有天真的進行了scale_font,這時候,字體就是60x60倍了
//有可能根本就看不到了
cairo_restore(cr); //恢復到SAVE_2,這時候畫筆仍然是初始狀態。


/*畫了一個大半圓曲線
如果,上面你沒有恢復到畫筆的初始狀態,那麼,這時候cairo畫筆位於70,70,猜猜會有什麼結果
你將畫一條直線,捎帶一個大半圓曲線,畫線的方向是這樣的,從當前坐標(70,70)到大半圓的開頭,然後到大半圓結尾,
也就是順時針方向。
*/


cairo_set_rgb_color(cr,0,1,0);
cairo_arc(cr,100,100,60,0, 1.5*M_PI);
cairo_set_line_width(cr,3);
cairo_set_line_cap(cr,CAIRO_LINE_CAP_ROUND);
cairo_stroke(cr);

cairo_destroy(cr);
cairo_destroy(cr);
fclose(file);



Cairo Path:
path的具體翻譯不太好作,理解為路徑又不能表達意思,就知道這個東西是path吧。

上面提到了:在畫半圓的時候,因為前面進行了move_to,所以,他會使用move_to到的坐標作為起點。是不是我們必須cairo_save和cairo_restore呢?
不用,有path就可以了。

比如

cairo_t *cr;
cr = cairo_create();

cairo_set_target_....;

cairo_move_to(70,70);

cairo_set_rgb_color(cr,0,1,0);
cairo_arc(cr,100,100,60,0, 1.5*M_PI);
cairo_set_line_width(cr,3);
cairo_set_line_cap(cr,CAIRO_LINE_CAP_ROUND);
cairo_stroke(cr);


自己執行一下,看看結果,還是一條直線加大半圓。

我們修改代碼如下:

cairo_t *cr;
cr = cairo_create();

cairo_set_target_....;

cairo_move_to(70,70);
cairo_new_path(cr);
cairo_set_rgb_color(cr,0,1,0);
cairo_arc(cr,100,100,60,0, 1.5*M_PI);
cairo_set_line_width(cr,3);
cairo_set_line_cap(cr,CAIRO_LINE_CAP_ROUND);
cairo_stroke(cr);
cairo_close_path(cr);


另外,還有一個cairo_text_path(char * utf;

這個函數可以用來顯示字體,這個功能跟cairo_show_text(cairo_t *, char *)類似。

另外一個很重要的功能就是,他可以把字體當成path處理,所以,你可以作出任何特效和變換。

比如,作一個空心字:
cairo_text_path("Hello");
cairo_set_line_width(cr,1);
cairo_stroke(cr);
這是通過cairo_show_text()做不到的。



Cairo 後端:
1,png 後端:
#include <cairo-png.h>
FILE * file;
file = fopen("cairo_out.png","w");
cairo_set_target_png(cr,file,CAIRO_FORMAT_ARGB32,400,400);

解釋一下這個函數,這個函數接受5個參數,第一個參數就是cairo畫筆,第二個參數是要輸出的文件,第三個參數是色彩深度。
色彩深度的類型是cairo_format_t *,有:
CAIRO_FORMAT_ARGB32:32位
CAIRO_FORMAT_RGB24:32位
CAIRO_FORMAT_A8:8位
CAIRO_FORMAT_A1:1位

後兩個參數,分別是PNG圖像的寬度和高度(如果沒有例外,cairo中所有的函數都是寬度在前,高度在後);

2,PDF後端:
#include <cairo-pdf.h>
FILE *file;
file = fopen("cairo.pdf","w");
double width = 10;//英吋
double height = 10;//英吋
double xPPI=10;//每英吋像素
double yPPI=10;//每英吋像素
cairo_set_target_pdf(cr,file,width,height,xPPI,yPPI);

3,PS後端:
函數原型跟pdf基本一樣。

#include <cairo-ps.h>
FILE *file;
file = fopen("cairo.ps","w");
double width = 10;//英吋
double height = 10;//英吋
double xPPI=10;//每英吋像素
double yPPI=10;//每英吋像素
cairo_set_target_ps(cr,file,width,height,xPPI,yPPI);

4,X後端:
也就是用來直接在X應用上畫圖,所以,所有基於xlib的應用全部都可以使用這種方式,比如qt和gtk等等

對Xlib應用來說,舉例:

Display *dpy;
Window win;
cairo_set_target_drawable (cr, dpy, win);
第一個參數必須是Display *,第二個參數可以是任何Drawable的內容,比如一個Pixmap.
Pixmap pix;
pix = XCreatePixmap(.....);
然後,再把pix draw到窗口上等等,總之怎麼作都行了。

對於qt應用,可以直接在控件上畫,也可以直接在QPixmap上畫,總之,凡是從
QPaintDevice類派生來的都可以。

比如:
QPixmap pixmap;
pixmap.resize(size());
Display *dpy = pixmap.x11AppDisplay();
Drawable drw = pixmap.handle();
cairo_set_target_drawable(cr,dpy,drw);
然後再把這個pixmap bitBlt到控件上。

同樣,也可以直接在控件上畫,也就是this.x11AppDisplay()和this.handle()
5,通用target,cairo_set_target_image()
使用方法;
char * bitmap;
int width=1024;
int height=768;
int stride = width*4
bimap = malloc(stride*height);
cairo_set_target_image(cr,bitmap,CAIRO_FORMAT_ARGB32,width,height,stride);
最後一個參數表示步進。


如何draw到framebuffer?很簡單了:
打開device,將device mmap到內存,然後畫就可以了。





幾個rel函數:
cairo_rel_move_to();
cairo_rel_line_to();
cairo_rel_curve_to();

什麼意思?相對坐標的意思。

其實很簡單,就是以你當前的點為坐標的原點,然後在移動或者畫到制定的位置。

比如:
cairo_move_to(cr,10,10);
cairo_rel_move_to(cr,10,10);
這時候點就在20,20上。

cairo_move_to(cr,20,10);
cairo_line_to(cr,100,100);
這時候畫了一跟線,起點坐標是(20,10),終點坐標是(100,100);
如果是
cairo_move_to(cr,20,10);
cairo_rel_line_to(cr,100,100);
這時候的線其實是從(20,10)畫到了(120,110);


也就是cairo_rel_line_to(cr,100,100);
等於
cairo_line_to(cr,120,110);

同樣cairo_curve_to也是一樣的算法。



取點函數:
cairo_current_point(cr,double *, double *);
double x;
double y;
cairo_move_to(cr,10,10);
cairo_rel_line_to(cr,70,100);
cairo_current_point(cr,&x,&y);
printf("x: %lf, y: %lf\n",x,y);
輸出結果就是:
80.000000
110.000000




scale和rotate

看下面一個例子:
cairo_t *cr;
cr = cairo_create();

cairo_set_target_.....;
cairo_rotate(cr,M_PI/6);//順時針旋轉30度。如果是-M_PI/6,也就是逆時針旋轉30度了。

cairo_rectangle(cr,10,10,100,100);//正方形
cairo_set_rgb_color(cr,1,0,0);
cairo_set_line_width(cr,10);
cairo_stroke(cr);

這時候,發現正方形確實順時針旋轉了三十度,但是還是正方形。


看這個:
cairo_t *cr;
cr = cairo_create();

cairo_set_target_.....;
cairo_scale(cr,1,0.5);也就是水平方向不變,垂直方向縮小為一半。

cairo_rotate(cr,M_PI/6);//順時針旋轉30度。如果是-M_PI/6,也就是逆時針旋轉30度了。

cairo_rectangle(cr,10,10,100,100);//正方形,經過了scale之後,變成了長方形。
cairo_set_rgb_color(cr,1,0,0);
cairo_set_line_width(cr,10);
cairo_stroke(cr);

注意,首先,上下兩條邊的粗細變成了原來的一半,同時,這個圖形不是一個直角矩形,而成了一個平行四邊形。
為什麼?

因為:
進行了scale之後,影響垂直方向的所有變換,都變成了原來的一半。所以,本來應該是每條邊都旋轉30度,現在
上下兩條邊都變成了旋轉一半的度數,也就是15度。

當然,如果你要旋轉一個長方形,只要直接用cairo_rectangle畫一個長方形就OK了

注意,cairo_rotate隻影響其後的內容,所以,不能在畫完了在rotate。

「轉貼」用 cairo 實現跨平台圖形(3)

7.正確的 cairo 術語
在討論任何技術時,都要使用正確的術語。cairo API 的術語分為三類:核心繪圖術語、外表術語和與字體相關的術語(更多細節見 參考資料)。
首先,cairo 有一個繪圖上下文(drawing context),這相當於畫家的畫布。上下文是 cairo_t 類型的,要呈現圖形,就必須有一個上下文。在繪圖上下文上的常見操作是繪製 Bezier 圖形、直線和曲線。表示一系列曲線和相關數據的 cairo 術語是路徑(path)。可以繪製這些路徑,並設置筆畫寬度或填充。
使用一個非常簡單的 API 將坐標轉換為路徑。這個 API 非常有意義,因為它使我們不必考慮線性代數或圖形學課本中討論的複雜的轉換矩陣。可以對 cairo 繪圖操作執行任何轉換,包括對圖形或部分圖形進行剪切、縮放或旋轉。通過指定點來繪製每條路徑。cairo 按照將點連接起來的方法執行操作。在後面將提供一個示例。
接下來,討論各種 cairo 外表類型。有幾種 cairo 外表類型,它們分別對應一種輸出目標。cairo 外表(surface)是執行繪圖的位置。具體地說,有用於圖像(內存緩衝區)的外表、用於 Open GL 的 glitz 外表、用於呈現文檔的 PDF 和 PostScript 外表以及用於直接執行繪圖的 XLib 和 Win32 外表。這些外表類型都派生自外表基類型 cairo_surface_t。
在 cairo 中,模式(pattern)是一種可以讀取的內容,它用作繪圖操作的源或蒙板。cairo 中的模式可以是實體模式、基於外表的模式甚至逐變模式。
到目前為止,我們只談到了筆畫路徑。但是,筆畫路徑一般只能產生不怎麼有趣的線條圖。實際上,簡單的線條筆畫只是 cairo 中的 5 種基本繪圖操作之一。這 5 種操作是:
(1)cairo_stroke
(2)cairo_fill
(3)cairo_show_text/cairo_show_glyphs
(4)cairo_paint
(5) cairo_mask
儘管簡單的線條繪圖很方便,但是它們的表達能力不夠強,不足以表示字體這樣的複雜圖形。cairo 為字體提供了一個基類 cairo_font_face_t。cairo 支持可縮放字體,其中包含給定字體大小的緩存標準。另外,可以用各種字體選項控制如何顯示給定的字體。在使用 cairo 時,在 UNIX 上常用的字體是 Freetype 字體,在 Windows 平台上使用 Win32 字體。
8.cairo 的發展過程
cairo 的版本號採用與 Linux 內核相似的規則 —— 也就是,奇數版本是實驗性的開發版本,不適合在生產環境中使用。偶數版本是穩定版本:最初的 1.0 版本主要關注順利地向用戶提供 API 和產生高質量輸出。1.2 API 完善了幾個開發不太完整的後端,當前的 1.4 系列主要關注優化和添加新功能。
cairo 的開發人員提供了一些出色的示例代碼片段,演示了 cairo API 的各種功能。所以不必等待 cairo 的下一個版本了,現在就下載並試用當前版本!

「轉貼」用 cairo 實現跨平台圖形(2)

3.為什麼要學習一種新的繪圖模型?

坦率地說,我認為目前的開放源碼解決方案在幾個方面存在欠缺。xprint 的優點是提供了統一的顯示和打印 API,但是它通常作為單獨的服務器進程運行,而且它的 API 很糟糕。libgnomeprint 提供單獨的打印和顯示模型,但是打印和繪圖 API 的分離使屏幕和打印機的呈現效果產生差異。
cairo 從以前的繪圖庫借鑑了許多經驗,而且從一開始就以實現統一 API 為設計目標。
4.cairo呈現目標
cairo 可以呈現以下輸出格式:
(1)X Window System(如果可能的話,會利用 Render 擴展)
(2)OpenGL(使用 glitz 後端)
(3)In-Memory Images(pixbuffs 等等)
(4)PostScript(適用於打印)
(5)PDF(Portable Document Format)文件
(6)SVG(Scalable Vector Graphics)格式
但是,並非所有呈現目標都產生相同的效果。儘管 cairo 力求在各種後端上產生相同的輸出,但是每種後端各有優勢。例如,PDF 後端會儘可能使用矢量計算(只在必要時生成圖像),而 PostScript 後端實際上會為每個頁面生成一個大圖像。
cairo 中的呈現模型受到許多原有技術的影響。cairo 採用了 PostScript 中的路徑、筆畫(stroke)和填充(fill)概念,還實現了 PDF 和現代 X 服務器實現的呈現擴展中的 Porter-Duff 圖像組合技術。另外,cairo 還實現了剪切、蒙板和漸變等補充特性。
5.cairo 的實際應用情況
許多有影響力的開放源碼項目已經採用了 cairo,cairo 已經成為 Linux 圖形領域的重要軟件。已經採用 cairo 的重要項目包括:
(1)Gtk+,一個廣受喜愛的跨平台圖形工具集
(2)Pango,一個用於佈置和呈現文本的免費軟件庫,它主要用於實現國際化
(3)Gnome,一個免費的桌面環境
(4)Mozilla,一個跨平台的 Web 瀏覽器基礎結構,Firefox 就是在這個基礎結構上構建的
(5)OpenOffice.org,一個可以與 Microsoft Office 匹敵的免費辦公套件
6.用cairo進行概念性繪圖
在用 cairo 進行繪圖時,最簡單的操作就像手工繪畫時一樣:選擇繪圖介質,選擇畫筆,選擇顏色,考慮線條的佈局,然後進行實際繪製。cairo 文檔通過與畫家的作畫過程進行類比來介紹 cairo 操作,下面的說明也採用這種方式。
對於畫家來說,最容易的事兒通常是選擇空白的繪圖介質。在現實世界中,藝術家可以選擇在紙上、畫布上甚至牆上作畫。在 cairo 中,也必須選擇空白的繪圖介質。在用 cairo 進行繪圖時,需要設置一個 cairo 上下文,這是主對象。在這個上下文中,可以選擇目標外表,比如 PostScript 文件、PDF 文檔或屏幕圖像。這樣就可以選擇我們要在什麼東西上進行繪圖。
現在考慮畫家的下一項任務:選擇畫筆。畫家往往花費相當長的時間來選擇形狀和大小合適的畫筆。在 cairo 中,也有畫筆形狀的概念,這由筆畫寬度來表達。不同的筆畫寬度會產生不同粗細的線條。
接下來,cairo 用戶需要以精確的坐標來表達繪圖操作,這一點與現實世界中的繪畫過程不同。藝術家只需將畫筆放在紙上的大概位置,但是計算機必須知道進行繪圖的 x 和 y 坐標。
設置了畫筆和開始繪圖的位置之後,就需要想像筆畫的形狀。簡單的圖形可能由直線組成,但是與現實的畫家一樣,也可以繪製曲線和弧線。
最後,必須定義筆畫終止的位置。同樣,這個位置也要用一對 (x,y) 坐標來指定。
另外,還可以給繪製的對象塗上顏色。按照 cairo 術語,這稱為填充(fill)。對於上面描述的每種操作,cairo 中都有對應的簡便的 API 實現。後面會介紹其中一些 API。
這些基本操作可以幫助您構建一些非常複雜的圖形。甚至可以使用 cairo 執行畫家無法輕鬆完成的工作,比如對現有的圖形進行放大等轉換,或者將圖形移動到虛擬紙張上的其他地方。
GIMP 或 Photoshop 也允許執行其中的許多操作,但是 cairo 是不同的:cairo 是一種編程式繪圖方式。GIMP 和 Photoshop 「在幕後」 使用 cairo 這樣的工具來實現繪圖。在用這些工具進行繪圖時,通過鼠標操作自動設置坐標點和工具類型(比如方框),並通過 GUI 環境選擇畫筆和筆畫寬度。如示例代碼所示(見 下載),cairo 需要顯式的交互,比如 「使用筆畫寬度 1 繪製一個弧線,半徑為 10,中心在位置 z」。

「轉貼」用 cairo 實現跨平台圖形(1)

cairo 的目標是以跨平台的方式在打印機和屏幕上產生相同的輸出,它正在成為 Linux® 圖形領域的重要軟件。GNOME、GTK+、Pango 等許多軟件已經使用了它提供的 2D 功能。

1.cairo 的優點和用途
cairo 是一個免費的矢量繪圖軟件庫,它可以繪製多種輸出格式。cairo 支持許多平台,包括 Linux、BSD、Microsoft® Windows® 和 OSX(BeOS 和 OS2 後端也正在開發)。Linux 繪圖可以通過 X Window 系統、Quartz、圖像緩衝格式或 OpenGL 上下文來實現。另外,cairo 還支持生成 PostScript 或 PDF 輸出,從而產生高質量的打印結果。在理想情況下,cairo 的用戶可以在打印機和屏幕上獲得非常接近的輸出效果。
本文將向您介紹 cairo,以及它對應用程序的意義。本文的示例將產生一個 pdf、ps、png、svg 和 gtk 窗口,顯示的圖形是 IBM 徽標。
cairo 的一項主要設計目標是提供儘可能接近的輸出。這種一致的輸出使 cairo 非常適合 GUI 工具集編程和跨平台應用程序開發。使用同一個繪圖庫打印高分辨率的屏幕和繪製屏幕內容,這種功能具有顯著的優點。
另外,在支持的每種目標平台上,cairo 嘗試智能化地使用底層硬件和軟件支持。高質量矢量圖形和高性能的結合使 cairo 成為優秀的 UNIX® 繪圖系統。
cairo 是用 C 編寫的,但是為大多數常用的語言提供了綁定。選用 C 語言有助於創建新的綁定,同時在進行 C 語言調用時可以提供高性能。應該特別注意 Python 綁定,它支持快速原型開發,而且降低了學習 cairo 繪圖 API 的門檻。
計算機繪圖系統和體系結構很早就出現了,cairo 的設計借鑑了 PostScript 和 PDF 模型的許多經驗。cairo 之所以借鑑 PostScript 和 Portable Document Format(PDF)方法是因為,它們都使用數學語句定義圖像。由於用幾何方法表示圖像,所以可以在任何時候在一定範圍內計算幾何描述,從而重新創建整 個圖像(或一部分圖像)。圖形的幾何性質被表示為點、曲線和直線(這些元素構成了矢量)。
因為可以通過重新計算數學描述來重新繪製圖像或其組成部分,所以在對圖像進行放大、縮小或變換時不會出現分辨率損失。但是,矢量繪圖也有一些限 制。例如,如果對矢量圖像進行極高比例的縮放,超出了大多數人實際需要的範圍,就可能導致失真。在放大時,由於計算中要對誤差進行舍入,所以某些直線可能 看起來不正確。在縮小時,某些直線可能會無法看到或難以分辨。
cairo 採用矢量繪圖還有一個優點:矢量圖像往往比較小。這是因為用相當簡單的數學表達式就可以表達大量信息。矢量繪圖的優點是繪圖過程相當簡單。由繪圖庫負責將點、線以及相關表達式轉換為用戶可以看到的東西。
描述曲線的表達式稱為 Bezier 曲線或路徑,這個名稱源自數學家 Pierre Bezier。Bezier 曲線由至少兩個錨點(anchor point)組成,在錨點之間還有一個或多個點,這些點稱為控制點(handle)。移動控制點就可以改變曲線的形狀。如果您用過 Photoshop 或 GIMP 這樣的工具,那麼可能熟悉這種曲線。但是,在使用那些工具時,最終保存的圖形格式可能是位圖!文件的格式決定是否保留 Bezier 路徑信息,還是在給定的範圍內計算它並以位圖格式保存計算結果。
到編寫本文時,可用的 cairo 綁定包括 C++、Ruby、Perl、Java™ 語言和 .Net/mono 等等。這些綁定處於不同的開發狀態,成熟度各不相同;可以通過 cairo 項目主頁瞭解它們的最新情況(見 參考資料 中的鏈接)。目前,Python 和 C++ 綁定似乎在開放源碼社區中得到了廣泛採用。
正如前面提到的,一些圖形工具集也為 cairo 提供了綁定,這使 cairo 開發更加簡便了。2.8 之後的 Gtk+ 版本包含對 cairo 的完整支持,而且以後的 GTK 版本一直選用 cairo 作為基本繪圖系統。另外,GNUstep 和 FLTK 等工具集已經開始支持用 cairo 來滿足圖形呈現需求。
如果您打算進行任何跨平台開發,而且需要對繪圖操作和組合進行低層控制,那麼選用 cairo 作為繪圖 API 是非常合適的。如果需要跨平台功能,但是不需要低層繪圖控制,那麼可以使用在 cairo 之上構建的一些繪圖庫。
2.矢量繪圖與位圖繪圖
cairo 是一個矢量繪圖(vector drawing)庫,因此繪圖需要對圖形進行幾何描述,而不是描述位圖中填充的像素。在採用位圖繪圖(bitmap drawing)時,按照預先決定的佈局用預先決定的顏色填充一系列像素,而且圖形的質量與位圖的大小成正比。
在放大或修改位圖圖像時,位圖繪圖方法的效果就會變差。圖像常常會變得模糊,就像是近距離觀看背投電視或其他大屏幕電視時的效果。在某一距離 上,圖像可能看起來很清楚,但是靠近之後就會看到許多離散的點。因為數據無法定義預先定義的像素之間應該是什麼,所以放大時會很明顯地損失清晰度。

「轉貼」用 cairo 實現跨平台圖形

cairo 的目標是以跨平台的方式在打印機和屏幕上產生相同的輸出,它正在成為 Linux® 圖形領域的重要軟件。GNOME、GTK+、Pango 等許多軟件已經使用了它提供的 2D 功能。

cairo 的優點和用途

cairo 是一個免費的矢量繪圖軟件庫,它可以繪製多種輸出格式。cairo 支持許多平台,包括 Linux、BSD、Microsoft® Windows® 和 OSX(BeOS 和 OS2 後端也正在開發)。Linux 繪圖可以通過 X Window 系統、Quartz、圖像緩衝格式或 OpenGL 上下文來實現。另外,cairo 還支持生成 PostScript 或 PDF 輸出,從而產生高質量的打印結果。在理想情況下,cairo 的用戶可以在打印機和屏幕上獲得非常接近的輸出效果。

本文將向您介紹 cairo,以及它對應用程序的意義。本文的示例將產生一個 pdf、ps、png、svg 和 gtk 窗口,顯示的圖形是 IBM 徽標。

cairo 的一項主要設計目標是提供儘可能接近的輸出。這種一致的輸出使 cairo 非常適合 GUI 工具集編程和跨平台應用程序開發。使用同一個繪圖庫打印高分辨率的屏幕和繪製屏幕內容,這種功能具有顯著的優點。

另外,在支持的每種目標平台上,cairo 嘗試智能化地使用底層硬件和軟件支持。高質量矢量圖形和高性能的結合使 cairo 成為優秀的 UNIX® 繪圖系統。

cairo 是用 C 編寫的,但是為大多數常用的語言提供了綁定。選用 C 語言有助於創建新的綁定,同時在進行 C 語言調用時可以提供高性能。應該特別注意 Python 綁定,它支持快速原型開發,而且降低了學習 cairo 繪圖 API 的門檻。

矢量繪圖與位圖繪圖

cairo 是一個矢量繪圖(vector drawing)庫,因此繪圖需要對圖形進行幾何描述,而不是描述位圖中填充的像素。在採用位圖繪圖(bitmap drawing)時,按照預先決定的佈局用預先決定的顏色填充一系列像素,而且圖形的質量與位圖的大小成正比。

在 放大或修改位圖圖像時,位圖繪圖方法的效果就會變差。圖像常常會變得模糊,就像是近距離觀看背投電視或其他大屏幕電視時的效果。在某一距離上,圖像可能看 起來很清楚,但是靠近之後就會看到許多離散的點。因為數據無法定義預先定義的像素之間應該是什麼,所以放大時會很明顯地損失清晰度。

計 算機繪圖系統和體系結構很早就出現了,cairo 的設計借鑑了 PostScript 和 PDF 模型的許多經驗。cairo 之所以借鑑 PostScript 和 Portable Document Format(PDF)方法是因為,它們都使用數學語句定義圖像。由於用幾何方法表示圖像,所以可以在任何時候在一定範圍內計算幾何描述,從而重新創建整 個圖像(或一部分圖像)。圖形的幾何性質被表示為點、曲線和直線(這些元素構成了矢量)。

因 為可以通過重新計算數學描述來重新繪製圖像或其組成部分,所以在對圖像進行放大、縮小或變換時不會出現分辨率損失。但是,矢量繪圖也有一些限制。例如,如 果對矢量圖像進行極高比例的縮放,超出了大多數人實際需要的範圍,就可能導致失真。在放大時,由於計算中要對誤差進行舍入,所以某些直線可能看起來不正 確。在縮小時,某些直線可能會無法看到或難以分辨。

cairo 採用矢量繪圖還有一個優點:矢量圖像往往比較小。這是因為用相當簡單的數學表達式就可以表達大量信息。矢量繪圖的優點是繪圖過程相當簡單。由繪圖庫負責將點、線以及相關表達式轉換為用戶可以看到的東西。

描述曲線的表達式稱為 Bezier 曲線或路徑,這個名稱源自數學家 Pierre Bezier。Bezier 曲線由至少兩個錨點(anchor point)組成,在錨點之間還有一個或多個點,這些點稱為控制點(handle)。 移動控制點就可以改變曲線的形狀。如果您用過 Photoshop 或 GIMP 這樣的工具,那麼可能熟悉這種曲線。但是,在使用那些工具時,最終保存的圖形格式可能是位圖!文件的格式決定是否保留 Bezier 路徑信息,還是在給定的範圍內計算它並以位圖格式保存計算結果。

到編寫本文時,可用的 cairo 綁定包括 C++、Ruby、Perl、Java™ 語言和 .Net/mono 等等。這些綁定處於不同的開發狀態,成熟度各不相同;可以通過 cairo 項目主頁瞭解它們的最新情況(見 參考資料 中的鏈接)。目前,Python 和 C++ 綁定似乎在開放源碼社區中得到了廣泛採用。

正 如前面提到的,一些圖形工具集也為 cairo 提供了綁定,這使 cairo 開發更加簡便了。2.8 之後的 Gtk+ 版本包含對 cairo 的完整支持,而且以後的 GTK 版本一直選用 cairo 作為基本繪圖系統。另外,GNUstep 和 FLTK 等工具集已經開始支持用 cairo 來滿足圖形呈現需求。

如果您打算進行任何跨平台開發,而且需要對繪圖操作和組合進行低層控制,那麼選用 cairo 作為繪圖 API 是非常合適的。如果需要跨平台功能,但是不需要低層繪圖控制,那麼可以使用在 cairo 之上構建的一些繪圖庫。



為什麼要學習一種新的繪圖模型?

坦 率地說,我認為目前的開放源碼解決方案在幾個方面存在欠缺。xprint 的優點是提供了統一的顯示和打印 API,但是它通常作為單獨的服務器進程運行,而且它的 API 很糟糕。libgnomeprint 提供單獨的打印和顯示模型,但是打印和繪圖 API 的分離使屏幕和打印機的呈現效果產生差異。

cairo 從以前的繪圖庫借鑑了許多經驗,而且從一開始就以實現統一 API 為設計目標。


cairo 呈現目標

cairo 可以呈現以下輸出格式:

  • X Window System(如果可能的話,會利用 Render 擴展)
  • OpenGL(使用 glitz 後端)
  • In-Memory Images(pixbuffs 等等)
  • PostScript(適用於打印)
  • PDF(Portable Document Format)文件
  • SVG(Scalable Vector Graphics)格式

但是,並非所有呈現目標都產生相同的效果。儘管 cairo 力求在各種後端上產生相同的輸出,但是每種後端各有優勢。例如,PDF 後端會儘可能使用矢量計算(只在必要時生成圖像),而 PostScript 後端實際上會為每個頁面生成一個大圖像。

cairo 中的呈現模型受到許多原有技術的影響。cairo 採用了 PostScript 中的路徑、筆畫(stroke)和填充(fill)概念,還實現了 PDF 和現代 X 服務器實現的呈現擴展中的 Porter-Duff 圖像組合技術。另外,cairo 還實現了剪切、蒙板和漸變等補充特性。


cairo 的實際應用情況

許多有影響力的開放源碼項目已經採用了 cairo,cairo 已經成為 Linux 圖形領域的重要軟件。已經採用 cairo 的重要項目包括:

  • Gtk+,一個廣受喜愛的跨平台圖形工具集
  • Pango,一個用於佈置和呈現文本的免費軟件庫,它主要用於實現國際化
  • Gnome,一個免費的桌面環境
  • Mozilla,一個跨平台的 Web 瀏覽器基礎結構,Firefox 就是在這個基礎結構上構建的
  • OpenOffice.org,一個可以與 Microsoft Office 匹敵的免費辦公套件

用 cairo 進行概念性繪圖

在用 cairo 進行繪圖時,最簡單的操作就像手工繪畫時一樣:選擇繪圖介質,選擇畫筆,選擇顏色,考慮線條的佈局,然後進行實際繪製。cairo 文檔通過與畫家的作畫過程進行類比來介紹 cairo 操作,下面的說明也採用這種方式。

對 於畫家來說,最容易的事兒通常是選擇空白的繪圖介質。在現實世界中,藝術家可以選擇在紙上、畫布上甚至牆上作畫。在 cairo 中,也必須選擇空白的繪圖介質。在用 cairo 進行繪圖時,需要設置一個 cairo 上下文,這是主對象。在這個上下文中,可以選擇目標外表,比如 PostScript 文件、PDF 文檔或屏幕圖像。這樣就可以選擇我們要在什麼東西上進行繪圖。

現在考慮畫家的下一項任務:選擇畫筆。畫家往往花費相當長的時間來選擇形狀和大小合適的畫筆。在 cairo 中,也有畫筆形狀的概念,這由筆畫寬度來表達。不同的筆畫寬度會產生不同粗細的線條。

接下來,cairo 用戶需要以精確的坐標來表達繪圖操作,這一點與現實世界中的繪畫過程不同。藝術家只需將畫筆放在紙上的大概位置,但是計算機必須知道進行繪圖的 x 和 y 坐標。

設置了畫筆和開始繪圖的位置之後,就需要想像筆畫的形狀。簡單的圖形可能由直線組成,但是與現實的畫家一樣,也可以繪製曲線和弧線。

最後,必須定義筆畫終止的位置。同樣,這個位置也要用一對 (x,y) 坐標來指定。

另外,還可以給繪製的對象塗上顏色。按照 cairo 術語,這稱為填充(fill)。對於上面描述的每種操作,cairo 中都有對應的簡便的 API 實現。後面會介紹其中一些 API。

這些基本操作可以幫助您構建一些非常複雜的圖形。甚至可以使用 cairo 執行畫家無法輕鬆完成的工作,比如對現有的圖形進行放大等轉換,或者將圖形移動到虛擬紙張上的其他地方。

GIMP 或 Photoshop 也允許執行其中的許多操作,但是 cairo 是不同的:cairo 是一種編程式繪圖方式。GIMP 和 Photoshop 「在幕後」 使用 cairo 這樣的工具來實現繪圖。在用這些工具進行繪圖時,通過鼠標操作自動設置坐標點和工具類型(比如方框),並通過 GUI 環境選擇畫筆和筆畫寬度。如示例代碼所示(見 下載),cairo 需要顯式的交互,比如 「使用筆畫寬度 1 繪製一個弧線,半徑為 10,中心在位置 z」。


正確的 cairo 術語

在討論任何技術時,都要使用正確的術語。cairo API 的術語分為三類:核心繪圖術語、外表術語和與字體相關的術語(更多細節見 參考資料)。

首 先,cairo 有一個繪圖上下文(drawing context),這相當於畫家的畫布。上下文是 cairo_t 類型的,要呈現圖形,就必須有一個上下文。在繪圖上下文上的常見操作是繪製 Bezier 圖形、直線和曲線。表示一系列曲線和相關數據的 cairo 術語是路徑(path)。可以繪製這些路徑,並設置筆畫寬度或填充。

使 用一個非常簡單的 API 將坐標轉換為路徑。這個 API 非常有意義,因為它使我們不必考慮線性代數或圖形學課本中討論的複雜的轉換矩陣。可以對 cairo 繪圖操作執行任何轉換,包括對圖形或部分圖形進行剪切、縮放或旋轉。通過指定點來繪製每條路徑。cairo 按照將點連接起來的方法執行操作。在後面將提供一個示例。

接下來,討論各種 cairo 外表類型。有幾種 cairo 外表類型,它們分別對應一種輸出目標。cairo 外表(surface)是 執行繪圖的位置。具體地說,有用於圖像(內存緩衝區)的外表、用於 Open GL 的 glitz 外表、用於呈現文檔的 PDF 和 PostScript 外表以及用於直接執行繪圖的 XLib 和 Win32 外表。這些外表類型都派生自外表基類型 cairo_surface_t。

在 cairo 中,模式(pattern)是一種可以讀取的內容,它用作繪圖操作的源或蒙板。cairo 中的模式可以是實體模式、基於外表的模式甚至逐變模式。

到目前為止,我們只談到了筆畫路徑。但是,筆畫路徑一般只能產生不怎麼有趣的線條圖。實際上,簡單的線條筆畫只是 cairo 中的 5 種基本繪圖操作之一。這 5 種操作是:

  • cairo_stroke
  • cairo_fill
  • cairo_show_text/cairo_show_glyphs
  • cairo_paint
  • cairo_mask

儘 管簡單的線條繪圖很方便,但是它們的表達能力不夠強,不足以表示字體這樣的複雜圖形。cairo 為字體提供了一個基類 cairo_font_face_t。cairo 支持可縮放字體,其中包含給定字體大小的緩存標準。另外,可以用各種字體選項控制如何顯示給定的字體。在使用 cairo 時,在 UNIX 上常用的字體是 Freetype 字體,在 Windows 平台上使用 Win32 字體。



cairo 示例應用程序

我編寫了一段繪製 IBM 徽標的 cairo 代碼。可以從下面的 下載 一節下載這段代碼。運行它應該會產生以下輸出:


圖 1. 用 cairo 生成的 IBM 徽標
用 cairo 生成的 IBM 徽标

請注意代碼中的 cairo_stroke (cr) 行,這一行出現在繪製字母之後,但在添加註冊標誌之前。如果不設置筆畫,cairo 就不繪製任何東西。忘記設置筆畫是初學者經常犯的錯誤。


cairo 的發展過程

cairo 的版本號採用與 Linux 內核相似的規則 —— 也就是,奇數版本是實驗性的開發版本,不適合在生產環境中使用。偶數版本是穩定版本:最初的 1.0 版本主要關注順利地向用戶提供 API 和產生高質量輸出。1.2 API 完善了幾個開發不太完整的後端,當前的 1.4 系列主要關注優化和添加新功能。

cairo 的開發人員提供了一些出色的示例代碼片段,演示了 cairo API 的各種功能(見 參考資料 一節中的鏈接)。所以不必等待 cairo 的下一個版本了,現在就下載並試用當前版本!


下載

描述 名字 大小 下載方法
繪製 IBM 徽標的 cairo 代碼 cairo-example.tar 20KB HTTP
關於下載方法的信息


參考資料

學習

獲得產品和技術
  • 索取 SEK for Linux,共有兩張 DVD,包含最新的 IBM Linux 試用版軟件,涉及 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere®。
  • 使用 IBM 試用軟件 構建您的下一個 Linux 開發項目,這些軟件可以從 developerWorks 直接下載。

討論


關於作者


Eli Dow 是位於 Poughkeepsie, NY. 的 IBM Linux Test and Integration Center 的一名軟件工程師。他擁有 Clarkson 大學的計算機科學和心理學學士學位,以及計算機科學碩士學位。他的興趣包括 GNOME 桌面、人與計算機的交互以及 Linux 系統編程。您可以通過 emdow@us.ibm.com 與 Eli 聯繫。