4

I would like to pass an array containing imageData from a Canvas to a Rust function to do some calculation with it. I have set the return type to null the get one way data passing to work:

JS

wasm.initialize({ noExitRuntime: true }).then(module => {
  const printImageData = module.cwrap("print_image_data", null, ["array"]);
  const functions = { add, printImageData };
  main(functions);
});


async function main(wasmFn) {
  const imageData = await getImageData();
  const data = new Uint8Array(imageData.data);
  wasmFn.printImageData(data);
}

Rust

#[no_mangle]
pub fn print_image_data(data: *mut [u8]) -> i32 {
    println!("{:?}", data);
    32
}

When executing this, I get the following error in my browser console:

thread 'main' panicked at 'cannot access stdout during shutdown', /checkout/src/libcore/option.rs:819:4
note: Run with `RUST_BACKTRACE=1` for a backtrace.
uncaught exception: 5261184

How can I access the array from Rust side? How could I then access that data again from JavaScript? I have read that passing back an array is not possible.

EDIT:

at the docs of emscripten I have found the following:

argTypes – An array of the types of arguments for the function (if there are no arguments, this can be omitted). Types are as in returnType, except that array is not supported as there is no way for us to know the length of the array).

Is there another way to do this? Maybe passing a Pointer and accessing the Array somehow? I find it quite hard to find documentation, so any link is appreciated :)

b-m-f
  • 1,278
  • 2
  • 13
  • 29
  • Why do you think you *aren't* accessing the array? Why do you need to pass "back" the array if you've only transferred a pointer to the array? Are you sure that `stdout` is even properly configured? The error message seems to indicate otherwise; try just setting a value in the array and using `console.log` on the calling side. – Shepmaster Oct 23 '17 at 17:18
  • I presume you'd use the same solution that people use for C / C++ - i.e. communicate a memory address which is the start of the array https://stackoverflow.com/questions/46748572/how-to-access-webassembly-linear-memory-from-c-c – ColinE Oct 23 '17 at 21:21
  • @ColinE that would be literally what the current code does (`data: *mut [u8]`) thus why I asked such in my previous comment. – Shepmaster Oct 23 '17 at 22:20
  • I tried it out with an add function. Adding two numbers in rust and then printing the result on the calling side. I also printed 'Hello world!' from the Rust side. All working fine. Also the array contains data of an Image. Printing it works from JS. – b-m-f Oct 24 '17 at 11:56
  • You may have missed out the `extern` on `pub fn print_image_data(...)`. But see my complete answer that works for me below. – psiphi75 Jun 14 '18 at 03:37

1 Answers1

2

This is one method of accessing a UInt8ClampedArray, based on this code.

JavaScript

fetch('hello_world.gc.wasm')
  .then(r => r.arrayBuffer())
  .then(r => WebAssembly.instantiate(r))
  .then(r => r.instance.exports)
  .then(wasm => {

    const width = 1;
    const height = 1;
    const byteSize = width * height * 4;
    let pointer = wasm.alloc(byteSize);
    let usub = new Uint8ClampedArray(wasm.memory.buffer, pointer, byteSize);
    let img = new ImageData(usub, width, height);
    wasm.abcd(pointer, width, height);

    console.log(`${img.data[0].toString(16)}`);
    console.log(`${img.data[1].toString(16)}`);
    console.log(`${img.data[2].toString(16)}`);
    console.log(`${img.data[3].toString(16)}`);
  });

Rust

use std::mem;
use std::os::raw::c_void;
use std::slice;

// In order to work with the memory we expose (de)allocation methods
#[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 dealloc(ptr: *mut c_void, cap: usize) {
    unsafe {
        let _buf = Vec::from_raw_parts(ptr, 0, cap);
    }
}

#[no_mangle]
pub extern "C" fn abcd(pointer: *mut u8, width: usize, height: usize) {
    let bytesize: usize = width * height * 4;
    let sl = unsafe { slice::from_raw_parts_mut(pointer, bytesize) };

    // Now you can change your buffer
    sl[0] = 0xaa;
    sl[1] = 0xab;
    sl[2] = 0xac;
    sl[3] = 0xad;
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
psiphi75
  • 1,985
  • 1
  • 24
  • 36