summaryrefslogtreecommitdiff
path: root/ext/canvas/lib.rs
diff options
context:
space:
mode:
authorLeo Kettmeir <crowlkats@toaxl.com>2024-01-22 12:08:01 +0100
committerGitHub <noreply@github.com>2024-01-22 12:08:01 +0100
commit8f767627938ef10802864419061e58a8a75db567 (patch)
tree81f61ba0f8c14fb820a72500840eb0c619d54362 /ext/canvas/lib.rs
parentb4990d1aa233db662cf22d7f872d45b3a947e0f6 (diff)
feat(web): ImageBitmap (#21898)
Diffstat (limited to 'ext/canvas/lib.rs')
-rw-r--r--ext/canvas/lib.rs153
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")
+}