首页 > 解决方案 > color_quant::NeuQuant 编译为 WebAssembly 输出零值

问题描述

我正在尝试在浏览器中加载图像并使用 NewQuant 算法通过 WebAssembly 在 Rust 中量化我的图像缓冲区。但是,无论我尝试输入什么 PNG,NewQuant 输出都包含零值。

我向 WASM 公开了两个 Rust 方法:

  1. alloc用于分配字节缓冲区
  2. read_img它将读取和处理 img 缓冲区

我知道我得到零值是因为我导入了一个称为log_nr记录简单u8数字的 JavaScript 方法。缓冲区似乎包含有效的像素值。

extern crate color_quant;
extern crate image;

use color_quant::NeuQuant;
use image::{DynamicImage, GenericImage, Pixel, Rgb};

use std::collections::BTreeMap;
use std::mem;
use std::os::raw::c_void;

static NQ_SAMPLE_FACTION: i32 = 10;
static NQ_PALETTE_SIZE: usize = 256;

extern "C" {
    fn log(s: &str, len: usize);
    fn log_nr(nr: u8);
}

fn get_pixels(img: DynamicImage) -> Vec<u8> {
    let mut pixels = Vec::new();

    for (_, _, px) in img.pixels() {
        let rgba = px.to_rgba();

        for channel in px.channels() {
            pixels.push(*channel);
        }
    }

    pixels
}

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

fn process_img(img: DynamicImage) {
    let pixels: Vec<u8> = get_pixels(img);
    let quantized = NeuQuant::new(NQ_SAMPLE_FACTION, NQ_PALETTE_SIZE, &pixels);

    let q = quantized.color_map_rgb();

    for c in &q {
        unsafe {
            log_nr(*c);
        }
    }
}

#[no_mangle]
pub extern "C" fn read_img(buff_ptr: *mut u8, buff_len: usize) {
    let mut img: Vec<u8> = unsafe { Vec::from_raw_parts(buff_ptr, buff_len, buff_len) };

    return match image::load_from_memory(&img) {
        Ok(img) => {
            process_img(img);
        }
        Err(err) => {
            let err_msg: String = err.to_string().to_owned();
            let mut ns: String = "[load_from_memory] ".to_owned();

            ns.push_str(&err_msg);

            unsafe {
                log(&ns, ns.len());
            }
        }
    };
}

fn main() {
    println!("Hello from rust 2");
}

JavaScript 代码如下:

run('sample.png');

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

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;
                }
            }

            importObject.env = Object.assign({}, importObject.env, {
                log: (ptr, len) => console.log(ptrToStr(ptr, len)),
                log_nr: (nr) => console.log(nr),
            });

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

function loadImgIntoMemEmscripten(img) {
    return new Promise(resolve => {
        fetch(img)
            .then(r => r.arrayBuffer())
            .then(buff => {
                const imgPtr = Module._malloc(buff.byteLength);
                const imgHeap = new Uint8Array(Module.HEAPU8.buffer, imgPtr, buff.byteLength);

                imgHeap.set(new Uint8Array(buff));

                resolve({ imgPtr });
            });
    });
}

标签: rustwebassembly

解决方案


推荐阅读