编译为 WebAssembly 时无法使 image::load_from_memory() 工作 [英] Can't get image::load_from_memory() to work when compiled to WebAssembly

查看:12
本文介绍了编译为 WebAssembly 时无法使 image::load_from_memory() 工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我有以下 Rust 代码:

extern crate image;extern crate libc;使用 libc::c_void;使用 std::mem;#[no_mangle]pub extern "C" fn alloc(size: usize) ->*mut c_void {让 mut buf = Vec::with_capacity(size);让 ptr = buf.as_mut_ptr();内存::忘记(缓冲);将 ptr 返回为 *mut c_void;}#[no_mangle]pub extern "C" fn read_img(buff_ptr: *mut u8, buff_len: usize) ->*mut i32 {让 mut img: Vec= 不安全 { Vec::from_raw_parts(buff_ptr, buff_len, buff_len) };让 ok = Box::new([333]);让错误 = Box::new([331]);返回匹配图像::load_from_memory(&img) {好的(img) =>Box::into_raw(ok) as *mut i32,错误(_)=>Box::into_raw(err) 为 *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') {返回获取(wasmFile).then(r => r.arrayBuffer()).then(r => {let module = new WebAssembly.Module(r);让 importObject = {}for (let imp of WebAssembly.Module.imports(module)) {if (typeof importObject[imp.module] === "undefined")importObject[imp.module] = {};开关(imp.kind){case "function": importObject[imp.module][imp.name] = () =>{};休息;case "table": importObject[imp.module][imp.name] = new WebAssembly.Table({ initial: 256, maximum: 256, element: "anyfunc" });休息;case "memory": importObject[imp.module][imp.name] = new WebAssembly.Memory({ initial: 256 });休息;案例全局":importObject[imp.module][imp.name] = 0;休息;}}返回 WebAssembly.instantiate(r, importObject);});}函数 loadImgIntoMem(img, memory, alloc) {返回新的承诺(解决 => {获取(图像).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));解析({ imgPtr,len:buff.byteLength });});});}功能运行(img){返回 compile().then(m => {返回 loadImgIntoMem(img, m.instance.exports.memory, m.instance.exports.alloc).then(r => {窗口.WASM = m;返回 m.instance.exports.read_img(r.imgPtr, r.len);});});}运行('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;外部板条箱图像;使用 wasm_bindgen::prelude::*;使用 std::mem;酒吧模组控制台{使用 wasm_bindgen::prelude::*;#[wasm_bindgen]外部{#[wasm_bindgen(js_namespace = 控制台)]pub fn 日志(s: &str);}}#[wasm_bindgen]pub fn alloc(len: usize) ->*mut u8 {让 mut buf = Vec::with_capacity(len);让 ptr = buf.as_mut_ptr();内存::忘记(缓冲);指针}#[wasm_bindgen]pub fn read_img(ptr: *mut u8, len: usize) {让 img = 不安全 { Vec::from_raw_parts(ptr, len, len) };如果让 Err(e) = image::load_from_memory(&img) {控制台::日志(&e.to_string());}}

更新的 JavaScript:

const js = import("./imaj_bg");异步函数 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));返回{ptr,len};}异步函数去(js){const { ptr, len } = await loadImgIntoMem('cat.jpg', js);js.read_img(ptr, len);};js.然后(去);

构建和提供代码:

$ cargo build --target wasm32-unknown-unknown --release$ wasm-bindgen target/wasm32-unknown-unknown/release/imaj.wasm --out-dir=.$纱线服务

访问页面并查看控制台日志显示了这条虎头蛇尾的消息:

wasm 尚不支持操作

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

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

但是,它似乎特定于给定的图像编解码器.如果您尝试相同的代码但加载 PNG 图像,则成功:

pub fn read_img(ptr: *mut u8, len: usize) {让 img = 不安全 { Vec::from_raw_parts(ptr, len, len) };让 img = 匹配图像::load_from_memory(&img) {好的(i) =>一世,错误(e) =>{控制台::日志(&e.to_string());返回;}};console::log(&format!("{:?}", img.to_rgba()));}

ImageBuffer { width: 305, height: 314, _phantom: PhantomData, data: [255, 255, 255, 0/* 剩余像素被跳过 */

这表明 JPEG 代码还不能用于 WASM.给定的编解码器可能还没有工作;最好向上游维护者提交问题.

I am trying to load an image from JavaScript to WebAssembly with Rust using the image crate.

I have the following Rust code:

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() {}

which I compile using the following tools:

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

In the read_img() function, I naively handle errors via two vectors: [333] for OK and [331] for any error. I read these vectors on the JavaScript side to know if the image was loaded successfully.

The load_from_memory method fails because I get the [331] vector. If I replace the load_from_memory method with the guess_format method, I get the [333] vector. I also did some pattern matching for PNG and JPG and it reads the buffer correctly.

I couldn't find how can I more thoroughly debug such behaviour.

On the JavaScript part, I simply load up the image's arrayBuffer into WASM's shared memory.

Here is what I am doing on the JavaScript side:

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)))

This console logs:

Int32Array [ 331 ]

解决方案

It's basically impossible to debug things without access to a debugger or the ability to print out messages. Because of this, I ported your code to use wasm-bindgen, purely for the ability to access the console from inside Rust code:

#![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());
    }
}

The updated 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);

Building and serving the code:

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

Accessing the page and reviewing the console log shows this anticlimactic message:

operation not supported on wasm yet

The truth is that there's large parts of the Rust standard library that don't exist yet in WebAssembly. Many of these are stubbed out to return this error.

I don't know exactly which platform support that is missing for your code. The most obvious one is threading, required by the jpeg_rayon and hdr feature, but turning off all of image's features except jpeg still reports same error. It's likely there's something else needed.

However, it does seem to be specific to a given image codec. If you try the same code but load a PNG image, it's successful:

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 */

This indicates that the JPEG code does not yet work with WASM. A given codec may or may not work yet; it's probably best to file issues with the upstream maintainers.

这篇关于编译为 WebAssembly 时无法使 image::load_from_memory() 工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆