mirror of
https://github.com/denoland/deno.git
synced 2025-03-09 13:49:37 -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",
|
||||
]
|
||||
|
||||
[[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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"] }
|
||||
|
|
|
@ -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
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() {
|
||||
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]));
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue