天天看点

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

1、api和自注册机制

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

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

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

2、解码的流程:

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

遍历的代码见:

external/skia/src/images/skimagedecoder_factoryregistrar.cpp

(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,直接从内存中读。

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

详细见:

external/skia/src/utils/skfrontbufferedstream.cpp

4、解码选项

external/skia/include/core/skimagedecoder.h

这些私有变量对应于bitmapfactory.options中的相应的配置项,见

frameworks/base/graphics/java/android/graphics/bitmapfactory.java

设置的代码见dodecode方法:

frameworks/base/core/jni/android/graphics/bitmapfactory.cpp

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

5、典型解码器的ondecode方法

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

(1)jpeg

a、配置jpeg解码选项(输出格式、采样率、idct类型、下采样率等等,在skimagedecoder.h的注释中解释过一些)

b、在不设置下采样且目标格式为rgba时,直接解码到目标bitmap上(jpeg是按yuv三分量分别压缩的,设置为jpeg库解码时作yuv-rgba的颜色转换)。

c、需要下采样时,由于jpeg库可作简化处理,因此重新计算一个下采样率,由skscaledbitmapsampler将解码出来的图像下采样到目标bitmap上。

(2)png

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

(3)gif

由于适配不同版本的gif库的原因,这段代码写得略显杂乱,也存在一些bug。

值得注意的是skia对于gif动态图,只解最后一帧(savedimage* image = &gif->savedimages[gif->imagecount-1];)

至于gif动画,由skgifmovie类处理,走不同的流程。应用需要调另外的类(详细见:frameworks/base/graphics/java/android/graphics/movie.java)。

(4)bmp

这个是最简单的一种格式,用得较少,skia里面的处理也比较随意。

a、将流中内容全部读到内存(copystreamtostorage)

b、解析流中的内容,将其转化为rgb格式(image_codec::bmpdecoderhelper::decodeimage)

c、使用skscaledbitmapsampler,将rgb转成目标格式,同时作下采样。

6、典型编码器的onencode方法

jpeg是有损压缩,传入的quality决定其量化参数表。

a、skia默认先将图片转为yuv444格式,再进行编码(we_convert_to_yuv宏默认打开状态,否则就是先转为rgb888格式,再传入jpeg编码时转yuv)。

b、默认使用jdct_ifast方法做傅立叶变换,很明显会造成一定的图片质量损失(即使quality设成100也存在,是计算精度的问题)。

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

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

继续阅读