0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-09 05:36:49 -04:00

basic support all image type pattern of unsigned 8bit when its blob except for JPG and animated image

- https://mimesniff.spec.whatwg.org/#matching-an-image-type-pattern
This commit is contained in:
Hajime-san 2024-08-20 20:06:52 +09:00
parent b1b72a8a49
commit 198c90a5c6
11 changed files with 112 additions and 43 deletions

24
Cargo.lock generated
View file

@ -3252,6 +3252,16 @@ dependencies = [
"polyval",
]
[[package]]
name = "gif"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
dependencies = [
"color_quant",
"weezl",
]
[[package]]
name = "gimli"
version = "0.29.0"
@ -3779,6 +3789,8 @@ dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"gif",
"jpeg-decoder",
"num-traits",
"png",
]
@ -3961,6 +3973,12 @@ dependencies = [
"libc",
]
[[package]]
name = "jpeg-decoder"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
[[package]]
name = "js-sys"
version = "0.3.69"
@ -8139,6 +8157,12 @@ dependencies = [
"rustls-pki-types",
]
[[package]]
name = "weezl"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
[[package]]
name = "wgpu-core"
version = "0.21.1"

View file

@ -1,7 +1,7 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { internals, primordials } from "ext:core/mod.js";
import { op_image_decode_png, op_image_process } from "ext:core/ops";
import { op_image_decode, op_image_process } from "ext:core/ops";
import * as webidl from "ext:deno_webidl/00_webidl.js";
import { DOMException } from "ext:deno_web/01_dom_exception.js";
import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
@ -204,7 +204,7 @@ function createImageBitmap(
if (options.resizeHeight === 0) {
return PromiseReject(
new DOMException(
"options.resizeWidth has to be greater than 0",
"options.resizeHeight has to be greater than 0",
"InvalidStateError",
),
);
@ -231,15 +231,12 @@ function createImageBitmap(
if (ObjectPrototypeIsPrototypeOf(BlobPrototype, image)) {
return (async () => {
const data = await image.arrayBuffer();
const mimetype = sniffImage(image.type);
if (mimetype !== "image/png") {
throw new DOMException(
`Unsupported type '${image.type}'`,
"InvalidStateError",
);
}
const { data: imageData, width, height } = op_image_decode_png(
const mimeType = sniffImage(image.type);
const { data: imageData, width, height } = op_image_decode(
new Uint8Array(data),
{
mimeType,
},
);
const processedImage = processImage(
imageData,

View file

@ -16,5 +16,5 @@ path = "lib.rs"
[dependencies]
deno_core.workspace = true
deno_webgpu.workspace = true
image = { version = "0.24.7", default-features = false, features = ["png"] }
image = { version = "0.24.7", default-features = false, features = ["png","jpeg","bmp","ico","webp","gif"] }
serde = { workspace = true, features = ["derive"] }

View file

@ -1,12 +1,10 @@
// 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::GenericImageView;
use image::Pixel;
use image::RgbaImage;
use serde::Deserialize;
@ -109,34 +107,56 @@ fn op_image_process(
}
#[derive(Debug, Serialize)]
struct DecodedPng {
struct DecodedImage {
data: ToJsBuffer,
width: u32,
height: u32,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
struct ImageDecodeOptions {
mime_type: String,
}
#[op2]
#[serde]
fn op_image_decode_png(#[buffer] buf: &[u8]) -> Result<DecodedPng, AnyError> {
let png = image::codecs::png::PngDecoder::new(buf)?;
fn op_image_decode(
#[buffer] buf: &[u8],
#[serde] options: ImageDecodeOptions,
) -> Result<DecodedImage, AnyError> {
let reader = std::io::BufReader::new(std::io::Cursor::new(buf));
let image = match &*options.mime_type {
"image/png" => {
let decoder = image::codecs::png::PngDecoder::new(reader)?;
image::DynamicImage::from_decoder(decoder)?
}
"image/jpeg" => {
let decoder = image::codecs::jpeg::JpegDecoder::new(reader)?;
image::DynamicImage::from_decoder(decoder)?
}
"image/gif" => {
let decoder = image::codecs::gif::GifDecoder::new(reader)?;
image::DynamicImage::from_decoder(decoder)?
}
"image/bmp" => {
let decoder = image::codecs::bmp::BmpDecoder::new(reader)?;
image::DynamicImage::from_decoder(decoder)?
}
"image/x-icon" => {
let decoder = image::codecs::ico::IcoDecoder::new(reader)?;
image::DynamicImage::from_decoder(decoder)?
}
"image/webp" => {
let decoder = image::codecs::webp::WebPDecoder::new(reader)?;
image::DynamicImage::from_decoder(decoder)?
}
_ => unreachable!(),
};
let (width, height) = image.dimensions();
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()
)));
}
// read_image will assert that the buffer is the correct size, so we need to fill it with zeros
let mut png_data = vec![0_u8; png.total_bytes() as usize];
png.read_image(&mut png_data)?;
Ok(DecodedPng {
data: png_data.into(),
Ok(DecodedImage {
data: image.into_bytes().into(),
width,
height,
})
@ -145,7 +165,7 @@ fn op_image_decode_png(#[buffer] buf: &[u8]) -> Result<DecodedPng, AnyError> {
deno_core::extension!(
deno_canvas,
deps = [deno_webidl, deno_web, deno_webgpu],
ops = [op_image_process, op_image_decode_png],
ops = [op_image_process, op_image_decode],
lazy_loaded_esm = ["01_image.js"],
);

BIN
tests/testdata/image/1x1-red8.bmp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 B

BIN
tests/testdata/image/1x1-red8.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 B

BIN
tests/testdata/image/1x1-red8.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 B

BIN
tests/testdata/image/1x1-red8.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 B

BIN
tests/testdata/image/1x1-red8.webp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 B

View file

@ -92,12 +92,40 @@ Deno.test(async function imageBitmapFlipY() {
});
Deno.test(async function imageBitmapFromBlob() {
const path = "tests/testdata/image/1x1-white.png";
const imageData = new Blob([await Deno.readFile(path)], {
type: "image/png",
});
const imageBitmap = await createImageBitmap(imageData);
// @ts-ignore: Deno[Deno.internal].core allowed
// deno-fmt-ignore
assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255,255,255,255]));
const prefix = "tests/testdata/image";
{
const imageData = new Blob([await Deno.readFile(`${prefix}/1x1-red8.png`)], { type: "image/png" });
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]));
}
{
const imageData = new Blob([await Deno.readFile(`${prefix}/1x1-red8.bmp`)], { type: "image/bmp" });
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]));
}
{
const imageData = new Blob([await Deno.readFile(`${prefix}/1x1-red8.gif`)], { type: "image/gif" });
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]));
}
{
const imageData = new Blob([await Deno.readFile(`${prefix}/1x1-red8.webp`)], { type: "image/webp" });
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]));
}
{
const imageData = new Blob([await Deno.readFile(`${prefix}/1x1-red8.ico`)], { type: "image/x-icon" });
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]));
}
});