我在在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图片解码模块流程分析相关推荐

  1. 【Android SDM660源码分析】- 03 - UEFI XBL GraphicsOutput BMP图片显示流程

    [Android SDM660源码分析]- 03 - UEFI XBL GraphicsOutput BMP图片显示流程 1. GraphicsOutput.h 2. 显示驱动初化 DisplayDx ...

  2. VLC架构及流程分析

    0x00 前置信息 VLC是一个非常庞大的工程,我从它的架构及流程入手进行分析,涉及到一些很细的概念先搁置一边,日后详细分析. 0x01 源码结构(Android Java相关的暂未分析) # bui ...

  3. Java版 QQ空间自动登录无需拷贝cookie一天抓取30WQQ说说数据流程分析【转】

    Java版 QQ空间自动登录无需拷贝cookie一天抓取30WQQ说说数据&流程分析 QQ空间说说抓取难度比较大,花了一个星期才研究清楚! 代码请移步到GitHub GitHub地址:http ...

  4. WebRTC视频数据流程分析

    本文来自<WebRTC Native开发实战>书籍作者许建林在LiveVideoStack线上分享中的内容,详细分析总结 WebRTC 的视频数据流程,并对大型项目如何快速上手:分析方法, ...

  5. 百度新闻后台逻辑流程分析

    /*版权声明:可以任意转载,转载时请务必标明文章原始出处和作者信息 .*/ 百度新闻后台逻辑流程分析 CopyMiddle:张俊林 TimeStamp:2008年1月9日 今天分析了一下百度新闻,琢磨 ...

  6. spark提交应用的全流程分析

    spark提交应用的全流程分析 @(SPARK)[spark] 本文分析一下spark的应用通过spark-submit后,如何提交到集群中并开始运行. 先介绍一下spark从提交到运行的全流程,下面 ...

  7. 生成验证码的流程分析.

    浏览器解析页面, 携带uuid向服务器发送请求获取图片,  服务器生成图片验证码, 返回图片本身给浏览器,  客户端根据图片输入验证码内容, 把输入的内容发送给服务器, 服务器对比输入的验证码是否正确 ...

  8. Android 8.0 学习(23)---recovery 流程分析

    Android 8.0 recovery 流程分析 这里主要分析non A/B模式下的recovery流程  A/B模式下的recovery在boot中  后续会不断补充,如果有疏漏或者错误的地方,请 ...

  9. GEF入门实例_总结_04_Eclipse插件启动流程分析

    一.前言 本文承接上一节:GEF入门实例_总结_03_显示菜单和工具栏 注意到app目录下的6个类文件. 这6个文件对RCP应用程序而言非常重要,可能我们现在对这几个文件的理解还是云里雾里,这一节我们 ...

最新文章

  1. 九十、Python的GUI系列 | QtDesigner进行界面设计
  2. java中的Iterator和Iterable 区别
  3. black-box优化——第二篇:直接搜索算法
  4. 面板数据回归模型(固定效应、随机效应、混合回归、变系数)、面板数据AR、VAR模型
  5. Python - 浅谈Python的编译与反编译
  6. 解决kali linux找不到更新的问题
  7. uniac是哪一代计算机的代表,Saint-Uniac
  8. FileSaver、js-xlsx、SheetJS在线报表预览导出
  9. python名称空间_一篇文章搞懂Python的类与对象名称空间
  10. 【我的世界Minecraft-MC】常见及各种指令大杂烩【2022.8版】
  11. 工字型钢弹性截面模量计算公式_截面模量计算方法
  12. 一个数据分析师的职业规划:人生本来就应该提前做好准备
  13. (重点)微服务核心研究之--编排
  14. Vue-cli3更改项目logo图标
  15. css div颜色渐变效果
  16. 2023,数字政务潮水已至
  17. 罗斯蒙特248温度变送器248HANANONS
  18. Selenium学习_常用场景代码示例
  19. 注册交管12123服务器异常,交管12123提示服务异常怎么解决
  20. 1094 谷歌的招聘(JAVA)

热门文章

  1. 大数据能否解决城市所面临的环境问题
  2. T 检验 (T test)
  3. 淘宝/天猫API:seller_info-获得淘宝店铺详情
  4. access设计视图
  5. windows C 盘扩容
  6. 【web素材】03-24款后台管理系统网站模板
  7. 验证tensorflow是否安装成功
  8. Windows10自带软件一款性能监控工具
  9. 第四周铁人战队学习总结
  10. 上号神器,英雄联盟手游扫码登录教程