文章目錄
- 3.1 註冊轉碼器
- 3.2 Encoder 和Decoder的Factory
- 3.3 註冊 encoder 和 decoder 到 SkTRegistry
- 3.4 通過第三方lib或硬體解決實現Encoder 或Decoder
1. 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(Skia):SkImageDecoder.cpp(static image)、SkMovie.cpp(dynamic image)
2. Skia功能介紹
Skia 是一個完整的2D映像庫,包括映像,動畫,文本繪製功能, RGB(8byte – 32byte)編碼(jpeg, png) 和解碼功能。(在android2.2 中支援 yuv 的編碼解碼)。.
1) 程式碼群組織
Skia 是一個 c++實現的程式碼程式庫,在android 中以擴充庫的形式存在,目錄為external/skia/。其中檔案 include/core/SkCavans.h 中定義了可以使用api.
Class SkCanvas:public SkRefnt
{
public:
drawARGB(...)
drawLine(....)
drawBitmap(....)
drawText(....)
}
4個 public函數用於draw 各種資料,這4個函數是 skia 最重要的函數。
Class SkImageDecoder{
static bool DecodeMemory(....)
static bool DecodeFile(....)
static bool DecodeStream(....);
}
decoder 可以使用的3個decoder的函數對資料進行decoder,Decoder 支援 jpeg, png, gif 等。
Class SkImageEncoder
{
public:
static bool EncodeFile(....)
static bool EncodeStream(....)
}
encoder 可以使用上面2個函數 對資料進行 encoder, 目前 encoder 只支援輸出為jpeg 和 png. 輸入只支援rawdata RGB(8byte – 32byte)編碼 。
如果有硬體的編碼和解碼器可以通過繼承SkImageDecoder和SkImageEncoder來實現硬體編碼解碼器。在 android 平台裡面 類skImageDecoder_libjpeg.cpp 就是通過繼承SkImageDecoder 使用類庫libjpeg 實現 jpeg 的解碼。
2) android 中如何支援skia
Skia 本身是一個 open source 的 project, 整合於android系統中。所以skia不是android 架構的一部分,不需要實現架構的api來支援skia。不過skia 同樣可以掛接其他的第3方編碼解碼庫或者硬體編解碼庫。
3. 分析Skia編解碼實現方案3.1 註冊轉碼器
Skia 定義了類template <typename T, typename P> class SkTRegistry : SkNoncopyable
SkTRegistry 內部實現為一個鏈表。代碼如下:
/** Template class that registers itself (in the constructor) into a linked-list and provides a function-pointer. This can be used to auto-register a set of services, e.g. a set of image codecs. */template <typename T, typename P> class SkTRegistry : SkNoncopyable {public: typedef T (*Factory)(P); SkTRegistry(Factory fact) {#ifdef ANDROID // work-around for double-initialization bug { SkTRegistry* reg = gHead; while (reg) { if (reg == this) { return; } reg = reg->fChain; } }#endif fFact = fact; fChain = gHead; gHead = this; } static const SkTRegistry* Head() { return gHead; } const SkTRegistry* next() const { return fChain; } Factory factory() const { return fFact; }private: Factory fFact; SkTRegistry* fChain; static SkTRegistry* gHead;};// The caller still needs to declare an instance of this somewheretemplate <typename T, typename P> SkTRegistry<T, P>* SkTRegistry<T, P>::gHead;
• SkTRegistry(Factory fact) 建構函式,用於註冊一個 fact 函數,在使用鏈表的時候,可以通過節點的fact 獲得需要的class, 如encoder 或 decoder codec.
• Ghead 成員變數,永遠指向最後一個節點。
• Fchain 指向前一個節點。
如果是encoder或decoder codec,就可以將自己的factory 函數註冊到這個鏈表裡面,然後當需要建立 encoder 或decoder codec 執行個體的時候,loop 這個list 找到對應的node, 然後調用factory函數。
3.2 Encoder 和Decoder的Factory
目錄 external/skia/src/images中有兩個檔案:
• SkImageDecoder_Factory.cpp
• SkImageEncoder_Factory.cpp
以上兩個檔案中定義了以下兩個重要函數:
SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) { SkImageDecoder* codec = NULL; const DecodeReg* curr = DecodeReg::Head(); while (curr) { codec = curr->factory()(stream); // we rewind here, because we promise later when we call "decode", that // the stream will be at its beginning. stream->rewind(); if (codec) { return codec; } curr = curr->next(); }#ifdef SK_ENABLE_LIBPNG codec = sk_libpng_dfactory(stream); stream->rewind(); if (codec) { return codec; }#endif return NULL;}
SkImageEncoder* SkImageEncoder::Create(Type t) { SkImageEncoder* codec = NULL; const EncodeReg* curr = EncodeReg::Head(); while (curr) { if ((codec = curr->factory()(t)) != NULL) { return codec; } curr = curr->next(); }#ifdef SK_ENABLE_LIBPNG if ((codec = sk_libpng_efactory(t)) != NULL) { return codec; }#endif return NULL;}
這兩個函數就是用來遍曆之前的list, 建立Encoder或Decoder 執行個體。由於通過template,class SkTRegistry 只要是不同的類型,就會有不同的gHeader, 所以不同類型都可以使用 SkTRegistry 而不發生衝突。
3.3 註冊 encoder 和 decoder 到 SkTRegistry
目錄external/skia/src/images 中有很多類似SkImageDecoder_lib<*>.cpp的檔案,這些檔案就是使用第3方的lib 來實現編碼和解碼的。在這些檔案中都有類似的代碼:(在檔案的末尾,並且沒有在檔案頭做聲明且是static)
static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(EFactory);
static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory);
定義在檔案的末尾且沒有在檔案頭做聲明且是static,目的只有一個通過SkTRegistry的建構函式註冊factory 到 list, 這樣就告訴android 我有編碼解碼某某格式的能力了。
如SkImageDecoder_libjpeg.cpp中的相關代碼如下:
#include "SkTRegistry.h"static SkImageDecoder* DFactory(SkStream* stream) { static const char gHeader[] = { 0xFF, 0xD8, 0xFF }; static const size_t HEADER_SIZE = sizeof(gHeader); char buffer[HEADER_SIZE]; size_t len = stream->read(buffer, HEADER_SIZE); if (len != HEADER_SIZE) { return NULL; // can't read enough } if (memcmp(buffer, gHeader, HEADER_SIZE)) { return NULL; } return SkNEW(SkJPEGImageDecoder);}static SkImageEncoder* EFactory(SkImageEncoder::Type t) { return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;}static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory);static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(EFactory);
3.4 通過第三方lib或硬體解決實現Encoder 或Decoder
通過繼承class SkImageEncoder 和 SkImageDecoder 並實現 onEncode 和 onDecode來通過第3方的lib實現Encoder或Decoder。