mirror of
https://github.com/denoland/deno.git
synced 2025-03-09 21:57:40 -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:
parent
b1b72a8a49
commit
198c90a5c6
11 changed files with 112 additions and 43 deletions
24
Cargo.lock
generated
24
Cargo.lock
generated
|
@ -3252,6 +3252,16 @@ dependencies = [
|
||||||
"polyval",
|
"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]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.29.0"
|
version = "0.29.0"
|
||||||
|
@ -3779,6 +3789,8 @@ dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"color_quant",
|
"color_quant",
|
||||||
|
"gif",
|
||||||
|
"jpeg-decoder",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"png",
|
"png",
|
||||||
]
|
]
|
||||||
|
@ -3961,6 +3973,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jpeg-decoder"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.69"
|
version = "0.3.69"
|
||||||
|
@ -8139,6 +8157,12 @@ dependencies = [
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "weezl"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wgpu-core"
|
name = "wgpu-core"
|
||||||
version = "0.21.1"
|
version = "0.21.1"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
import { internals, primordials } from "ext:core/mod.js";
|
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 * as webidl from "ext:deno_webidl/00_webidl.js";
|
||||||
import { DOMException } from "ext:deno_web/01_dom_exception.js";
|
import { DOMException } from "ext:deno_web/01_dom_exception.js";
|
||||||
import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
|
import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
|
||||||
|
@ -204,7 +204,7 @@ function createImageBitmap(
|
||||||
if (options.resizeHeight === 0) {
|
if (options.resizeHeight === 0) {
|
||||||
return PromiseReject(
|
return PromiseReject(
|
||||||
new DOMException(
|
new DOMException(
|
||||||
"options.resizeWidth has to be greater than 0",
|
"options.resizeHeight has to be greater than 0",
|
||||||
"InvalidStateError",
|
"InvalidStateError",
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -231,15 +231,12 @@ function createImageBitmap(
|
||||||
if (ObjectPrototypeIsPrototypeOf(BlobPrototype, image)) {
|
if (ObjectPrototypeIsPrototypeOf(BlobPrototype, image)) {
|
||||||
return (async () => {
|
return (async () => {
|
||||||
const data = await image.arrayBuffer();
|
const data = await image.arrayBuffer();
|
||||||
const mimetype = sniffImage(image.type);
|
const mimeType = sniffImage(image.type);
|
||||||
if (mimetype !== "image/png") {
|
const { data: imageData, width, height } = op_image_decode(
|
||||||
throw new DOMException(
|
|
||||||
`Unsupported type '${image.type}'`,
|
|
||||||
"InvalidStateError",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const { data: imageData, width, height } = op_image_decode_png(
|
|
||||||
new Uint8Array(data),
|
new Uint8Array(data),
|
||||||
|
{
|
||||||
|
mimeType,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
const processedImage = processImage(
|
const processedImage = processImage(
|
||||||
imageData,
|
imageData,
|
||||||
|
|
|
@ -16,5 +16,5 @@ path = "lib.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
deno_core.workspace = true
|
deno_core.workspace = true
|
||||||
deno_webgpu.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"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
// 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::error::AnyError;
|
||||||
use deno_core::op2;
|
use deno_core::op2;
|
||||||
use deno_core::ToJsBuffer;
|
use deno_core::ToJsBuffer;
|
||||||
use image::imageops::FilterType;
|
use image::imageops::FilterType;
|
||||||
use image::ColorType;
|
use image::GenericImageView;
|
||||||
use image::ImageDecoder;
|
|
||||||
use image::Pixel;
|
use image::Pixel;
|
||||||
use image::RgbaImage;
|
use image::RgbaImage;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -109,34 +107,56 @@ fn op_image_process(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
struct DecodedPng {
|
struct DecodedImage {
|
||||||
data: ToJsBuffer,
|
data: ToJsBuffer,
|
||||||
width: u32,
|
width: u32,
|
||||||
height: u32,
|
height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[op2]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde]
|
#[serde(rename_all = "camelCase")]
|
||||||
fn op_image_decode_png(#[buffer] buf: &[u8]) -> Result<DecodedPng, AnyError> {
|
struct ImageDecodeOptions {
|
||||||
let png = image::codecs::png::PngDecoder::new(buf)?;
|
mime_type: String,
|
||||||
|
|
||||||
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
|
#[op2]
|
||||||
let mut png_data = vec![0_u8; png.total_bytes() as usize];
|
#[serde]
|
||||||
|
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();
|
||||||
|
|
||||||
png.read_image(&mut png_data)?;
|
Ok(DecodedImage {
|
||||||
|
data: image.into_bytes().into(),
|
||||||
Ok(DecodedPng {
|
|
||||||
data: png_data.into(),
|
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
})
|
})
|
||||||
|
@ -145,7 +165,7 @@ fn op_image_decode_png(#[buffer] buf: &[u8]) -> Result<DecodedPng, AnyError> {
|
||||||
deno_core::extension!(
|
deno_core::extension!(
|
||||||
deno_canvas,
|
deno_canvas,
|
||||||
deps = [deno_webidl, deno_web, deno_webgpu],
|
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"],
|
lazy_loaded_esm = ["01_image.js"],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
BIN
tests/testdata/image/1x1-red8.bmp
vendored
Normal file
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
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
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
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
BIN
tests/testdata/image/1x1-red8.webp
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 B |
BIN
tests/testdata/image/1x1-white.png
vendored
BIN
tests/testdata/image/1x1-white.png
vendored
Binary file not shown.
Before Width: | Height: | Size: 109 B |
|
@ -92,12 +92,40 @@ Deno.test(async function imageBitmapFlipY() {
|
||||||
});
|
});
|
||||||
|
|
||||||
Deno.test(async function imageBitmapFromBlob() {
|
Deno.test(async function imageBitmapFromBlob() {
|
||||||
const path = "tests/testdata/image/1x1-white.png";
|
const prefix = "tests/testdata/image";
|
||||||
const imageData = new Blob([await Deno.readFile(path)], {
|
{
|
||||||
type: "image/png",
|
const imageData = new Blob([await Deno.readFile(`${prefix}/1x1-red8.png`)], { type: "image/png" });
|
||||||
});
|
|
||||||
const imageBitmap = await createImageBitmap(imageData);
|
const imageBitmap = await createImageBitmap(imageData);
|
||||||
// @ts-ignore: Deno[Deno.internal].core allowed
|
// @ts-ignore: Deno[Deno.internal].core allowed
|
||||||
// deno-fmt-ignore
|
// deno-fmt-ignore
|
||||||
assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([255,255,255,255]));
|
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]));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue