小程序 arraybuffer_WebAssembly进阶系列三:微信小程序支持webP的WebAssembly方案

1e7ef115d514c2c33575a7f662f98ce9.png

导语:相信不少人听说过 WebAssembly,它是由 Google、Microsoft、Mozilla、Apple 等几家大公司合作发起的一个关于面向Web的通用二进制和文本格式的项目。现在就让我们一步步揭开WebAssembly的神秘面纱,并亲自动手将WebAssembly应用在实际业务中。

1. 引言

通过《WebAssembly进阶系列一:WebAssembly是什么》和《WebAssembly进阶系列二:WebAssembly处于编译阶段哪个环节》这两篇文章深入了解WebAssembly的由来、优势及适用场景后,接下来便是实践检验真理的时候,让我们一起动手将WebAssembly应用在微信小程序场景中,让微信小程序环境支持解码webP格式(不了解或没听过webP的各位同仁,请先移步到“探究WebP的一些事儿”)。

2. WebAssembly工作流程

动手之前,让我们先来了解下如何加载和运行WebAssembly的代码:C / C++ / Rust / Java等高级语言开发的代码或功能库 -> Emscripten编译 -> wasm文件 -> 结合WebAssembly JS API -> 浏览器环境中运行,如下图所示:

1e73fb8ff68ccd2b3f2e2a3288950333.png

简单来说,编译前端LLVM / Emscripten流程可以获得wasm文件和胶水js。然后,通过胶水js来加载wasm并转为arrayBuffer格式。紧接着进行编译和实例化后,即可用JavaScript与WebAssembly通信。

a2be1c51530c057582b3d0669e89efc8.png

详细过程以及每个过程调用的API如下图所示:

95a393b2296073e83cbd5b639ffe61d4.png

3. 浏览器环境支持webP

了解完WebAssembly的工作流程后,是不是还不清楚要从哪开始搞起?你可以去github官网上看一下libwebp开源项目,Google已经完全支持把libwebp源码编译为wasm和asm.js两个版本了。针对不支持WebAssembly的系统或不兼容WebAssembly的浏览器,可以在损失一点性能的情况下降级为使用asm.js。具体编译步骤如下图所示:

a65892d3159ab6206f7304b1dd012467.png

待编译完之后,我们便可获得wasm文件和胶水JS。然后,我们可以用“python -m SimpleHTTPServer 8080”启动一个本地服务,在浏览器地址栏输入 http://localhost:8080 后就能看到webP解码后的图片。

da6f95efd39ae3df1151afb1b7f03aff.png

最后,让我们来总结下整个流程。

(1)用LLVM / Emscripten / CMake工具对libwebp解码库进行编译,获得wasm文件和胶水JS。

(2)胶水JS申请内存,对wasm文件进行编译、加载和实例化后,导出Module对象。

(3)利用Module对象上的WebpToSDL方法对webP进行解码,并转成Canvas在浏览器渲染显示出来,呈现最终的图片。

d5bcd6a5ca1dd3daf593f03cd9489641.png

4. 微信小程序环境支持webP

微信小程序在Android / iOS上用于执行脚本以及渲染组件的环境都不尽相同。

在Android上,微信小程序逻辑层的JavaScript代码运行在V8中,视图层是由自研XWeb引擎基于Mobile Chrome 67内核来渲染,天然支持webP格式;在iOS上,微信小程序逻辑层的JavaScript代码运行在JavaScriptCore中,视图层是由WKWebView来渲染,宿主Safari浏览器内核不支持webP格式。

通过第3节内容,我们知道浏览器环境已经能够支持webP了,那直接把之前编译好的wasm文件和胶水JS扔进微信小程序的运行环境,然后跑起来不就搞定了?Too young too simple!

浏览器环境支持webP的思路是libwebp解码webP -> jpg / png / gif的canvas图片渲染显示,这已经改变了原来image组件的结构。

e88fd54e655e59d518cb121b7ba75b60.png

而微信小程序提供给开发者的组件不允许去改变它原来的结构,因此换种思路是libwebp解码webP -> jpg / png / gif的rgb data -> jpg / png / gif base64 -> 回传给JS并赋值给image src进行渲染显示。

0f17446844549f6b53671d5a6df98a3d.png

下面我罗列下从libwebp编译wasm文件和胶水JS开始,直到在微信小程序环境跑通为止,整个过程中遇到的一些坑点和优化点:

(1)编译CMakeLists.txt时需加上“-O3”选项,大大提升编译速度。

(2)编译CMakeLists.txt时需加上“-s USE_PTHREADS=0”选项,因为iOS Safari浏览器不兼容ShareArrayBuffer共享缓冲区。

(3)编译CMakeLists.txt时需加上“-s ALLOW_MEMORY_GROWTH=1”选项,目的是为了解决解码超大分辨率的webP图片时出现的OOM问题。

(4)由于微信小程序环境的兼容性问题,去除胶水JS代码中libwebp编译时加上的SDL相关代码,能节省100KB左右的空间。

(5)去除胶水JS中ENVIRONMENT_IS_NODE / ENVIRONMENT_IS_SHELL相关的代码,因为微信小程序环境并未使用到。

(6)由于iOS Safari浏览器的兼容性问题,将胶水JS中流式编译和实例化的方法去掉,替换成非流式编译和实例化的方法。

(7)由于WebAssembly还没有和<script type='module'>或ES6的import语句集成,因此将wasm文件先转成base64字符串。等胶水JS运行加载逻辑时,再将base64转成ArrayBuffer并编译和实例化后导出Module对象,节省从服务器下载wasm文件的时间。

(8)编译CMakeLists.txt时需加上“-s USE_LIBPNG=1”选项编译libpng.a库,然后将webP解码获得的rgb数据,通过png解码库转成png内存数据,紧接着转成base64回传给JS,最后赋值给image src进行渲染显示。难点是rgb转成png内存数据这一步出了点问题,但是wasm无法调试代码,只能通过搭建libpng的VS工程进行断点调试,最终定位到是rgb转png data时传入的data_size为0导致。

(9)胶水JS里的new WebAssembly.Memory代码在微信小程序环境运行时,会报“refused to create a webassembly object without 'unsafe-eval'”的错误,必须在page-frame.html里的CSP设置里加上unsafe-eval才能解决。

踩了这么多坑之后,终于能在微信小程序环境里支持webP了。实测WebAssembly在解码不同格式不同分辨率的webP时,性能都完胜JavaScript。

f7ef6a833434a1310bd2e5cdb6171989.png

5. 写在最后

虽然WebAssembly的解码性能比JavaScript快不少,但遇到超大分辨率(如1920 x 1080等)的webP时,却远远落后于客户端的解码性能。综合对比各种方案的性能和兼容性之后,我们还是采用了基于iOS客户端自定义协议webphttps的方案,大致步骤如下:

(1)首先,微信小程序基础库判断开发者在image组件使用的是webP格式时,则在image src里加上webp头部如webphttps://example.png。

(2)然后,客户端通过NSURLProtocol协议挟持webphttps的请求,并下载相应的webP数据进行解码。

(3)最后,再把解码后的image数据回吐给浏览器进行渲染显示。

到最后,我们完成了微信小程序环境支持webP的方案落地,敬请期待。

参考资料

  1. webassembly介绍
  2. 加载和运行WebAssembly代码
  3. WebAssembly在企业邮箱中的一次实践
  4. Download and install — Emscripten 1.38.38 documentation
  5. 探究WebP的一些事儿
  6. libwebp开源项目

e57dab9f8c6fa1bd5c4eade2c21a2068.png

个人公众号:前端开发升值记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值