Android Skia 和 2D 圖形系統
1 Skia 概述
Skia 是 Google 一個底層的圖形、圖像、動畫、 SVG 、文本等多方面的圖形庫,是 Android 中圖形系統的引擎。
Skia 作為第三方軟件放在 external 目錄下: external/skia/ 。
Skia 的結構如下圖所示:
主要包含三個庫:
libcorecg.so: 包含 /skia/src/core 的部分內容,比如其中的 Region , Rect 是在 SurfaceFlinger 里面用來計算可視區域的;
libsgl.so: 包含 /skia/src/core|effects|images|ports|utils 的部分和全部內容,這個實現了 skia 大部分的圖形效果,以及圖形格式的編解碼;
libskiagl.so: 包含 /skia/src/gl 里面的內容,主要用來調用 opengl 實現部分效果。
2 Skia 對上層的接口( API )
skia 的源文件及部分頭文件都在 external/skia/src 目錄下,導出的頭文件在 external/skia/include 目錄下。最主要的是 SKCanvas 類,幾乎整個 Android GUI 系統的底層繪制都是由這個類來完成的。其頭文件和源代碼文件的路徑分別為: external/skia/include/core/SKCanvas.cpp
external/skia/include/core/SKCanvas.h
SKCanvas 類主要有三種繪制功能:
a 基本圖形繪制 ( 如 drawARGB,drawLine 函數 )
b 圖像文件繪制( drawBitmap 函數)
c 文本繪制( drawText 函數)
3 Skia 的圖像編解碼部分
這部分的接口主要是:
external/include/image/SKImageDecoder.h // 把圖像文件或者流解碼到 skia 的內部內存 SKBitmap 中 ;
external/include/image/SKImageEncoder.h // 把 skia 內部內存 SKBitmap 編碼成文件或流的形式;
這些接口需要具體的類實現,主要代碼在 src/image 文件中。
4 Android 圖形系統的 JNI 接口
主要提供了從 Skia 底層庫到 Java 上層的支持,代碼路徑為:
frameworks/base/core/jni/android/graphic/
主要為 Canvas.cpp 文件。
5 Android 的圖形包( graphics )
Android 圖形類的包是 android.graphics, 它通過調用圖形系統的 JNI 提供了對 Java 框架中圖形系統的支持,在 Android 的 Java 框架中和 Java 應用程序中, 2D 繪制的功能(基本圖形、圖片文件,文字)也是通過調用它來實現的。代碼路徑為:
frameworks/base/graphics/java/android/graphics/
其中 Canvas.cpp 文件實現了 Android 圖形系統中最重要的一個類 android.graphic.canvas 。
6 Android 2D 圖形硬件加速
目前 Android 2D 圖形硬件加速主要是通過 copybit 模塊來實現, Copybit 是封裝在 Android 系統 opengl 軟件實現庫( libagl )的一部分,僅對 openGL ES 2D API 進行封裝,實現 openGL ES 2D API 到硬件的加速功能。
copybit 模塊以 HAL 的形式實現,代碼 hardware/msm7k/libcopybit/copybit.c
另外,在 http://code.google.com/p/skia/wiki/FAQ 看到關於 Skia 硬件加速和字體支持的相關信息:
Does Skia support HW acceleration?
There are two ways Skia can take advantage of HW.
1. Subclass SkCanvas
Since all drawing calls go through SkCanvas , those calls can be redirected to a different graphics API. SkGLCanvas has been written to direct its drawing calls to OpenGL. See src/gl/
2. Custom bottleneck routines
There are sets of bottleneck routines inside the blits of Skia that can be replace on a platform in order to take advantage of specific CPU features. One such example is the NEON SIMD instructions on ARM v7 devices. See src/opts/
Does Skia support Font hinting?
Skia has a built-in font cache, but it does not know how to actual render font files like TrueType into its cache. For that it relies on the platform to supply an instance of SkScalerContext. This is Skia's abstract interface for communicating with a font scaler engine. In src/ports you can see support files for FreeType, Mac OS X, and Windows GDI font engines. Other font engines can easily be supported in a like manner
Skia API的簡單應用
1 Skia 繪圖概述
使用 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