天天看點

cairo介紹

http://www.linuxfans.org/nuke/modules.php?name=Forums&file=viewtopic&t=111396&postdays=0&postorder=asc&highlight=cairo&start=0

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

#include

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

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

//可惜,以上代碼無效

cairo介紹

......

}

還好,用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_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 * utf8);

這個函數可以用來顯示字型,這個功能跟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。

目前,gtk-cairo 已經可用,可以draw出任意樣式的控件。

qt cairo 沒有明确定義,但是qt4應該已經明确要使用cairo ,目前qt4還沒有定型,太多的技術特征還沒有明确,是以,還不清楚到底怎麼樣,我跟過qt4preview版本,canvas的實作還是不錯的,性能有待提升。

cairo 可以用來寫theme,寫控件,或者就是作一個類似于畫闆一樣的畫圖工具,都可以。

當然cairo 可以用來畫特效,但是,cairo 的目标不在于特效,而且,cairo 能夠提供的隻是豐富的繪圖功能以及平滑效果,比如gtk color select,如果使用cairo ,那個圓圈要平滑的多。

如果用cairo 來作特效,其實還是要依賴算法,比如sproing這個例子,其實就是算法的例子,跟cairo 關系反而不大了,cairo 僅僅做了表示層的一個實作。

個人認為,cairo 真正的目的,不是在gtk/qt之上或者直接面向程式設計人員,他很有可能會成為基本庫,比如,沒有cairo ,構件一個豎寫的文本控件也不困難,但是,你還需要建構一個列印的實作來作豎寫的列印。

cairo 的特長就在于cross device 的rendering。也就是說使用同一套api函數來實作向任何裝置上的輸出。

windows是這麼作的,畫和打是一套函數。

是以,他是一個基礎2D繪圖庫。

有可能他會動搖X的根基或者成為xorg的一部分,走的更底層一點。

也有可能有朝一日跳出X這一層,而直接作為2D繪圖引擎。

總之,這是一個很好的方向。

目前,cairo 也有不完善的地方:

1,API命名有缺陷,比如cairo_current_point之類,不夠規範。

2,還沒有完全實作各種backend的統一,對一個text作rotate,輸出到ps/png和輸出到pdf的結果還是有差别的。

等等。

但是,呵呵,用cairo 畫出來的東西真的很漂亮。

下面的 Hello World 代碼可以在 2005.4.06 的 cairo 和 pixman 下編譯通過,Fedora Core 3,gcc version 3.4.2 20041017 (Red Hat 3.4.2-6.fc3)。反正可以跑,有沒有 memory leak 不知道。

開源就是好啊,否則這種一點文檔都沒有的東西,根本不可能用起來。

大家都來動手寫 Cairo 程式吧

cairo介紹
代碼:

#include <stdio.h>

#include <cairo .h>

#include <cairo -png.h>

int main()

{

  FILE *fp = fopen("hw.png", "w");

  cairo_t *cr = cairo_create();

  cairo_set_target_image_no_data(cr, CAIRO_FORMAT_ARGB32, 200, 100);

  cairo_select_font(cr, "Serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);

  cairo_scale_font(cr, 32.0);

  cairo_set_rgb_color(cr, 0.0, 0.3, 1.0);

  cairo_move_to(cr, 10.0, 50.0);

  cairo_show_text(cr, "Hello World");

  cairo_surface_write_png (cairo_get_target_surface (cr), fp);

  cairo_destroy(cr);

  fclose(fp);

}