我正在尝试使用 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]
用于 OK,[331]
用于任何错误。我在 JavaScript 端读取了这些向量,以了解图像是否已成功加载。
load_from_memory
方法失败,因为我得到了 [331]
向量。如果我用 guess_format
方法替换 load_from_memory
方法,我会得到 [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 ]
最佳答案
如果无法访问调试器或无法打印消息,则基本上不可能进行调试。因此,我移植了您的代码以使用 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。给定的编解码器可能工作也可能不工作;最好向上游维护者提出问题。
关于rust - 编译为 WebAssembly 时无法使 image::load_from_memory() 工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50415623/