Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 0 additions & 16 deletions ext/canvas/01_image.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,28 +260,12 @@ function createImageBitmap(
mimeType = 2;
} else if (mimeTypeString === "image/gif") {
mimeType = 3;
// NOTE: Temporarily not supported due to build size concerns
// https://github.com/denoland/deno/pull/25517#issuecomment-2626044644
return PromiseReject(
new DOMException(
"The MIME type of source image is not supported currently",
"InvalidStateError",
),
);
} else if (mimeTypeString === "image/bmp") {
mimeType = 4;
} else if (mimeTypeString === "image/x-icon") {
mimeType = 5;
} else if (mimeTypeString === "image/webp") {
mimeType = 6;
// NOTE: Temporarily not supported due to build size concerns
// https://github.com/denoland/deno/pull/25517#issuecomment-2626044644
return PromiseReject(
new DOMException(
"The MIME type of source image is not supported currently",
"InvalidStateError",
),
);
} else {
return PromiseReject(
new DOMException(
Expand Down
2 changes: 1 addition & 1 deletion ext/canvas/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ path = "lib.rs"
bytemuck.workspace = true
deno_core.workspace = true
deno_error.workspace = true
image = { workspace = true, features = ["png", "jpeg", "bmp", "ico"] }
image = { workspace = true, features = ["png", "jpeg", "bmp", "ico", "webp", "gif"] }
# NOTE: The qcms is a color space conversion crate which parses ICC profiles that used in Gecko,
# however it supports only 8-bit color depth currently.
# https://searchfox.org/mozilla-central/rev/f09e3f9603a08b5b51bf504846091579bc2ff531/gfx/qcms/src/transform.rs#130-137
Expand Down
52 changes: 23 additions & 29 deletions ext/canvas/op_create_image_bitmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ use std::io::Cursor;
use deno_core::JsBuffer;
use deno_core::ToJsBuffer;
use deno_core::op2;
// use image::codecs::webp::WebPDecoder;
use image::DynamicImage;
use image::ImageDecoder;
use image::RgbaImage;
use image::codecs::bmp::BmpDecoder;
// use image::codecs::gif::GifDecoder;
use image::codecs::gif::GifDecoder;
use image::codecs::ico::IcoDecoder;
use image::codecs::jpeg::JpegDecoder;
use image::codecs::png::PngDecoder;
use image::codecs::webp::WebPDecoder;
use image::imageops::FilterType;
use image::imageops::overlay;
use image::metadata::Orientation;
Expand Down Expand Up @@ -122,20 +122,17 @@ fn decode_bitmap_data(
)
}
MimeType::Gif => {
// NOTE: Temporarily not supported due to build size concerns
// https://github.com/denoland/deno/pull/25517#issuecomment-2626044644
unimplemented!();
// The GifDecoder decodes the first frame.
// let mut decoder = GifDecoder::new(BufReader::new(Cursor::new(buf)))
// .map_err(CanvasError::image_error_to_invalid_image)?;
// let orientation = decoder.orientation()?;
// let icc_profile = decoder.icc_profile()?;
// (
// DynamicImage::from_decoder(decoder)
// .map_err(CanvasError::image_error_to_invalid_image)?,
// orientation,
// icc_profile,
// )
let mut decoder = GifDecoder::new(BufReader::new(Cursor::new(buf)))
.map_err(CanvasError::image_error_to_invalid_image)?;
let orientation = decoder.orientation()?;
let icc_profile = decoder.icc_profile()?;
(
DynamicImage::from_decoder(decoder)
.map_err(CanvasError::image_error_to_invalid_image)?,
orientation,
icc_profile,
)
}
MimeType::Bmp => {
let mut decoder = BmpDecoder::new(BufReader::new(Cursor::new(buf)))
Expand All @@ -162,21 +159,18 @@ fn decode_bitmap_data(
)
}
MimeType::Webp => {
// NOTE: Temporarily not supported due to build size concerns
// https://github.com/denoland/deno/pull/25517#issuecomment-2626044644
unimplemented!();
// The WebPDecoder decodes the first frame.
// let mut decoder =
// WebPDecoder::new(BufReader::new(Cursor::new(buf)))
// .map_err(CanvasError::image_error_to_invalid_image)?;
// let orientation = decoder.orientation()?;
// let icc_profile = decoder.icc_profile()?;
// (
// DynamicImage::from_decoder(decoder)
// .map_err(CanvasError::image_error_to_invalid_image)?,
// orientation,
// icc_profile,
// )
let mut decoder =
WebPDecoder::new(BufReader::new(Cursor::new(buf)))
.map_err(CanvasError::image_error_to_invalid_image)?;
let orientation = decoder.orientation()?;
let icc_profile = decoder.icc_profile()?;
(
DynamicImage::from_decoder(decoder)
.map_err(CanvasError::image_error_to_invalid_image)?,
orientation,
icc_profile,
)
}
// This pattern is unreachable due to current block is already checked by the ImageBitmapSource above.
MimeType::NoMatch => unreachable!(),
Expand Down
24 changes: 8 additions & 16 deletions tests/unit/image_bitmap_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,23 +250,19 @@ Deno.test("imageBitmapFromBlob", async (t) => {
const imageData = new Blob(
[await Deno.readFile(`${prefix}/1x1-red8.gif`)],
);
await assertRejects(() => createImageBitmap(imageData), DOMException);
// TODO(Hajime-san): remove the comment out when the implementation is ready
// const imageBitmap = await createImageBitmap(imageData);
const imageBitmap = await createImageBitmap(imageData);
// @ts-ignore: Deno[Deno.internal].core allowed
// deno-fmt-ignore
// assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 255]));
assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 255]));
});
await t.step("8-bit webp", async () => {
const imageData = new Blob(
[await Deno.readFile(`${prefix}/1x1-red8.webp`)],
);
await assertRejects(() => createImageBitmap(imageData), DOMException);
// TODO(Hajime-san): remove the comment out when the implementation is ready
// const imageBitmap = await createImageBitmap(imageData);
const imageBitmap = await createImageBitmap(imageData);
// @ts-ignore: Deno[Deno.internal].core allowed
// deno-fmt-ignore
// assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 255]));
assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 255]));
});
await t.step("8-bit ico", async () => {
const imageData = new Blob(
Expand Down Expand Up @@ -324,12 +320,10 @@ Deno.test("imageBitmapFromBlobAnimatedImage", async (t) => {
`${prefix}/1x1-3f-lossless-animated-semi-transparent.webp`,
),
]);
await assertRejects(() => createImageBitmap(imageData), DOMException);
// TODO(Hajime-san): remove the comment out when the implementation is ready
// const imageBitmap = await createImageBitmap(imageData);
const imageBitmap = await createImageBitmap(imageData);
// @ts-ignore: Deno[Deno.internal].core allowed
// deno-fmt-ignore
// assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 127]));
assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 127]));
});
await t.step("animated gif", async () => {
// the chunk of animated gif is below (3 frames, 1x1, 8-bit, RGBA)
Expand All @@ -339,12 +333,10 @@ Deno.test("imageBitmapFromBlobAnimatedImage", async (t) => {
const imageData = new Blob([
await Deno.readFile(`${prefix}/1x1-3f-animated.gif`),
]);
await assertRejects(() => createImageBitmap(imageData), DOMException);
// TODO(Hajime-san): remove the comment out when the implementation is ready
// const imageBitmap = await createImageBitmap(imageData);
const imageBitmap = await createImageBitmap(imageData);
// @ts-ignore: Deno[Deno.internal].core allowed
// deno-fmt-ignore
// assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 255]));
assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 255]));
});
});

Expand Down
Loading