Skip to content

Instantly share code, notes, and snippets.

@udzura
Created December 18, 2024 10:01
Show Gist options
  • Save udzura/6409550d7200f52b32b057e02ba2f8e9 to your computer and use it in GitHub Desktop.
Save udzura/6409550d7200f52b32b057e02ba2f8e9 to your computer and use it in GitHub Desktop.
grayscale.wasm
[package]
name = "advent-ruby-grayscale"
version = "0.1.0"
edition = "2021"
[dependencies]
base64 = "0.22.1"
image = { version = "0.25.2", default-features = false, features = ["png"] }
[lib]
crate-type = ["cdylib", "rlib"]
extern crate image;
use core::str;
use core::slice::from_raw_parts;
use base64::{engine::general_purpose, Engine};
#[no_mangle]
pub unsafe fn grayscale(
width: u32,
height: u32,
src: *const u8, // メモリにセットしたdataURLのoffset
slen: i32, // メモリにセットしたdataURLの長さ
) -> *const u8 {
let src = from_raw_parts(src, slen as usize);
// メモリを内部でも確保してコピーする(元の領域が万一破壊されるのを防ぐ)
let mut tmp_buf: Vec<u8> = Vec::<u8>::new();
tmp_buf.resize(slen as usize, 0);
tmp_buf.copy_from_slice(src);
// 返却用のバッファを、大きく確保する
let mut result_buf: Vec<u8> = Vec::<u8>::new();
result_buf.resize(1<<22, 0);
// URLをstrに変換し、base64部分だけ取り出す
let url = str::from_utf8(&tmp_buf).unwrap();
let collected = url.split(",").collect::<Vec<&str>>();
let src = collected[1].as_bytes();
// decode base64
let blob: Vec<u8> = general_purpose::STANDARD.decode(src).unwrap();
// 渡されたデータをPNGとして解釈する
let img = image::load_from_memory_with_format(
&blob, image::ImageFormat::Png).unwrap();
let img: image::ImageBuffer<image::Rgb<u8>, _> = img.to_rgb8();
// あとは元のgrayscaleと同様のコード
let mut dest = image::GrayImage::new(width, height);
for y in 0..height {
for x in 0..width {
let pixel: &image::Rgb<u8> = img.get_pixel(x, y);
let val =
(pixel[0] as f32 + pixel[1] as f32 + pixel[2] as f32) / 3.0;
let val = [val as u8; 1];
dest.put_pixel(x, y, image::Luma(val));
}
}
// デバッグのため、wasmでないときにファイルを生成する
#[cfg(not(target_arch="wasm32"))]
{
dest.save("/tmp/debug.png").unwrap();
}
// オンメモリで変換後のPNGを生成する
let mut inter_buf = Vec::<u8>::new();
let enc = image::codecs::png::PngEncoder::new(&mut inter_buf);
dest.write_with_encoder(enc).unwrap_or_else(|_| panic!("encode failed") );
// オンメモリのデータをbase64にエンコードする
general_purpose::STANDARD.encode_slice(&inter_buf, &mut result_buf).unwrap();
// データの先頭ポインタ = offset を返却
result_buf.as_ptr()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment