Skia图片解码模块流程分析
我在在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\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源码如下:
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方法如下:
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中实现,代码:
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来的:
typedef SkTRegistry<SkImageDecoder*(*)(SkStreamRewindable*)> SkImageDecoder_DecodeReg;
好,SKTRegistry这个模板类浮出水面了。它利用构造函数来注册一个创建SkImageDecoder的工厂函数。而它的静态方法Head()则返回静态成员gHead作为链表首指针。这个静态成员gHead的初始化,就在SkImageDecoder_FactoryRegistrar.cpp中:
template SkImageDecoder_FormatReg* SkImageDecoder_FormatReg::gHead;
SKTRegistry模板工厂很有意思,也比较巧妙。
要把一个ImageDecoder注册到工厂里,可以在cpp中写上类似下面的代码:
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开发】。
Skia图片解码模块流程分析相关推荐
- 【Android SDM660源码分析】- 03 - UEFI XBL GraphicsOutput BMP图片显示流程
[Android SDM660源码分析]- 03 - UEFI XBL GraphicsOutput BMP图片显示流程 1. GraphicsOutput.h 2. 显示驱动初化 DisplayDx ...
- VLC架构及流程分析
0x00 前置信息 VLC是一个非常庞大的工程,我从它的架构及流程入手进行分析,涉及到一些很细的概念先搁置一边,日后详细分析. 0x01 源码结构(Android Java相关的暂未分析) # bui ...
- Java版 QQ空间自动登录无需拷贝cookie一天抓取30WQQ说说数据流程分析【转】
Java版 QQ空间自动登录无需拷贝cookie一天抓取30WQQ说说数据&流程分析 QQ空间说说抓取难度比较大,花了一个星期才研究清楚! 代码请移步到GitHub GitHub地址:http ...
- WebRTC视频数据流程分析
本文来自<WebRTC Native开发实战>书籍作者许建林在LiveVideoStack线上分享中的内容,详细分析总结 WebRTC 的视频数据流程,并对大型项目如何快速上手:分析方法, ...
- 百度新闻后台逻辑流程分析
/*版权声明:可以任意转载,转载时请务必标明文章原始出处和作者信息 .*/ 百度新闻后台逻辑流程分析 CopyMiddle:张俊林 TimeStamp:2008年1月9日 今天分析了一下百度新闻,琢磨 ...
- spark提交应用的全流程分析
spark提交应用的全流程分析 @(SPARK)[spark] 本文分析一下spark的应用通过spark-submit后,如何提交到集群中并开始运行. 先介绍一下spark从提交到运行的全流程,下面 ...
- 生成验证码的流程分析.
浏览器解析页面, 携带uuid向服务器发送请求获取图片, 服务器生成图片验证码, 返回图片本身给浏览器, 客户端根据图片输入验证码内容, 把输入的内容发送给服务器, 服务器对比输入的验证码是否正确 ...
- Android 8.0 学习(23)---recovery 流程分析
Android 8.0 recovery 流程分析 这里主要分析non A/B模式下的recovery流程 A/B模式下的recovery在boot中 后续会不断补充,如果有疏漏或者错误的地方,请 ...
- GEF入门实例_总结_04_Eclipse插件启动流程分析
一.前言 本文承接上一节:GEF入门实例_总结_03_显示菜单和工具栏 注意到app目录下的6个类文件. 这6个文件对RCP应用程序而言非常重要,可能我们现在对这几个文件的理解还是云里雾里,这一节我们 ...
最新文章
- 九十、Python的GUI系列 | QtDesigner进行界面设计
- java中的Iterator和Iterable 区别
- black-box优化——第二篇:直接搜索算法
- 面板数据回归模型(固定效应、随机效应、混合回归、变系数)、面板数据AR、VAR模型
- Python - 浅谈Python的编译与反编译
- 解决kali linux找不到更新的问题
- uniac是哪一代计算机的代表,Saint-Uniac
- FileSaver、js-xlsx、SheetJS在线报表预览导出
- python名称空间_一篇文章搞懂Python的类与对象名称空间
- 【我的世界Minecraft-MC】常见及各种指令大杂烩【2022.8版】
- 工字型钢弹性截面模量计算公式_截面模量计算方法
- 一个数据分析师的职业规划:人生本来就应该提前做好准备
- (重点)微服务核心研究之--编排
- Vue-cli3更改项目logo图标
- css div颜色渐变效果
- 2023,数字政务潮水已至
- 罗斯蒙特248温度变送器248HANANONS
- Selenium学习_常用场景代码示例
- 注册交管12123服务器异常,交管12123提示服务异常怎么解决
- 1094 谷歌的招聘(JAVA)