静悄悄掀开 WebAssembly 的面纱 (四)- iOS 图片优化之 webp

一、WebAssembly 是什么?能做什么?

WebAssembly 是通过 *.wasm 文件进行存储的,这是编译好的可移植、精悍、载入迅捷的二进制格式文件,它的体积非常的小。更多查看 webassembly 官网

.wasm 文件 与 .wat 文件的区别

  • .wat 文件: 可读的S表达式文本文件
  • .wasm 文件: 是 WebAssembly 的二进制格式文件

二、WebAssembly 处于编译阶段哪个环节

WebAssembly 能做到像Java字节码一样,一次编译到处运行,具有跨平台特性。以此同时,作为中间码的WebAssembly直接省略编译前端的步骤,而JavaScript需要实时编译,相比之下性能优势显著

WebAssembly?作为中间码

三、WebAssembly 怎么跟 js 互通

在浏览器中,提供了一个全局的 window.WebAssembly 对象,可以用于实例化 WASM 模块。

window.WebAssembly

四、利用 WebAssembly 解决 WKwebview 宿主 iOS Safira 不支持解析 webp 的问题

先看 ios13.2.3 safari 解码 webp 图片与不使用 webp 编码加载时间对比
demo 中 webp_wasm.wasm 文件于 js 胶水 文件 webp_wasm.js 可通过 github 谷歌 libwebp 开源项目 readme的指引编译

  • 经 webp 编码的淘宝图片
  • 未经 webp 编码的淘宝图片

ios13.2.3 safari 解码 `webp` 图片与不使用 webp 编码加载时间对比

二话不说直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>WASM demo of WebP decoding</title>
  <script type="text/javascript">
    var Module = {
      noInitialRun : true
    };
  </script>
  <script type="text/javascript">

function init() {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', 'webp_wasm.wasm', true);
  xhr.responseType = 'arraybuffer';
  xhr.onload = function() {
    Module.wasmBinary = xhr.response;
    var script = document.createElement('script');
    script.src = "webp_wasm.js";
    document.body.appendChild(script);
  };
  xhr.send(null);
}

function decode(webp_data, canvas_id) {
  var result;
  if (Module["asm"] != undefined) {
    // wrapper for the function decoding a WebP into a canvas object
    WebpToCanvas = Module.cwrap('WebpToSDL', 'number', ['array', 'number']);
    // get the canvas to decode into
    // var canvas = document.getElementById(canvas_id);
    canvas = document.createElement('canvas');
    if (canvas == null) return;
    // clear previous picture (if any)
    Module.canvas = canvas;
    ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    // decode and measure timing
    start = new Date();
    var ret = WebpToCanvas(webp_data, webp_data.length); // webp 图像像素写入canvas
    console.log(ret);
    base64 = canvas.toDataURL("image/png");
    document.getElementById('decode_base64').src = base64;

    end = new Date();
    var decode_time = end - start;
    result = 'decoding time: ' + decode_time +' ms.';
  } else {
    result = "WASM module not finished loading! Please retry";
  }
  // display timing result
  speed_result = document.getElementById('timing');
  if (speed_result != null) {
    speed_result.innerHTML = '<p>'+ result + '</p>';
  }
}

function loadfile(filename, canvas_id) {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', filename);
  xhr.responseType = 'arraybuffer';
  xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status == 200) {
      var webp_data = new Uint8Array(xhr.response);
      decode(webp_data, canvas_id);
    }
  };
  xhr.send();
}


function loadImage(filename,img_id,time_id){
  var img = new Image();
  var start = new Date();
  var speed_result2 = document.getElementById(time_id);
  img.onload = function(){
 

    // display timing result
  if (speed_result2 != null) {
    document.getElementById('output_webp_img').appendChild(img);
    var end  = new Date();
    var decode_time = end - start;
    speed_result2.innerHTML = '<p>'+time_id+' decoding time: ' + decode_time +' ms.</p>';
   
  }  
  }
  img.src = filename;
 

}

  </script>
</head>

<body onload='init()' bgcolor='#f0f8ff'>
  <p>
    <strong>WebP demo using <a href="http://webassembly.org/">Web-Assembly</a></strong> -
  </p>
  <p>
    WASM version of the WebP decoder, using <a
    href="https://github.com/webmproject/libwebp">libwebp</a> compiled with
    <a href="https://github.com/kripken/emscripten/wiki">Emscripten</a>.
  </p>
  <p>
    <br/>
    Requires a <a href="https://developer.mozilla.org/en-US/docs/WebAssembly#Browser_compatibility">native-wasm
    enabled browser</a> (Chrome M58, WebKit)
    <br/>
  </p>

  <p id="image_buttons">
 
    <input type="button" value="lossy example" name="//img.alicdn.com/bao/uploaded/i4/2549599360/O1CN01YZ4MJz2J0vkikfMTT_!!0-item_pic.jpg_.webp"
           onclick="loadfile(this.name, 'output_canvas')">
    <input type="button" value="lossy+alpha example" name="./test2.webp"
           onclick="loadfile(this.name, 'output_canvas')">
    <input type="button" value="lossless example" name="./test3.webp"
           onclick="loadfile(this.name, 'output_canvas')">
 
           <input type="button" value="img load" onclick="loadImage(this.name,'output_webp_img','b_load_img')"  name="//img.alicdn.com/bao/uploaded/i4/2549599360/O1CN01YZ4MJz2J0vkikfMTT_!!0-item_pic.jpg">
           <input type="button" onclick="loadImage(this.name,'output_webp_img','b_load_webp_img')" value="img webp load" name="//img.alicdn.com/bao/uploaded/i4/2549599360/O1CN01YZ4MJz2J0vkikfMTT_!!0-item_pic.jpg_b.jpg_.webp">
 
  </p>
  <p id="timing">Timing: N/A</p>
  <p id="load_decode_time"></p>
  <p id="b_load_webp_img"></p>
  <p id="b_load_img"></p>
  <div id="output_webp_img"></div>
  <canvas id="output_canvas">Your browser does not support canvas</canvas>
  <img id="decode_base64">

</body>
</html>

参考文章

  • 悄悄掀起 WebAssembly 的神秘面纱
  • 20分钟上手 webAssembly
  • WebAssembly进阶系列三:微信小程序支持webP的WebAssembly方案
  • 谷歌 libwebp 开源项目
  • webassembly 的那些事