1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-21 21:50:00 -05:00

support imageOrientation correctly

This commit is contained in:
Hajime-san 2024-10-20 20:17:05 +09:00
parent acc4119d74
commit 83c40c4f1a
4 changed files with 148 additions and 134 deletions

8
Cargo.lock generated
View file

@ -3825,9 +3825,9 @@ dependencies = [
[[package]] [[package]]
name = "image" name = "image"
version = "0.25.2" version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99314c8a2152b8ddb211f924cdae532d8c5e4c8bb54728e12fff1b0cd5963a10" checksum = "bc144d44a31d753b02ce64093d532f55ff8dc4ebf2ffb8a63c0dda691385acae"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"byteorder-lite", "byteorder-lite",
@ -3842,9 +3842,9 @@ dependencies = [
[[package]] [[package]]
name = "image-webp" name = "image-webp"
version = "0.1.3" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904" checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f"
dependencies = [ dependencies = [
"byteorder-lite", "byteorder-lite",
"quick-error 2.0.1", "quick-error 2.0.1",

View file

@ -18,7 +18,7 @@ bytemuck = "1.17.1"
deno_core.workspace = true deno_core.workspace = true
deno_terminal.workspace = true deno_terminal.workspace = true
deno_webgpu.workspace = true deno_webgpu.workspace = true
image = { version = "0.25.2", default-features = false, features = ["png", "jpeg", "bmp", "ico", "webp", "gif"] } image = { version = "0.25.4", default-features = false, features = ["png", "jpeg", "bmp", "ico", "webp", "gif"] }
# NOTE: The qcms is a color space conversion crate which parses ICC profiles that used in Gecko, # 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. # however it supports only 8-bit color depth currently.
# https://searchfox.org/mozilla-central/rev/f09e3f9603a08b5b51bf504846091579bc2ff531/gfx/qcms/src/transform.rs#130-137 # https://searchfox.org/mozilla-central/rev/f09e3f9603a08b5b51bf504846091579bc2ff531/gfx/qcms/src/transform.rs#130-137

View file

@ -16,7 +16,9 @@ use image::codecs::png::PngDecoder;
use image::codecs::webp::WebPDecoder; use image::codecs::webp::WebPDecoder;
use image::imageops::overlay; use image::imageops::overlay;
use image::imageops::FilterType; use image::imageops::FilterType;
use image::metadata::Orientation;
use image::DynamicImage; use image::DynamicImage;
use image::ImageDecoder;
use image::ImageError; use image::ImageError;
use image::RgbaImage; use image::RgbaImage;
@ -72,7 +74,8 @@ enum MimeType {
Webp, Webp,
} }
type DecodeBitmapDataReturn = (DynamicImage, u32, u32, Option<Vec<u8>>); type DecodeBitmapDataReturn =
(DynamicImage, u32, u32, Option<Orientation>, Option<Vec<u8>>);
fn decode_bitmap_data( fn decode_bitmap_data(
buf: &[u8], buf: &[u8],
@ -81,7 +84,8 @@ fn decode_bitmap_data(
image_bitmap_source: &ImageBitmapSource, image_bitmap_source: &ImageBitmapSource,
mime_type: MimeType, mime_type: MimeType,
) -> Result<DecodeBitmapDataReturn, AnyError> { ) -> Result<DecodeBitmapDataReturn, AnyError> {
let (image, width, height, icc_profile) = match image_bitmap_source { let (image, width, height, orientation, icc_profile) =
match image_bitmap_source {
ImageBitmapSource::Blob => { ImageBitmapSource::Blob => {
fn image_decoding_error(error: ImageError) -> AnyError { fn image_decoding_error(error: ImageError) -> AnyError {
DOMExceptionInvalidStateError::new(&image_error_message( DOMExceptionInvalidStateError::new(&image_error_message(
@ -90,7 +94,7 @@ fn decode_bitmap_data(
)) ))
.into() .into()
} }
let (image, icc_profile) = match mime_type { let (image, orientation, icc_profile) = match mime_type {
// Should we support the "image/apng" MIME type here? // Should we support the "image/apng" MIME type here?
MimeType::Png => { MimeType::Png => {
let mut decoder: PngDecoder<ImageDecoderFromReaderType> = let mut decoder: PngDecoder<ImageDecoderFromReaderType> =
@ -98,9 +102,11 @@ fn decode_bitmap_data(
BufReader::new(Cursor::new(buf)), BufReader::new(Cursor::new(buf)),
image_decoding_error, image_decoding_error,
)?; )?;
let orientation = decoder.orientation()?;
let icc_profile = decoder.get_icc_profile(); let icc_profile = decoder.get_icc_profile();
( (
decoder.to_intermediate_image(image_decoding_error)?, decoder.to_intermediate_image(image_decoding_error)?,
orientation,
icc_profile, icc_profile,
) )
} }
@ -110,9 +116,11 @@ fn decode_bitmap_data(
BufReader::new(Cursor::new(buf)), BufReader::new(Cursor::new(buf)),
image_decoding_error, image_decoding_error,
)?; )?;
let orientation = decoder.orientation()?;
let icc_profile = decoder.get_icc_profile(); let icc_profile = decoder.get_icc_profile();
( (
decoder.to_intermediate_image(image_decoding_error)?, decoder.to_intermediate_image(image_decoding_error)?,
orientation,
icc_profile, icc_profile,
) )
} }
@ -122,9 +130,11 @@ fn decode_bitmap_data(
BufReader::new(Cursor::new(buf)), BufReader::new(Cursor::new(buf)),
image_decoding_error, image_decoding_error,
)?; )?;
let orientation = decoder.orientation()?;
let icc_profile = decoder.get_icc_profile(); let icc_profile = decoder.get_icc_profile();
( (
decoder.to_intermediate_image(image_decoding_error)?, decoder.to_intermediate_image(image_decoding_error)?,
orientation,
icc_profile, icc_profile,
) )
} }
@ -134,9 +144,11 @@ fn decode_bitmap_data(
BufReader::new(Cursor::new(buf)), BufReader::new(Cursor::new(buf)),
image_decoding_error, image_decoding_error,
)?; )?;
let orientation = decoder.orientation()?;
let icc_profile = decoder.get_icc_profile(); let icc_profile = decoder.get_icc_profile();
( (
decoder.to_intermediate_image(image_decoding_error)?, decoder.to_intermediate_image(image_decoding_error)?,
orientation,
icc_profile, icc_profile,
) )
} }
@ -146,9 +158,11 @@ fn decode_bitmap_data(
BufReader::new(Cursor::new(buf)), BufReader::new(Cursor::new(buf)),
image_decoding_error, image_decoding_error,
)?; )?;
let orientation = decoder.orientation()?;
let icc_profile = decoder.get_icc_profile(); let icc_profile = decoder.get_icc_profile();
( (
decoder.to_intermediate_image(image_decoding_error)?, decoder.to_intermediate_image(image_decoding_error)?,
orientation,
icc_profile, icc_profile,
) )
} }
@ -158,9 +172,11 @@ fn decode_bitmap_data(
BufReader::new(Cursor::new(buf)), BufReader::new(Cursor::new(buf)),
image_decoding_error, image_decoding_error,
)?; )?;
let orientation = decoder.orientation()?;
let icc_profile = decoder.get_icc_profile(); let icc_profile = decoder.get_icc_profile();
( (
decoder.to_intermediate_image(image_decoding_error)?, decoder.to_intermediate_image(image_decoding_error)?,
orientation,
icc_profile, icc_profile,
) )
} }
@ -171,7 +187,7 @@ fn decode_bitmap_data(
let width = image.width(); let width = image.width();
let height = image.height(); let height = image.height();
(image, width, height, icc_profile) (image, width, height, Some(orientation), icc_profile)
} }
ImageBitmapSource::ImageData => { ImageBitmapSource::ImageData => {
// > 4.12.5.1.15 Pixel manipulation // > 4.12.5.1.15 Pixel manipulation
@ -188,11 +204,11 @@ fn decode_bitmap_data(
} }
}; };
(image, width, height, None) (image, width, height, None, None)
} }
}; };
Ok((image, width, height, icc_profile)) Ok((image, width, height, orientation, icc_profile))
} }
/// According to the spec, it's not clear how to handle the color space conversion. /// According to the spec, it's not clear how to handle the color space conversion.
@ -400,7 +416,7 @@ pub(super) fn op_create_image_bitmap(
); );
// 6. Switch on image: // 6. Switch on image:
let (image, width, height, icc_profile) = let (image, width, height, orientation, icc_profile) =
decode_bitmap_data(&buf, width, height, &image_bitmap_source, mime_type)?; decode_bitmap_data(&buf, width, height, &image_bitmap_source, mime_type)?;
// crop bitmap data // crop bitmap data
@ -480,27 +496,29 @@ pub(super) fn op_create_image_bitmap(
}; };
// should use resize_exact // should use resize_exact
// https://github.com/image-rs/image/issues/1220#issuecomment-632060015 // https://github.com/image-rs/image/issues/1220#issuecomment-632060015
let image = image.resize_exact(output_width, output_height, filter_type); let mut image = image.resize_exact(output_width, output_height, filter_type);
// 8. // 8.
// issues for imageOrientation let image = match image_bitmap_source {
// https://github.com/whatwg/html/issues/8085 ImageBitmapSource::Blob => {
// https://github.com/whatwg/html/issues/7210 // Note: According to browser behavior and wpt results, if Exif contains image orientation,
// https://github.com/whatwg/html/issues/8118 // it applies the rotation from it before following the value of imageOrientation.
let image = if image_orientation == ImageOrientation::FlipY { // This is not stated in the spec but in MDN currently.
image.flipv() // https://github.com/mdn/content/pull/34366
} else {
// TODO(Hajime-san): If the EXIF contains image orientation information, decode the image in the appropriate orientation. // SAFETY: The orientation is always Some if the image is from a Blob.
// The image create is expected to release this feature soon. let orientation = orientation.unwrap();
// https://github.com/image-rs/image/blob/bd0f7451a367de7ae3d898dcf1e96e9d0a1c4fa1/CHANGES.md#version-0253 DynamicImage::apply_orientation(&mut image, orientation);
// https://github.com/image-rs/image/issues/1958
// https://github.com/image-rs/image/pull/2299 match image_orientation {
// https://github.com/image-rs/image/pull/2328 ImageOrientation::FlipY => image.flipv(),
// if image_bitmap_source == ImageBitmapSource::Blob { ImageOrientation::FromImage => image,
// image.apply_orientation() }
// } else { }
image ImageBitmapSource::ImageData => match image_orientation {
// } ImageOrientation::FlipY => image.flipv(),
ImageOrientation::FromImage => image,
},
}; };
// 9. // 9.

View file

@ -1,6 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { assertEquals, assertNotEquals, assertRejects } from "./test_util.ts"; import { assertEquals, assertRejects } from "./test_util.ts";
const prefix = "tests/testdata/image"; const prefix = "tests/testdata/image";
@ -112,9 +112,7 @@ Deno.test("imageOrientation", async (t) => {
START, START,
END, END,
); );
// FIXME: When the implementation is fixed, fix this to assertEquals assertEquals(targetPixel, new Uint8Array([253, 0, 0]));
// However, in the case of jpg images, numerical errors may occur.
assertNotEquals(targetPixel, new Uint8Array([255, 1, 0]));
}); });
// reference: // reference:
// https://github.com/web-platform-tests/wpt/blob/a1f4bbf4c6e1a9a861a145a34cd097ea260b5a49/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation.html#L55 // https://github.com/web-platform-tests/wpt/blob/a1f4bbf4c6e1a9a861a145a34cd097ea260b5a49/html/canvas/element/manual/imagebitmap/createImageBitmap-exif-orientation.html#L55
@ -127,9 +125,7 @@ Deno.test("imageOrientation", async (t) => {
START, START,
END, END,
); );
// FIXME: When the implementation is fixed, fix this to assertEquals assertEquals(targetPixel, new Uint8Array([253, 127, 127]));
// However, in the case of jpg images, numerical errors may occur.
assertNotEquals(targetPixel, new Uint8Array([254, 128, 129]));
}); });
}); });