分享

Android图片编解码实现方案(Skia) demo

 mandrave 2013-04-01

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 内部实现为一个链表。代码如下:

  1. /** Template class that registers itself (in the constructor) into a linked-list 
  2.     and provides a function-pointer. This can be used to auto-register a set of 
  3.     services, e.g. a set of image codecs. 
  4.  */  
  5. template <typename T, typename P> class SkTRegistry : SkNoncopyable {  
  6. public:  
  7.     typedef T (*Factory)(P);  
  8.   
  9.     SkTRegistry(Factory fact) {  
  10. #ifdef ANDROID  
  11.         // work-around for double-initialization bug  
  12.         {  
  13.             SkTRegistry* reg = gHead;  
  14.             while (reg) {  
  15.                 if (reg == this) {  
  16.                     return;  
  17.                 }  
  18.                 reg = reg->fChain;  
  19.             }  
  20.         }  
  21. #endif  
  22.         fFact = fact;  
  23.         fChain = gHead;  
  24.         gHead = this;  
  25.     }  
  26.   
  27.     static const SkTRegistry* Head() { return gHead; }  
  28.   
  29.     const SkTRegistry* next() const { return fChain; }  
  30.     Factory factory() const { return fFact; }  
  31.   
  32. private:  
  33.     Factory      fFact;  
  34.     SkTRegistry* fChain;  
  35.   
  36.     static SkTRegistry* gHead;  
  37. };  
  38.   
  39. // The caller still needs to declare an instance of this somewhere  
  40. template <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
   以上两个文件中定义了以下两个重要函数:

  1. SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {  
  2.     SkImageDecoder* codec = NULL;  
  3.     const DecodeReg* curr = DecodeReg::Head();  
  4.     while (curr) {  
  5.         codec = curr->factory()(stream);  
  6.         // we rewind here, because we promise later when we call "decode", that  
  7.         // the stream will be at its beginning.  
  8.         stream->rewind();  
  9.         if (codec) {  
  10.             return codec;  
  11.         }  
  12.         curr = curr->next();  
  13.     }  
  14. #ifdef SK_ENABLE_LIBPNG  
  15.     codec = sk_libpng_dfactory(stream);  
  16.     stream->rewind();  
  17.     if (codec) {  
  18.         return codec;  
  19.     }  
  20. #endif  
  21.     return NULL;  
  22. }  

 

  1. SkImageEncoder* SkImageEncoder::Create(Type t) {  
  2.     SkImageEncoder* codec = NULL;  
  3.     const EncodeReg* curr = EncodeReg::Head();  
  4.     while (curr) {  
  5.         if ((codec = curr->factory()(t)) != NULL) {  
  6.             return codec;  
  7.         }  
  8.         curr = curr->next();  
  9.     }  
  10. #ifdef SK_ENABLE_LIBPNG  
  11.     if ((codec = sk_libpng_efactory(t)) != NULL) {  
  12.         return codec;  
  13.     }  
  14. #endif  
  15.     return NULL;  
  16. }  

    这两个函数就是用来遍历之前的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中的相关代码如下:

  1. #include "SkTRegistry.h"  
  2.   
  3. static SkImageDecoder* DFactory(SkStream* stream) {  
  4.     static const char gHeader[] = { 0xFF, 0xD8, 0xFF };  
  5.     static const size_t HEADER_SIZE = sizeof(gHeader);  
  6.   
  7.     char buffer[HEADER_SIZE];  
  8.     size_t len = stream->read(buffer, HEADER_SIZE);  
  9.   
  10.     if (len != HEADER_SIZE) {  
  11.         return NULL;   // can't read enough  
  12.     }  
  13.     if (memcmp(buffer, gHeader, HEADER_SIZE)) {  
  14.         return NULL;  
  15.     }  
  16.     return SkNEW(SkJPEGImageDecoder);  
  17. }  
  18.   
  19. static SkImageEncoder* EFactory(SkImageEncoder::Type t) {  
  20.     return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;  
  21. }  
  22.   
  23. static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory);  
  24. static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(EFactory);  


3.4  通过第三方lib或硬件解决实现Encoder 或Decoder

    通过继承class SkImageEncoder  和 SkImageDecoder 并实现 onEncode 和 onDecode来通过第3方的lib实现Encoder或Decoder。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多