Skia深入分析6——skia中图像编解码代码概述

1、API和自注册机制

Skia中编码解码图片都只需要一行代码:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. SkBitmap bitmap;  
  2. SkImageDecoder::DecodeFile("test.xxx", &bitmap);//由文件名解码,自动推断图片类型  
  3. //或者由流解码  
  4. SkFILEStream stream("test.xxx");  
  5. SkImageDecoder::DecodeStream(stream, &bitmap);//由输入流解码,自动推断图片类型  
  6. //编码  
  7. SkImageEncoder::EncodeFile("test.jpg", bitmap, SkImageEncoder::kJPEG_Type, 90/*编码图片质量,对jpeg格式和webp格式有用*/);  

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. </pre><p></p><p>其设计是用抽象工厂模式产生编码器、解码器实例,将产生函数用自注册方式录入,使之在加载库时即初始化完成。</p><p>通用注册模板见:include/core/SkTRegistry.h</p><p></p><pre code_snippet_id="1582962" snippet_file_name="blog_20160220_3_9002512" name="code" class="cpp">template <typename T> class SkTRegistry : SkNoncopyable {  
  2. public:  
  3.     typedef T Factory;  
  4.   
  5.     explicit SkTRegistry(T fact) : fFact(fact) {  
  6. #ifdef SK_BUILD_FOR_ANDROID  
  7.         // work-around for double-initialization bug  
  8.         {  
  9.             SkTRegistry* reg = gHead;  
  10.             while (reg) {  
  11.                 if (reg == this) {  
  12.                     return;  
  13.                 }  
  14.                 reg = reg->fChain;  
  15.             }  
  16.         }  
  17. #endif  
  18.         fChain = gHead;  
  19.         gHead  = this;  
  20.     }  
  21.   
  22.     static const SkTRegistry* Head() { return gHead; }  
  23.   
  24.     const SkTRegistry* next() const { return fChain; }  
  25.     const Factory& factory() const { return fFact; }  
  26.   
  27. private:  
  28.     Factory      fFact;  
  29.     SkTRegistry* fChain;  
  30.   
  31.     static SkTRegistry* gHead;  
  32. };  
  33.   
  34. // The caller still needs to declare an instance of this somewhere  
  35. template <typename T> SkTRegistry<T>* SkTRegistry<T>::gHead;  

SkImageDecoder中规定的解码器工厂注册函数:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. typedef SkTRegistry<SkImageDecoder*(*)(SkStreamRewindable*)>        SkImageDecoder_DecodeReg;  

gif的解码器工厂便按如下方法注册:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static bool is_gif(SkStreamRewindable* stream) {  
  2.     char buf[GIF_STAMP_LEN];  
  3.     if (stream->read(buf, GIF_STAMP_LEN) == GIF_STAMP_LEN) {  
  4.         if (memcmp(GIF_STAMP,   buf, GIF_STAMP_LEN) == 0 ||  
  5.                 memcmp(GIF87_STAMP, buf, GIF_STAMP_LEN) == 0 ||  
  6.                 memcmp(GIF89_STAMP, buf, GIF_STAMP_LEN) == 0) {  
  7.             return true;  
  8.         }  
  9.     }  
  10.     return false;  
  11. }  
  12.   
  13. static SkImageDecoder* sk_libgif_dfactory(SkStreamRewindable* stream) {  
  14.     if (is_gif(stream)) {  
  15.         return SkNEW(SkGIFImageDecoder);  
  16.     }  
  17.     return NULL;  
  18. }  
  19. static SkImageDecoder_DecodeReg gReg(sk_libgif_dfactory);  


2、解码的流程:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. bool SkImageDecoder::DecodeStream(SkStreamRewindable* stream, SkBitmap* bm, SkColorType pref,  
  2.                                   Mode mode, Format* format) {  
  3.     SkASSERT(stream);  
  4.     SkASSERT(bm);  
  5.   
  6.     bool success = false;  
  7.     SkImageDecoder* codec = SkImageDecoder::Factory(stream);  
  8.   
  9.     if (NULL != codec) {  
  10.         success = codec->decode(stream, bm, pref, mode);  
  11.         if (success && format) {  
  12.             *format = codec->getFormat();  
  13.             if (kUnknown_Format == *format) {  
  14.                 if (stream->rewind()) {  
  15.                     *format = GetStreamFormat(stream);  
  16.                 }  
  17.             }  
  18.         }  
  19.         delete codec;  
  20.     }  
  21.     return success;  
  22. }  
  23.   
  24. bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm, SkColorType pref, Mode mode) {  
  25.     // we reset this to false before calling onDecode  
  26.     fShouldCancelDecode = false;  
  27.     // assign this, for use by getPrefColorType(), in case fUsePrefTable is false  
  28.     fDefaultPref = pref;  
  29.   
  30.     // pass a temporary bitmap, so that if we return false, we are assured of  
  31.     // leaving the caller's bitmap untouched.  
  32.     SkBitmap    tmp;  
  33.     if (!this->onDecode(stream, &tmp, mode)) {  
  34.         return false;  
  35.     }  
  36.     bm->swap(tmp);  
  37.     return true;  
  38. }  

(1)遍历所有解码器,找到支持该文件的解码器:
遍历的代码见:

external/skia/src/images/SkImageDecoder_FactoryRegistrar.cpp

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. SkImageDecoder* SkImageDecoder::Factory(SkStreamRewindable* stream) {  
  2.     return image_decoder_from_stream(stream);  
  3. }  
  4.   
  5. SkImageDecoder* image_decoder_from_stream(SkStreamRewindable* stream) {  
  6.     SkImageDecoder* codec = NULL;  
  7.     const SkImageDecoder_DecodeReg* curr = SkImageDecoder_DecodeReg::Head();  
  8.     while (curr) {  
  9.         codec = curr->factory()(stream);  
  10.         // we rewind here, because we promise later when we call "decode", that  
  11.         // the stream will be at its beginning.  
  12.         bool rewindSuceeded = stream->rewind();  
  13.   
  14.         // our image decoder's require that rewind is supported so we fail early  
  15.         // if we are given a stream that does not support rewinding.  
  16.         if (!rewindSuceeded) {  
  17.             SkDEBUGF(("Unable to rewind the image stream."));  
  18.             SkDELETE(codec);  
  19.             return NULL;  
  20.         }  
  21.   
  22.         if (codec) {  
  23.             return codec;  
  24.         }  
  25.         curr = curr->next();  
  26.     }  
  27.     return NULL;  
  28. }  

(2)解码器调用相应的解码库函数(简单的图片如bmp自行处理),解出原始图片,见各个解码器的onDecode方法

(3)在onDecode方法中,一般需要用SkScaledBitmapSampler类作图片后续的缩放和透明度预乘
编码的流程相对简单,就是根据类型取相应的编码器做文件编码,不详述


3、解码中流的设置:
从framework层调Skia的解码,主要是以下几个函数:
nativeDecodeStream:以Java的InputStream类作为输入解码,创建JavaInputStreamAdaptor流(其实现是先回调Java中InputStream的读取方法,将其读到缓存区,再从缓存区中读到目标内存。)
nativeDecodeFileDescriptor:以文件描述符作为输入解码,创建SkFILEStream,直接读取文件内容
nativeDecodeAsset:以Asset作为输入解码,创建AssetStreamAdaptor(frameworks/base/core/jni/android/graphics/Utils.cpp)。
nativeDecodeByteArray:以ByteArray作为输入解码,创建SkMemoryStream,直接从内存中读。

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. #define BYTES_TO_BUFFER 64  
  2.   
  3. static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,  
  4.         jobject padding, jobject options) {  
  5.   
  6.     jobject bitmap = NULL;  
  7.     SkAutoTUnref<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage));  
  8.   
  9.     if (stream.get()) {  
  10.         SkAutoTUnref<SkStreamRewindable> bufferedStream(  
  11.                 SkFrontBufferedStream::Create(stream, BYTES_TO_BUFFER));  
  12.         SkASSERT(bufferedStream.get() != NULL);  
  13.         bitmap = doDecode(env, bufferedStream, padding, options);  
  14.     }  
  15.     return bitmap;  
  16. }  
  17.   
  18. static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz, jobject fileDescriptor,  
  19.         jobject padding, jobject bitmapFactoryOptions) {  
  20.   
  21.     NPE_CHECK_RETURN_ZERO(env, fileDescriptor);  
  22.   
  23.     int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);  
  24.   
  25.     struct stat fdStat;  
  26.     if (fstat(descriptor, &fdStat) == -1) {  
  27.         doThrowIOE(env, "broken file descriptor");  
  28.         return nullObjectReturn("fstat return -1");  
  29.     }  
  30.   
  31.     // Restore the descriptor's offset on exiting this function. Even though  
  32.     // we dup the descriptor, both the original and dup refer to the same open  
  33.     // file description and changes to the file offset in one impact the other.  
  34.     AutoFDSeek autoRestore(descriptor);  
  35.   
  36.     // Duplicate the descriptor here to prevent leaking memory. A leak occurs  
  37.     // if we only close the file descriptor and not the file object it is used to  
  38.     // create.  If we don't explicitly clean up the file (which in turn closes the  
  39.     // descriptor) the buffers allocated internally by fseek will be leaked.  
  40.     int dupDescriptor = dup(descriptor);  
  41.   
  42.     FILE* file = fdopen(dupDescriptor, "r");  
  43.     if (file == NULL) {  
  44.         // cleanup the duplicated descriptor since it will not be closed when the  
  45.         // file is cleaned up (fclose).  
  46.         close(dupDescriptor);  
  47.         return nullObjectReturn("Could not open file");  
  48.     }  
  49.   
  50.     SkAutoTUnref<SkFILEStream> fileStream(new SkFILEStream(file,  
  51.                          SkFILEStream::kCallerPasses_Ownership));  
  52.   
  53.     // Use a buffered stream. Although an SkFILEStream can be rewound, this  
  54.     // ensures that SkImageDecoder::Factory never rewinds beyond the  
  55.     // current position of the file descriptor.  
  56.     SkAutoTUnref<SkStreamRewindable> stream(SkFrontBufferedStream::Create(fileStream,  
  57.             BYTES_TO_BUFFER));  
  58.   
  59.     return doDecode(env, stream, padding, bitmapFactoryOptions);  
  60. }  

由于以文件描述符和InputStream为输入时,不能保证该流可以rewind,因此加了一层 SkFrontBufferedStream 的包装,这个主要作用是缓存输入流中最前面的一段区域(#define BYTES_TO_BUFFER 64),以便在读这段区域时可以回溯(rewind),这个主要作用让解码器读完文件头判断类型后可以回溯。
详细见:
external/skia/src/utils/SkFrontBufferedStream.cpp


4、解码选项

external/skia/include/core/SkImageDecoder.h

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. class SkImageDecoder : SkNoncopyable {  
  2. private:  
  3.     Peeker*                 fPeeker;//仅在解ninepatch图片中使用,用于提取子图片断,仅对png格式有效  
  4.     SkBitmap::Allocator*    fAllocator;//解码需要产生SkBitmap,这里是其像素的内存分配器  
  5.     int                     fSampleSize;//图片下采样率,为2的幂次,这个设计主要针对的是jpeg格式的图像。Jpeg格式编码时以8*8像素单位为一个block作傅立叶变换,设成2、4、8时可以针对性简化反傅立叶变换(idct)的运算。  
  6.     SkColorType             fDefaultPref;//优先选取的解码出来的图片格式  
  7.     bool                    fDitherImage;//是否做抖动处理,主要针对16位色,在png和jpeg解码时生效  
  8.     bool                    fSkipWritingZeroes;//是否默认结果图已经清零并不写入像素值零的点。这个选项仅在 SkScaledBitmapSampler 中有判断,如(get_RGBA_to_8888_proc函数),试图起到后续缩放时一些计算节省,意义不大。(真要优化不如用simd改写,如neon)  
  9.     mutable bool            fShouldCancelDecode;//比如用户在A图片未解析完时,已经划过A图片所在区域,此时可设置fShouldCancelDecode使A图片的解码取消。  
  10.     bool                    fPreferQualityOverSpeed;//只对jpeg格式有用,决定反傅立叶变换(idct)的运算精度,速度优先时,以8位精度整数替代浮点运算,质量优先时,以16位精度整数替代浮点运算  
  11.     bool                    fRequireUnpremultipliedColors;//解带透明度的图片,如png时有用,表示是否对图片像素作透明度预乘,默认是作预乘,以便渲染时不用再做乘法  
  12. };  

这些私有变量对应于BitmapFactory.Options中的相应的配置项,见
frameworks/base/graphics/java/android/graphics/BitmapFactory.java
设置的代码见doDecode方法:

frameworks/base/core/jni/Android/graphics/BitmapFactory.cpp

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {  
  2.     /*......*/  
  3.     SkImageDecoder* decoder = SkImageDecoder::Factory(stream);  
  4.     if (decoder == NULL) {  
  5.         return nullObjectReturn("SkImageDecoder::Factory returned null");  
  6.     }  
  7.   
  8.     decoder->setSampleSize(sampleSize);  
  9.     decoder->setDitherImage(doDither);  
  10.     decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);  
  11.     decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);  
  12.     /*......*/  
  13. }  

部分Java层的配置项并不反映在SkImageDecoder中,而是作为函数参数传入,如解码模式Mode


5、典型解码器的onDecode方法

这一部分的代码可做为这些图像编解码库使用方法的参考

(1)Jpeg

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. bool SkJPEGImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {  
  2. #ifdef TIME_DECODE  
  3.     SkAutoTime atm("JPEG Decode");  
  4. #endif  
  5.   
  6.     JPEGAutoClean autoClean;  
  7.   
  8.     jpeg_decompress_struct  cinfo;  
  9.     skjpeg_source_mgr       srcManager(stream, this);  
  10.   
  11.     skjpeg_error_mgr errorManager;  
  12.     set_error_mgr(&cinfo, &errorManager);  
  13.   
  14.     // All objects need to be instantiated before this setjmp call so that  
  15.     // they will be cleaned up properly if an error occurs.  
  16.     if (setjmp(errorManager.fJmpBuf)) {  
  17.         return return_false(cinfo, *bm, "setjmp");  
  18.     }  
  19.   
  20.     initialize_info(&cinfo, &srcManager);  
  21.     autoClean.set(&cinfo);  
  22.   
  23.     int status = jpeg_read_header(&cinfo, true);  
  24.     if (status != JPEG_HEADER_OK) {  
  25.         return return_false(cinfo, *bm, "read_header");  
  26.     }  
  27.   
  28.     /*  Try to fulfill the requested sampleSize. Since jpeg can do it (when it 
  29.         can) much faster that we, just use their num/denom api to approximate 
  30.         the size. 
  31.     */  
  32.     int sampleSize = this->getSampleSize();  
  33.   
  34.     set_dct_method(*this, &cinfo);  
  35.   
  36.     SkASSERT(1 == cinfo.scale_num);  
  37.     cinfo.scale_denom = sampleSize;  
  38.   
  39.     turn_off_visual_optimizations(&cinfo);  
  40.   
  41.     const SkColorType colorType = this->getBitmapColorType(&cinfo);  
  42.     const SkAlphaType alphaType = kAlpha_8_SkColorType == colorType ?  
  43.                                       kPremul_SkAlphaType : kOpaque_SkAlphaType;  
  44.   
  45.     adjust_out_color_space_and_dither(&cinfo, colorType, *this);  
  46.   
  47.     if (1 == sampleSize && SkImageDecoder::kDecodeBounds_Mode == mode) {  
  48.         // Assume an A8 bitmap is not opaque to avoid the check of each  
  49.         // individual pixel. It is very unlikely to be opaque, since  
  50.         // an opaque A8 bitmap would not be very interesting.  
  51.         // Otherwise, a jpeg image is opaque.  
  52.         return bm->setInfo(SkImageInfo::Make(cinfo.image_width, cinfo.image_height,  
  53.                                              colorType, alphaType));  
  54.     }  
  55.   
  56.     /*  image_width and image_height are the original dimensions, available 
  57.         after jpeg_read_header(). To see the scaled dimensions, we have to call 
  58.         jpeg_start_decompress(), and then read output_width and output_height. 
  59.     */  
  60.     if (!jpeg_start_decompress(&cinfo)) {  
  61.         /*  If we failed here, we may still have enough information to return 
  62.             to the caller if they just wanted (subsampled bounds). If sampleSize 
  63.             was 1, then we would have already returned. Thus we just check if 
  64.             we're in kDecodeBounds_Mode, and that we have valid output sizes. 
  65.  
  66.             One reason to fail here is that we have insufficient stream data 
  67.             to complete the setup. However, output dimensions seem to get 
  68.             computed very early, which is why this special check can pay off. 
  69.          */  
  70.         if (SkImageDecoder::kDecodeBounds_Mode == mode && valid_output_dimensions(cinfo)) {  
  71.             SkScaledBitmapSampler smpl(cinfo.output_width, cinfo.output_height,  
  72.                                        recompute_sampleSize(sampleSize, cinfo));  
  73.             // Assume an A8 bitmap is not opaque to avoid the check of each  
  74.             // individual pixel. It is very unlikely to be opaque, since  
  75.             // an opaque A8 bitmap would not be very interesting.  
  76.             // Otherwise, a jpeg image is opaque.  
  77.             return bm->setInfo(SkImageInfo::Make(smpl.scaledWidth(), smpl.scaledHeight(),  
  78.                                                  colorType, alphaType));  
  79.         } else {  
  80.             return return_false(cinfo, *bm, "start_decompress");  
  81.         }  
  82.     }  
  83.     sampleSize = recompute_sampleSize(sampleSize, cinfo);  
  84.   
  85. #ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER  
  86.     // should we allow the Chooser (if present) to pick a colortype for us???  
  87.     if (!this->chooseFromOneChoice(colorType, cinfo.output_width, cinfo.output_height)) {  
  88.         return return_false(cinfo, *bm, "chooseFromOneChoice");  
  89.     }  
  90. #endif  
  91.   
  92.     SkScaledBitmapSampler sampler(cinfo.output_width, cinfo.output_height, sampleSize);  
  93.     // Assume an A8 bitmap is not opaque to avoid the check of each  
  94.     // individual pixel. It is very unlikely to be opaque, since  
  95.     // an opaque A8 bitmap would not be very interesting.  
  96.     // Otherwise, a jpeg image is opaque.  
  97.     bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),  
  98.                                   colorType, alphaType));  
  99.     if (SkImageDecoder::kDecodeBounds_Mode == mode) {  
  100.         return true;  
  101.     }  
  102.     if (!this->allocPixelRef(bm, NULL)) {  
  103.         return return_false(cinfo, *bm, "allocPixelRef");  
  104.     }  
  105.   
  106.     SkAutoLockPixels alp(*bm);  
  107.   
  108. #ifdef ANDROID_RGB  
  109.     /* short-circuit the SkScaledBitmapSampler when possible, as this gives 
  110.        a significant performance boost. 
  111.     */  
  112.     if (sampleSize == 1 &&  
  113.         ((kN32_SkColorType == colorType && cinfo.out_color_space == JCS_RGBA_8888) ||  
  114.          (kRGB_565_SkColorType == colorType && cinfo.out_color_space == JCS_RGB_565)))  
  115.     {  
  116.         JSAMPLE* rowptr = (JSAMPLE*)bm->getPixels();  
  117.         INT32 const bpr =  bm->rowBytes();  
  118.   
  119.         while (cinfo.output_scanline < cinfo.output_height) {  
  120.             int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);  
  121.             if (0 == row_count) {  
  122.                 // if row_count == 0, then we didn't get a scanline,  
  123.                 // so return early.  We will return a partial image.  
  124.                 fill_below_level(cinfo.output_scanline, bm);  
  125.                 cinfo.output_scanline = cinfo.output_height;  
  126.                 break;  // Skip to jpeg_finish_decompress()  
  127.             }  
  128.             if (this->shouldCancelDecode()) {  
  129.                 return return_false(cinfo, *bm, "shouldCancelDecode");  
  130.             }  
  131.             rowptr += bpr;  
  132.         }  
  133.         jpeg_finish_decompress(&cinfo);  
  134.         return true;  
  135.     }  
  136. #endif  
  137.   
  138.     // check for supported formats  
  139.     SkScaledBitmapSampler::SrcConfig sc;  
  140.     int srcBytesPerPixel;  
  141.   
  142.     if (!get_src_config(cinfo, &sc, &srcBytesPerPixel)) {  
  143.         return return_false(cinfo, *bm, "jpeg colorspace");  
  144.     }  
  145.   
  146.     if (!sampler.begin(bm, sc, *this)) {  
  147.         return return_false(cinfo, *bm, "sampler.begin");  
  148.     }  
  149.   
  150.     SkAutoMalloc srcStorage(cinfo.output_width * srcBytesPerPixel);  
  151.     uint8_t* srcRow = (uint8_t*)srcStorage.get();  
  152.   
  153.     //  Possibly skip initial rows [sampler.srcY0]  
  154.     if (!skip_src_rows(&cinfo, srcRow, sampler.srcY0())) {  
  155.         return return_false(cinfo, *bm, "skip rows");  
  156.     }  
  157.   
  158.     // now loop through scanlines until y == bm->height() - 1  
  159.     for (int y = 0;; y++) {  
  160.         JSAMPLE* rowptr = (JSAMPLE*)srcRow;  
  161.         int row_count = jpeg_read_scanlines(&cinfo, &rowptr, 1);  
  162.         if (0 == row_count) {  
  163.             // if row_count == 0, then we didn't get a scanline,  
  164.             // so return early.  We will return a partial image.  
  165.             fill_below_level(y, bm);  
  166.             cinfo.output_scanline = cinfo.output_height;  
  167.             break;  // Skip to jpeg_finish_decompress()  
  168.         }  
  169.         if (this->shouldCancelDecode()) {  
  170.             return return_false(cinfo, *bm, "shouldCancelDecode");  
  171.         }  
  172.   
  173.         if (JCS_CMYK == cinfo.out_color_space) {  
  174.             convert_CMYK_to_RGB(srcRow, cinfo.output_width);  
  175.         }  
  176.   
  177.         sampler.next(srcRow);  
  178.         if (bm->height() - 1 == y) {  
  179.             // we're done  
  180.             break;  
  181.         }  
  182.   
  183.         if (!skip_src_rows(&cinfo, srcRow, sampler.srcDY() - 1)) {  
  184.             return return_false(cinfo, *bm, "skip rows");  
  185.         }  
  186.     }  
  187.   
  188.     // we formally skip the rest, so we don't get a complaint from libjpeg  
  189.     if (!skip_src_rows(&cinfo, srcRow,  
  190.                        cinfo.output_height - cinfo.output_scanline)) {  
  191.         return return_false(cinfo, *bm, "skip rows");  
  192.     }  
  193.     jpeg_finish_decompress(&cinfo);  
  194.   
  195.     return true;  
  196. }  

a、配置Jpeg解码选项(输出格式、采样率、idct类型、下采样率等等,在SkImageDecoder.h的注释中解释过一些)
b、在不设置下采样且目标格式为RGBA时,直接解码到目标Bitmap上(Jpeg是按YUV三分量分别压缩的,设置为Jpeg库解码时作YUV-RGBA的颜色转换)。
c、需要下采样时,由于jpeg库可作简化处理,因此重新计算一个下采样率,由SkScaledBitmapSampler将解码出来的图像下采样到目标bitmap上。

(2)Png

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. bool SkPNGImageDecoder::onDecodeInit(SkStream* sk_stream, png_structp *png_ptrp,  
  2.                                      png_infop *info_ptrp) {  
  3.     /* Create and initialize the png_struct with the desired error handler 
  4.     * functions.  If you want to use the default stderr and longjump method, 
  5.     * you can supply NULL for the last three parameters.  We also supply the 
  6.     * the compiler header file version, so that we know if the application 
  7.     * was compiled with a compatible version of the library.  */  
  8.   
  9.     png_error_ptr user_warning_fn =  
  10.         (c_suppressPNGImageDecoderWarnings) ? (&do_nothing_warning_fn) : NULL;  
  11.     /* NULL means to leave as default library behavior. */  
  12.     /* c_suppressPNGImageDecoderWarnings default depends on SK_DEBUG. */  
  13.     /* To suppress warnings with a SK_DEBUG binary, set the 
  14.      * environment variable "skia_images_png_suppressDecoderWarnings" 
  15.      * to "true".  Inside a program that links to skia: 
  16.      * SK_CONF_SET("images.png.suppressDecoderWarnings", true); */  
  17.   
  18.     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,  
  19.         NULL, sk_error_fn, user_warning_fn);  
  20.     //   png_voidp user_error_ptr, user_error_fn, user_warning_fn);  
  21.     if (png_ptr == NULL) {  
  22.         return false;  
  23.     }  
  24.   
  25.     *png_ptrp = png_ptr;  
  26.   
  27.     /* Allocate/initialize the memory for image information. */  
  28.     png_infop info_ptr = png_create_info_struct(png_ptr);  
  29.     if (info_ptr == NULL) {  
  30.         png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);  
  31.         return false;  
  32.     }  
  33.     *info_ptrp = info_ptr;  
  34.   
  35.     /* Set error handling if you are using the setjmp/longjmp method (this is 
  36.     * the normal method of doing things with libpng).  REQUIRED unless you 
  37.     * set up your own error handlers in the png_create_read_struct() earlier. 
  38.     */  
  39.     if (setjmp(png_jmpbuf(png_ptr))) {  
  40.         png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);  
  41.         return false;  
  42.     }  
  43.   
  44.     /* If you are using replacement read functions, instead of calling 
  45.     * png_init_io() here you would call: 
  46.     */  
  47.     png_set_read_fn(png_ptr, (void *)sk_stream, sk_read_fn);  
  48. #ifdef SK_BUILD_FOR_ANDROID  
  49.     png_set_seek_fn(png_ptr, sk_seek_fn);  
  50. #endif  
  51.     /* where user_io_ptr is a structure you want available to the callbacks */  
  52.     /* If we have already read some of the signature */  
  53. //  png_set_sig_bytes(png_ptr, 0 /* sig_read */ );  
  54.   
  55.     // hookup our peeker so we can see any user-chunks the caller may be interested in  
  56.     png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"", 0);  
  57.     if (this->getPeeker()) {  
  58.         png_set_read_user_chunk_fn(png_ptr, (png_voidp)this->getPeeker(), sk_read_user_chunk);  
  59.     }  
  60.   
  61.     /* The call to png_read_info() gives us all of the information from the 
  62.     * PNG file before the first IDAT (image data chunk). */  
  63.     png_read_info(png_ptr, info_ptr);  
  64.     png_uint_32 origWidth, origHeight;  
  65.     int bitDepth, colorType;  
  66.     png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,  
  67.                  &colorType, int_p_NULL, int_p_NULL, int_p_NULL);  
  68.   
  69.     /* tell libpng to strip 16 bit/color files down to 8 bits/color */  
  70.     if (bitDepth == 16) {  
  71.         png_set_strip_16(png_ptr);  
  72.     }  
  73.     /* Extract multiple pixels with bit depths of 1, 2, and 4 from a single 
  74.      * byte into separate bytes (useful for paletted and grayscale images). */  
  75.     if (bitDepth < 8) {  
  76.         png_set_packing(png_ptr);  
  77.     }  
  78.     /* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */  
  79.     if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {  
  80.         png_set_expand_gray_1_2_4_to_8(png_ptr);  
  81.     }  
  82.   
  83.     return true;  
  84. }  
  85.   
  86. bool SkPNGImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* decodedBitmap,  
  87.                                  Mode mode) {  
  88.     png_structp png_ptr;  
  89.     png_infop info_ptr;  
  90.   
  91.     if (!onDecodeInit(sk_stream, &png_ptr, &info_ptr)) {  
  92.         return false;  
  93.     }  
  94.   
  95.     PNGAutoClean autoClean(png_ptr, info_ptr);  
  96.   
  97.     if (setjmp(png_jmpbuf(png_ptr))) {  
  98.         return false;  
  99.     }  
  100.   
  101.     png_uint_32 origWidth, origHeight;  
  102.     int bitDepth, pngColorType, interlaceType;  
  103.     png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth,  
  104.                  &pngColorType, &interlaceType, int_p_NULL, int_p_NULL);  
  105.   
  106.     SkColorType         colorType;  
  107.     bool                hasAlpha = false;  
  108.     SkPMColor           theTranspColor = 0; // 0 tells us not to try to match  
  109.   
  110.     if (!this->getBitmapColorType(png_ptr, info_ptr, &colorType, &hasAlpha, &theTranspColor)) {  
  111.         return false;  
  112.     }  
  113.   
  114.     SkAlphaType alphaType = this->getRequireUnpremultipliedColors() ?  
  115.                                 kUnpremul_SkAlphaType : kPremul_SkAlphaType;  
  116.     const int sampleSize = this->getSampleSize();  
  117.     SkScaledBitmapSampler sampler(origWidth, origHeight, sampleSize);  
  118.     decodedBitmap->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),  
  119.                                              colorType, alphaType));  
  120.   
  121.     if (SkImageDecoder::kDecodeBounds_Mode == mode) {  
  122.         return true;  
  123.     }  
  124.   
  125.     // from here down we are concerned with colortables and pixels  
  126.   
  127.     // we track if we actually see a non-opaque pixels, since sometimes a PNG sets its colortype  
  128.     // to |= PNG_COLOR_MASK_ALPHA, but all of its pixels are in fact opaque. We care, since we  
  129.     // draw lots faster if we can flag the bitmap has being opaque  
  130.     bool reallyHasAlpha = false;  
  131.     SkColorTable* colorTable = NULL;  
  132.   
  133.     if (pngColorType == PNG_COLOR_TYPE_PALETTE) {  
  134.         decodePalette(png_ptr, info_ptr, &hasAlpha, &reallyHasAlpha, &colorTable);  
  135.     }  
  136.   
  137.     SkAutoUnref aur(colorTable);  
  138.   
  139.     if (!this->allocPixelRef(decodedBitmap,  
  140.                              kIndex_8_SkColorType == colorType ? colorTable : NULL)) {  
  141.         return false;  
  142.     }  
  143.   
  144.     SkAutoLockPixels alp(*decodedBitmap);  
  145.   
  146.     /* Turn on interlace handling.  REQUIRED if you are not using 
  147.     *  png_read_image().  To see how to handle interlacing passes, 
  148.     *  see the png_read_row() method below: 
  149.     */  
  150.     const int number_passes = (interlaceType != PNG_INTERLACE_NONE) ?  
  151.                               png_set_interlace_handling(png_ptr) : 1;  
  152.   
  153.     /* Optional call to gamma correct and add the background to the palette 
  154.     *  and update info structure.  REQUIRED if you are expecting libpng to 
  155.     *  update the palette for you (ie you selected such a transform above). 
  156.     */  
  157.     png_read_update_info(png_ptr, info_ptr);  
  158.   
  159.     if ((kAlpha_8_SkColorType == colorType || kIndex_8_SkColorType == colorType) &&  
  160.             1 == sampleSize) {  
  161.         if (kAlpha_8_SkColorType == colorType) {  
  162.             // For an A8 bitmap, we assume there is an alpha for speed. It is  
  163.             // possible the bitmap is opaque, but that is an unlikely use case  
  164.             // since it would not be very interesting.  
  165.             reallyHasAlpha = true;  
  166.             // A8 is only allowed if the original was GRAY.  
  167.             SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);  
  168.         }  
  169.         for (int i = 0; i < number_passes; i++) {  
  170.             for (png_uint_32 y = 0; y < origHeight; y++) {  
  171.                 uint8_t* bmRow = decodedBitmap->getAddr8(0, y);  
  172.                 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);  
  173.             }  
  174.         }  
  175.     } else {  
  176.         SkScaledBitmapSampler::SrcConfig sc;  
  177.         int srcBytesPerPixel = 4;  
  178.   
  179.         if (colorTable != NULL) {  
  180.             sc = SkScaledBitmapSampler::kIndex;  
  181.             srcBytesPerPixel = 1;  
  182.         } else if (kAlpha_8_SkColorType == colorType) {  
  183.             // A8 is only allowed if the original was GRAY.  
  184.             SkASSERT(PNG_COLOR_TYPE_GRAY == pngColorType);  
  185.             sc = SkScaledBitmapSampler::kGray;  
  186.             srcBytesPerPixel = 1;  
  187.         } else if (hasAlpha) {  
  188.             sc = SkScaledBitmapSampler::kRGBA;  
  189.         } else {  
  190.             sc = SkScaledBitmapSampler::kRGBX;  
  191.         }  
  192.   
  193.         /*  We have to pass the colortable explicitly, since we may have one 
  194.             even if our decodedBitmap doesn't, due to the request that we 
  195.             upscale png's palette to a direct model 
  196.          */  
  197.         SkAutoLockColors ctLock(colorTable);  
  198.         if (!sampler.begin(decodedBitmap, sc, *this, ctLock.colors())) {  
  199.             return false;  
  200.         }  
  201.         const int height = decodedBitmap->height();  
  202.   
  203.         if (number_passes > 1) {  
  204.             SkAutoMalloc storage(origWidth * origHeight * srcBytesPerPixel);  
  205.             uint8_t* base = (uint8_t*)storage.get();  
  206.             size_t rowBytes = origWidth * srcBytesPerPixel;  
  207.   
  208.             for (int i = 0; i < number_passes; i++) {  
  209.                 uint8_t* row = base;  
  210.                 for (png_uint_32 y = 0; y < origHeight; y++) {  
  211.                     uint8_t* bmRow = row;  
  212.                     png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);  
  213.                     row += rowBytes;  
  214.                 }  
  215.             }  
  216.             // now sample it  
  217.             base += sampler.srcY0() * rowBytes;  
  218.             for (int y = 0; y < height; y++) {  
  219.                 reallyHasAlpha |= sampler.next(base);  
  220.                 base += sampler.srcDY() * rowBytes;  
  221.             }  
  222.         } else {  
  223.             SkAutoMalloc storage(origWidth * srcBytesPerPixel);  
  224.             uint8_t* srcRow = (uint8_t*)storage.get();  
  225.             skip_src_rows(png_ptr, srcRow, sampler.srcY0());  
  226.   
  227.             for (int y = 0; y < height; y++) {  
  228.                 uint8_t* tmp = srcRow;  
  229.                 png_read_rows(png_ptr, &tmp, png_bytepp_NULL, 1);  
  230.                 reallyHasAlpha |= sampler.next(srcRow);  
  231.                 if (y < height - 1) {  
  232.                     skip_src_rows(png_ptr, srcRow, sampler.srcDY() - 1);  
  233.                 }  
  234.             }  
  235.   
  236.             // skip the rest of the rows (if any)  
  237.             png_uint_32 read = (height - 1) * sampler.srcDY() +  
  238.                                sampler.srcY0() + 1;  
  239.             SkASSERT(read <= origHeight);  
  240.             skip_src_rows(png_ptr, srcRow, origHeight - read);  
  241.         }  
  242.     }  
  243.   
  244.     /* read rest of file, and get additional chunks in info_ptr - REQUIRED */  
  245.     png_read_end(png_ptr, info_ptr);  
  246.   
  247.     if (0 != theTranspColor) {  
  248.         reallyHasAlpha |= substituteTranspColor(decodedBitmap, theTranspColor);  
  249.     }  
  250.     if (reallyHasAlpha && this->getRequireUnpremultipliedColors()) {  
  251.         switch (decodedBitmap->colorType()) {  
  252.             case kIndex_8_SkColorType:  
  253.                 // Fall through.  
  254.             case kARGB_4444_SkColorType:  
  255.                 // We have chosen not to support unpremul for these colortypes.  
  256.                 return false;  
  257.             default: {  
  258.                 // Fall through to finish the decode. This colortype either  
  259.                 // supports unpremul or it is irrelevant because it has no  
  260.                 // alpha (or only alpha).  
  261.                 // These brackets prevent a warning.  
  262.             }  
  263.         }  
  264.     }  
  265.   
  266.     if (!reallyHasAlpha) {  
  267.         decodedBitmap->setAlphaType(kOpaque_SkAlphaType);  
  268.     }  
  269.     return true;  
  270. }  


Png格式的图像可能包含透明度。Skia作了一个额外处理是检查其透明度是否全为255,是则置不透明标志,毕竟不透明的图像在后续渲染时消耗较少。

(3)Gif

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {  
  2. #if GIFLIB_MAJOR < 5  
  3.     GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);  
  4. #else  
  5.     GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL);  
  6. #endif  
  7.     if (NULL == gif) {  
  8.         return error_return(*bm, "DGifOpen");  
  9.     }  
  10.   
  11.     SkAutoTCallIProc<GifFileType, DGifCloseFile> acp(gif);  
  12.   
  13.     SavedImage temp_save;  
  14.     temp_save.ExtensionBlocks=NULL;  
  15.     temp_save.ExtensionBlockCount=0;  
  16.     SkAutoTCallVProc<SavedImage, CheckFreeExtension> acp2(&temp_save);  
  17.   
  18.     int width, height;  
  19.     GifRecordType recType;  
  20.     GifByteType *extData;  
  21. #if GIFLIB_MAJOR >= 5  
  22.     int extFunction;  
  23. #endif  
  24.     int transpIndex = -1;   // -1 means we don't have it (yet)  
  25.     int fillIndex = gif->SBackGroundColor;  
  26.   
  27.     do {  
  28.         if (DGifGetRecordType(gif, &recType) == GIF_ERROR) {  
  29.             return error_return(*bm, "DGifGetRecordType");  
  30.         }  
  31.   
  32.         switch (recType) {  
  33.         case IMAGE_DESC_RECORD_TYPE: {  
  34.             if (DGifGetImageDesc(gif) == GIF_ERROR) {  
  35.                 return error_return(*bm, "IMAGE_DESC_RECORD_TYPE");  
  36.             }  
  37.   
  38.             if (gif->ImageCount < 1) {    // sanity check  
  39.                 return error_return(*bm, "ImageCount < 1");  
  40.             }  
  41.   
  42.             width = gif->SWidth;  
  43.             height = gif->SHeight;  
  44.   
  45.             SavedImage* image = &gif->SavedImages[gif->ImageCount-1];  
  46.             const GifImageDesc& desc = image->ImageDesc;  
  47.   
  48.             int imageLeft = desc.Left;  
  49.             int imageTop = desc.Top;  
  50.             const int innerWidth = desc.Width;  
  51.             const int innerHeight = desc.Height;  
  52.             if (innerWidth <= 0 || innerHeight <= 0) {  
  53.                 return error_return(*bm, "invalid dimensions");  
  54.             }  
  55.   
  56.             // check for valid descriptor  
  57.             if (innerWidth > width) {  
  58.                 gif_warning(*bm, "image too wide, expanding output to size");  
  59.                 width = innerWidth;  
  60.                 imageLeft = 0;  
  61.             } else if (imageLeft + innerWidth > width) {  
  62.                 gif_warning(*bm, "shifting image left to fit");  
  63.                 imageLeft = width - innerWidth;  
  64.             } else if (imageLeft < 0) {  
  65.                 gif_warning(*bm, "shifting image right to fit");  
  66.                 imageLeft = 0;  
  67.             }  
  68.   
  69.   
  70.             if (innerHeight > height) {  
  71.                 gif_warning(*bm, "image too tall,  expanding output to size");  
  72.                 height = innerHeight;  
  73.                 imageTop = 0;  
  74.             } else if (imageTop + innerHeight > height) {  
  75.                 gif_warning(*bm, "shifting image up to fit");  
  76.                 imageTop = height - innerHeight;  
  77.             } else if (imageTop < 0) {  
  78.                 gif_warning(*bm, "shifting image down to fit");  
  79.                 imageTop = 0;  
  80.             }  
  81.   
  82. #ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER  
  83.             // FIXME: We could give the caller a choice of images or configs.  
  84.             if (!this->chooseFromOneChoice(kIndex_8_SkColorType, width, height)) {  
  85.                 return error_return(*bm, "chooseFromOneChoice");  
  86.             }  
  87. #endif  
  88.   
  89.             SkScaledBitmapSampler sampler(width, height, this->getSampleSize());  
  90.   
  91.             bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),  
  92.                                           kIndex_8_SkColorType, kPremul_SkAlphaType));  
  93.   
  94.             if (SkImageDecoder::kDecodeBounds_Mode == mode) {  
  95.                 return true;  
  96.             }  
  97.   
  98.   
  99.             // now we decode the colortable  
  100.             int colorCount = 0;  
  101.             {  
  102.                 // Declare colorPtr here for scope.  
  103.                 SkPMColor colorPtr[256]; // storage for worst-case  
  104.                 const ColorMapObject* cmap = find_colormap(gif);  
  105.                 SkAlphaType alphaType = kOpaque_SkAlphaType;  
  106.                 if (cmap != NULL) {  
  107.                     SkASSERT(cmap->ColorCount == (1 << (cmap->BitsPerPixel)));  
  108.                     colorCount = cmap->ColorCount;  
  109.                     if (colorCount > 256) {  
  110.                         colorCount = 256;  // our kIndex8 can't support more  
  111.                     }  
  112.                     for (int index = 0; index < colorCount; index++) {  
  113.                         colorPtr[index] = SkPackARGB32(0xFF,  
  114.                                                        cmap->Colors[index].Red,  
  115.                                                        cmap->Colors[index].Green,  
  116.                                                        cmap->Colors[index].Blue);  
  117.                     }  
  118.                 } else {  
  119.                     // find_colormap() returned NULL.  Some (rare, broken)  
  120.                     // GIFs don't have a color table, so we force one.  
  121.                     gif_warning(*bm, "missing colormap");  
  122.                     colorCount = 256;  
  123.                     sk_memset32(colorPtr, SK_ColorWHITE, colorCount);  
  124.                 }  
  125.                 transpIndex = find_transpIndex(temp_save, colorCount);  
  126.                 if (transpIndex >= 0) {  
  127.                     colorPtr[transpIndex] = SK_ColorTRANSPARENT; // ram in a transparent SkPMColor  
  128.                     alphaType = kPremul_SkAlphaType;  
  129.                     fillIndex = transpIndex;  
  130.                 } else if (fillIndex >= colorCount) {  
  131.                     // gif->SBackGroundColor should be less than colorCount.  
  132.                     fillIndex = 0;  // If not, fix it.  
  133.                 }  
  134.   
  135.                 SkAutoTUnref<SkColorTable> ctable(SkNEW_ARGS(SkColorTable,  
  136.                                                   (colorPtr, colorCount,  
  137.                                                    alphaType)));  
  138.                 if (!this->allocPixelRef(bm, ctable)) {  
  139.                     return error_return(*bm, "allocPixelRef");  
  140.                 }  
  141.             }  
  142.   
  143.             // abort if either inner dimension is <= 0  
  144.             if (innerWidth <= 0 || innerHeight <= 0) {  
  145.                 return error_return(*bm, "non-pos inner width/height");  
  146.             }  
  147.   
  148.             SkAutoLockPixels alp(*bm);  
  149.   
  150.             SkAutoMalloc storage(innerWidth);  
  151.             uint8_t* scanline = (uint8_t*) storage.get();  
  152.   
  153.             // GIF has an option to store the scanlines of an image, plus a larger background,  
  154.             // filled by a fill color. In this case, we will use a subset of the larger bitmap  
  155.             // for sampling.  
  156.             SkBitmap subset;  
  157.             SkBitmap* workingBitmap;  
  158.             // are we only a subset of the total bounds?  
  159.             if ((imageTop | imageLeft) > 0 ||  
  160.                  innerWidth < width || innerHeight < height) {  
  161.                 // Fill the background.  
  162.                 memset(bm->getPixels(), fillIndex, bm->getSize());  
  163.   
  164.                 // Create a subset of the bitmap.  
  165.                 SkIRect subsetRect(SkIRect::MakeXYWH(imageLeft / sampler.srcDX(),  
  166.                                                      imageTop / sampler.srcDY(),  
  167.                                                      innerWidth / sampler.srcDX(),  
  168.                                                      innerHeight / sampler.srcDY()));  
  169.                 if (!bm->extractSubset(&subset, subsetRect)) {  
  170.                     return error_return(*bm, "Extract failed.");  
  171.                 }  
  172.                 // Update the sampler. We'll now be only sampling into the subset.  
  173.                 sampler = SkScaledBitmapSampler(innerWidth, innerHeight, this->getSampleSize());  
  174.                 workingBitmap = ⊂  
  175.             } else {  
  176.                 workingBitmap = bm;  
  177.             }  
  178.   
  179.             // bm is already locked, but if we had to take a subset, it must be locked also,  
  180.             // so that getPixels() will point to its pixels.  
  181.             SkAutoLockPixels alpWorking(*workingBitmap);  
  182.   
  183.             if (!sampler.begin(workingBitmap, SkScaledBitmapSampler::kIndex, *this)) {  
  184.                 return error_return(*bm, "Sampler failed to begin.");  
  185.             }  
  186.   
  187.             // now decode each scanline  
  188.             if (gif->Image.Interlace) {  
  189.                 // Iterate over the height of the source data. The sampler will  
  190.                 // take care of skipping unneeded rows.  
  191.                 GifInterlaceIter iter(innerHeight);  
  192.                 for (int y = 0; y < innerHeight; y++) {  
  193.                     if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {  
  194.                         gif_warning(*bm, "interlace DGifGetLine");  
  195.                         memset(scanline, fillIndex, innerWidth);  
  196.                         for (; y < innerHeight; y++) {  
  197.                             sampler.sampleInterlaced(scanline, iter.currY());  
  198.                             iter.next();  
  199.                         }  
  200.                         return true;  
  201.                     }  
  202.                     sampler.sampleInterlaced(scanline, iter.currY());  
  203.                     iter.next();  
  204.                 }  
  205.             } else {  
  206.                 // easy, non-interlace case  
  207.                 const int outHeight = workingBitmap->height();  
  208.                 skip_src_rows(gif, scanline, innerWidth, sampler.srcY0());  
  209.                 for (int y = 0; y < outHeight; y++) {  
  210.                     if (DGifGetLine(gif, scanline, innerWidth) == GIF_ERROR) {  
  211.                         gif_warning(*bm, "DGifGetLine");  
  212.                         memset(scanline, fillIndex, innerWidth);  
  213.                         for (; y < outHeight; y++) {  
  214.                             sampler.next(scanline);  
  215.                         }  
  216.                         return true;  
  217.                     }  
  218.                     // scanline now contains the raw data. Sample it.  
  219.                     sampler.next(scanline);  
  220.                     if (y < outHeight - 1) {  
  221.                         skip_src_rows(gif, scanline, innerWidth, sampler.srcDY() - 1);  
  222.                     }  
  223.                 }  
  224.                 // skip the rest of the rows (if any)  
  225.                 int read = (outHeight - 1) * sampler.srcDY() + sampler.srcY0() + 1;  
  226.                 SkASSERT(read <= innerHeight);  
  227.                 skip_src_rows(gif, scanline, innerWidth, innerHeight - read);  
  228.             }  
  229.             sanitize_indexed_bitmap(bm);  
  230.             return true;  
  231.             } break;  
  232.   
  233.         case EXTENSION_RECORD_TYPE:  
  234. #if GIFLIB_MAJOR < 5  
  235.             if (DGifGetExtension(gif, &temp_save.Function,  
  236.                                  &extData) == GIF_ERROR) {  
  237. #else  
  238.             if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) {  
  239. #endif  
  240.                 return error_return(*bm, "DGifGetExtension");  
  241.             }  
  242.   
  243.             while (extData != NULL) {  
  244.                 /* Create an extension block with our data */  
  245. #if GIFLIB_MAJOR < 5  
  246.                 if (AddExtensionBlock(&temp_save, extData[0],  
  247.                                       &extData[1]) == GIF_ERROR) {  
  248. #else  
  249.                 if (GifAddExtensionBlock(&gif->ExtensionBlockCount,  
  250.                                          &gif->ExtensionBlocks,  
  251.                                          extFunction,  
  252.                                          extData[0],  
  253.                                          &extData[1]) == GIF_ERROR) {  
  254. #endif  
  255.                     return error_return(*bm, "AddExtensionBlock");  
  256.                 }  
  257.                 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {  
  258.                     return error_return(*bm, "DGifGetExtensionNext");  
  259.                 }  
  260. #if GIFLIB_MAJOR < 5  
  261.                 temp_save.Function = 0;  
  262. #endif  
  263.             }  
  264.             break;  
  265.   
  266.         case TERMINATE_RECORD_TYPE:  
  267.             break;  
  268.   
  269.         default:    /* Should be trapped by DGifGetRecordType */  
  270.             break;  
  271.         }  
  272.     } while (recType != TERMINATE_RECORD_TYPE);  
  273.   
  274.     sanitize_indexed_bitmap(bm);  
  275.     return true;  
  276. }  


由于适配不同版本的gif库的原因,这段代码写得略显杂乱,也存在一些Bug。
值得注意的是Skia对于Gif动态图,只解最后一帧(SavedImage* image = &gif->SavedImages[gif->ImageCount-1];)
至于Gif动画,由SkGIFMovie类处理,走不同的流程。应用需要调另外的类(详细见:frameworks/base/graphics/java/android/graphics/Movie.java)。

(4)bmp

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {  
  2.     // First read the entire stream, so that all of the data can be passed to  
  3.     // the BmpDecoderHelper.  
  4.   
  5.     // Allocated space used to hold the data.  
  6.     SkAutoMalloc storage;  
  7.     // Byte length of all of the data.  
  8.     const size_t length = CopyStreamToStorage(&storage, stream);  
  9.     if (0 == length) {  
  10.         return 0;  
  11.     }  
  12.   
  13.     const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode;  
  14.     SkBmpDecoderCallback callback(justBounds);  
  15.   
  16.     // Now decode the BMP into callback's rgb() array [r,g,b, r,g,b, ...]  
  17.     {  
  18.         image_codec::BmpDecoderHelper helper;  
  19.         const int max_pixels = 16383*16383; // max width*height  
  20.         if (!helper.DecodeImage((const char*)storage.get(), length,  
  21.                                 max_pixels, &callback)) {  
  22.             return false;  
  23.         }  
  24.     }  
  25.   
  26.     // we don't need this anymore, so free it now (before we try to allocate  
  27.     // the bitmap's pixels) rather than waiting for its destructor  
  28.     storage.free();  
  29.   
  30.     int width = callback.width();  
  31.     int height = callback.height();  
  32.     SkColorType colorType = this->getPrefColorType(k32Bit_SrcDepth, false);  
  33.   
  34.     // only accept prefConfig if it makes sense for us  
  35.     if (kARGB_4444_SkColorType != colorType && kRGB_565_SkColorType != colorType) {  
  36.         colorType = kN32_SkColorType;  
  37.     }  
  38.   
  39.     SkScaledBitmapSampler sampler(width, height, getSampleSize());  
  40.   
  41.     bm->setInfo(SkImageInfo::Make(sampler.scaledWidth(), sampler.scaledHeight(),  
  42.                                   colorType, kOpaque_SkAlphaType));  
  43.   
  44.     if (justBounds) {  
  45.         return true;  
  46.     }  
  47.   
  48.     if (!this->allocPixelRef(bm, NULL)) {  
  49.         return false;  
  50.     }  
  51.   
  52.     SkAutoLockPixels alp(*bm);  
  53.   
  54.     if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, *this)) {  
  55.         return false;  
  56.     }  
  57.   
  58.     const int srcRowBytes = width * 3;  
  59.     const int dstHeight = sampler.scaledHeight();  
  60.     const uint8_t* srcRow = callback.rgb();  
  61.   
  62.     srcRow += sampler.srcY0() * srcRowBytes;  
  63.     for (int y = 0; y < dstHeight; y++) {  
  64.         sampler.next(srcRow);  
  65.         srcRow += sampler.srcDY() * srcRowBytes;  
  66.     }  
  67.     return true;  
  68. }  


这个是最简单的一种格式,用得较少,Skia里面的处理也比较随意。
a、将流中内容全部读到内存(CopyStreamToStorage)
b、解析流中的内容,将其转化为RGB格式(image_codec::BmpDecoderHelper::DecodeImage)
c、使用SkScaledBitmapSampler,将RGB转成目标格式,同时作下采样。


6、典型编码器的onEncode方法

(1)Jpeg

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. class SkJPEGImageEncoder : public SkImageEncoder {  
  2. protected:  
  3.     virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {  
  4. #ifdef TIME_ENCODE  
  5.         SkAutoTime atm("JPEG Encode");  
  6. #endif  
  7.   
  8.         SkAutoLockPixels alp(bm);  
  9.         if (NULL == bm.getPixels()) {  
  10.             return false;  
  11.         }  
  12.   
  13.         jpeg_compress_struct    cinfo;  
  14.         skjpeg_error_mgr        sk_err;  
  15.         skjpeg_destination_mgr  sk_wstream(stream);  
  16.   
  17.         // allocate these before set call setjmp  
  18.         SkAutoMalloc    oneRow;  
  19.         SkAutoLockColors ctLocker;  
  20.   
  21.         cinfo.err = jpeg_std_error(&sk_err);  
  22.         sk_err.error_exit = skjpeg_error_exit;  
  23.         if (setjmp(sk_err.fJmpBuf)) {  
  24.             return false;  
  25.         }  
  26.   
  27.         // Keep after setjmp or mark volatile.  
  28.         const WriteScanline writer = ChooseWriter(bm);  
  29.         if (NULL == writer) {  
  30.             return false;  
  31.         }  
  32.   
  33.         jpeg_create_compress(&cinfo);  
  34.         cinfo.dest = &sk_wstream;  
  35.         cinfo.image_width = bm.width();  
  36.         cinfo.image_height = bm.height();  
  37.         cinfo.input_components = 3;  
  38. #ifdef WE_CONVERT_TO_YUV  
  39.         cinfo.in_color_space = JCS_YCbCr;  
  40. #else  
  41.         cinfo.in_color_space = JCS_RGB;  
  42. #endif  
  43.         cinfo.input_gamma = 1;  
  44.   
  45.         jpeg_set_defaults(&cinfo);  
  46.         jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);  
  47. #ifdef DCT_IFAST_SUPPORTED  
  48.         cinfo.dct_method = JDCT_IFAST;  
  49. #endif  
  50.   
  51.         jpeg_start_compress(&cinfo, TRUE);  
  52.   
  53.         const int       width = bm.width();  
  54.         uint8_t*        oneRowP = (uint8_t*)oneRow.reset(width * 3);  
  55.   
  56.         const SkPMColor* colors = ctLocker.lockColors(bm);  
  57.         const void*      srcRow = bm.getPixels();  
  58.   
  59.         while (cinfo.next_scanline < cinfo.image_height) {  
  60.             JSAMPROW row_pointer[1];    /* pointer to JSAMPLE row[s] */  
  61.   
  62.             writer(oneRowP, srcRow, width, colors);  
  63.             row_pointer[0] = oneRowP;  
  64.             (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);  
  65.             srcRow = (const void*)((const char*)srcRow + bm.rowBytes());  
  66.         }  
  67.   
  68.         jpeg_finish_compress(&cinfo);  
  69.         jpeg_destroy_compress(&cinfo);  
  70.   
  71.         return true;  
  72.     }  
  73. };  

Jpeg是有损压缩,传入的quality决定其量化参数表。
a、Skia默认先将图片转为YUV444格式,再进行编码(WE_CONVERT_TO_YUV宏默认打开状态,否则就是先转为RGB888格式,再传入Jpeg编码时转YUV)。
b、默认使用JDCT_IFAST方法做傅立叶变换,很明显会造成一定的图片质量损失(即使quality设成100也存在,是计算精度的问题)。

(2)Png

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static transform_scanline_proc choose_proc(SkColorType ct, bool hasAlpha) {  
  2.     // we don't care about search on alpha if we're kIndex8, since only the  
  3.     // colortable packing cares about that distinction, not the pixels  
  4.     if (kIndex_8_SkColorType == ct) {  
  5.         hasAlpha = false;   // we store false in the table entries for kIndex8  
  6.     }  
  7.   
  8.     static const struct {  
  9.         SkColorType             fColorType;  
  10.         bool                    fHasAlpha;  
  11.         transform_scanline_proc fProc;  
  12.     } gMap[] = {  
  13.         { kRGB_565_SkColorType,     false,  transform_scanline_565 },  
  14.         { kN32_SkColorType,         false,  transform_scanline_888 },  
  15.         { kN32_SkColorType,         true,   transform_scanline_8888 },  
  16.         { kARGB_4444_SkColorType,   false,  transform_scanline_444 },  
  17.         { kARGB_4444_SkColorType,   true,   transform_scanline_4444 },  
  18.         { kIndex_8_SkColorType,     false,  transform_scanline_memcpy },  
  19.     };  
  20.   
  21.     for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {  
  22.         if (gMap[i].fColorType == ct && gMap[i].fHasAlpha == hasAlpha) {  
  23.             return gMap[i].fProc;  
  24.         }  
  25.     }  
  26.     sk_throw();  
  27.     return NULL;  
  28. }  
  29. bool SkPNGImageEncoder::onEncode(SkWStream* stream, const SkBitmap& bitmap, int /*quality*/) {  
  30.     SkColorType ct = bitmap.colorType();  
  31.   
  32.     const bool hasAlpha = !bitmap.isOpaque();  
  33.     int colorType = PNG_COLOR_MASK_COLOR;  
  34.     int bitDepth = 8;   // default for color  
  35.     png_color_8 sig_bit;  
  36.   
  37.     switch (ct) {  
  38.         case kIndex_8_SkColorType:  
  39.             colorType |= PNG_COLOR_MASK_PALETTE;  
  40.             // fall through to the ARGB_8888 case  
  41.         case kN32_SkColorType:  
  42.             sig_bit.red = 8;  
  43.             sig_bit.green = 8;  
  44.             sig_bit.blue = 8;  
  45.             sig_bit.alpha = 8;  
  46.             break;  
  47.         case kARGB_4444_SkColorType:  
  48.             sig_bit.red = 4;  
  49.             sig_bit.green = 4;  
  50.             sig_bit.blue = 4;  
  51.             sig_bit.alpha = 4;  
  52.             break;  
  53.         case kRGB_565_SkColorType:  
  54.             sig_bit.red = 5;  
  55.             sig_bit.green = 6;  
  56.             sig_bit.blue = 5;  
  57.             sig_bit.alpha = 0;  
  58.             break;  
  59.         default:  
  60.             return false;  
  61.     }  
  62.   
  63.     if (hasAlpha) {  
  64.         // don't specify alpha if we're a palette, even if our ctable has alpha  
  65.         if (!(colorType & PNG_COLOR_MASK_PALETTE)) {  
  66.             colorType |= PNG_COLOR_MASK_ALPHA;  
  67.         }  
  68.     } else {  
  69.         sig_bit.alpha = 0;  
  70.     }  
  71.   
  72.     SkAutoLockPixels alp(bitmap);  
  73.     // readyToDraw checks for pixels (and colortable if that is required)  
  74.     if (!bitmap.readyToDraw()) {  
  75.         return false;  
  76.     }  
  77.   
  78.     // we must do this after we have locked the pixels  
  79.     SkColorTable* ctable = bitmap.getColorTable();  
  80.     if (NULL != ctable) {  
  81.         if (ctable->count() == 0) {  
  82.             return false;  
  83.         }  
  84.         // check if we can store in fewer than 8 bits  
  85.         bitDepth = computeBitDepth(ctable->count());  
  86.     }  
  87.   
  88.     return doEncode(stream, bitmap, hasAlpha, colorType, bitDepth, ct, sig_bit);  
  89. }  
  90.   
  91. bool SkPNGImageEncoder::doEncode(SkWStream* stream, const SkBitmap& bitmap,  
  92.                   const bool& hasAlpha, int colorType,  
  93.                   int bitDepth, SkColorType ct,  
  94.                   png_color_8& sig_bit) {  
  95.   
  96.     png_structp png_ptr;  
  97.     png_infop info_ptr;  
  98.   
  99.     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, sk_error_fn,  
  100.                                       NULL);  
  101.     if (NULL == png_ptr) {  
  102.         return false;  
  103.     }  
  104.   
  105.     info_ptr = png_create_info_struct(png_ptr);  
  106.     if (NULL == info_ptr) {  
  107.         png_destroy_write_struct(&png_ptr,  png_infopp_NULL);  
  108.         return false;  
  109.     }  
  110.   
  111.     /* Set error handling.  REQUIRED if you aren't supplying your own 
  112.     * error handling functions in the png_create_write_struct() call. 
  113.     */  
  114.     if (setjmp(png_jmpbuf(png_ptr))) {  
  115.         png_destroy_write_struct(&png_ptr, &info_ptr);  
  116.         return false;  
  117.     }  
  118.   
  119.     png_set_write_fn(png_ptr, (void*)stream, sk_write_fn, png_flush_ptr_NULL);  
  120.   
  121.     /* Set the image information here.  Width and height are up to 2^31, 
  122.     * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on 
  123.     * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, 
  124.     * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, 
  125.     * or PNG_COLOR_TYPE_RGB_ALPHA.  interlace is either PNG_INTERLACE_NONE or 
  126.     * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST 
  127.     * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED 
  128.     */  
  129.   
  130.     png_set_IHDR(png_ptr, info_ptr, bitmap.width(), bitmap.height(),  
  131.                  bitDepth, colorType,  
  132.                  PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,  
  133.                  PNG_FILTER_TYPE_BASE);  
  134.   
  135.     // set our colortable/trans arrays if needed  
  136.     png_color paletteColors[256];  
  137.     png_byte trans[256];  
  138.     if (kIndex_8_SkColorType == ct) {  
  139.         SkColorTable* ct = bitmap.getColorTable();  
  140.         int numTrans = pack_palette(ct, paletteColors, trans, hasAlpha);  
  141.         png_set_PLTE(png_ptr, info_ptr, paletteColors, ct->count());  
  142.         if (numTrans > 0) {  
  143.             png_set_tRNS(png_ptr, info_ptr, trans, numTrans, NULL);  
  144.         }  
  145.     }  
  146.   
  147.     png_set_sBIT(png_ptr, info_ptr, &sig_bit);  
  148.     png_write_info(png_ptr, info_ptr);  
  149.   
  150.     const char* srcImage = (const char*)bitmap.getPixels();  
  151.     SkAutoSMalloc<1024> rowStorage(bitmap.width() << 2);  
  152.     char* storage = (char*)rowStorage.get();  
  153.     transform_scanline_proc proc = choose_proc(ct, hasAlpha);  
  154.   
  155.     for (int y = 0; y < bitmap.height(); y++) {  
  156.         png_bytep row_ptr = (png_bytep)storage;  
  157.         proc(srcImage, bitmap.width(), storage);  
  158.         png_write_rows(png_ptr, &row_ptr, 1);  
  159.         srcImage += bitmap.rowBytes();  
  160.     }  
  161.   
  162.     png_write_end(png_ptr, info_ptr);  
  163.   
  164.     /* clean up after the write, and free any memory allocated */  
  165.     png_destroy_write_struct(&png_ptr, &info_ptr);  
  166.     return true;  
  167. }  

a、将图像格式统一转为RGBA8888,再去作png编码,另外注明是否包含透明度信息。

b、对于原先带透明的图像格式(RGBA8888,RGBA4444),做反alpha预乘,也即每个像素值除以其alpha值,自然,除法会做些转化由乘法替代的。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值