#[cfg(all(target_arch = "wasm32", feature = "wasm"))]
#[global_allocator]
static ALLOC: talc::TalckWasm = unsafe { talc::TalckWasm::new_global() };
use wasm_bindgen::prelude::*;
use crate::color::ColorType;
use crate::jpeg::{self, JpegOptions, Subsampling};
use crate::png::{self, PngOptions};
use crate::resize::{self, ResizeAlgorithm};
fn color_type_from_u8(value: u8) -> Result<ColorType, JsError> {
ColorType::try_from(value).map_err(|v| {
JsError::new(&format!(
"Invalid color type: {v}. Expected 0 (Gray), 1 (GrayAlpha), 2 (Rgb), or 3 (Rgba)",
))
})
}
#[wasm_bindgen(js_name = "encodePng")]
pub fn encode_png(
data: &[u8],
width: u32,
height: u32,
color_type: u8,
preset: u8,
lossy: bool,
) -> Result<Vec<u8>, JsError> {
let color = color_type_from_u8(color_type)?;
let options = PngOptions::builder(width, height)
.color_type(color)
.preset(preset)
.lossy(lossy)
.build();
png::encode(data, &options).map_err(|e| JsError::new(&e.to_string()))
}
#[wasm_bindgen(js_name = "encodeJpeg")]
pub fn encode_jpeg(
data: &[u8],
width: u32,
height: u32,
color_type: u8,
quality: u8,
preset: u8,
subsampling_420: bool,
) -> Result<Vec<u8>, JsError> {
let color = match ColorType::try_from(color_type) {
Ok(ColorType::Gray) => ColorType::Gray,
Ok(ColorType::Rgb) => ColorType::Rgb,
_ => {
return Err(JsError::new(&format!(
"Invalid color type for JPEG: {color_type}. Expected 0 (Gray) or 2 (Rgb)",
)))
}
};
let options = JpegOptions::builder(width, height)
.color_type(color)
.quality(quality)
.preset(preset)
.subsampling(if subsampling_420 {
Subsampling::S420
} else {
Subsampling::S444
})
.build();
jpeg::encode(data, &options).map_err(|e| JsError::new(&e.to_string()))
}
#[wasm_bindgen(js_name = "bytesPerPixel")]
pub fn bytes_per_pixel(color_type: u8) -> Result<u8, JsError> {
let color = color_type_from_u8(color_type)?;
Ok(color.bytes_per_pixel() as u8)
}
fn resize_algorithm_from_u8(value: u8) -> Result<ResizeAlgorithm, JsError> {
match value {
0 => Ok(ResizeAlgorithm::Nearest),
1 => Ok(ResizeAlgorithm::Bilinear),
2 => Ok(ResizeAlgorithm::Lanczos3),
_ => Err(JsError::new(&format!(
"Invalid resize algorithm: {value}. Expected 0 (Nearest), 1 (Bilinear), or 2 (Lanczos3)",
))),
}
}
#[wasm_bindgen(js_name = "resizeImage")]
pub fn resize_image(
data: &[u8],
src_width: u32,
src_height: u32,
dst_width: u32,
dst_height: u32,
color_type: u8,
algorithm: u8,
) -> Result<Vec<u8>, JsError> {
let color = color_type_from_u8(color_type)?;
let algo = resize_algorithm_from_u8(algorithm)?;
let options = resize::ResizeOptions::builder(src_width, src_height)
.dst(dst_width, dst_height)
.color_type(color)
.algorithm(algo)
.build();
resize::resize(data, &options).map_err(|e| JsError::new(&e.to_string()))
}
#[cfg(all(test, target_arch = "wasm32"))]
mod tests {
use super::*;
#[test]
fn test_encode_png_1x1() {
let pixels = vec![255, 0, 0, 255]; let result = encode_png(&pixels, 1, 1, 3, 1, false);
assert!(result.is_ok());
let png = result.unwrap();
assert_eq!(
&png[0..8],
&[0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
);
}
#[test]
fn test_encode_jpeg_1x1() {
let pixels = vec![255, 0, 0]; let result = encode_jpeg(&pixels, 1, 1, 2, 85, 1, false);
assert!(result.is_ok());
let jpeg = result.unwrap();
assert_eq!(&jpeg[0..2], &[0xFF, 0xD8]);
}
#[test]
fn test_invalid_color_type() {
let pixels = vec![255, 0, 0];
let result = encode_png(&pixels, 1, 1, 99, 1, false);
assert!(result.is_err());
}
#[test]
fn test_jpeg_invalid_color_type() {
let pixels = vec![255, 0, 0, 255];
let result = encode_jpeg(&pixels, 1, 1, 3, 85, 1, false);
assert!(result.is_err());
}
#[test]
fn test_bytes_per_pixel() {
assert_eq!(bytes_per_pixel(0).unwrap(), 1);
assert_eq!(bytes_per_pixel(1).unwrap(), 2);
assert_eq!(bytes_per_pixel(2).unwrap(), 3);
assert_eq!(bytes_per_pixel(3).unwrap(), 4);
assert!(bytes_per_pixel(99).is_err());
}
#[test]
fn test_resize_image_basic() {
let pixels = vec![255u8; 4 * 4 * 4]; let result = resize_image(&pixels, 4, 4, 2, 2, 3, 1); assert!(result.is_ok());
let resized = result.unwrap();
assert_eq!(resized.len(), 2 * 2 * 4);
}
#[test]
fn test_resize_image_invalid_color_type() {
let pixels = vec![255u8; 4 * 4 * 4];
let result = resize_image(&pixels, 4, 4, 2, 2, 99, 1);
assert!(result.is_err());
}
#[test]
fn test_resize_image_invalid_algorithm() {
let pixels = vec![255u8; 4 * 4 * 4];
let result = resize_image(&pixels, 4, 4, 2, 2, 3, 99);
assert!(result.is_err());
}
}