cairo的代碼

最近在看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。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章