- 图形学
- 图像处理
【免费下载链接】skia
Skia is a complete 2D graphic library for drawing Text, Geometries, and Images.
本篇指南基于仓库中 modules/canvaskit/README.md 展开,系统讲解 CanvasKit(Skia 的 WebAssembly/WebGL 移植)的完整工程流程:如何使用 GN + emscripten 编译出.js/.wasm产物、如何编译并运行本地示例、如何组合编译开关裁剪体积、如何用 Karma 跑单元测试与覆盖率、如何用 Puppeteer 做性能测量、如何用wasm2wat检查 SIMD 指令,以及 CI(Gerrit/Docker)侧的测试与基础设施运维套路。读完本文,你可以独立完成 CanvasKit 的任意构建变体,并复现其在浏览器端的全部测试与验证手段。
前置条件与工具链
运行 CanvasKit 测试需要Node v14 及以上,并使用 npm(Node Package Manager)安装测试依赖;较新的 Node 发行版已自带 npm。README 明确说明:CanvasKit 没有其它外部源码依赖,其余能力均由 Skia 仓库内部提供。
编译侧则依赖 Skia 的 GN 构建体系与 emscripten 工具链:
- 需要按照 Skia 官方的下载说明获取 Skia 源码树及其依赖(DEPS 体系)。
- 必须下载并激活 emscripten。仓库提供了 bin/activate-emsdk 脚本(
//tools/git-sync-deps也会调用它),激活后相关文件位于 third_party/externals/emsdk,GN 构建脚本默认使用该路径。 - 若想使用自己版本的 emscripten,可设置 GN 参数
skia_emsdk_dir。从源码结构看,该参数在 gn/toolchain/wasm.gni 中声明,默认值正是仓库内的 emsdk 目录;同文件还定义了skia_wasm_lib模板,它会把 wasm 库目标拆成xxx_js可执行目标与聚合 group,这是 CanvasKit 目标canvaskit的构建基础。 - modules/canvaskit/compile.sh 自动化了默认的 GN 配置,用户可以自行设置参数;其余可用参数见 modules/canvaskit/BUILD.gn 与 modules/canvaskit/canvaskit.gni。
macOS 特别提示(来自 README 的踩坑记录):
- 确保安装了 Python3,否则下载 emscripten 工具链时可能因 SSL 证书问题失败。
- Python3 使用错误证书的情况,可参考 emscripten 社区 issue 中给出的解决方案。
使用 GN 编译:compile.sh 的开关体系
modules/canvaskit/compile.sh 是默认构建入口。它先pushd到仓库根目录并执行./bin/fetch-gn,然后根据命令行关键字解析出一组内部布尔变量,最终拼出一长串./bin/gn gen ${BUILD_DIR} --args=...并调用 ninja 编译canvaskit.js目标。从脚本源码可以确认其完整的开关体系如下:
构建类型与后端选择
| 命令行关键字 | 内部变量 / GN 参数 | 输出目录 |
|---|---|---|
| (默认) | ENABLE_WEBGL=true,skia_enable_ganesh=true | out/canvaskit_wasm |
cpu_only/cpu | 关闭 Ganesh(GPU),纯 CPU 后端,-sUSE_WEBGL2=0 | out/canvaskit_wasm |
use_webgpu/webgpu | skia_use_dawn=true、skia_enable_graphite=true、关闭 Ganesh,启用 WebGPU 绑定 | out/canvaskit_wasm |
debug_build | is_debug=true、is_official_build=false | out/canvaskit_wasm_debug |
profiling | skia_canvaskit_profile_build=true(BUILD.gn 断言其必须搭配is_debug=false) | out/canvaskit_wasm_profile |
force_tracing | 未出现该关键字时skia_canvaskit_force_tracing=true;即 release/profiling 默认关闭 tracing,debug 默认开启 | — |
功能裁剪开关
以下开关均通过--args中的skia_canvaskit_*参数生效,对应的 GN 参数默认值可查 modules/canvaskit/canvaskit.gni(declare_args段):
| 命令行关键字 | 作用 | 对应 GN 参数 / 备注 |
|---|---|---|
no_skp_serialization | 禁用 SKP 序列化,脚本注释称约省 20KB 压缩体积 | skia_canvaskit_enable_skp_serialization=false |
no_effects_deserialization | 禁用效果反序列化,约省 60KB 压缩体积 | skia_canvaskit_enable_effects_deserialization=false |
no_skottie | 省略 Lottie 动画支持(Skottie) | skia_enable_skottie=false |
viewer | 内嵌 SKP/SVG 查看器,同时启用 expat | skia_canvaskit_include_viewer=true、skia_use_expat=true |
no_pathops | 省略 PathOps,约省 2KB 压缩体积 | skia_canvaskit_enable_pathops=false |
no_matrix | 省略矩阵辅助代码;同时会连带关闭 HTML Canvas 绑定(绑定依赖矩阵助手) | skia_canvaskit_enable_matrix_helper=false(隐含no_canvas) |
no_canvas | 省略 HTML Canvas API 绑定 | skia_canvaskit_enable_canvas_bindings=false |
no_font | 省略内置字体、字体管理器及全部字体相关代码 | skia_canvaskit_enable_font=false且skia_canvaskit_enable_embedded_font=false |
no_embedded_font | 仅省略内置字体(保留字体管理器) | skia_canvaskit_enable_embedded_font=false |
no_woff2 | 关闭 woff2 支持 | skia_use_freetype_woff2=false |
no_alias_font | 关闭字体别名查找 | skia_canvaskit_enable_alias_font=false |
legacy_draw_vertices | 恢复旧版drawVertices无 shader 时的 blend 行为(对应 Flutter issue 98531) | skia_canvaskit_legacy_draw_vertices_blend_mode=true |
no_paragraph(隐含于primitive_shaper/no_font) | 省略 SkParagraph,前提是有字体且非 primitive shaper | skia_canvaskit_enable_paragraph=false |
primitive_shaper | 用原始终端 shaper 替代 HarfBuzz/ICU 组合,同时关闭 paragraph | skia_use_icu=false skia_use_harfbuzz=false |
client_unicode | 使用客户端提供的 skunicode 数据与 harfbuzz | skia_use_client_icu=true |
no_codecs | 关闭全部图片解码,并顺带关闭 png/jpeg/webp 编码 | skia_use_libjpeg_turbo_decode=false等全部置 false |
no_encode_png/no_encode_jpeg/no_encode_webp | 分别关闭对应格式的编码 | skia_use_lib*_encode=false及skia_use_no_*_encode=true |
enable_debugger | 构建带调试器绑定的版本 | skia_canvaskit_enable_debugger=true |
默认文本栈为skia_use_icu=true skia_use_harfbuzz=true(均非 system 版本);默认开启skia_canvaskit_enable_rt_shader=true(运行时着色器)。
这些参数如何落到最终产物,可以直接在 modules/canvaskit/BUILD.gn 的skia_wasm_lib("canvaskit")目标中看到对应关系:
- release 模式:
-Oz --closure=1 --pre-js release.js,closure 编译器使用 modules/canvaskit/externs.js 作为 externs(这正是 README 提醒“release 测试能暴露 closure 编译与漏写 externs 问题”的底层原因)。 - debug 模式:
-O0 -sASSERTIONS=1 -sGL_ASSERTIONS=1 -g3 --pre-js debug.js并启用 DEMANGLE_SUPPORT。 - WebGL 路径:
-lGL、-sUSE_WEBGL2=1、-sMAX_WEBGL_VERSION=2,并预注入 modules/canvaskit/cpu.js 与 modules/canvaskit/webgl.js。 - WebGPU 路径:
-sUSE_WEBGPU=1、-sASYNCIFY、导出WebGPU,JsValStore运行时方法,且因 closure 与 ASYNCIFY 不兼容而强制--closure=0。 - 公共链接参数:
--bind(embind 生成 JS 绑定)、-sMODULARIZE、-sEXPORT_NAME=CanvasKitInit(模块初始化入口)、-sINITIAL_MEMORY=128MB、-sALLOW_MEMORY_GROWTH、-sWASM、-sSTRICT=1等。 - 各功能开关同时控制
--pre-js的注入文件,如font.js、pathops.js、skp.js、rt_shader.js、paragraph.js,以及开启 Canvas 绑定时的 modules/canvaskit/htmlcanvas/ 全套 HTMLCanvas API 胶水代码(canvas2dcontext.js、htmlcanvas.js、path2d.js等)。 - 条件编译宏也在此映射:
CK_EMBED_FONT、CK_INCLUDE_PARAGRAPH、CK_SERIALIZE_SKP、CK_INCLUDE_PATHOPS、CK_INCLUDE_RUNTIME_EFFECT、CK_NO_FONTS、CK_NO_ALIAS_FONT等;内置字体 modules/canvaskit/fonts/NotoMono-Regular.ttf 会通过embed_resources.py生成为 C++ 数组参与编译。
此外,gn/toolchain/wasm.gni 还会向所有 wasm 目标统一注入SKNX_NO_SIMD、SK_FORCE_8_BYTE_ALIGNMENT等宏定义,并在关闭效果反序列化或 SKP 序列化时追加SK_DISABLE_EFFECT_DESERIALIZATION。
编译并运行本地示例
README 给出的标准操作流程是:
# 安装全部 npm 依赖;仅在首次设置或依赖变更时需要(很少发生) npm ci make release # make debug 更快且报错信息更友好 make local-example其中make local-example的定义见 modules/canvaskit/Makefile:它打印提示并启动python3 ../../tools/serve_wasm.py(即 tools/serve_wasm.py),随后在浏览器打开http://localhost:8000/npm_build/example.html即可。你可以修改 modules/canvaskit/npm_build/example.html 来实验 CanvasKit API 并刷新页面;针对一些更实验性的 API,还有 modules/canvaskit/npm_build/extra.html 可用。npm_build目录下另有多用途示例页面:multicanvas.html(多画布)、paragraphs.html、shaping.html 以及 Node 端示例 node.example.js(对应make node-example,以--expose-wasm模式运行)。
Makefile 中的构建目标全览
modules/canvaskit/Makefile 暴露的目标可分为几类:
- 常规构建:
release、release_cpu、release_webgpu、release_viewer、debug、debug_cpu、debug_webgpu、debug_viewer、profile。每个目标都是“调用compile.sh <对应参数>+ 把out/canvaskit_wasm*/canvaskit.{js,wasm}拷入本地build/目录”,注释明确说明会尽可能增量构建。 - npm 发布:
make npm会先打 full 版本(./compile.sh release),再构建一个为通用场景裁剪过体积的版本(关闭 skottie、sksl trace、alias font、效果反序列化、jpeg/webp 编码、内嵌字体并启用 legacy draw vertices),最后构建 profiling 版本,三类产物分别放入npm_build/bin/full、npm_build/bin、npm_build/bin/profiling。 - GM 测试构建:
gm_tests与gm_tests_debug调用compile_gm.sh(见下文)。 - 服务与测试:
local-example、single-gm、test-continuous、test-continuous-headless、node-example。 - 基础设施:
docker-compile(使用gcr.io/skia-public/canvaskit-emsdk:2.0.0_v1镜像挂载仓库执行infra/canvaskit/build_canvaskit.sh)、typecheck(进入npm_build运行npm run dtslint做 TS 类型检查)。 - Bazel 构建:
bazel_canvaskit_debug/bazel_canvaskit_release(bazelisk build :canvaskit --config=ck_full_webgl2_debug|release)与bazel_test_canvaskit,产物来自bazel-bin/modules/canvaskit/canvaskit/。 - 调试器/着色器专用:
with_debugger、with_debugger_release、for_shaders,除构建外还会把产物同步到infra/debugger-app/wasm_libs/local_build/或infra/shaders/wasm_libs/local_build/。
裁剪版构建
构建一个不带文本支持、也不含任何 "extras" 的精简 CanvasKit,例如:
./compile.sh no_skottie no_fontREADME 指出,这样的精简版体积约为默认 release 构建的一半。结合前文开关表,还可以进一步叠加no_skp_serialization、no_effects_deserialization、no_codecs等以继续压缩体积——make npm发布目标就是这一思路的工程化落地。
构建失败排查
README 给出两条经验:
- 若 CanvasKit 构建失败且编译错误看起来不像 Skia 代码问题,可能需要全新安装 npm 模块:找到报错信息中提到的
.d.ts文件,删除后重新执行npm ci。 - 若模块版本正确且使用了受支持的最新 TypeScript 仍失败,则可能需要更新 modules/canvaskit/package.json 中列出的模块版本。
单元测试、覆盖率与持续测试
在 debug GPU 构建上运行单元测试并计算覆盖率:
make debug make test-continuous从 Makefile 看,test-continuous实际执行npx karma start ./karma.conf.js --no-single-run --watch-poll(headless 版本见test-continuous-headless)。行为特征(README 原文归纳):
- 读取 modules/canvaskit/karma.conf.js,打开 Chrome 浏览器开始运行
tests/目录下的全部测试; - 检测到该目录下的测试文件变化时自动重跑,并且会自动重新构建并重新加载 CanvasKit;
- 关闭 Chrome 窗口只会让它重新打开;要停止持续监听,需要杀掉 karma 进程。
测试运行的是你最后一次构建的 CanvasKit 版本。README 强调必须同时在release、debug_cpu、release_cpu上测试——release 构建会暴露 closure 编译问题与通常被忘记的 externs(与 BUILD.gn 中 release 分支强制--closure=1并注入externs.js的实现相呼应)。
覆盖率
本地运行test-continuous时覆盖率会自动计算,且只有在 debug 构建下结果才有意义。打开coverage/<浏览器版本>/index.html可查看汇总与逐行详情。
测试的组织方式与 gm 快照
tests/中的测试按主题分组为文件,从目录可以看到具体分组:canvas_test.js、path_test.js、font_test.js、matrix_test.js、skottie_test.js、paragraph_test.js、rtshader_test.js、canvas2d_test.js、core_test.js 等,测试资产(字体、图片、SKP、Lottie JSON)位于 modules/canvaskit/tests/assets/。
每个文件内用 Jasmine 的describe块进一步组织,describe内是测试具体行为的it()函数。两者都可临时重命名为fdescribe/fit,让 Jasmine 只运行被聚焦的用例。
除此之外还定义了gm方法:用于定义把某些内容画到 canvas、截图并上报 gold.skia.org 的测试,可与 head 版本的快照进行对比,即 CanvasKit 也接入了 Skia 的图像回归比对基础设施。
性能测量
性能数据由 Puppeteer 驱动 Chrome 以一致的方式采集,实现位于 tools/perf-canvaskit-puppeteer/。该目录自带 README,其中描述的三类基准均值得了解:
- 基础性能测试:基准代码片段追加到
canvas_perf.js,harness 为canvas_perf.html+benchmark.js;运行make perf_js后会对 test() 代码段跑多帧采集数据,并上报 90/95/99 分位帧时间、平均帧时间、中位数与标准差。三种度量分别是without_flush_ms(仅 test() 调用)、with_flush_ms(test() + flush())、total_frame_ms(帧到帧时间,包含 GPU 在 CanvasKit flush 之后仍需完成的工作)。 - Skottie 帧性能:循环渲染 600 帧 Lottie 动画并采集指标(前 5 帧时间、平均帧时间、90/95/99 分位)。
- SKP 性能:反复播放 SKP 并测量各类指标。
CI 中这些结果统一上报到 perf.skia.org。
从 Gerrit 发起测试
在 Gerrit 提交 CL 时,点击 "choose tryjobs" 并输入 CanvasKit 过滤,全部选中(撰写时为 4 个任务,覆盖 perf/test × gpu/cpu 的组合)。性能结果上报 perf.skia.org,正确性结果上报 gold.skia.org;以这种方式运行测试时不测量覆盖率。
检查输出 WASM 中的 SIMD 指令
WebAssembly Binary Toolkit 提供的wasm2wat工具可以把.wasm文件转成人类可读的文本形式。README 对版本有硬性要求:
wasm2wat --version的输出应为1.0.13 (1.0.17)。
该版本经过验证,可与 modules/canvaskit/wasm_tools/SIMD/ 中的工具配合工作。这套工具会程序化地检查 CanvasKit 构建产出的.wasm,以检测其中是否存在 wasm SIMD 操作。该目录包含 build_simd_test.sh、simd_test.sh、能力探测源码 simd_float_capabilities.cpp 与 simd_int_capabilities.cpp。值得注意的是,gn/toolchain/wasm.gni 默认给 wasm 目标注入SKNX_NO_SIMD,也就是说 CanvasKit 主构建路径默认禁用 Skia 自身的 NEON 式 SIMD 优化路径,上述检查正是为了在引入 SIMD 时确认工具链行为是否符合预期。
基础设施手册:Docker、Emscripten 升级与 GM/单元测试验证
CanvasKit / PathKit 的 Docker 化
在 Skia 的 bots 上处理 CanvasKit(或 PathKit)时使用 Docker。构建/测试镜像的构建与编辑说明见 infra/wasm-common/docker/README.md。该目录实际包含emsdk-base、karma-chrome-tests、gold-karma-chrome-tests、perf-karma-chrome-tests等镜像定义及 Makefile。本地也可以直接用make docker-compile复现该流程。
更新构建/测试所用的 Emscripten 版本
前提:你已在本地把 emscripten 升级到目标版本,并验证/修复了由此产生的构建问题。然后:
- 编辑 bin/activate-emsdk,安装并激活期望的 Emscripten 版本;
- 上传包含全部变更的 CL,运行全部 Test.+CanvasKit、Perf.+Puppeteer、Test.+PathKit、Perf.+PathKit 任务,确认新构建通过所有测试且不搞崩性能 harness;
- 发出 CL 评审,可以把评审人引到上述步骤说明。
在 wasm+WebGL 上运行 Skia 的 GM 与单元测试
通用技巧(README 原文归纳):
- 利用 tools/run-wasm-gm-tests/run-wasm-gm-tests.html 中的 skip list 与起始索引,聚焦定位有问题的测试;
- 看到
Uncaught (in promise) RuntimeError: function signature mismatch通常意味着某处解引用了 null,可加SkASSERT验证。
单个 GM/单元测试的快速调试:为了缩短周期,建议只编译特定 GM 而非全部。做法是修改 modules/canvaskit/compile_gm.sh(但不要把该修改提交入库):脚本中把GMS_TO_BUILD/TESTS_TO_BUILD设为最小文件集,配合其中的if false; then段(注释掉false即可启用)。随后在本目录执行make gm_tests或make gm_tests_debug,会在未入库的build子目录生成.js与.wasm。再执行make single-gm并访问http://localhost:8000/wasm_tools/gms.html——该服务由 tools/serve_wasm.py 提供;modules/canvaskit/wasm_tools/gms.html 会加载新构建的wasm_gm_tests二进制并运行编译进去的那一个 GM/单元测试,你可以按需修改该 HTML 来运行关心的测试。
从 compile_gm.sh 源码还能看到更完整的上下文:它要求设置EMSDK环境变量并 sourceemsdk_env.sh;GM 侧默认编译gm/*.cpp、测试侧默认编译tests/*.cpp,并用GLOBIGNORE显式排除了若干无法在 wasm 下编译或链接的 GM(如compressed_textures、animated_gif、fiddle、fontations、video_decoder)与单元测试(如CodecTest、ImageTest、FCITest等),以及一个会崩溃的GrThreadSafeCacheTest。最终链接参数与主构建同源风格:--bind、-sMODULARIZE=1、-sEXPORT_NAME="InitWasmGMTests"、-sINITIAL_MEMORY=256MB、-sFILESYSTEM=1等,且注释特意说明 Emscripten 希望.a库排在链接顺序最后,否则可能误删符号。
全量 GM/单元测试:以当前 GN 构建,全部编译与重编译耗时相当可观(README 备注“即将到来的 Bazel 构建应能缓解这一点”——对应 Makefile 中的bazel_*目标)。流程与单测一致:先make gm_tests/make gm_tests_debug产出build/下的.js+.wasm,然后进入 tools/run-wasm-gm-tests 目录执行make run_local,所有 GM 产出的 PNG 会写入/tmp/wasm-gmtests,并同时运行全部单元测试。该目录的 run-wasm-gm-tests.js 与 HTML 页面即上文提到的 skip list / 起始索引功能所在。
小结
CanvasKit 的构建体系可以概括为一条主线:compile.sh(功能开关 → GN 参数)→BUILD.gn的skia_wasm_lib目标(embind + pre-js 胶水 + closure/优化配置)→canvaskit.{js,wasm}产物;测试与验证则由四层组成——Karma 持续单元测试(含覆盖率)、gold 图像比对(gm测试)、Puppeteer 性能 harness(without_flush_ms/with_flush_ms/total_frame_ms三类指标)、wasm2watSIMD 检查,最终由 Docker 化的 CI 镜像与 Gerrit tryjobs 闭环。所有脚本与配置都可直接在仓库内查阅:入口 modules/canvaskit/README.md、构建 modules/canvaskit/compile.sh 与 modules/canvaskit/BUILD.gn、参数默认值 modules/canvaskit/canvaskit.gni、目标汇总 modules/canvaskit/Makefile、GM 测试 modules/canvaskit/compile_gm.sh 与 tools/run-wasm-gm-tests、性能 tools/perf-canvaskit-puppeteer、基础设施 infra/wasm-common/docker/README.md。
- 图形学
- 图像处理
【免费下载链接】skia
Skia is a complete 2D graphic library for drawing Text, Geometries, and Images.
相关推荐
CanvasKit 构建与测试完全指南:从 Emscripten 编译到 WASM 调试(Skia 官方实践)
CanvasKit 构建与测试完全指南:从 Emscripten 编译到 WASM 调试(Skia 官方实践) 本指南以 Skia 仓库中 modules/ca
图形学PDBRipper GUI使用教程:图形界面下的PDB分析完全指南
PDBRipper GUI使用教程:图形界面下的PDB分析完全指南 PDBRipper是一款专业的 PDB文件分析工具 ,专为开发者和逆向工程师设计,能够从PD
逆向工程开发工具Skia wasm-common Docker:用四张 Docker 镜像构建与测试 PathKit/CanvasKit WASM 的完整指南
Skia wasm common Docker:用四张 Docker 镜像构建与测试 PathKit/CanvasKit WASM 的完整指南 本文基于 Ski
图形学图像处理
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考