mirror of
https://github.com/denoland/deno.git
synced 2025-02-01 12:16:11 -05:00
perf: pass u8
to op
This commit is contained in:
parent
69addc8faf
commit
acc4119d74
2 changed files with 330 additions and 100 deletions
|
@ -233,38 +233,111 @@ function createImageBitmap(
|
|||
|
||||
// 4.
|
||||
return (async () => {
|
||||
//
|
||||
// For performance reasons, the arguments passed to op are represented as numbers that don't need to be serialized.
|
||||
//
|
||||
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
let mimeType = "";
|
||||
// If the of image doesn't have a MIME type, mark it as 0.
|
||||
let mimeType = 0;
|
||||
let imageBitmapSource, buf;
|
||||
if (isBlob) {
|
||||
imageBitmapSource = imageBitmapSources[0];
|
||||
imageBitmapSource = 0;
|
||||
buf = new Uint8Array(await image.arrayBuffer());
|
||||
mimeType = sniffImage(image.type);
|
||||
}
|
||||
if (isImageData) {
|
||||
const mimeTypeString = sniffImage(image.type);
|
||||
|
||||
if (mimeTypeString === "image/png") {
|
||||
mimeType = 1;
|
||||
} else if (mimeTypeString === "image/jpeg") {
|
||||
mimeType = 2;
|
||||
} else if (mimeTypeString === "image/gif") {
|
||||
mimeType = 3;
|
||||
} else if (mimeTypeString === "image/bmp") {
|
||||
mimeType = 4;
|
||||
} else if (mimeTypeString === "image/x-icon") {
|
||||
mimeType = 5;
|
||||
} else if (mimeTypeString === "image/webp") {
|
||||
mimeType = 6;
|
||||
} else if (mimeTypeString === "") {
|
||||
return PromiseReject(
|
||||
new DOMException(
|
||||
`The MIME type of source image is not specified.
|
||||
hint: When you want to get a "Blob" from "fetch", make sure to go through a file server that returns the appropriate content-type response header,
|
||||
and specify the URL to the file server like "await(await fetch('http://localhost:8000/sample.png').blob()".
|
||||
Alternatively, if you are reading a local file using 'Deno.readFile' etc.,
|
||||
set the appropriate MIME type like "new Blob([await Deno.readFile('sample.png')], { type: 'image/png' })".\n`,
|
||||
"InvalidStateError",
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return PromiseReject(
|
||||
new DOMException(
|
||||
`The the MIME type ${mimeTypeString} of source image is not a supported format.
|
||||
info: The following MIME types are supported:
|
||||
https://mimesniff.spec.whatwg.org/#image-type-pattern-matching-algorithm\n`,
|
||||
"InvalidStateError",
|
||||
),
|
||||
);
|
||||
}
|
||||
} else if (isImageData) {
|
||||
width = image[_width];
|
||||
height = image[_height];
|
||||
imageBitmapSource = imageBitmapSources[1];
|
||||
imageBitmapSource = 1;
|
||||
buf = new Uint8Array(TypedArrayPrototypeGetBuffer(image[_data]));
|
||||
}
|
||||
|
||||
const sx = typeof sxOrOptions === "number" ? sxOrOptions : undefined;
|
||||
// If those options are not provided, assign 0 to mean undefined(None).
|
||||
const _sx = typeof sxOrOptions === "number" ? sxOrOptions : 0;
|
||||
const _sy = sy ?? 0;
|
||||
const _sw = sw ?? 0;
|
||||
const _sh = sh ?? 0;
|
||||
|
||||
// If those options are not provided, assign 0 to mean undefined(None).
|
||||
const resizeWidth = options.resizeWidth ?? 0;
|
||||
const resizeHeight = options.resizeHeight ?? 0;
|
||||
|
||||
// If the imageOrientation option is set "from-image" or not set, assign 0.
|
||||
const imageOrientation = options.imageOrientation === "flipY" ? 1 : 0;
|
||||
|
||||
// If the premultiplyAlpha option is "default" or not set, assign 0.
|
||||
let premultiplyAlpha = 0;
|
||||
if (options.premultiplyAlpha === "premultiply") {
|
||||
premultiplyAlpha = 1;
|
||||
} else if (options.premultiplyAlpha === "none") {
|
||||
premultiplyAlpha = 2;
|
||||
}
|
||||
|
||||
// If the colorSpaceConversion option is "default" or not set, assign 0.
|
||||
const colorSpaceConversion = options.colorSpaceConversion === "none"
|
||||
? 1
|
||||
: 0;
|
||||
|
||||
// If the resizeQuality option is "low" or not set, assign 0.
|
||||
let resizeQuality = 0;
|
||||
if (options.resizeQuality === "pixelated") {
|
||||
resizeQuality = 1;
|
||||
} else if (options.resizeQuality === "medium") {
|
||||
resizeQuality = 2;
|
||||
} else if (options.resizeQuality === "high") {
|
||||
resizeQuality = 3;
|
||||
}
|
||||
|
||||
// TODO(Hajime-san): this should be real async
|
||||
const processedImage = op_create_image_bitmap(
|
||||
buf,
|
||||
width,
|
||||
height,
|
||||
sx,
|
||||
sy,
|
||||
sw,
|
||||
sh,
|
||||
options.imageOrientation ?? "from-image",
|
||||
options.premultiplyAlpha ?? "default",
|
||||
options.colorSpaceConversion ?? "default",
|
||||
options.resizeWidth,
|
||||
options.resizeHeight,
|
||||
options.resizeQuality ?? "low",
|
||||
_sx,
|
||||
_sy,
|
||||
_sw,
|
||||
_sh,
|
||||
imageOrientation,
|
||||
premultiplyAlpha,
|
||||
colorSpaceConversion,
|
||||
resizeWidth,
|
||||
resizeHeight,
|
||||
resizeQuality,
|
||||
imageBitmapSource,
|
||||
mimeType,
|
||||
);
|
||||
|
|
|
@ -8,8 +8,6 @@ use deno_core::error::AnyError;
|
|||
use deno_core::op2;
|
||||
use deno_core::JsBuffer;
|
||||
use deno_core::ToJsBuffer;
|
||||
use deno_terminal::colors::cyan;
|
||||
use deno_terminal::colors::yellow;
|
||||
use image::codecs::bmp::BmpDecoder;
|
||||
use image::codecs::gif::GifDecoder;
|
||||
use image::codecs::ico::IcoDecoder;
|
||||
|
@ -21,7 +19,6 @@ use image::imageops::FilterType;
|
|||
use image::DynamicImage;
|
||||
use image::ImageError;
|
||||
use image::RgbaImage;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::error::image_error_message;
|
||||
use crate::error::DOMExceptionInvalidStateError;
|
||||
|
@ -31,38 +28,32 @@ use crate::image_ops::premultiply_alpha as process_premultiply_alpha;
|
|||
use crate::image_ops::to_srgb_from_icc_profile;
|
||||
use crate::image_ops::unpremultiply_alpha;
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
// Follow the cases defined in the spec
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum ImageBitmapSource {
|
||||
Blob,
|
||||
ImageData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum ImageOrientation {
|
||||
FlipY,
|
||||
#[serde(rename = "from-image")]
|
||||
FromImage,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum PremultiplyAlpha {
|
||||
Default,
|
||||
Premultiply,
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum ColorSpaceConversion {
|
||||
Default,
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum ResizeQuality {
|
||||
Pixelated,
|
||||
Low,
|
||||
|
@ -70,6 +61,17 @@ enum ResizeQuality {
|
|||
High,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum MimeType {
|
||||
NoMatch,
|
||||
Png,
|
||||
Jpeg,
|
||||
Gif,
|
||||
Bmp,
|
||||
Ico,
|
||||
Webp,
|
||||
}
|
||||
|
||||
type DecodeBitmapDataReturn = (DynamicImage, u32, u32, Option<Vec<u8>>);
|
||||
|
||||
fn decode_bitmap_data(
|
||||
|
@ -77,7 +79,7 @@ fn decode_bitmap_data(
|
|||
width: u32,
|
||||
height: u32,
|
||||
image_bitmap_source: &ImageBitmapSource,
|
||||
mime_type: &str,
|
||||
mime_type: MimeType,
|
||||
) -> Result<DecodeBitmapDataReturn, AnyError> {
|
||||
let (image, width, height, icc_profile) = match image_bitmap_source {
|
||||
ImageBitmapSource::Blob => {
|
||||
|
@ -90,81 +92,80 @@ fn decode_bitmap_data(
|
|||
}
|
||||
let (image, icc_profile) = match mime_type {
|
||||
// Should we support the "image/apng" MIME type here?
|
||||
"image/png" => {
|
||||
MimeType::Png => {
|
||||
let mut decoder: PngDecoder<ImageDecoderFromReaderType> =
|
||||
ImageDecoderFromReader::to_decoder(BufReader::new(Cursor::new(
|
||||
buf,
|
||||
)), image_decoding_error)?;
|
||||
ImageDecoderFromReader::to_decoder(
|
||||
BufReader::new(Cursor::new(buf)),
|
||||
image_decoding_error,
|
||||
)?;
|
||||
let icc_profile = decoder.get_icc_profile();
|
||||
(decoder.to_intermediate_image(image_decoding_error)?, icc_profile)
|
||||
(
|
||||
decoder.to_intermediate_image(image_decoding_error)?,
|
||||
icc_profile,
|
||||
)
|
||||
}
|
||||
"image/jpeg" => {
|
||||
MimeType::Jpeg => {
|
||||
let mut decoder: JpegDecoder<ImageDecoderFromReaderType> =
|
||||
ImageDecoderFromReader::to_decoder(BufReader::new(Cursor::new(
|
||||
buf,
|
||||
)), image_decoding_error)?;
|
||||
ImageDecoderFromReader::to_decoder(
|
||||
BufReader::new(Cursor::new(buf)),
|
||||
image_decoding_error,
|
||||
)?;
|
||||
let icc_profile = decoder.get_icc_profile();
|
||||
(decoder.to_intermediate_image(image_decoding_error)?, icc_profile)
|
||||
(
|
||||
decoder.to_intermediate_image(image_decoding_error)?,
|
||||
icc_profile,
|
||||
)
|
||||
}
|
||||
"image/gif" => {
|
||||
MimeType::Gif => {
|
||||
let mut decoder: GifDecoder<ImageDecoderFromReaderType> =
|
||||
ImageDecoderFromReader::to_decoder(BufReader::new(Cursor::new(
|
||||
buf,
|
||||
)), image_decoding_error)?;
|
||||
ImageDecoderFromReader::to_decoder(
|
||||
BufReader::new(Cursor::new(buf)),
|
||||
image_decoding_error,
|
||||
)?;
|
||||
let icc_profile = decoder.get_icc_profile();
|
||||
(decoder.to_intermediate_image(image_decoding_error)?, icc_profile)
|
||||
(
|
||||
decoder.to_intermediate_image(image_decoding_error)?,
|
||||
icc_profile,
|
||||
)
|
||||
}
|
||||
"image/bmp" => {
|
||||
MimeType::Bmp => {
|
||||
let mut decoder: BmpDecoder<ImageDecoderFromReaderType> =
|
||||
ImageDecoderFromReader::to_decoder(BufReader::new(Cursor::new(
|
||||
buf,
|
||||
)), image_decoding_error)?;
|
||||
ImageDecoderFromReader::to_decoder(
|
||||
BufReader::new(Cursor::new(buf)),
|
||||
image_decoding_error,
|
||||
)?;
|
||||
let icc_profile = decoder.get_icc_profile();
|
||||
(decoder.to_intermediate_image(image_decoding_error)?, icc_profile)
|
||||
(
|
||||
decoder.to_intermediate_image(image_decoding_error)?,
|
||||
icc_profile,
|
||||
)
|
||||
}
|
||||
"image/x-icon" => {
|
||||
MimeType::Ico => {
|
||||
let mut decoder: IcoDecoder<ImageDecoderFromReaderType> =
|
||||
ImageDecoderFromReader::to_decoder(BufReader::new(Cursor::new(
|
||||
buf,
|
||||
)), image_decoding_error)?;
|
||||
ImageDecoderFromReader::to_decoder(
|
||||
BufReader::new(Cursor::new(buf)),
|
||||
image_decoding_error,
|
||||
)?;
|
||||
let icc_profile = decoder.get_icc_profile();
|
||||
(decoder.to_intermediate_image(image_decoding_error)?, icc_profile)
|
||||
(
|
||||
decoder.to_intermediate_image(image_decoding_error)?,
|
||||
icc_profile,
|
||||
)
|
||||
}
|
||||
"image/webp" => {
|
||||
MimeType::Webp => {
|
||||
let mut decoder: WebPDecoder<ImageDecoderFromReaderType> =
|
||||
ImageDecoderFromReader::to_decoder(BufReader::new(Cursor::new(
|
||||
buf,
|
||||
)), image_decoding_error)?;
|
||||
ImageDecoderFromReader::to_decoder(
|
||||
BufReader::new(Cursor::new(buf)),
|
||||
image_decoding_error,
|
||||
)?;
|
||||
let icc_profile = decoder.get_icc_profile();
|
||||
(decoder.to_intermediate_image(image_decoding_error)?, icc_profile)
|
||||
}
|
||||
"" => {
|
||||
return Err(
|
||||
DOMExceptionInvalidStateError::new(
|
||||
&format!("The MIME type of source image is not specified.
|
||||
{} When you want to get a `Blob` from `fetch`, make sure to go through a file server that returns the appropriate content-type response header,
|
||||
and specify the URL to the file server like {}.
|
||||
Alternatively, if you are reading a local file using `Deno.readFile` etc.,
|
||||
set the appropriate MIME type like {}.\n",
|
||||
cyan("hint:"),
|
||||
cyan("await(await fetch('http://localhost:8000/sample.png').blob()"),
|
||||
cyan("new Blob([await Deno.readFile('sample.png')], { type: 'image/png' })")
|
||||
)).into(),
|
||||
)
|
||||
}
|
||||
// return an error if the MIME type is not supported in the variable list of ImageTypePatternTable below
|
||||
// ext/web/01_mimesniff.js
|
||||
x => {
|
||||
return Err(
|
||||
DOMExceptionInvalidStateError::new(
|
||||
&format!("The the MIME type {} of source image is not a supported format.
|
||||
{} The following MIME types are supported:
|
||||
https://mimesniff.spec.whatwg.org/#image-type-pattern-matching-algorithm\n",
|
||||
x,
|
||||
yellow("info:")
|
||||
)).into()
|
||||
(
|
||||
decoder.to_intermediate_image(image_decoding_error)?,
|
||||
icc_profile,
|
||||
)
|
||||
}
|
||||
// This pattern is unreachable due to current block is already checked by the ImageBitmapSource above.
|
||||
MimeType::NoMatch => unreachable!(),
|
||||
};
|
||||
|
||||
let width = image.width();
|
||||
|
@ -249,6 +250,107 @@ fn apply_premultiply_alpha(
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct ParsedArgs {
|
||||
resize_width: Option<u32>,
|
||||
resize_height: Option<u32>,
|
||||
sx: Option<i32>,
|
||||
sy: Option<i32>,
|
||||
sw: Option<i32>,
|
||||
sh: Option<i32>,
|
||||
image_orientation: ImageOrientation,
|
||||
premultiply_alpha: PremultiplyAlpha,
|
||||
color_space_conversion: ColorSpaceConversion,
|
||||
resize_quality: ResizeQuality,
|
||||
image_bitmap_source: ImageBitmapSource,
|
||||
mime_type: MimeType,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn parse_args(
|
||||
sx: i32,
|
||||
sy: i32,
|
||||
sw: i32,
|
||||
sh: i32,
|
||||
image_orientation: u8,
|
||||
premultiply_alpha: u8,
|
||||
color_space_conversion: u8,
|
||||
resize_width: u32,
|
||||
resize_height: u32,
|
||||
resize_quality: u8,
|
||||
image_bitmap_source: u8,
|
||||
mime_type: u8,
|
||||
) -> ParsedArgs {
|
||||
let resize_width = if resize_width == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(resize_width)
|
||||
};
|
||||
let resize_height = if resize_height == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(resize_height)
|
||||
};
|
||||
let sx = if sx == 0 { None } else { Some(sx) };
|
||||
let sy = if sy == 0 { None } else { Some(sy) };
|
||||
let sw = if sw == 0 { None } else { Some(sw) };
|
||||
let sh = if sh == 0 { None } else { Some(sh) };
|
||||
|
||||
// Their unreachable wildcard patterns are validated in JavaScript-side.
|
||||
let image_orientation = match image_orientation {
|
||||
0 => ImageOrientation::FromImage,
|
||||
1 => ImageOrientation::FlipY,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let premultiply_alpha = match premultiply_alpha {
|
||||
0 => PremultiplyAlpha::Default,
|
||||
1 => PremultiplyAlpha::Premultiply,
|
||||
2 => PremultiplyAlpha::None,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let color_space_conversion = match color_space_conversion {
|
||||
0 => ColorSpaceConversion::Default,
|
||||
1 => ColorSpaceConversion::None,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let resize_quality = match resize_quality {
|
||||
0 => ResizeQuality::Low,
|
||||
1 => ResizeQuality::Pixelated,
|
||||
2 => ResizeQuality::Medium,
|
||||
3 => ResizeQuality::High,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let image_bitmap_source = match image_bitmap_source {
|
||||
0 => ImageBitmapSource::Blob,
|
||||
1 => ImageBitmapSource::ImageData,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let mime_type = match mime_type {
|
||||
0 => MimeType::NoMatch,
|
||||
1 => MimeType::Png,
|
||||
2 => MimeType::Jpeg,
|
||||
3 => MimeType::Gif,
|
||||
4 => MimeType::Bmp,
|
||||
5 => MimeType::Ico,
|
||||
6 => MimeType::Webp,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
ParsedArgs {
|
||||
resize_width,
|
||||
resize_height,
|
||||
sx,
|
||||
sy,
|
||||
sw,
|
||||
sh,
|
||||
image_orientation,
|
||||
premultiply_alpha,
|
||||
color_space_conversion,
|
||||
resize_quality,
|
||||
image_bitmap_source,
|
||||
mime_type,
|
||||
}
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -256,19 +358,47 @@ pub(super) fn op_create_image_bitmap(
|
|||
#[buffer] buf: JsBuffer,
|
||||
width: u32,
|
||||
height: u32,
|
||||
sx: Option<i32>,
|
||||
sy: Option<i32>,
|
||||
sw: Option<i32>,
|
||||
sh: Option<i32>,
|
||||
#[serde] image_orientation: ImageOrientation,
|
||||
#[serde] premultiply_alpha: PremultiplyAlpha,
|
||||
#[serde] color_space_conversion: ColorSpaceConversion,
|
||||
resize_width: Option<u32>,
|
||||
resize_height: Option<u32>,
|
||||
#[serde] resize_quality: ResizeQuality,
|
||||
#[serde] image_bitmap_source: ImageBitmapSource,
|
||||
#[string] mime_type: &str,
|
||||
sx: i32,
|
||||
sy: i32,
|
||||
sw: i32,
|
||||
sh: i32,
|
||||
image_orientation: u8,
|
||||
premultiply_alpha: u8,
|
||||
color_space_conversion: u8,
|
||||
resize_width: u32,
|
||||
resize_height: u32,
|
||||
resize_quality: u8,
|
||||
image_bitmap_source: u8,
|
||||
mime_type: u8,
|
||||
) -> Result<(ToJsBuffer, u32, u32), AnyError> {
|
||||
let ParsedArgs {
|
||||
resize_width,
|
||||
resize_height,
|
||||
sx,
|
||||
sy,
|
||||
sw,
|
||||
sh,
|
||||
image_orientation,
|
||||
premultiply_alpha,
|
||||
color_space_conversion,
|
||||
resize_quality,
|
||||
image_bitmap_source,
|
||||
mime_type,
|
||||
} = parse_args(
|
||||
sx,
|
||||
sy,
|
||||
sw,
|
||||
sh,
|
||||
image_orientation,
|
||||
premultiply_alpha,
|
||||
color_space_conversion,
|
||||
resize_width,
|
||||
resize_height,
|
||||
resize_quality,
|
||||
image_bitmap_source,
|
||||
mime_type,
|
||||
);
|
||||
|
||||
// 6. Switch on image:
|
||||
let (image, width, height, icc_profile) =
|
||||
decode_bitmap_data(&buf, width, height, &image_bitmap_source, mime_type)?;
|
||||
|
@ -383,3 +513,30 @@ pub(super) fn op_create_image_bitmap(
|
|||
|
||||
Ok((image.into_bytes().into(), output_width, output_height))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_args() {
|
||||
let parsed_args = parse_args(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
||||
assert_eq!(
|
||||
parsed_args,
|
||||
ParsedArgs {
|
||||
resize_width: None,
|
||||
resize_height: None,
|
||||
sx: None,
|
||||
sy: None,
|
||||
sw: None,
|
||||
sh: None,
|
||||
image_orientation: ImageOrientation::FromImage,
|
||||
premultiply_alpha: PremultiplyAlpha::Default,
|
||||
color_space_conversion: ColorSpaceConversion::Default,
|
||||
resize_quality: ResizeQuality::Low,
|
||||
image_bitmap_source: ImageBitmapSource::Blob,
|
||||
mime_type: MimeType::NoMatch,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue