编辑 WebAssembly/Rust 中的 canvas 像素数据
Editing canvas pixel data in WebAssembly/Rust
我正在尝试使用 WebAssembly 和 Rust 来创建 canvas 像素数据。作为初始实验,我试图让 Rust 写入其线性内存,然后我将使用它来创建一个 ImageData
对象,我可以将其写入 canvas.
底层 ImageData 是一个 Uint8Array,其中每个像素是 4 个 rgba 数字。我使用以下结构在 rust 中表示:
struct Pixel {
r: u8,
g: u8,
b: u8,
a: u8,
}
我已经将一个函数导出到 JavaScript,它将尝试为 500 x 500 像素中的所有 250,000 个像素着色 canvas:
#[no_mangle]
pub fn color(width: u32, height: u32) {
for i in 0..width * height {
let ptr = (i * 4) as u64 as *mut Pixel;
let mut pixel = unsafe { &mut *ptr };
pixel.r = 10;
pixel.g = 10;
pixel.b = 10;
pixel.a = 255;
}
}
这里是对应的前端HTML/JS
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
canvas {
border: 1px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" height="500" width="500"></canvas>
<script>
const WIDTH = 500;
const HEIGHT = 500;
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
fetch('/rotate.wasm')
.then((res) => res.arrayBuffer())
.then((ab) => WebAssembly.instantiate(ab))
.then(({ instance }) => {
instance.exports.memory.grow(100); // make memory big enough
instance.exports.color(WIDTH, HEIGHT);
const data = new Uint8ClampedArray(instance.exports.memory.buffer, 0, WIDTH * HEIGHT * 4)
const imageData = new ImageData(data, 500, 500);
ctx.putImageData(imageData, 0, 0);
});
</script>
</body>
</html>
结果是并非所有像素都是彩色的。顶部只有一个部分:
当我检查 WebAssembly 内存时,我可以看到它似乎在大约 42k 像素后放弃了着色。
我想我找到了答案。不能保证 JavaScript 可以使用线性内存的开头。 Rust 包含在 wasm 二进制文件中的运行时可以自由写入该内存位置。我通过在我的程序中静态分配一块内存并返回指向 JavaScript 的指针来解决我的问题,因此它知道可以安全写入的位置。
// Statically allocate space for 1m pixels
static mut PIXELS: [Pixel; 1_000_000] = [Pixel {
r: 255,
g: 0,
b: 0,
a: 255,
}; 1_000_000];
// return pointer to JavaScript
#[no_mangle]
pub fn get_memory_offset() -> i32 {
return unsafe { &PIXELS as *const _ as i32 };
}
最好也动态分配内存,但我还不确定该怎么做。
您的代码从位置 0 开始将图像数据写入线性内存,您确定这样做安全吗?大多数语言在编译为 WebAssembly 时,都会为自己的运行时使用线性内存。
一个更安全的选择是创建一个表示您的图像的结构,然后从您的 JavaScript 代码中获取对此的引用,以便您可以确保您的 JS 和 Rust 代码对齐:
https://github.com/ColinEberhardt/wasm-rust-chip8/blob/master/web/chip8.js#L124
我正在尝试使用 WebAssembly 和 Rust 来创建 canvas 像素数据。作为初始实验,我试图让 Rust 写入其线性内存,然后我将使用它来创建一个 ImageData
对象,我可以将其写入 canvas.
底层 ImageData 是一个 Uint8Array,其中每个像素是 4 个 rgba 数字。我使用以下结构在 rust 中表示:
struct Pixel {
r: u8,
g: u8,
b: u8,
a: u8,
}
我已经将一个函数导出到 JavaScript,它将尝试为 500 x 500 像素中的所有 250,000 个像素着色 canvas:
#[no_mangle]
pub fn color(width: u32, height: u32) {
for i in 0..width * height {
let ptr = (i * 4) as u64 as *mut Pixel;
let mut pixel = unsafe { &mut *ptr };
pixel.r = 10;
pixel.g = 10;
pixel.b = 10;
pixel.a = 255;
}
}
这里是对应的前端HTML/JS
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
canvas {
border: 1px solid red;
}
</style>
</head>
<body>
<canvas id="canvas" height="500" width="500"></canvas>
<script>
const WIDTH = 500;
const HEIGHT = 500;
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
fetch('/rotate.wasm')
.then((res) => res.arrayBuffer())
.then((ab) => WebAssembly.instantiate(ab))
.then(({ instance }) => {
instance.exports.memory.grow(100); // make memory big enough
instance.exports.color(WIDTH, HEIGHT);
const data = new Uint8ClampedArray(instance.exports.memory.buffer, 0, WIDTH * HEIGHT * 4)
const imageData = new ImageData(data, 500, 500);
ctx.putImageData(imageData, 0, 0);
});
</script>
</body>
</html>
结果是并非所有像素都是彩色的。顶部只有一个部分:
当我检查 WebAssembly 内存时,我可以看到它似乎在大约 42k 像素后放弃了着色。
我想我找到了答案。不能保证 JavaScript 可以使用线性内存的开头。 Rust 包含在 wasm 二进制文件中的运行时可以自由写入该内存位置。我通过在我的程序中静态分配一块内存并返回指向 JavaScript 的指针来解决我的问题,因此它知道可以安全写入的位置。
// Statically allocate space for 1m pixels
static mut PIXELS: [Pixel; 1_000_000] = [Pixel {
r: 255,
g: 0,
b: 0,
a: 255,
}; 1_000_000];
// return pointer to JavaScript
#[no_mangle]
pub fn get_memory_offset() -> i32 {
return unsafe { &PIXELS as *const _ as i32 };
}
最好也动态分配内存,但我还不确定该怎么做。
您的代码从位置 0 开始将图像数据写入线性内存,您确定这样做安全吗?大多数语言在编译为 WebAssembly 时,都会为自己的运行时使用线性内存。
一个更安全的选择是创建一个表示您的图像的结构,然后从您的 JavaScript 代码中获取对此的引用,以便您可以确保您的 JS 和 Rust 代码对齐:
https://github.com/ColinEberhardt/wasm-rust-chip8/blob/master/web/chip8.js#L124