• 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模块,用来解码、编码图片。

    我们来了解以下两件事:

    • 编译脚本
    • 解码器的创建

    编译脚本

    skiagypimages.gyp定义了images模块的编译规则。依据不同平台来设置须要依赖的模块、要编译的源文件。

    sync-and-gyp时会依据skiagypimages.gyp来生成实际的ninja文件——outReleaseobjgypimages.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
    ");
        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开发】。

  • 相关阅读:
    项目目标文档
    系统利益相关者描述案例
    软件需求模式 读书笔记二
    软件需求分析 读书笔记1
    专业实训题目需求分析
    2015年秋季个人阅读计划
    CodeVs 1615 数据备份
    HDU 3900 Unblock Me
    HDU 5898 odd-even number
    HDU 5877 Weak Pair
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8494732.html
Copyright © 2020-2023  润新知