编译为WebAssembly时无法使image :: load_from_memory()工作

时间:2018-05-18 16:25:18

标签: rust webassembly

我正在尝试使用image crate将图像从JavaScript加载到带有Rust的WebAssembly。

我有以下Rust代码:

extern crate image;
extern crate libc;

use libc::c_void;
use std::mem;

#[no_mangle]
pub extern "C" fn alloc(size: usize) -> *mut c_void {
    let mut buf = Vec::with_capacity(size);
    let ptr = buf.as_mut_ptr();
    mem::forget(buf);

    return ptr as *mut c_void;
}

#[no_mangle]
pub extern "C" fn read_img(buff_ptr: *mut u8, buff_len: usize) -> *mut i32 {
    let mut img: Vec<u8> = unsafe { Vec::from_raw_parts(buff_ptr, buff_len, buff_len) };
    let ok = Box::new([333]);
    let err = Box::new([331]);

    return match image::load_from_memory(&img) {
        Ok(img) => Box::into_raw(ok) as *mut i32,
        Err(_) => Box::into_raw(err) as *mut i32,
    };
}

fn main() {}

我使用以下工具编译:

cargo +nightly build --target wasm32-unknown-unknown --release

read_img()函数中,我通过两个向量天真地处理错误:[333]表示确定,[331]表示任何错误。我在JavaScript端读取了这些向量,以了解图像是否已成功加载。

load_from_memory方法失败,因为我得到了[331]向量。如果我使用load_from_memory方法替换guess_format方法,则会获得[333]向量。我还为PNG和JPG做了一些模式匹配,它正确地读取了缓冲区。

我找不到如何更彻底地调试这种行为。

在JavaScript部分,我只是将图像的arrayBuffer加载到WASM的共享内存中。

以下是我在JavaScript方面所做的事情:

function compile(wasmFile = 'distil_wasm.gc.wasm') {
    return fetch(wasmFile)
        .then(r => r.arrayBuffer())
        .then(r => {
            let module = new WebAssembly.Module(r);
            let importObject = {}
            for (let imp of WebAssembly.Module.imports(module)) {
                if (typeof importObject[imp.module] === "undefined")
                    importObject[imp.module] = {};
                switch (imp.kind) {
                case "function": importObject[imp.module][imp.name] = () => {}; break;
                case "table": importObject[imp.module][imp.name] = new WebAssembly.Table({ initial: 256, maximum: 256, element: "anyfunc" }); break;
                case "memory": importObject[imp.module][imp.name] = new WebAssembly.Memory({ initial: 256 }); break;
                case "global": importObject[imp.module][imp.name] = 0; break;
                }
            }

            return WebAssembly.instantiate(r, importObject);
        });
}

function loadImgIntoMem(img, memory, alloc) {
    return new Promise(resolve => {
        fetch(img)
            .then(r => r.arrayBuffer())
            .then(buff => {
                const imgPtr = alloc(buff.byteLength);
                const imgHeap = new Uint8Array(memory.buffer, imgPtr, buff.byteLength);

                imgHeap.set(new Uint8Array(buff));

                resolve({ imgPtr, len: buff.byteLength });
            });
    });
}


function run(img) {
    return compile().then(m => {
        return loadImgIntoMem(img, m.instance.exports.memory, m.instance.exports.alloc).then(r => {
            window.WASM = m;
            return m.instance.exports.read_img(r.imgPtr, r.len);
        });
    });
}

run('img-2.jpg')
   .then(ptr => console.log(new Int32Array(WASM.instance.exports.memory.buffer, ptr, 1)))

此控制台记录:

Int32Array [ 331 ]

1 个答案:

答案 0 :(得分:5)

如果没有访问调试器或打印消息的能力,基本上无法调试。因此,我将您的代码移植到wasm-bindgen,纯粹是为了能够从Rust代码中访问控制台:

#![feature(proc_macro, wasm_custom_section, wasm_import_module)]

extern crate wasm_bindgen;
extern crate image;

use wasm_bindgen::prelude::*;
use std::mem;

pub mod console {
    use wasm_bindgen::prelude::*;

    #[wasm_bindgen]
    extern {
        #[wasm_bindgen(js_namespace = console)]
        pub fn log(s: &str);
    }
}

#[wasm_bindgen]
pub fn alloc(len: usize) -> *mut u8 {
    let mut buf = Vec::with_capacity(len);
    let ptr = buf.as_mut_ptr();
    mem::forget(buf);
    ptr
}

#[wasm_bindgen]
pub fn read_img(ptr: *mut u8, len: usize) {
    let img = unsafe { Vec::from_raw_parts(ptr, len, len) };

    if let Err(e) = image::load_from_memory(&img) {
        console::log(&e.to_string());
    }
}

更新后的JavaScript:

const js = import("./imaj_bg");

async function loadImgIntoMem(img, { alloc, memory }) {
  const resp = await fetch(img);
  const buf = await resp.arrayBuffer();

  const len = buf.byteLength;
  const ptr = alloc(len);

  const imgArray = new Uint8Array(memory.buffer, ptr, len);    
  imgArray.set(new Uint8Array(buf));

  return { ptr, len };
}

async function go(js) {
  const { ptr, len } = await loadImgIntoMem('cat.jpg', js);
  js.read_img(ptr, len);
};

js.then(go);

构建并提供代码:

$ cargo build --target wasm32-unknown-unknown --release
$ wasm-bindgen target/wasm32-unknown-unknown/release/imaj.wasm --out-dir=.
$ yarn serve

访问该页面并查看控制台日志会显示以下消息:

operation not supported on wasm yet

事实上,Rust标准库的大部分内容在WebAssembly中尚不存在。其中许多都被删除以返回此错误。

我不确切知道您的代码缺少哪个平台支持。最明显的一个是jpeg_rayonhdr功能所需的线程,但关闭除jpeg之外的所有图片功能仍会报告相同的错误。可能还有其他需要的东西。

但是,它似乎特定于给定的图像编解码器。如果您尝试相同的代码但加载了PNG图像,那么它就会成功:

pub fn read_img(ptr: *mut u8, len: usize) {
    let img = unsafe { Vec::from_raw_parts(ptr, len, len) };

    let img = match image::load_from_memory(&img) {
        Ok(i) => i,
        Err(e) => {
            console::log(&e.to_string());
            return;
        }
    };

    console::log(&format!("{:?}", img.to_rgba()));
}
ImageBuffer { width: 305, height: 314, _phantom: PhantomData, data: [255, 255, 255, 0 /* remaining pixels skipped */

这表示JPEG代码尚不适用于WASM。给定的编解码器可能会或可能不会工作;最好向上游维护者提出问题。