我正在尝试使用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 ]
答案 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_rayon
和hdr
功能所需的线程,但关闭除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。给定的编解码器可能会或可能不会工作;最好向上游维护者提出问题。