diff options
author | Leo Kettmeir <crowlkats@toaxl.com> | 2024-01-22 12:08:01 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-22 12:08:01 +0100 |
commit | 8f767627938ef10802864419061e58a8a75db567 (patch) | |
tree | 81f61ba0f8c14fb820a72500840eb0c619d54362 /ext/canvas/lib.rs | |
parent | b4990d1aa233db662cf22d7f872d45b3a947e0f6 (diff) |
feat(web): ImageBitmap (#21898)
Diffstat (limited to 'ext/canvas/lib.rs')
-rw-r--r-- | ext/canvas/lib.rs | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/ext/canvas/lib.rs b/ext/canvas/lib.rs new file mode 100644 index 000000000..b05332c3f --- /dev/null +++ b/ext/canvas/lib.rs @@ -0,0 +1,153 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +use deno_core::error::type_error; +use deno_core::error::AnyError; +use deno_core::op2; +use deno_core::ToJsBuffer; +use image::imageops::FilterType; +use image::ColorType; +use image::ImageDecoder; +use image::Pixel; +use image::RgbaImage; +use serde::Deserialize; +use serde::Serialize; +use std::path::PathBuf; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "snake_case")] +enum ImageResizeQuality { + Pixelated, + Low, + Medium, + High, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct ImageProcessArgs { + width: u32, + height: u32, + surface_width: u32, + surface_height: u32, + input_x: i64, + input_y: i64, + output_width: u32, + output_height: u32, + resize_quality: ImageResizeQuality, + flip_y: bool, + premultiply: Option<bool>, +} + +#[op2] +#[serde] +fn op_image_process( + #[buffer] buf: &[u8], + #[serde] args: ImageProcessArgs, +) -> Result<ToJsBuffer, AnyError> { + let view = + RgbaImage::from_vec(args.width, args.height, buf.to_vec()).unwrap(); + + let surface = if !(args.width == args.surface_width + && args.height == args.surface_height + && args.input_x == 0 + && args.input_y == 0) + { + let mut surface = RgbaImage::new(args.surface_width, args.surface_height); + + image::imageops::overlay(&mut surface, &view, args.input_x, args.input_y); + + surface + } else { + view + }; + + let filter_type = match args.resize_quality { + ImageResizeQuality::Pixelated => FilterType::Nearest, + ImageResizeQuality::Low => FilterType::Triangle, + ImageResizeQuality::Medium => FilterType::CatmullRom, + ImageResizeQuality::High => FilterType::Lanczos3, + }; + + let mut image_out = image::imageops::resize( + &surface, + args.output_width, + args.output_height, + filter_type, + ); + + if args.flip_y { + image::imageops::flip_vertical_in_place(&mut image_out); + } + + // ignore 9. + + if let Some(premultiply) = args.premultiply { + let is_not_premultiplied = image_out.pixels().any(|pixel| { + (pixel.0[0].max(pixel.0[1]).max(pixel.0[2])) > (255 * pixel.0[3]) + }); + + if premultiply { + if is_not_premultiplied { + for pixel in image_out.pixels_mut() { + let alpha = pixel.0[3]; + pixel.apply_without_alpha(|channel| { + (channel as f32 * (alpha as f32 / 255.0)) as u8 + }) + } + } + } else if !is_not_premultiplied { + for pixel in image_out.pixels_mut() { + let alpha = pixel.0[3]; + pixel.apply_without_alpha(|channel| { + (channel as f32 / (alpha as f32 / 255.0)) as u8 + }) + } + } + } + + Ok(image_out.to_vec().into()) +} + +#[derive(Debug, Serialize)] +struct DecodedPng { + data: ToJsBuffer, + width: u32, + height: u32, +} + +#[op2] +#[serde] +fn op_image_decode_png(#[buffer] buf: &[u8]) -> Result<DecodedPng, AnyError> { + let png = image::codecs::png::PngDecoder::new(buf)?; + + let (width, height) = png.dimensions(); + + // TODO(@crowlKats): maybe use DynamicImage https://docs.rs/image/0.24.7/image/enum.DynamicImage.html ? + if png.color_type() != ColorType::Rgba8 { + return Err(type_error(format!( + "Color type '{:?}' not supported", + png.color_type() + ))); + } + + let mut png_data = Vec::with_capacity(png.total_bytes() as usize); + + png.read_image(&mut png_data)?; + + Ok(DecodedPng { + data: png_data.into(), + width, + height, + }) +} + +deno_core::extension!( + deno_canvas, + deps = [deno_webidl, deno_web, deno_webgpu], + ops = [op_image_process, op_image_decode_png], + lazy_loaded_esm = ["01_image.js"], +); + +pub fn get_declaration() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_canvas.d.ts") +} |