SKIA圖形庫


SKIA:
open source path: http://code.google.com/p/skia/
license : apache license2.0
應用項目 : android, chrome

Skia是一個完整的2D圖像庫,包括圖像、動畫、文本繪製功能、RGB/YUV(8~32byte)編解碼(jpeg,png)功能。

================================================================================

【Android圖片解碼流程】

1) APP:BitmapDecode.java

2) API:BitmapFactory.java(static image)、Movie.java(dynamic image)

3) JNI:BitmapFactory.cpp(static image)、Movie.cpp(dynamic image)

4) C Native Service:SkImageDecoder.cpp(static image)、SkMovie.cpp(dynamic image)  -  即SKIA庫
=================================================================================

【核心類】

SkCanvas
是Skia引擎的一個核心類,他封裝了所有對設備進行的畫圖操作。 這個類自身包含了一個設備的引用,以及一個矩陣和裁剪棧。
所有的畫圖操作,都是在經過棧內存放的矩陣變幻之後才進行的(這點和OpenGL類似)。當然,最終顯示給用戶的圖像,還必須經過裁剪堆棧的運算。

SkPaint
SkCanvas記錄着整個設備的繪畫狀態,而設備上面繪製的對象的狀態又是由SkPaint類來記錄的。
SkPaint類裏記錄着如顏色(color)、字體(typeface),、文字大小(textSize)、文字粗細(strokeWidth)、漸變(gradients, patterns)等,作爲參數,傳遞給不同SkCanvas類的成員函數drawXXXX().(比如:drawPoints, drawLine, drawRect, drawCircle)。

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個方法都可以通過定義特定的矩陣,再調用此函數實現。
            concat (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(...) 繪製點,可以有紋理,顏色,等附加選項。
            
======================================================================================

【Skia核心接口】

include/core/SkCanvas.h
Class SkCanvas:public SkRefnt
{
public:
drawARGB(...)
drawLine(....)
drawBitmap(....)
drawText(....)
}
4個 public函數用於draw 各種數據,這4個函數是 skia 最重要的函數。

include/images/SkImageDecoder.h
Class SkImageDecoder{
static bool DecodeMemory(....)
static bool DecodeFile(....)
static bool DecodeStream(....);
}
decoder 可以使用的3個decoder的函數對數據進行decoder.  Decoder 支持 jpeg, png, gif 等。

include/images/SkImageEncoder.h
Class SkImageEncoder
{
public:
static bool EncodeFile(....)
static bool EncodeStream(....)
}
encoder 可以使用上面2個函數 對數據進行 encoder。 例如:支持輸出jpeg 和 png,輸入支持rawdata RGB(8~32byte)編碼。

Skia 的使用 主要就是用來畫圖, 它的encoder  和 decoder  接口相當簡單,不過由於支持的類型不多,所以單純的 encoder 和 decoder 用的不多。

總體來說 skia 是個相對簡單的lib,如何需要畫圖和簡單的encoder 和 decoder 可以使用這個類的public 接口實現。
==========================================================================================

[Test decoder] 解碼png,得到bitmap,再編碼。

 #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; 
 
     ret = SkImageDecoder::DecodeFile("./snapshot.png", &bitmap); 
     cout<< "get the decode type = "<< bitmap.config() << endl; 
     
     ret = SkImageEncoder::EncodeFile("new.png", bitmap, 
         //SkImageEncoder::kJPEG_Type, 
         SkImageEncoder::kPNG_Type, 
         /* Quality ranges from 0..100 */ 100); 
     cout<< "encode data to jpg result = "<< ret<< endl; 
     return 0; 
 }
=======================================================================================

[test draw and encoder] 畫bitmap,再編碼得到png。

 #include "SkBitmap.h" 
 #include "SkDevice.h" 
 #include "SkPaint.h" 
 #include "SkRect.h" 
 #include "SkImageEncoder.h" 
 #include <iostream> 
 
 using namespace std; 
 
 int main() 
 { 
     int ret = -1; 
     SkBitmap bitmap; 
     bitmap.setConfig(SkBitmap::kARGB_8888_Config, 200, 200); 
     bitmap.allocPixels(); 
 
     SkCanvas canvas(new SkDevice(bitmap)); 
     SkPaint paint; 
 
     paint.setARGB(255, 255, 0, 0); 
     canvas.drawBitmap(bitmap, 25, 25); 
     canvas.drawText("fengguilai", 10, 25, 25, paint); 
     canvas.drawLine(25, 25, 100, 100, paint); 
     canvas.rotate(45); 
     paint.setARGB(255, 0, 255, 0); 
     r.offset(20, 20); 
     canvas.drawRect(r, paint); 
 
     paint.setARGB(255, 0, 0, 255); 
     r.offset(20, 20); 
     canvas.drawRect(r, paint); 
     ret = SkImageEncoder::EncodeFile("snapshot.png", bitmap, 
         //SkImageEncoder::kJPEG_Type, 
         SkImageEncoder::kPNG_Type, 
         /* Quality ranges from 0..100 */ 100); 
     cout<< "encode data to jpg result = "<< ret<< endl; 
     return 0; 
 }   
 =======================================================================================
 
 【註冊編解碼接口】
 
 Skia 定義了類template <typename T, typename P> class SkTRegistry : SkNoncopyable 
 SkTRegistry 內部實現爲一個鏈表,encoder decoder  codec都可以將自己的factory 函數註冊到 這個鏈表裏面。
 然後當需要創建 encoder decoder  codec 實例的時候,loop 這個 list 找到對應的 node, 然後調用factory函數。
 
external/skia/src/images/SkImageDecoder_Factory.cpp
external/skia/src/images/SkImageDecoder_Factory.cpp
這兩個文件中定義的SkImageDecoder::Factory和SkImageEncoder::Create,就是用來遍歷之前的list, 創建encoder decoder 實例。
由於通過template,class SkTRegistry 只要是不同的類型,就會有不同的gHeader, 所以不同類型都可以使用 SkTRegistry 而不發生衝突。

external/skia/src/images中有很多SkImageDecoder_lib<*>.cpp的文件,例如SkImageDecoder_libpng.cpp,這些文件就是使用第3方的lib來實現編碼和解碼的,註冊 encoder 和 decoder 到 SkTRegistry。
定義在文件末尾的static函數,就是通過SkTRegistry的構造函數,註冊factory 到 list,,這樣就告訴android有編碼解碼xx格式的能力了:
static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(EFactory); 
static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory); 
具體的編解碼功能,就是通過繼承class SkImageEncoder 和 SkImageDecoder,並實現其中的 onEncode 和 onDecode來實現第3方的lib。

=====================================================================================

如何利用硬件加速SKIA?

There are two ways Skia can take advantage of HW. 

1. SkiaGPU

一套獨立的實現,全部繪製都使用OpenGL,替代Skia的CPU實現。

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. NEON或OpenCL優化

優化SKIA的關鍵算法,例如使用NEON或OpenCL。

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/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章