我在在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. thttpd和cgilua安装与运行流程分析

    安装 参考如下博文安装thttpd软件 http://blog.csdn.net/21aspnet/article/details/7045845 http://blog.csdn.net/drago ...

  2. u-boot启动流程分析(2)_板级(board)部分

    转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global dat ...

  3. u-boot启动流程分析(1)_平台相关部分

    转自:http://www.wowotech.net/u-boot/boot_flow_1.html 1. 前言 本文将结合u-boot的“board—>machine—>arch—> ...

  4. 【转】android SystemUI 流程分析

    android4 SystemUI 流程分析 什么是SystemUI? 对于Phone来说SystemUI指的是:StatusBar(状态栏).NavigationBar(导航栏).而对于Tablet ...

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

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

  6. MSM8909中LK阶段LCM屏适配与显示流程分析(二)

    1.前言 在前面的文章MSM8909中LK阶段LCM屏适配与显示流程分析(一),链接如下: https://www.cnblogs.com/Cqlismy/p/12019317.html 介绍了如何使 ...

  7. VLC架构及流程分析

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

  8. Netty 源码学习——客户端流程分析

    Netty 源码学习--客户端流程分析 友情提醒: 需要观看者具备一些 NIO 的知识,否则看起来有的地方可能会不明白. 使用版本依赖 <dependency> <groupId&g ...

  9. 【逆向&编程实战】Metasploit安卓载荷运行流程分析_复现meterpreter模块接管shell

    /QQ:3496925334 作者:MG193.7 CNBLOG博客号:ALDYS4 未经许可,禁止转载/ 关于metasploit的安卓模块,前几次的博客我已经写了相应的分析和工具 [Android ...

随机推荐

  1. Sql批量插入方法

    using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...

  2. $luogu2375[NOI2014]$

    \(problem\) 其中,\(next[i],next[next[i]],next[next[next[i]]]......\)都是这个前缀串i的公共前后缀,而且只有它们是公共前后缀 那么,我们其 ...

  3. Jquery音频播放插件下载地址(有Html、JS、CSS、音频)

    有详细的html文件.全部JS代码文件.Css样式文件.测试音频资料 音频播放插件下载链接(百度云): http://pan.baidu.com/s/1pKC904F 提取码评论留邮箱发送,谢谢!

  4. Mysql(三):多表查询和存储程序

    今天内容: ● 多表查询(内连接 外连接 子查询) ● 存储程序(存储过程 函数) 多表查询 ​ 同时从多张数据表中查取到需要的数据即是多表查询. 多表查询时,参与查询的表中每条数据进行组合,这种效果 ...

  5. [Codeforces]Codeforces Round #490 (Div. 3)

    Mishka and Contest #pragma comment(linker, "/STACK:102400000,102400000") #ifndef ONLINE_JU ...

  6. Leetcode0133--Clone Graph 克隆无向图

    [转载请注明]:https://www.cnblogs.com/igoslly/p/9699791.html 一.题目 二.题目分析 给出一个无向图,其中保证每点之间均有连接,给出原图中的一个点 no ...

  7. java攻城师之路--复习java web之Cookie_Session

    Servlet技术 用来动态生成 网页数据资源Servlet生成HTML 页面数据时,所有内容都是通过 response.getWriter response.getOutputStream 向浏览器 ...

  8. CNN-CV识别简史2012-2017:从 AlexNet、ResNet 到 Mask RCNN

    原文:计算机视觉识别简史:从 AlexNet.ResNet 到 Mask RCNN 总是找不到原文,标记一下.        一切从这里开始:现代物体识别随着ConvNets的发展而发展,这一切始于2 ...

  9. element ui 日期控件范围时间限制记录、以及计算两个日期之间的天数

    日期的筛选经常会有最小的日期选择,例如:当前日期 :clearable="false" :picker-options="pickerOptions0" val ...

  10. java同学毕业后学习之路建议

    第一部分:对于尚未做过Java工作的同学,包括一些在校生以及刚准备转行Java的同学. 滤过: 第二部分:对于参加工作一年以内的同学. 恭喜你,这个时候,你已经拥有了一份Java的工作.这个阶段是你成 ...