Skip to content

Commit f47ad1f

Browse files
authored
feat(ext/canvas): support gif, webp for createImageBitmap (#31402)
# Summary continues from #25517 The PR(#28470) gave us a chance to reconsider supporting gif and webp, which we had previously abandoned. I can also confirm that there is demand for webp at least. #25517 (comment) ## build size on main at 1146973 ```bash % cargo build --release --target-dir "target/$(git rev-parse --abbrev-ref HEAD)-$(git rev-parse --short HEAD)" ... % ls -l target/main-1146973cd/release/deno -rwxr-xr-x 1 user 46682944 132048656 Nov 25 14:53 target/main-1146973cd/release/deno -> 132.048656 mb ``` this PR ```bash % cargo build --release --target-dir "target/$(git rev-parse --abbrev-ref HEAD)-$(git rev-parse --short HEAD)" ... % ls -l target/feat-webp-gif-2485a575d/release/deno -rwxr-xr-x 1 user 46682944 132311168 Nov 25 17:14 target/feat-webp-gif-2485a575d/release/deno -> 132.311168 mb ``` about 263kb larger
1 parent 658638b commit f47ad1f

File tree

5 files changed

+74
-63
lines changed

5 files changed

+74
-63
lines changed

Cargo.lock

Lines changed: 42 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/image/01_image.js

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -238,28 +238,12 @@ function createImageBitmap(
238238
mimeType = 2;
239239
} else if (mimeTypeString === "image/gif") {
240240
mimeType = 3;
241-
// NOTE: Temporarily not supported due to build size concerns
242-
// https://github.com/denoland/deno/pull/25517#issuecomment-2626044644
243-
return PromiseReject(
244-
new DOMException(
245-
"The MIME type of source image is not supported currently",
246-
"InvalidStateError",
247-
),
248-
);
249241
} else if (mimeTypeString === "image/bmp") {
250242
mimeType = 4;
251243
} else if (mimeTypeString === "image/x-icon") {
252244
mimeType = 5;
253245
} else if (mimeTypeString === "image/webp") {
254246
mimeType = 6;
255-
// NOTE: Temporarily not supported due to build size concerns
256-
// https://github.com/denoland/deno/pull/25517#issuecomment-2626044644
257-
return PromiseReject(
258-
new DOMException(
259-
"The MIME type of source image is not supported currently",
260-
"InvalidStateError",
261-
),
262-
);
263247
} else {
264248
return PromiseReject(
265249
new DOMException(

ext/image/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ path = "lib.rs"
1717
bytemuck.workspace = true
1818
deno_core.workspace = true
1919
deno_error.workspace = true
20-
image = { workspace = true, features = ["png", "jpeg", "bmp", "ico"] }
20+
image = { workspace = true, features = ["png", "jpeg", "bmp", "ico", "webp", "gif"] }
2121
# NOTE: The qcms is a color space conversion crate which parses ICC profiles that used in Gecko,
2222
# however it supports only 8-bit color depth currently.
2323
# https://searchfox.org/mozilla-central/rev/f09e3f9603a08b5b51bf504846091579bc2ff531/gfx/qcms/src/transform.rs#130-137

ext/image/bitmap.rs

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ use deno_core::GarbageCollected;
99
use deno_core::JsBuffer;
1010
use deno_core::op2;
1111
use deno_core::webidl::WebIdlInterfaceConverter;
12-
// use image::codecs::webp::WebPDecoder;
1312
use image::DynamicImage;
1413
use image::ImageDecoder;
1514
use image::RgbaImage;
1615
use image::codecs::bmp::BmpDecoder;
17-
// use image::codecs::gif::GifDecoder;
16+
use image::codecs::gif::GifDecoder;
1817
use image::codecs::ico::IcoDecoder;
1918
use image::codecs::jpeg::JpegDecoder;
2019
use image::codecs::png::PngDecoder;
20+
use image::codecs::webp::WebPDecoder;
2121
use image::imageops::FilterType;
2222
use image::imageops::overlay;
2323
use image::metadata::Orientation;
@@ -125,20 +125,17 @@ fn decode_bitmap_data(
125125
)
126126
}
127127
MimeType::Gif => {
128-
// NOTE: Temporarily not supported due to build size concerns
129-
// https://github.com/denoland/deno/pull/25517#issuecomment-2626044644
130-
unimplemented!();
131128
// The GifDecoder decodes the first frame.
132-
// let mut decoder = GifDecoder::new(BufReader::new(Cursor::new(buf)))
133-
// .map_err(CanvasError::image_error_to_invalid_image)?;
134-
// let orientation = decoder.orientation()?;
135-
// let icc_profile = decoder.icc_profile()?;
136-
// (
137-
// DynamicImage::from_decoder(decoder)
138-
// .map_err(CanvasError::image_error_to_invalid_image)?,
139-
// orientation,
140-
// icc_profile,
141-
// )
129+
let mut decoder = GifDecoder::new(BufReader::new(Cursor::new(buf)))
130+
.map_err(ImageError::image_error_to_invalid_image)?;
131+
let orientation = decoder.orientation()?;
132+
let icc_profile = decoder.icc_profile()?;
133+
(
134+
DynamicImage::from_decoder(decoder)
135+
.map_err(ImageError::image_error_to_invalid_image)?,
136+
orientation,
137+
icc_profile,
138+
)
142139
}
143140
MimeType::Bmp => {
144141
let mut decoder = BmpDecoder::new(BufReader::new(Cursor::new(buf)))
@@ -165,21 +162,18 @@ fn decode_bitmap_data(
165162
)
166163
}
167164
MimeType::Webp => {
168-
// NOTE: Temporarily not supported due to build size concerns
169-
// https://github.com/denoland/deno/pull/25517#issuecomment-2626044644
170-
unimplemented!();
171165
// The WebPDecoder decodes the first frame.
172-
// let mut decoder =
173-
// WebPDecoder::new(BufReader::new(Cursor::new(buf)))
174-
// .map_err(CanvasError::image_error_to_invalid_image)?;
175-
// let orientation = decoder.orientation()?;
176-
// let icc_profile = decoder.icc_profile()?;
177-
// (
178-
// DynamicImage::from_decoder(decoder)
179-
// .map_err(CanvasError::image_error_to_invalid_image)?,
180-
// orientation,
181-
// icc_profile,
182-
// )
166+
let mut decoder =
167+
WebPDecoder::new(BufReader::new(Cursor::new(buf)))
168+
.map_err(ImageError::image_error_to_invalid_image)?;
169+
let orientation = decoder.orientation()?;
170+
let icc_profile = decoder.icc_profile()?;
171+
(
172+
DynamicImage::from_decoder(decoder)
173+
.map_err(ImageError::image_error_to_invalid_image)?,
174+
orientation,
175+
icc_profile,
176+
)
183177
}
184178
// This pattern is unreachable due to current block is already checked by the ImageBitmapSource above.
185179
MimeType::NoMatch => unreachable!(),

tests/unit/image_bitmap_test.ts

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -250,23 +250,19 @@ Deno.test("imageBitmapFromBlob", async (t) => {
250250
const imageData = new Blob(
251251
[await Deno.readFile(`${prefix}/1x1-red8.gif`)],
252252
);
253-
await assertRejects(() => createImageBitmap(imageData), DOMException);
254-
// TODO(Hajime-san): remove the comment out when the implementation is ready
255-
// const imageBitmap = await createImageBitmap(imageData);
253+
const imageBitmap = await createImageBitmap(imageData);
256254
// @ts-ignore: Deno[Deno.internal].core allowed
257255
// deno-fmt-ignore
258-
// assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 255]));
256+
assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 255]));
259257
});
260258
await t.step("8-bit webp", async () => {
261259
const imageData = new Blob(
262260
[await Deno.readFile(`${prefix}/1x1-red8.webp`)],
263261
);
264-
await assertRejects(() => createImageBitmap(imageData), DOMException);
265-
// TODO(Hajime-san): remove the comment out when the implementation is ready
266-
// const imageBitmap = await createImageBitmap(imageData);
262+
const imageBitmap = await createImageBitmap(imageData);
267263
// @ts-ignore: Deno[Deno.internal].core allowed
268264
// deno-fmt-ignore
269-
// assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 255]));
265+
assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 255]));
270266
});
271267
await t.step("8-bit ico", async () => {
272268
const imageData = new Blob(
@@ -324,12 +320,10 @@ Deno.test("imageBitmapFromBlobAnimatedImage", async (t) => {
324320
`${prefix}/1x1-3f-lossless-animated-semi-transparent.webp`,
325321
),
326322
]);
327-
await assertRejects(() => createImageBitmap(imageData), DOMException);
328-
// TODO(Hajime-san): remove the comment out when the implementation is ready
329-
// const imageBitmap = await createImageBitmap(imageData);
323+
const imageBitmap = await createImageBitmap(imageData);
330324
// @ts-ignore: Deno[Deno.internal].core allowed
331325
// deno-fmt-ignore
332-
// assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 127]));
326+
assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 127]));
333327
});
334328
await t.step("animated gif", async () => {
335329
// the chunk of animated gif is below (3 frames, 1x1, 8-bit, RGBA)
@@ -339,12 +333,10 @@ Deno.test("imageBitmapFromBlobAnimatedImage", async (t) => {
339333
const imageData = new Blob([
340334
await Deno.readFile(`${prefix}/1x1-3f-animated.gif`),
341335
]);
342-
await assertRejects(() => createImageBitmap(imageData), DOMException);
343-
// TODO(Hajime-san): remove the comment out when the implementation is ready
344-
// const imageBitmap = await createImageBitmap(imageData);
336+
const imageBitmap = await createImageBitmap(imageData);
345337
// @ts-ignore: Deno[Deno.internal].core allowed
346338
// deno-fmt-ignore
347-
// assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 255]));
339+
assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255, 0, 0, 255]));
348340
});
349341
});
350342

0 commit comments

Comments
 (0)