http://code.google.com/p/skia/
svn checkout http://skia.googlecode.com/svn/trunk/ skia-read-only
Skia引擎重要類簡介
(PS: 注意是簡介了,觀眾不要要求太高,我也是在摸索中整理的文檔)
1. SkCanvas
這個類是Skia引擎的一個核心類,他封裝了所有對設備進行的畫圖操作。這個類自身包含了一個設備的引用,以及一個矩陣和裁剪棧。所有的畫圖操作, 都是在經過棧內存放的矩陣變幻之后才進行的(這點和OpenGL類似)。當然,最終顯示給用戶的圖像,還必須經過裁剪堆棧的運算。
SkCanvas記錄着整個設備的繪畫狀態,而設備上面繪制的對象的狀態又是由SkPaint類來記錄的,SkPaint類作為參數,傳遞給不同 SkCanvas類的成員函數drawXXXX().(比如:drawPoints, drawLine, drawRect, drawCircle)。SkPaint類里記錄着如顏色(color), 字體(typeface), 文字大小(textSize), 文字粗細(strokeWidth), 漸變(gradients, patterns)等。
SkCanvas類的主要成員函數:
> 構造函數,給定一個Bitmap或者Device,在給定的這個對象上進行畫圖,Device可以為空。
SkCanvas(const SkBitmap& bitmap);
SkCanvas(SkDevice* device = NULL);
> setViewport, getViewport, 這2個函數只有在支持OpenGL視圖時才有效。
> save, saveLayer, saveLayerAlpha, restore, 這4個函數用於保存和恢復顯示矩陣,剪切,過濾堆棧,不同函數有不同的附加功能。
> 移位,縮放,旋轉,變形函數。
translate(SkiaScalar dx, SkiaScalar dy);
scale(SkScalar sx, SkScalar sy);
rotate(SkScalar degrees);
skew(SkScalar sx, SkScalar sy);
> 指定具體矩陣,進行相應的變換的函數,以上4個方法都可以通過定義特定的矩陣,再調用此函數實現。
cancat(const SkMatrix& matrix);
> 圖像剪輯,把指定的區域顯示出來。
clipRect(SkRect&...);
clipPath(SkPath&...);
clipRegion(SkRegion&...);
> 在當前畫布內畫圖,有以下多種畫圖方式:
drawARGB(u8 a, u8 r, u8 g, u8 b....) 給定透明度以及紅,綠,蘭3色,填充整個可繪制區域。
drawColor(SkColor color...) 給定顏色color, 填充整個繪制區域。
drawPaint(SkPaint& paint) 用指定的畫筆填充整個區域。
drawPoint(...)/drawPoints(...) 根據各種不同參數繪制不同的點。
drawLine(x0, y0, x1, y1, paint) 畫線,起點(x0, y0), 終點(x1, y1), 使用paint作為畫筆。
drawRect(rect, paint) 畫矩形,矩形大小由rect指定,畫筆由paint指定。
drawRectCoords(left, top, right, bottom, paint), 給定4個邊界畫矩陣。
drawOval(SkRect& oval, SkPaint& paint) 畫橢圓,橢圓大小由oval矩形指定。
drawCicle(cx, cy, radius, paint), 給定圓心坐標和半徑畫圓。
drawArcSkRect& oval...) 畫弧線,用法類似於畫橢圓。
drawRoundRect(rect, rx, ry, paint) 畫圓角矩形,x, y方向的弧度用rx, ry指定。
drawPath(path, paint) 路徑繪制,根據path指定的路徑繪制路徑。
drawBitmap(SkBitmap& bitmap, left, top, paint = NULL) 繪制指定的位圖, paint可以為空。
drawBitmapRect(bitmap, src, dest, paint=NULL), 繪制給定位圖的一部分區域,此區域由src指定,然后把截取的部分位圖繪制到dest指定的區域,可能進行縮放。
drawBitmapMatrix(bitmap, matrix, paint=NULL), 功效同上,可以通過給定矩陣來進行裁剪和縮放變換。
drawSprite(bitmap, left, top, paint=NULL), 繪制位圖,不受當前變換矩陣影響。
drawText(void* text, byteLength, x, y, paint), 以(x,y)為起始點寫文字,文字存儲在text指針內,長度有byteLength指定。
drawPosText(...) 功能同上,不過每個文字可以單獨指定位置。
drawPosTextH(...) 功能同上,不過由一個變量指定了當前所有文字的統一Y坐標,即在同一條水平線上以不同的間隔寫字。
drawTextOnPathHV, drawTextOnPath, drawTextOnPath, 以不同方式在給點定的path上面繪制文字。
drawPicture(SkPicture& picture) 在畫布上繪制圖片,比較高效的繪圖函數。
drawShape(SkShape*) 在畫布上繪制指定形狀的圖像。
drawVertices(...) 繪制點,可以有紋理,顏色,等附加選項。
1 Skia 繪圖概述
Skia 是 Google 一個底層的圖形、文本、圖像、動畫等多方面的圖形庫,是 Android 中圖形系統的引擎。 Skia 作為第三方軟件放在 external 目錄下: external/skia/ 。 skia 的源文件及部分頭文件都在 src 目錄下,導出的頭文件在 include 目錄下。
使用 Skia 的 API 進行圖形繪制時主要會用到一下幾個類:
SkBitmap
SkCanvas
SkPaint
SkRect
其中
SkBitmap 用來設置像素,
SkCanvas 寫入位圖,
SkPaint 設置顏色和樣式,
SkRect 用來繪制矩形。
其實現代碼主要在 src/core 目錄下。
2 使用 Skia 繪圖的步驟
a) 定義一個位圖 32 位像素並初始化
SkBitmap bitmap;bitmap.setConfig(SkBitmap::kARGB_8888_Config, 200, 200);
其中 setConfig 為設置位圖的格式,原型為 void setConfig(Config, int width, int height, int rowBytes = 0)
Config 為一個數據結構
enum Config {
kNo_Config, // 不確定的位圖格式
kA1_Config, //1 位 ( 黑 , 白 ) 位圖
kA8_Config, //8 位 ( 黑 , 白 ) 位圖
kIndex8_Config, // 類似 windows 下的顏色索引表,具體請查看 SkColorTable 類結構
kRGB_565_Config, //16 位象素 565 格式位圖,詳情請查看 SkColorPriv.h 文件
kARGB_4444_Config, //16 位象素 4444 格式位圖,詳情請查看 SkColorPriv.h 文件
kARGB_8888_Config, //32 位象素 8888 格式位圖,詳情請查看 SkColorPriv.h 文件
kRLE_Index8_Config,
kConfigCount
};
b) 分配位圖所占的空間
bitmap.allocPixels()
其實 allocPixels 為重載函數,原型為 bool allocPixels(SkColorTable* ctable = NULL)
參數 ctable 為顏色索引表,一般情況下為 NULL 。
c) 指定輸出設備
SkCanvas canvas(new SkDevice(bitmap));
其中 canvas 為一個多構造函數,原型為explicit SkCanvas(const SkBitmap& bitmap) ,
explicit SkCanvas(SkDevice* device = NULL)
explicit 關健字的意思為:不允許類型轉換
輸出設備可以為一個上下文 Device, 也可以指定為一張位圖。
d) 設備繪制的風格
Paint paint;
SkRect r;
paint.setARGB(255, 255, 0, 0);
r.set(25, 25, 145, 145);
canvas.drawRect(r, paint);
paint 可以指定繪圖的顏色,文本的大小及對齊方式,編碼格式等等,因為以前位圖的格式設置為 kARGB_8888_Config ,所以這里要設置繪制的顏色 setARGB(255, 255, 0, 0) ,第一位參數為透明顏色通道,其它三位分別為 R 、 G 、 B 。 r 設置要繪制的范圍,最后通過 drawRect 繪制出指定區域的一個方形。這樣,一個紅色的矩形就繪制成功了。
SkCanvas 主要完成三種繪制功能:
a 基本圖形繪制 ( 如 drawARGB,drawLine 函數 )
b 圖像文件繪制( drawBitmap 函數)
c 文本繪制( drawText 函數)
相關 API 有:
canvas.drawRect(rect, paint);
canvas.drawOval(oval, paint);
canvas.drawCircle(x, y, radius, paint);
canvas.drawRoundRect(rect, rx, ry, paint);
canvas.drawPath(path, paint);
canvas.drawBitmap(bitmap, x, y, &paint);
canvas.drawBitmapRect(bitmap, &srcRect, dstRect, &paint);
canvas.drawBitmapMatrix(bitmap, matrix, &paint);
canvas.drawText(text, length, x, y, paint);
canvas.drawPosText(text, length, pos[], paint);
canvas.drawTextOnPath(text, length, path, paint);
e)
例程
i )畫點、線、圓、文字
#include "SkBitmap.h"
#include "SkDevice.h"
#include "SkPaint.h"
#include "SkRect.h"
#include "SkImageEncoder.h"
#include "SkTypeface.h"
using namespace std;
int main()
{
SkBitmap bitmap;
bitmap.setConfig(SkBitmap::kARGB_8888_Config,320,240);
bitmap.allocPixels();
SkCanvas canvas(new SkDevice(bitmap));
SkPaint paint;
// draw points with red.
paint.setARGB(255, 255, 0, 0);
paint.setStrokeWidth(4);
canvas.drawPoint(40,30, paint);
canvas.drawPoint(80,60, paint);
canvas.drawPoint(120,90, paint);
//draw a line with green.
paint.setARGB(255, 0, 255, 0);
paint.setStrokeWidth(4);
canvas.drawLine(160,10,320,110,paint);
//draw a circle with bule.
paint.setARGB(255, 0, 0, 255);
canvas.drawCircle(80,180,50,paint);
//draw text with red
SkTypeface *font = SkTypeface::CreateFromFile("simkai.ttf");
if ( font )
{
paint.setARGB(255, 255, 0, 0);
paint.setTypeface( font );
paint.setTextSize(24);
canvas.drawText("HELLO!:)", 8, 200, 180, paint);
}
SkImageEncoder::EncodeFile("snapshot.png", bitmap,SkImageEncoder::kPNG_Type,100);
return 0;
}
程序執行后,得到如下輸出結果:
ii) 圖像的編解碼
該例程目前測試只支持 .png 格式的圖片, .jpg 還不支持,還未找到原因。
#include "SkBitmap.h"
#include "SkDevice.h"
#include "SkPaint.h"
#include "SkRect.h"
#include "SkImageEncoder.h"
#include "SkImageDecoder.h"
#include <iostream>
using namespace std;
int main()
{
int ret = -1;
SkBitmap bitmap;
//SkImageDecoder
ret = SkImageDecoder::DecodeFile("./old.png", &bitmap);
cout<< "get the decode type = "<< bitmap.config() << endl;
//SkImageEncoder
ret = SkImageEncoder::EncodeFile("new1.png",bitmap,SkImageEncoder::kPNG_Type,100);
cout<< "encode data to png result = "<< ret<< endl;
return 0;
}
SkImageDecoder::DecodeFile("./old.png", &bitmap);
將 png 轉換成位圖格式,並將數據放到 bitmap 變量中
SkImageEncoder::EncodeFile("snapshot.png", bitmap,SkImageEncoder::kPNG_Type,/* Quality ranges from 0..100 */ 100);
將 bitmap 中的數據編碼輸出為 .png 格式,第一位參數為 png 文件路徑,第二位為指定的輸出位圖,第三位為文件的類型,第四位參數指定了輸出位圖的質量,范圍為 0..100 ,默認為 80 。
3 圖形圖像特效
src/effects 目錄的文件主要實現一些圖形圖像的特效,包括 遮罩、浮雕、模糊、濾鏡、漸變色、離散、透明以及 PATH 的各種特效等。
4 動畫
src/animator 目錄的文件主要實現了 Skia 的動畫效果,Android不支持。
5 界面 UI 庫
src/view 目錄 構建了一套界面 UI 庫。
組件包括 Window,Menu, TextBox, ListView, ProgressBar, Widget, ScrollBar,TagList,Image 等。
6 其它
a) src/gl 目錄: 這部分是 skia 調用 OpenGL 或 OpenGL ES 來實現 3D 效果。
如果定義了 MAC ,則使用 OpenGL ,如果定義了 Android ,則使用嵌入式 系統 上的 esgl 三維圖形庫。
b)src/images 目錄: 主要是 SkImageDecoder 和 SkImageEncoder 以及 SkMovie 。主要是用來處理 images 的,能處理的圖像類型包括: BMP 、 JPEG/PVJPEG 、 PNG 、 ICO ,而 SkMovie 是用來處理 gif 動畫的。
c) src/opts 目錄:性能優化的代碼。
d) src/pdf 目錄: 處理 PDF 文檔,用了一個 fpdfemb 庫。
e) src/ports 目錄: 這部分是 skia 的一些接口在不同系統上的實現,平台相關的代碼,比如字體、線程、時間等,主要包括幾個部分: Font , Event , File , Thread , Time , XMLParser
這些與 Skia 的接口,需要針對不同的 操作系統 實現。
f) src/svg 目錄: 矢量圖像,Android不支持。
SkSVGPath, SkSVGPolyline, SkSVGRect, SkSVGText, SkSVGLine, SkSVGImage, SkSVGEllipse 等等。
g) src/text 目錄:???
h) src/utils 目錄: 是一些輔助工具類。
SkCamera, SkColorMatrix,SkOSFile,SkProxyCanvas,SkInterpolator 等文件。
i) src/xml : 這是處理 xml 數據的部分, skia 在這里只是對 xml 解析器做了一層包裝,具體的 xml 解析器的實現需要根據不同的操作系統及宿主程序來實現。
j) Third-party library
除了自身的所有文件外, skia 還使用了一些 third-party library 以及包含了不少 linux 上的頭文件。
通過分析 skia 源程序,發現 skia 主要使用以下幾個第三方庫:
Zlib ,處理數據的壓縮和解壓縮
Jpeglib ,處理 jpeg 圖像的編碼解碼
Pnglib ,處理 png 圖像的編碼解碼
giflib ,處理 gif 圖像
fpdfemb ,處理 pdf 文檔
skia 還需要一些 linux/unix 下的頭文件(可能還需要更多):
stdint.h
unistd.h
features.h
cdefs.h
stubs.h
posix_opt.h
types.h
wordsize.h
typesizes.h
confname.h
getopt.h
mman.h
3D圖片旋轉,旋轉窗口我看行
SkBitmap *pskBitmap;
SkCanvas *pskCanvas;
BITMAPINFO *lpbmi;
HWND g_hWnd;
SkBitmap *bkBitmap;//背景圖片
SkRect g_rtImg;// 圖片最初按鈕。
SkRect g_rtClip;//矩陣裁剪用 ,做圖片旋轉時,每次旋轉時的裁剪會用到上一次的裁剪范圍。
//g_rtClip是共用裁剪范圍,多個不同的位置共用,每次旋轉前初始化為要旋轉圖片的原始位置
//初始化背景圖片,
void MyInitBkImage(char *filename)
{
SkFILEStream stream(filename);
SkImageDecoder * coder = SkImageDecoder::Factory(&stream);
if (coder)
{
bkBitmap = new SkBitmap();
coder->decode(&stream,bkBitmap,SkBitmap::kRGB_565_Config,SkImageDecoder::kDecodePixels_Mode);
}
}
//整體初始化
void MyInit()
{
pskBitmap = new SkBitmap();
pskBitmap->setConfig(SkBitmap::kRGB_565_Config,800,480);
pskBitmap->allocPixels();//分配位圖所占空間
pskCanvas = new SkCanvas();
pskCanvas->setBitmapDevice(*pskBitmap);
lpbmi = (BITMAPINFO*)malloc( sizeof(BITMAPINFO)+sizeof(RGBQUAD)*(2) );
//printf("%d,%d\n",sizeof(BITMAPINFOHEADER),sizeof(BITMAPINFO));40,44
memset( lpbmi, 0, sizeof(BITMAPINFO)+sizeof(RGBQUAD)*(2) );//必須同上方一直
lpbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);//位圖信息頭大小 40字節
lpbmi->bmiHeader.biWidth = 800;
lpbmi->bmiHeader.biHeight = -480;
lpbmi->bmiHeader.biPlanes = 1;
lpbmi->bmiHeader.biBitCount = 16; //16位位圖 565模式0xF800、0x07E0、0x001F
lpbmi->bmiHeader.biCompression = BI_BITFIELDS; //壓縮參數 BI_RGB=0表示無壓縮,
lpbmi->bmiHeader.biSizeImage = 0;
lpbmi->bmiColors[0].rgbBlue = 0;
lpbmi->bmiColors[0].rgbGreen = 0xF8; //248?
lpbmi->bmiColors[0].rgbRed = 0;
lpbmi->bmiColors[0].rgbReserved = 0;
lpbmi->bmiColors[1].rgbBlue = 0xE0; //224
lpbmi->bmiColors[1].rgbGreen = 0x07; //7
lpbmi->bmiColors[1].rgbRed = 0;
lpbmi->bmiColors[1].rgbReserved = 0;
lpbmi->bmiColors[2].rgbBlue = 0x1F; //31
lpbmi->bmiColors[2].rgbGreen = 0;
lpbmi->bmiColors[2].rgbRed = 0;
lpbmi->bmiColors[2].rgbReserved = 0;
MyInitBkImage("\\USER\\skia\\bk.png");
g_rtImg.setLTRB(151,214,249,346); //初始化圖片位置
//g_rtClip 在每次旋轉前初始化
}
//畫圖片filename,rt為其范圍,圖片沒有保存,每次臨時加載
void DrawImage(char * filename,SkCanvas *canvas, SkRect rt, SkPaint *paint)
{
int ti = GetTickCount();
SkFILEStream stream(filename);
SkImageDecoder* coder = SkImageDecoder::Factory(&stream);
SkBitmap *bitmap;
if (coder)
{
//printf(" file %s code success\n",filename);
bitmap = new SkBitmap();
coder->decode(&stream, bitmap, SkBitmap::kRGB_565_Config,
SkImageDecoder::kDecodePixels_Mode);
}
else
{
printf(" file %s code fail\n",filename);
return;
}
//printf("24bit800*480png,code time =%d\n",GetTickCount()-ti);//367
ti = GetTickCount();
canvas->drawBitmap(*bitmap,rt.fLeft, rt.fTop);
//printf("24bit800*480png,draw time =%d\n",GetTickCount()-ti);//12
delete bitmap;
return;
}
//畫背景
void DrawBKImage()
{
SkIRect rt;
rt.setXYWH(0,0,800,480);
int ti = GetTickCount();
pskCanvas->drawBitmap(*bkBitmap,rt.fLeft,rt.fTop);
printf("--------------time draw bk 24bit800*480=%d\n",GetTickCount()-ti);//11
}
//畫圖片filename,效果使其繞Y軸旋轉rotateY角度,調用DrawImage()
void DrawRotateYImage(char * filename,int rotateY,SkRect rtImg)
{
SkRect rtClip = g_rtClip;//保留上次裁剪范圍
pskCanvas->resetMatrix();
SkMatrix matrix;
Sk3DView sk3DView;
sk3DView.rotateY(rotateY); //繞Y軸旋轉
sk3DView.getMatrix(&matrix);
matrix.preTranslate(-(rtImg.fLeft+rtImg.width()/2), 0);
matrix.postTranslate((rtImg.fLeft+rtImg.width()/2), 0);
matrix.mapRect(&g_rtClip,rtImg); //兩個參數都是SkRect類型
//matrix.mapRect 作用:src經過matrix變化,形成dst
//圖片的最初范圍經過matrix變化(每次繞Y軸旋轉角度不一樣),得出新的裁剪范圍
rtClip.join(g_rtClip); //計算最終裁剪范圍
g_rtClip = rtClip ; //保存裁剪范圍,供下次計算最終裁剪范圍
//DrawBKImage();//此處畫背景,不然會保留不同ratateY角度圖片的痕跡 ,放在此處 畫背景用時多 ,為4或者3,
pskCanvas->save();
pskCanvas->clipRect(rtClip);//矩陣裁剪
DrawBKImage();//此處畫背景,不然會保留不同ratateY角度圖片的痕跡 放在此處畫背景用時小 ,為0或者1,
//具體畫的內容,與pskCanvas畫布的裁剪有關系?
//pskCanvas->save(SkCanvas::kMatrix_SaveFlag); 可以去掉,之前已經有pskCanvas->save();
pskCanvas->concat(matrix);
DrawImage(filename,pskCanvas,rtImg,NULL);
//此處的位置參數須是圖片原始位置,不能是裁剪范圍,否則顯示的位置偏離
//pskCanvas->restore(); 與save對應
pskCanvas->restore();
//pskCanvas->resetMatrix();
}
//觸發圖片旋轉函數
void MyLButtonDown()
{
g_rtClip = g_rtImg; //初始裁剪范圍為要畫圖片的正常范圍。
for (int i =0;i<=10;i++)
{
DrawRotateYImage("\\user\\skia\\music-n.png",36*(i+0),g_rtImg);
HDC dc = GetDC(g_hWnd);
SetDIBitsToDevice(dc, 0, 0, 800, 480, 0, 0, 0, 800, pskBitmap->getPixels(), lpbmi, DIB_RGB_COLORS);
//將數據顯示到屏幕上
ReleaseDC(g_hWnd,dc);
}
}