前言

简介

开始尝试

准备工作

开始

重点文件分析

改造

接下来实现一个简单的图片处理工具,帮助web端更快的处理图片内容。

  1. 会用到image这个rust包。所以先改造它的Cargo.toml
[dependencies]
wasm-bindgen = "0.2.63"
// 在之前的dependencies下面添加image包
image = "0.24.6"
  1. 就以处理图片的其中内置的一个灰度方法来看一下。接下来改造lib.rs文件。
// 新增一个灰度方法,接受一个字节类型的集合,返回一个新的字节类型的集合
#[wasm_bindgen]
pub fn gray(_array: &mut [u8]) -> Vec<u8> {
    let mut img = image::load_from_memory(_array).unwrap(); // 将传入的u8集合转化成一个image对象
    img = img.grayscale(); // 调用第三方包的灰度方法得到新的图片
    let mut bytes: Vec<u8> = Vec::new(); // 定义一个新的u8集合
    img.write_to(&mut Cursor::new(&mut bytes), image::ImageOutputFormat::Png).unwrap(); // 将变换后的image转化成新的u8集合
    bytes // 将u8集合作为返回值抛出
}
  1. 运行命令wasm-pack build --release --target web,将rust文件打包成目标为web项目的可用二进制文件以及加载的js文件。可以看到项目多了一个pkg文件夹

    pkg

    .gitignore
    package.json
    README.md
    test_webassembly_bg.wasm // 二进制文件
    test_webassembly_bg.wasm.d.ts
    test_webassembly.d.ts
    test_webassembly.js // 这个就是加载这个二进制的文件

// test_webassembly.js这个文件主要看几个重点地方,其实不看也行,这个地方wasm-pack已经帮你处理好了,基本只需要学会用就行
// 加载二进制后的处理,返回暴露出的模块的实例
async function load(module, imports) {
    if (typeof Response === 'function' && module instanceof Response) {
      // 在支持instantiateStreaming的情况下,优先使用instantiateStreaming加载对应的二进制文件
        if (typeof WebAssembly.instantiateStreaming === 'function') {
            try {
                return await WebAssembly.instantiateStreaming(module, imports); 
            } catch (e) {
                if (module.headers.get('Content-Type') != 'application/wasm') {
                    console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
                } else {
                    throw e;
                }
            }
        }
        const bytes = await module.arrayBuffer();
        return await WebAssembly.instantiate(bytes, imports);
    } else {
      // 不支持的时候,就使用instantiate文件来加载
        const instance = await WebAssembly.instantiate(module, imports);
        if (instance instanceof WebAssembly.Instance) {
            return { instance, module };
        } else {
            return instance;
        }
    }
}
// 针对已经解析完的实例,做一些初始化操作,抛出模块实例的exports
function finalizeInit(instance, module) {
    wasm = instance.exports;
    init.__wbindgen_wasm_module = module;
    cachedInt32Memory0 = null;
    cachedUint8Memory0 = null;
    return wasm;
}
// 文件暴露的入口
async function init(input) {
    if (typeof input === 'undefined') {
        input = new URL('test_webassembly_bg.wasm', import.meta.url); // 默认的二进制文件地址
    }
    const imports = getImports();
    if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
        input = fetch(input); // 加载对应的二进制文件
    }
    initMemory(imports); // 这个就是之前上面申请空间的,可以注释掉的,不用看
    const { instance, module } = await load(await input, imports); // 调用webassembly的api去加载二进制文件
    return finalizeInit(instance, module); // 得到模块中抛出的exports
}

在html中使用,新建一个index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <input type="file" multiple="false" id="file">
    <div id="box"></div>
    <div>
        <button id="gray-btn">灰度</button>
    </div>
    <div id="target"></div>
    <script type="module">
        import init, { gray } from './pkg/test_webassembly.js';
        await init();
        const file = document.getElementById('file');
        const grayBtn = document.getElementById('gray-btn');
        let current = null;
        let uint8Array = [];
        file.addEventListener('change', (e) => {
            current = e.target.files[0];
            var reader = new FileReader(); 
            reader.readAsDataURL(current);
            reader.onload = function() {
                const img = new Image();
                img.src = this.result;
                document.querySelector('#box').appendChild(img);
            }
        });
        grayBtn.addEventListener('click', () => {
            var reader = new FileReader();
            reader.readAsArrayBuffer(current);
            reader.onload = function () {
                uint8Array = new Uint8Array(this.result);
                let newUnit8Array = gray(uint8Array); // 调用webassembly提供的灰度方法获取到灰度之后的结果
                const img = new Image();
                let blob = new Blob([newUnit8Array], { type: 'image/png' });
                img.src = window.URL.createObjectURL(blob);
                document.querySelector('#target').appendChild(img);
            }
        })
    </script>
</body>
</html>

使用npx http-server --port 3000启动静态服务器,因为webassembly是不支持本地的文件的,下面就是最终效果,先选择一张图片,之后点击灰度就能快速得到一张灰度的图片

WebAssembly初尝试

小结