windows下编译skia

Posted Redrain

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了windows下编译skia相关的知识,希望对你有一定的参考价值。

转载请说明原出处,谢谢~~:https://redrain.blog.csdn.net/article/details/111685123

 

目录

准备环境

1.配置代理

2.下载depot_tools

3.下载skia

4.假如没有梯子

开始编译

1.args的参数说明

2.生成项目文件

3.删除多余的命令

用LLVM编译

Skia剪裁

手动去掉三方库依赖

去掉字体依赖

去掉图片依赖

针对skia老版本

编译cmake


skia是谷歌的一个开源2D引擎,用来实现利用CPU实现2D图形绘制。skia应用非常广泛,chrome、android、firefox、flutter等项目的渲染引擎都是skia。

最近打算给ui库替换为skia渲染引擎,所以搞一下windows下的skia编译。以前也搞过老版本的skia编译https://blog.csdn.net/zhuhongshu/article/details/51272050,现在打算编译一个最新的skia。

这次的skia编译时间为2020-12-22,SHA-1:71f75e3111aebcb9895934ecdf2a84a82ac87ba4

 

虽然官网中有相关的文档,但是实际编译时会遇到一些官网没有说明的情况,所以专门写一篇文章。

 

准备环境

打开网站需要梯子,skia的官网:https://skia.org/

下载文档:https://skia.org/user/download

编译文档:https://skia.org/user/build

 

  • Win7/Win10(我是win10)
  • Visual Studio 2017/2019(我是2017)
  • python v2.7.x(必须是v2,需要添加到Path环境变量)
  • Git(需要添加到Path环境变量)

 

1.配置代理

skia本身和相关代码的下载都需要梯子,可以用ShadowSocks或者其他代理

git config --global http.proxy 'socks5://127.0.0.1:1080' git config --global https.proxy 'socks5://127.0.0.1:1080'

使用完毕后取消git代理

git config --global --unset http.proxy git config --global --unset https.proxy

 

2.下载depot_tools

depot_tools是编译谷歌系列源码需要用到的工具

git clone 'https://chromium.googlesource.com/chromium/tools/depot_tools.git'

下载后需要添加到Path环境变量

3.下载skia

git clone https://skia.googlesource.com/skia.git

打开cmd,切换到skia目录,执行脚本

python2 tools/git-sync-deps

这一步是自动下载skia依赖的三方库,会自动下载到skia/third_party/externals目录;以及用于生成项目文件的工具gn.exe

 

4.假如没有梯子

如果没有梯子,可以在github上找depot_tools和skia的镜像,也可以用。

但是skia依赖的三方库会比较麻烦。python2 tools/git-sync-deps命令会按照skia/DEPS文件里的列表下载依赖的三方库,几乎都是谷歌的地址。这时需要根据自己的需求自己另外下载需要的三方库放到skia/third_party/externals目录。有网友建立了这些三方库的镜像,可以把DEPS文件中的代码改为:

use_relative_paths = True
 
vars = 
  "checkout_chromium": False,

 
deps = 
  "buildtools"                            : "https://github.com/GoogleDepends/buildtools.git@505de88083136eefd056e5ee4ca0f01fe9b33de8",
  "common"                                : "https://github.com/GoogleDepends/common.git@9737551d7a52c3db3262db5856e6bcd62c462b92",
  "third_party/externals/angle2"          : "https://github.com/GoogleDepends/angle2.git@1cc49bb2e230555fb3dc33d3400a5f7a0cefe943",
  # Dawn requires jinja2 and markupsafe for the code generator, and glslang and shaderc for SPIRV compilation.
  # When the Dawn revision is updated these should be updated from the Dawn DEPS as well.
  "third_party/externals/dawn"            : "https://github.com/GoogleDepends/dawn.git@11652ff8f8b3c3104eb2627717fa652d432d5b84",
  "third_party/externals/glslang"         : "https://github.com/GoogleDepends/glslang@1f0fcbe5a30fdc9632a8bff36277103fabf0797c",
  "third_party/externals/jinja2"          : "https://github.com/GoogleDepends/jinja2@b41863e42637544c2941b574c7877d3e1f663e25",
  "third_party/externals/markupsafe"      : "https://github.com/GoogleDepends/markupsafe@8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
  "third_party/externals/shaderc"         : "https://github.com/GoogleDepends/shaderc@362becca1ff2a841c21fd675ac3a9c1ee9bb5612",
  "third_party/externals/dng_sdk"         : "https://github.com/GoogleDepends/dng_sdk.git@c8d0c9b1d16bfda56f15165d39e0ffa360a11123",
  "third_party/externals/egl-registry"    : "https://github.com/GoogleDepends/EGL-Registry@a0bca08de07c7d7651047bedc0b653cfaaa4f2ae",
  "third_party/externals/expat"           : "https://github.com/GoogleDepends/expat.git@e5aa0a2cb0a5f759ef31c0819dc67d9b14246a4a",
  "third_party/externals/freetype"        : "https://github.com/GoogleDepends/freetype2.git@0a3d2bb99b45b72e1d45185ab054efa993d97210",
  "third_party/externals/harfbuzz"        : "https://github.com/GoogleDepends/harfbuzz.git@3a74ee528255cc027d84b204a87b5c25e47bff79",
  "third_party/externals/icu"             : "https://github.com/GoogleDepends/icu.git@dbd3825b31041d782c5b504c59dcfb5ac7dda08c",
  "third_party/externals/imgui"           : "https://github.com/GoogleDepends/imgui.git@d38d7c6628bebd02692cfdd6fa76b4d992a35b75",
  "third_party/externals/libgifcodec"     : "https://github.com/GoogleDepends/libgifcodec@d06d2a6d42baf6c0c91cacc28df2542a911d05fe",
  "third_party/externals/libjpeg-turbo"   : "https://github.com/GoogleDepends/libjpeg-turbo.git@574f3a772c96dc9db2c98ef24706feb3f6dbda9a",
  "third_party/externals/libpng"          : "https://github.com/GoogleDepends/libpng.git@386707c6d19b974ca2e3db7f5c61873813c6fe44",
  "third_party/externals/libwebp"         : "https://github.com/GoogleDepends/libwebp.git@0fe1a89dbf1930fc2554dbe76adad5d962054ead",
  "third_party/externals/lua"             : "https://github.com/GoogleDepends/lua.git@e354c6355e7f48e087678ec49e340ca0696725b1",
  "third_party/externals/microhttpd"      : "https://github.com/GoogleDepends/libmicrohttpd@748945ec6f1c67b7efc934ab0808e1d32f2fb98d",
  "third_party/externals/opencl-lib"      : "https://github.com/GoogleDepends/common-lib-amd-APPSDK-3.0@4e6d30e406d2e5a65e1d65e404fe6df5f772a32b",
  "third_party/externals/opencl-registry" : "https://github.com/GoogleDepends/OpenCL-Registry@932ed55c85f887041291cef8019e54280c033c35",
  "third_party/externals/opengl-registry" : "https://github.com/GoogleDepends/OpenGL-Registry@14b80ebeab022b2c78f84a573f01028c96075553",
  "third_party/externals/piex"            : "https://github.com/GoogleDepends/piex.git@bb217acdca1cc0c16b704669dd6f91a1b509c406",
  "third_party/externals/sdl"             : "https://github.com/GoogleDepends/sdl@5d7cfcca344034aff9327f77fc181ae3754e7a90",
  "third_party/externals/sfntly"          : "https://github.com/GoogleDepends/sfntly.git@b55ff303ea2f9e26702b514cf6a3196a2e3e2974",
  "third_party/externals/spirv-cross"     : "https://github.com/GoogleDepends/SPIRV-Cross@871c85d7f0edc6b613e3959bc51d13bfbc2fe2df",
  "third_party/externals/spirv-headers"   : "https://github.com/GoogleDepends/SPIRV-Headers.git@f8bf11a0253a32375c32cad92c841237b96696c0",
  "third_party/externals/spirv-tools"     : "https://github.com/GoogleDepends/SPIRV-Tools.git@1c8bda3721e6b9302f694b58c26d32eff341b126",
  "third_party/externals/swiftshader"     : "https://github.com/GoogleDepends/swiftshader@f99302c4efe6f32297a619d407b4410ec3ee6412",
  #"third_party/externals/v8"              : "https://chromium.googlesource.com/v8/v8.git@5f1ae66d5634e43563b2d25ea652dfb94c31a3b4",
  "third_party/externals/wuffs"           : "https://github.com/GoogleDepends/wuffs.git@4080840928c0b05a80cda0d14ac2e2615f679f1a",
  "third_party/externals/zlib"            : "https://github.com/GoogleDepends/zlib@47af7c547f8551bd25424e56354a2ae1e9062859",
 
  "../src": 
    "url": "https://chromium.googlesource.com/chromium/src.git@d086f1bdb67d4d54ba424d42d01eb256c9a3765f",
    "condition": "checkout_chromium",
  ,

 
recursedeps = [
  "common",
  "../src",
]
 
gclient_gn_args_from = 'src'

剩下的就是工具gn.exe,这个可以在网上搜索编译好的:https://download.csdn.net/download/qq_35824650/12398406,下载后放到skia\\bin目录下。

不过这样做容易出现各个依赖库版本不一致的文件,最好还是弄个梯子。

 

开始编译

打开cmd并且切换到skia目录,按照官方的方法执行命令:

生成静态库

bin/gn gen out/Static --args='is_official_build=true'

生成动态库

bin/gn gen out/Shared --args='is_official_build=true is_component_build=true'

但是直接这样做行不通,有几个坑,编译肯定失败。需要额外注意几个参数:

 

1.args的参数说明

  • is_component_build:是否生成动态库
  • is_debug:是否生成调试版本
  • target_cpu="x86":表示win32平台,不指定的话默认是64位
  • is_official_build:是否只编译skia自身
  • win_sdk:如果windows sdk没有安装到默认路径,需要指定此参数
  • win_vc:如果vs没有安装到默认路径,需要指定此参数

 

其中is_official_build参数表示是否只编译skia自身,官方示例写为true,这样在编译过程中会因为缺少三方库而编译失败,所以我直接设置为false。

2.生成项目文件

另外有一个坑,就是官方文档的参数描述(单引号以及斜杠)都是linux环境下的,在windows的cmd下直接报错。可以使用Cygwin工具来执行命令,或者对命令中的路径进行转义。最终的命令如下:

生成静态库

bin\\gn gen out\\Static --args="is_official_build=false is_debug=false win_vc=\\"D:/Software/Program/VS2017/VC\\" win_sdk=\\"C:/Program Files (x86)/Windows Kits/10\\"  target_cpu=\\"x86\\"" --ide="vs2017"

生成动态库

bin\\gn gen out\\Shared --args="is_official_build=false is_debug=false is_component_build=true win_vc=\\"D:/Software/Program/VS2017/VC\\" win_sdk=\\"C:/Program Files (x86)/Windows Kits/10\\"  target_cpu=\\"x86\\"" --ide="vs2017"

这样就可以在skia\\out目录下看到生成的项目文件了,可以使用vs打开来查看代码。不过编译的话还是需要使用谷歌的ninja工具(depot_tools里有)。命令为:

ninja -C out/Static

但是这个生成的项目依然会编译失败,cmd解析Program Files(x86)中的空格会报错。

 

3.删除多余的命令

打开skia\\out\\Static\\toolchain.ninja文件,搜索所有的文本内容然后删掉:

cmd /c C:/Program Files (x86)/Windows Kits/10/bin/SetEnv.cmd /x86 &&

打开vs安装的目录,比如我的

D:\\Software\\Program\\VS2017\\VC\\Tools\\MSVC\\14.16.27023\\bin\\Hostx64\\x64

找到三个dll文件道北到skia\\out\\Static目录下:

  • msobj140.dll
  • mspdb140.dll
  • mspdbcore.dll

接下来就可以执行ninja -C out/Static来编译了,大概需要3小时时间

 

用LLVM编译

官方文档里强烈建议使用LLVM的clang来编译,因为skia针对clang有专门优化,性能要比vs编译的更好。为了需要单独下载LLMV环境:https://releases.llvm.org/download.html。我直接安装到C:/LLVM,不然编译时又会因为Program Files(x86)中的空格而报错。这时需要改生成项目的命令加一个参数:

clang_win = "C:\\LLVM" cc="clang" cxx="clang"

示例如下:

bin\\gn gen out\\StaticLLVM --args="is_official_build=false is_debug=false win_vc=\\"D:/Software/Program/VS2017/VC\\" win_sdk=\\"C:/Program Files (x86)/Windows Kits/10\\"  cc=\\"clang\\" cxx=\\"clang++\\" clang_win=\\"C:/LLVM\\" target_cpu=\\"x86\\"" --ide="vs2017"

LLVM编译速度比VS快了很多,大概只花了50分钟。vs编译的静态skia.lib大小为1.04G;LLVM编译的静态skia.lib大小是280M,动态库skia.dll大小为skia.dll

Skia剪裁

前面说到is_official_build参数用来配置只编译skia自身还是自动编译的三方库依赖。如果加入三方库,那么编译的静态库就会非常大。假如我们只需要skia核心功能或者针对某些功能进行剪裁,可以设置is_official_build为true,然后单独设置禁用或启用某个三方库功能。这是官方文档:

Most users of Skia should set is_official_build=true, and most developers should leave it to its false default.

This mode configures Skia in a way that's suitable to ship: an optimized build with no debug symbols, dynamically linked against its third-party dependencies using the ordinary library search path.

In contrast, the developer-oriented default is an unoptimized build with full debug symbols and all third-party dependencies built from source and embedded into libskia. This is how we do all our manual and automated testing.

Skia offers several features that make use of third-party libraries, like libpng, libwebp, or libjpeg-turbo to decode images, or ICU and sftnly to subset fonts. All these third-party dependencies are optional and can be controlled by a GN argument that looks something like skia_use_foo for appropriate foo.

If skia_use_foo is enabled, enabling skia_use_system_foo will build and link Skia against the headers and libaries found on the system paths. is_official_build=true enables all skia_use_system_foo by default. You can use extra_cflags and extra_ldflags to add include or library paths if needed.

文档中说到:所有这些第三方依赖项都是可选的,并且可以由skia_use_foo来控制,如果设置skia_use_foo为false则会禁止这个foo功能。所有的参数信息参见:skia\\gn\\skia.gni

有一个skia_use_system_foo配置项。当is_official_build=true时,并且开启了某个skia_use_foo,那么skia_use_system_foo也是默认开启的。skia_use_system_foo表示从系统里查找这个foo库来编译而不是是从skia自带的三方库。这个时候有两个方法来编译这个三方库:

  1. 在--args里增加extra_cflags和extra_ldflags参数来指定这个三方库的头文件好库文件路径
  2. 关闭skia_use_system_foo开关,那么依然会从skia自带的三方库里编译

实际上,我们一般直接用skia自带的三方库就可以,我们只是需要禁用某些三方库。比如我要禁用icu、xml、pdf、xps、webp编解码,只保留png、jpg图片编解码功能。

那么我要禁用:

  • skia_use_icu=false
  • skia_use_expat=false
  • skia_use_libwebp_decode=false
  • skia_use_libwebp_encode=false
  • skia_use_xps=false
  • skia_enable_pdf=false

再开启我需要的(默认的各种功能都是开的,所以其实不用单独设置):

  • skia_use_libjpeg_turbo_decode = true
  • skia_use_libjpeg_turbo_encode = true
  • skia_use_libpng_decode = true
  • skia_use_libpng_encode = true
  • skia_use_zlib=true

另外我系统直接使用skia自带的三方库,而不是另外设置路径,那么需要:

  • skia_use_system_libpng=false
  • skia_use_system_libjpeg_turbo=false
  • skia_use_system_zlib=false

所以最终的命令我:

bin\\gn gen out\\StaticLLVMSkiaOnlyWithImage --args="is_official_build=true skia_use_system_libpng=false skia_use_system_libjpeg_turbo=false skia_use_system_zlib=false skia_use_icu=false skia_use_expat=false skia_use_libwebp_decode=false skia_use_libwebp_encode=false skia_use_xps=false skia_enable_pdf=false is_debug=false win_vc=\\"D:/Software/Program/VS2017/VC\\" win_sdk=\\"C:/Program Files (x86)/Windows Kits/10\\"  cc=\\"clang\\" cxx=\\"clang++\\" clang_win=\\"C:/LLVM\\" target_cpu=\\"x86\\"" --ide="vs2017"

最后用LLVM编出来的静态文件只有15.6M,大大减小了体积和编译时

 

手动去掉三方库依赖

skia虽然编译时会依赖很多三方库,包括字体相关freetype库,图片相关png、jpeg、gif等等。但是如果是单单windows平台的话,其实用不到这些依赖。字体功能windows系统有GDI和DriectWrite,图片功能使用WIC组件。所以这些三方库完全可以去掉,单单使用include和src文件夹里的代码来编译就可以。

去掉字体依赖

在src\\ports目录下,是skia为了适配各个平台而专门写的适配代码。其中字体相关的有:

其中SkFontHost_win.cpp是GDI字体渲染的实现,SkFontMgr_indirect.cpp、SkFontMgr_win_dw.cpp、SkScalerContext_win_dw.cpp、SkTypeface_win_dw.cpp是DirectWrite字体渲染的实现。SkFontMgr_win_gdi_factory.cpp是创建GDI实现的工厂,SkFontMgr_win_dw_factory.cpp是创建DirectWrite实现的工厂。skia默认使用DirectWrite功能,因为他效果和性能都更好。但是xp系统下不支持DirectWrite。所以可以修改DirectWrite工厂:

原代码:

sk_sp<SkFontMgr> SkFontMgr::Factory() 
    return SkFontMgr_New_DirectWrite();

修改后:

 

sk_sp<SkFontMgr> SkFontMgr::Factory() 
    auto fontMgr = SkFontMgr_New_DirectWrite();
    if (fontMgr)
        return fontMgr;
    else
        return SkFontMgr_New_GDI();

去掉图片依赖

src\\ports下图片相关的文件:

 

src\\images目录下:

其中SkImageEncoder.cpp文件里是解析图片的入口:

bool SkEncodeImage(SkWStream* dst, const SkPixmap& src,
                   SkEncodedImageFormat format, int quality) 
    #ifdef SK_USE_CG_ENCODER
        (void)quality;
        return SkEncodeImageWithCG(dst, src, format);
    #elif SK_USE_WIC_ENCODER
        return SkEncodeImageWithWIC(dst, src, format, quality);
    #elif SK_ENABLE_NDK_IMAGES
        return SkEncodeImageWithNDK(dst, src, format, quality);
    #else
        switch(format) 
            case SkEncodedImageFormat::kJPEG: 
                SkJpegEncoder::Options opts;
                opts.fQuality = quality;
                return SkJpegEncoder::Encode(dst, src, opts);
            
            case SkEncodedImageFormat::kPNG: 
                SkPngEncoder::Options opts;
                return SkPngEncoder::Encode(dst, src, opts);
            
            case SkEncodedImageFormat::kWEBP: 
                SkWebpEncoder::Options opts;
                if (quality == 100) 
                    opts.fCompression = SkWebpEncoder::Compression::kLossless;
                    // Note: SkEncodeImage treats 0 quality as the lowest quality
                    // (greatest compression) and 100 as the highest quality (least
                    // compression). For kLossy, this matches libwebp's
                    // interpretation, so it is passed directly to libwebp. But
                    // with kLossless, libwebp always creates the highest quality
                    // image. In this case, fQuality is reinterpreted as how much
                    // effort (time) to put into making a smaller file. This API
                    // does not provide a way to specify this value (though it can
                    // be specified by using SkWebpEncoder::Encode) so we have to
                    // pick one arbitrarily. This value matches that chosen by
                    // blink::ImageEncoder::ComputeWebpOptions as well
                    // WebPConfigInit.
                    opts.fQuality = 75;
                 else 
                    opts.fCompression = SkWebpEncoder::Compression::kLossy;
                    opts.fQuality = quality;
                
                return SkWebpEncoder::Encode(dst, src, opts);
            
            default:
                return false;
        
    #endif

我们可以开启SK_USE_WIC_ENCODER宏(skia没有开启),然后把SkJpegEncoder.cpp、SkJPEGWriteUtility.cpp、SkJPEGWriteUtility.h、SkPngEncoder.cpp、SkWebpEncoder.cpp从项目文件中去掉,就可以使用WIC来解析并去掉额外的三方库依赖了

 

针对skia老版本

这次搞得skia版本比较高,必须要c++17的支持,所以我专门搞过老版本的编译。编译的是m49分支(对应chrome49版本)。直接用cmkae生成vs2013工程,来编译。老版本的字体依赖的处理和前面的方法一直。图片依赖的处理,它默认就启用了WIC,只要把png、jpeg、gif等解析器的cpp文件从项目中去掉就可以了。

 

编译cmake

bin\\gn gen out\\config --ide=json --json-ide-script=..\\..\\gn\\gn_to_cmake.py --args="win_vc=\\"D:/Software/Program/VS2017/VC\\" win_sdk=\\"C:/Program Files (x86)/Windows Kits/10\\"" 

 

Redrain QQ:491646717 2020-12-25

以上是关于windows下编译skia的主要内容,如果未能解决你的问题,请参考以下文章

windows下编译skia

windows下编译skia

在Windows下编译多种VS版本的Skia

Windows下编译PythonQt3.2正确姿势

如何在 Windows 上编译skia

windows下编译调试nginx