Skia图片解码模块流程分析
2016-02-22 09:13
274 查看
我在在PPAPI插件中使用Skia绘图中说可以在PPAPI插件内使用Skia来绘图。这里面会有一个与色彩空间(像素格式)相关的问题。在那篇文章里我们在PPAPI中使用PPB_ImageData创建2D图像缓冲区时使用了PP_IMAGEDATAFORMAT_BGRA_PREMUL这种图像格式。Skia在Intel Pentium系列的主机上(小端字节序)编译时,刚好生成的Native颜色格式就是kBGRA_8888__SkColorType,所以一切都是好好的。
然而当我使用kRGBA_8888__SkColorType格式的SkBitmap作为SkCanvas的backend来绘图时就显示不出来了。我被这个问题折磨了三四天,解决了,但再从文件解码png等图片时,Red通道和Blue通道又反了。下篇文章我们再说那个问题,现在先来了解Skia的images模块的一些信息。
Skia的images模块,用来解码、编码图片。我们来了解下面两件事:
编译脚本
解码器的创建
Skia构建系统与编译脚本分析中给出了我修改过的images.ninja文件。那是我花了老大代价搞明白的,默认生成的不是那样子的。我的修改,是配合PPAPI插件使用RGBA格式的SkBitmap的。PPAPI那侧的代码也做了一些改动。
不过现在看来,修改images.gyp是更彻底的解决方案。这是后话。
DecodeStream源码如下:
它从一个工厂内创建一个能解码给定stream的codec。这个工厂方法,SkImageDecoder::Factory,有好几个实现:
SkImageDecoder_FactoryDefault.cpp
SkImageDecoder_WIC.cpp
SkImageDecoder_CG.cpp
SkImageDecoder_empty.cpp
其中SkImageDecoder_WIC.cpp内的实现是Windows下默认的,SkImageDecoder_CG.cpp是MAC、iOS平台默认的。其它平台使用SkImageDecoder_FactoryDefault.cpp。
SkImageDecoder_WIC.cpp这个文件内的Factory方法如下:
它先调用image_decoder_from_stream()来创建decoder,找不到合适的decoder就用SkImageDecoder_WIC来替代。SkImageDecoder_WIC干嘛的呢?用Windwos特有的COM组件IWICImagingFactory和IWICBitmapDecoder来解码图片,支持BMP、ICO、PNG、GIF、JPEG等格式。
image_decoder_from_stream()在SkImageDecoder_FactoryRegistrar.cpp中实现,代码:
可以看到它调用SkImageDecoder_DecodeReg这个类的静态方法Head()获取了一个列表的指针,列表元素的类型是SkImageDecoder_DecodeReg。这个类型是在SkImageDecoder.h中typedef来的:
好,SKTRegistry这个模板类浮出水面了。它利用构造函数来注册一个创建SkImageDecoder的工厂函数。而它的静态方法Head()则返回静态成员gHead作为链表首指针。这个静态成员gHead的初始化,就在SkImageDecoder_FactoryRegistrar.cpp中:
SKTRegistry模板工厂很有意思,也比较巧妙。
要把一个ImageDecoder注册到工厂里,可以在cpp中写上类似下面的代码:
这行代码定义了一个静态对象,这个对象的构造函数会在main()之前执行,就会把sk_libpng_dfactory注册到解码器工厂中。
去翻看SkImageDecoder_libpng.cpp、SkImageDecoder_libjpeg.cpp、SkImageDecoder_libgif.cpp、SkImageDecoder_libbmp.cpp、SkImageDecoder_libico.cpp等文件,都有类似代码。
如果这些文件编译到images模块,就会使用注册到图片解码器工厂。
Windows下没有编译SkImageDecoder_libpng.cpp、SkImageDecoder_libgif.cpp等,默认使用COM组件来解码png、gif、jpeg等图片。
就这样吧。
其他参考文章详见我的专栏:【CEF与PPAPI开发】。
然而当我使用kRGBA_8888__SkColorType格式的SkBitmap作为SkCanvas的backend来绘图时就显示不出来了。我被这个问题折磨了三四天,解决了,但再从文件解码png等图片时,Red通道和Blue通道又反了。下篇文章我们再说那个问题,现在先来了解Skia的images模块的一些信息。
Skia的images模块,用来解码、编码图片。我们来了解下面两件事:
编译脚本
解码器的创建
编译脚本
skia\gyp\images.gyp定义了images模块的编译规则,根据不同平台来设置需要依赖的模块、要编译的源文件。sync-and-gyp时会根据skia\gyp\images.gyp来生成实际的ninja文件——out\Release\obj\gyp\images.ninja。Skia构建系统与编译脚本分析中给出了我修改过的images.ninja文件。那是我花了老大代价搞明白的,默认生成的不是那样子的。我的修改,是配合PPAPI插件使用RGBA格式的SkBitmap的。PPAPI那侧的代码也做了一些改动。
不过现在看来,修改images.gyp是更彻底的解决方案。这是后话。
解码器的创建
以解码为例,接口是SkImageDecoder,它的DecodeMemory、DecodeFile、DecodeStream三个静态方法是解码图片的入口,前两个方法最终会再调用到DecodeStream方法。DecodeStream源码如下:
[code]bool SkImageDecoder::DecodeStream(SkStreamRewindable* stream, SkBitmap* bm, SkColorType pref, Mode mode, Format* format) { SkASSERT(stream); SkASSERT(bm); bool success = false; SkImageDecoder* codec = SkImageDecoder::Factory(stream); if (codec) { success = codec->decode(stream, bm, pref, mode) != kFailure; if (success && format) { *format = codec->getFormat(); if (kUnknown_Format == *format) { if (stream->rewind()) { *format = GetStreamFormat(stream); } } } delete codec; } return success; }
它从一个工厂内创建一个能解码给定stream的codec。这个工厂方法,SkImageDecoder::Factory,有好几个实现:
SkImageDecoder_FactoryDefault.cpp
SkImageDecoder_WIC.cpp
SkImageDecoder_CG.cpp
SkImageDecoder_empty.cpp
其中SkImageDecoder_WIC.cpp内的实现是Windows下默认的,SkImageDecoder_CG.cpp是MAC、iOS平台默认的。其它平台使用SkImageDecoder_FactoryDefault.cpp。
SkImageDecoder_WIC.cpp这个文件内的Factory方法如下:
[code]SkImageDecoder* SkImageDecoder::Factory(SkStreamRewindable* stream) { SkImageDecoder* decoder = image_decoder_from_stream(stream); if (nullptr == decoder) { // If no image decoder specific to the stream exists, use SkImageDecoder_WIC. return new SkImageDecoder_WIC; } else { return decoder; } }
它先调用image_decoder_from_stream()来创建decoder,找不到合适的decoder就用SkImageDecoder_WIC来替代。SkImageDecoder_WIC干嘛的呢?用Windwos特有的COM组件IWICImagingFactory和IWICBitmapDecoder来解码图片,支持BMP、ICO、PNG、GIF、JPEG等格式。
image_decoder_from_stream()在SkImageDecoder_FactoryRegistrar.cpp中实现,代码:
[code]SkImageDecoder* image_decoder_from_stream(SkStreamRewindable* stream) { //OutputDebugStringA("image_decoder_from_stream, SkImageDecoder_FactoryRegistrar.cpp\r\n"); SkImageDecoder* codec = nullptr; const SkImageDecoder_DecodeReg* curr = SkImageDecoder_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. bool rewindSuceeded = stream->rewind(); // our image decoder's require that rewind is supported so we fail early // if we are given a stream that does not support rewinding. if (!rewindSuceeded) { SkDEBUGF(("Unable to rewind the image stream.")); delete codec; return nullptr; } if (codec) { return codec; } curr = curr->next(); } return nullptr; }
可以看到它调用SkImageDecoder_DecodeReg这个类的静态方法Head()获取了一个列表的指针,列表元素的类型是SkImageDecoder_DecodeReg。这个类型是在SkImageDecoder.h中typedef来的:
[code]typedef SkTRegistry<SkImageDecoder*(*)(SkStreamRewindable*)> SkImageDecoder_DecodeReg;
好,SKTRegistry这个模板类浮出水面了。它利用构造函数来注册一个创建SkImageDecoder的工厂函数。而它的静态方法Head()则返回静态成员gHead作为链表首指针。这个静态成员gHead的初始化,就在SkImageDecoder_FactoryRegistrar.cpp中:
[code]template SkImageDecoder_FormatReg* SkImageDecoder_FormatReg::gHead;
SKTRegistry模板工厂很有意思,也比较巧妙。
要把一个ImageDecoder注册到工厂里,可以在cpp中写上类似下面的代码:
[code]static SkImageDecoder_DecodeReg gDReg(sk_libpng_dfactory);
这行代码定义了一个静态对象,这个对象的构造函数会在main()之前执行,就会把sk_libpng_dfactory注册到解码器工厂中。
去翻看SkImageDecoder_libpng.cpp、SkImageDecoder_libjpeg.cpp、SkImageDecoder_libgif.cpp、SkImageDecoder_libbmp.cpp、SkImageDecoder_libico.cpp等文件,都有类似代码。
如果这些文件编译到images模块,就会使用注册到图片解码器工厂。
Windows下没有编译SkImageDecoder_libpng.cpp、SkImageDecoder_libgif.cpp等,默认使用COM组件来解码png、gif、jpeg等图片。
就这样吧。
其他参考文章详见我的专栏:【CEF与PPAPI开发】。
相关文章推荐
- 怎样通过css的media属性,适配不同分辨率的终端设备?
- Android,iOS打开手机QQ与指定用户聊天界面
- [iOS]UIScrollview自定义分页的实现方法
- 在Eclipse中,如何把一个java项目变成web项目?
- Log4j配置详细说明
- php课程---php使用PDO方法详解(转)
- 从头认识Spring-3.2 简单的AOP日志实现-需要记录方法的运行时间
- $.data在$.extend和$.fn下的应用
- js实现手机号码和身份证号码校验
- 深入分析JavaWeb 33 -- 开发自己简易的JDBC框架
- PHP.INI配置:Session配置详细说明教程
- OJ学习笔记5
- 【小镇的技术天梯】vma的基本操作
- xcode设置项目图标玻璃镜效果
- C/C++内存泄漏及检测 http://www.cnblogs.com/skynet/archive/2011/02/20/1959162.html
- learn python the hard way(笨办法学python) 练习39
- 整个手机界就是一部天龙八部
- 阿里云、青云、腾讯云服务器,Mysql数据库,Redis等产品性能对比
- 日经春秋 20160222
- java核心技术卷I-第三章学习笔记