Cairo將成爲Linux 2D 繪圖的未來,相信我,沒錯的。這是一個筆記,另外還有一個cairo粗斜體顯示中文的補丁,這個補丁我永遠也不會向外放,因爲,這麼作,就跟firefly和akito的做法一樣,用一個錯誤的方法解決錯誤的問題。
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下所有的畫圖接口,那麼,所見所得可能就會成爲顯示。畢竟現在顯示和打印是兩套代碼
目前的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-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_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);//關閉文件
}
gcc -o t1 t1.c -lcairo -I/usr/include/cairo
運行./t1,就生成了一個png圖片文件,自己用工具打開看就可以了。
TIPS:
神來一筆,描線函數,也就是將一個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);
設置線寬。
設置線條兩端的顯示方式
CAIRO_LINE_CAP_ROUND,圓形,圓形的中心就是你定義的兩個端點
CAIRO_LINE_CAP_BUTT,方形,就從你定義的點開始。
CAIRO_LINE_CAP_SQUARE,方形,方形的中心就是你定義的兩個端點。
cairo_curve_to(cr,x1,y1,x2,y2,x3,y3)
通過三點定義一道弧線。
畫一個圓,圓心x,y,半徑,然後注意:指定弧度,比如從0,M_PI(在math.h裏)。就可以是一個半圓。
從0到2*M_PI就是一個整圓。其他弧度,自己用M_PI去除就OK了。
這個函數功能跟上面的函數一樣,只是逆時針畫。
做一下cairo_restore能方便一點。
填充函數,將你前面畫的東西用指定顏色填充起來。
比如:
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並不會給你畫上直線。
x指寬度縮放倍數,y指高度縮放的倍數。
字體操作:
其中cairo_font_slant_t有
CAIRO_FONT_SLANT_NORMAL//正常
CAIRO_FONT_SLANT_ITALIC//斜體
CAIRO_FONT_SLANT_OBLIQUE//更斜體
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中的
作一下偏移就可以拿到一個fake 的bold,不過,這個是治標不是治本,真正要搞定,還是要修改
freetype,有興趣的自己去看freetype的代碼吧。
cairo_set_font();接受參數cairo_t *和cairo_font_t *
在這裏我據個例子:
#include <fontconfig.h>
{
......
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);
cairo_matrix_t *matrix = cairo_matrix_create();
cairo_font_t * cft = cairo_ft_font_create(matched,matrix);
cairo_set_font(cr,cft);
......
}
Cairo畫筆拷貝操作:
cairo 提供了以下函數操作畫筆:
cairo_create();創建畫筆。
cairo_reference();增加引用計數1。
cairo_destroy();減少引用計數1,如果返回0,所有的資源均被釋放。
cairo_save();保存當前cairo畫筆。
cairo_restore();恢復上一次保存的cairo畫筆,也就是說必須跟cairo_save()配對。
cairo_t * cr;
cr = cairo_create(); //聲明畫筆並創建
file=fopen("c.png","w");
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_scale_font(cr,60);//設置爲60,雖然是scale,但是cairo內部實際上用的是字號
cairo_show_text(cr,"hehe");//顯示hehe,藍色,60號,Nimbus Sans L字體
cairo_set_rgb_color(cr,1,0,0);
cairo_scale_font(cr,60);
//有可能根本就看不到了
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);
fclose(file);
path的具體翻譯不太好作,理解爲路徑又不能表達意思,就知道這個東西是path吧。
不用,有path就可以了。
cr = cairo_create();
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);
自己執行一下,看看結果,還是一條直線加大半圓。
cr = cairo_create();
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_text_path("Hello");
cairo_set_line_width(cr,1);
cairo_stroke(cr);
這是通過cairo_show_text()做不到的。
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中所有的函數都是寬度在前,高度在後);
#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);
函數原型跟pdf基本一樣。
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等等
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_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_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_rotate(cr,M_PI/6);//順時針旋轉30度。如果是-M_PI/6,也就是逆時針旋轉30度了。
cairo_set_rgb_color(cr,1,0,0);
cairo_set_line_width(cr,10);
cairo_stroke(cr);
看這個:
cairo_t *cr;
cr = cairo_create();
cairo_scale(cr,1,0.5);也就是水平方向不變,垂直方向縮小爲一半。
cairo_set_rgb_color(cr,1,0,0);
cairo_set_line_width(cr,10);
cairo_stroke(cr);
爲什麼?
進行了scale之後,影響垂直方向的所有變換,都變成了原來的一半。所以,本來應該是每條邊都旋轉30度,現在
上下兩條邊都變成了旋轉一半的度數,也就是15度。