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

revert "support colorspaceConversion for ImageData"

This commit is contained in:
Hajime-san 2024-09-09 11:26:26 +09:00
parent 86f0000bb6
commit 75d26df537
7 changed files with 216 additions and 280 deletions

View file

@ -18,7 +18,6 @@ const {
ArrayPrototypeJoin,
} = primordials;
import {
_colorSpace,
_data,
_height,
_width,
@ -237,7 +236,7 @@ function createImageBitmap(
let width = 0;
let height = 0;
let mimeType = "";
let imageBitmapSource, buf, predefinedColorSpace;
let imageBitmapSource, buf;
if (isBlob) {
imageBitmapSource = imageBitmapSources[0];
buf = new Uint8Array(await image.arrayBuffer());
@ -248,7 +247,6 @@ function createImageBitmap(
height = image[_height];
imageBitmapSource = imageBitmapSources[1];
buf = new Uint8Array(TypedArrayPrototypeGetBuffer(image[_data]));
predefinedColorSpace = image[_colorSpace];
}
let sx;
@ -267,7 +265,6 @@ function createImageBitmap(
sh,
imageOrientation: options.imageOrientation ?? "from-image",
premultiplyAlpha: options.premultiplyAlpha ?? "default",
predefinedColorSpace: predefinedColorSpace ?? "srgb",
colorSpaceConversion: options.colorSpaceConversion ?? "default",
resizeWidth: options.resizeWidth,
resizeHeight: options.resizeHeight,

View file

@ -1,11 +0,0 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use serde::Deserialize;
#[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum PredefinedColorSpace {
Srgb,
#[serde(rename = "display-p3")]
DisplayP3,
}

View file

@ -239,181 +239,6 @@ pub(crate) fn unpremultiply_alpha(
}
}
// reference
// https://www.w3.org/TR/css-color-4/#color-conversion-code
fn srgb_to_linear<T: Primitive>(value: T) -> f32 {
if value.to_f32().unwrap() <= 0.04045 {
value.to_f32().unwrap() / 12.92
} else {
((value.to_f32().unwrap() + 0.055) / 1.055).powf(2.4)
}
}
// reference
// https://www.w3.org/TR/css-color-4/#color-conversion-code
fn linear_to_display_p3<T: Primitive>(value: T) -> f32 {
if value.to_f32().unwrap() <= 0.0031308 {
value.to_f32().unwrap() * 12.92
} else {
1.055 * value.to_f32().unwrap().powf(1.0 / 2.4) - 0.055
}
}
fn normalize_value_to_0_1<T: Primitive>(value: T) -> f32 {
value.to_f32().unwrap() / T::DEFAULT_MAX_VALUE.to_f32().unwrap()
}
fn unnormalize_value_from_0_1<T: Primitive>(value: f32) -> T {
NumCast::from(
(value.clamp(0.0, 1.0) * T::DEFAULT_MAX_VALUE.to_f32().unwrap()).round(),
)
.unwrap()
}
fn apply_conversion_matrix_srgb_to_display_p3<T: Primitive>(
r: T,
g: T,
b: T,
) -> (T, T, T) {
// normalize the value to 0.0 - 1.0
let (r, g, b) = (
normalize_value_to_0_1(r),
normalize_value_to_0_1(g),
normalize_value_to_0_1(b),
);
// sRGB -> Linear RGB
let (r, g, b) = (srgb_to_linear(r), srgb_to_linear(g), srgb_to_linear(b));
// Display-P3 (RGB) -> Display-P3 (XYZ)
//
// inv[ P3-D65 (D65) to XYZ ] * [ sRGB (D65) to XYZ ]
// http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
// https://fujiwaratko.sakura.ne.jp/infosci/colorspace/colorspace2_e.html
// [ sRGB (D65) to XYZ ]
#[rustfmt::skip]
let (m1x, m1y, m1z) = (
[0.4124564, 0.3575761, 0.1804375],
[0.2126729, 0.7151522, 0.0721750],
[0.0193339, 0.119_192, 0.9503041],
);
let (r, g, b) = (
r * m1x[0] + g * m1x[1] + b * m1x[2],
r * m1y[0] + g * m1y[1] + b * m1y[2],
r * m1z[0] + g * m1z[1] + b * m1z[2],
);
// inv[ P3-D65 (D65) to XYZ ]
#[rustfmt::skip]
let (m2x, m2y, m2z) = (
[ 2.493_497, -0.931_383_6, -0.402_710_8 ],
[ -0.829_489, 1.762_664_1, 0.023_624_687 ],
[ 0.035_845_83, -0.076_172_39, 0.956_884_5 ],
);
let (r, g, b) = (
r * m2x[0] + g * m2x[1] + b * m2x[2],
r * m2y[0] + g * m2y[1] + b * m2y[2],
r * m2z[0] + g * m2z[1] + b * m2z[2],
);
// This calculation is similar as above that it is a little faster, but less accurate.
// let r = 0.8225 * r + 0.1774 * g + 0.0000 * b;
// let g = 0.0332 * r + 0.9669 * g + 0.0000 * b;
// let b = 0.0171 * r + 0.0724 * g + 0.9108 * b;
// Display-P3 (Linear) -> Display-P3
let (r, g, b) = (
linear_to_display_p3(r),
linear_to_display_p3(g),
linear_to_display_p3(b),
);
// unnormalize the value from 0.0 - 1.0
(
unnormalize_value_from_0_1(r),
unnormalize_value_from_0_1(g),
unnormalize_value_from_0_1(b),
)
}
pub(crate) trait ColorSpaceConversion {
/// Display P3 Color Encoding (v 1.0)
/// https://www.color.org/chardata/rgb/DisplayP3.xalter
fn srgb_to_display_p3(&self) -> Self;
}
impl<T: Primitive> ColorSpaceConversion for Rgb<T> {
fn srgb_to_display_p3(&self) -> Self {
let (r, g, b) = (self.0[0], self.0[1], self.0[2]);
let (r, g, b) = apply_conversion_matrix_srgb_to_display_p3(r, g, b);
Rgb::<T>([r, g, b])
}
}
impl<T: Primitive> ColorSpaceConversion for Rgba<T> {
fn srgb_to_display_p3(&self) -> Self {
let (r, g, b, a) = (self.0[0], self.0[1], self.0[2], self.0[3]);
let (r, g, b) = apply_conversion_matrix_srgb_to_display_p3(r, g, b);
Rgba::<T>([r, g, b, a])
}
}
// make public if needed
fn process_srgb_to_display_p3<I, P, S>(image: &I) -> ImageBuffer<P, Vec<S>>
where
I: GenericImageView<Pixel = P>,
P: Pixel<Subpixel = S> + ColorSpaceConversion + 'static,
S: Primitive + 'static,
{
let (width, height) = image.dimensions();
let mut out = ImageBuffer::new(width, height);
for (x, y, pixel) in image.pixels() {
let pixel = pixel.srgb_to_display_p3();
out.put_pixel(x, y, pixel);
}
out
}
/// Convert the color space of the image from sRGB to Display-P3.
pub(crate) fn srgb_to_display_p3(
image: DynamicImage,
unmatch_color_handler: fn(
ColorType,
DynamicImage,
) -> Result<DynamicImage, AnyError>,
) -> Result<DynamicImage, AnyError> {
match image.color() {
// The conversion of the lumincance color types to the display-p3 color space is meaningless.
ColorType::L8 => Ok(DynamicImage::ImageLuma8(image.to_luma8())),
ColorType::L16 => Ok(DynamicImage::ImageLuma16(image.to_luma16())),
ColorType::La8 => Ok(DynamicImage::ImageLumaA8(image.to_luma_alpha8())),
ColorType::La16 => Ok(DynamicImage::ImageLumaA16(image.to_luma_alpha16())),
ColorType::Rgb8 => Ok(DynamicImage::ImageRgb8(process_srgb_to_display_p3(
&image.to_rgb8(),
))),
ColorType::Rgb16 => Ok(DynamicImage::ImageRgb16(
process_srgb_to_display_p3(&image.to_rgb16()),
)),
ColorType::Rgba8 => Ok(DynamicImage::ImageRgba8(
process_srgb_to_display_p3(&image.to_rgba8()),
)),
ColorType::Rgba16 => Ok(DynamicImage::ImageRgba16(
process_srgb_to_display_p3(&image.to_rgba16()),
)),
x => unmatch_color_handler(x, image),
}
}
pub(crate) trait SliceToPixel {
fn slice_to_pixel(pixel: &[u8]) -> Self;
}
@ -593,6 +418,185 @@ pub(crate) fn to_srgb_from_icc_profile(
}
}
// NOTE: The following code is not used in the current implementation,
// but it is left as a reference for future use about implementing CanvasRenderingContext2D.
// https://github.com/denoland/deno/issues/5701#issuecomment-1833304511
// // reference
// // https://www.w3.org/TR/css-color-4/#color-conversion-code
// fn srgb_to_linear<T: Primitive>(value: T) -> f32 {
// if value.to_f32().unwrap() <= 0.04045 {
// value.to_f32().unwrap() / 12.92
// } else {
// ((value.to_f32().unwrap() + 0.055) / 1.055).powf(2.4)
// }
// }
// // reference
// // https://www.w3.org/TR/css-color-4/#color-conversion-code
// fn linear_to_display_p3<T: Primitive>(value: T) -> f32 {
// if value.to_f32().unwrap() <= 0.0031308 {
// value.to_f32().unwrap() * 12.92
// } else {
// 1.055 * value.to_f32().unwrap().powf(1.0 / 2.4) - 0.055
// }
// }
// fn normalize_value_to_0_1<T: Primitive>(value: T) -> f32 {
// value.to_f32().unwrap() / T::DEFAULT_MAX_VALUE.to_f32().unwrap()
// }
// fn unnormalize_value_from_0_1<T: Primitive>(value: f32) -> T {
// NumCast::from(
// (value.clamp(0.0, 1.0) * T::DEFAULT_MAX_VALUE.to_f32().unwrap()).round(),
// )
// .unwrap()
// }
// fn apply_conversion_matrix_srgb_to_display_p3<T: Primitive>(
// r: T,
// g: T,
// b: T,
// ) -> (T, T, T) {
// // normalize the value to 0.0 - 1.0
// let (r, g, b) = (
// normalize_value_to_0_1(r),
// normalize_value_to_0_1(g),
// normalize_value_to_0_1(b),
// );
// // sRGB -> Linear RGB
// let (r, g, b) = (srgb_to_linear(r), srgb_to_linear(g), srgb_to_linear(b));
// // Display-P3 (RGB) -> Display-P3 (XYZ)
// //
// // inv[ P3-D65 (D65) to XYZ ] * [ sRGB (D65) to XYZ ]
// // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
// // https://fujiwaratko.sakura.ne.jp/infosci/colorspace/colorspace2_e.html
// // [ sRGB (D65) to XYZ ]
// #[rustfmt::skip]
// let (m1x, m1y, m1z) = (
// [0.4124564, 0.3575761, 0.1804375],
// [0.2126729, 0.7151522, 0.0721750],
// [0.0193339, 0.119_192, 0.9503041],
// );
// let (r, g, b) = (
// r * m1x[0] + g * m1x[1] + b * m1x[2],
// r * m1y[0] + g * m1y[1] + b * m1y[2],
// r * m1z[0] + g * m1z[1] + b * m1z[2],
// );
// // inv[ P3-D65 (D65) to XYZ ]
// #[rustfmt::skip]
// let (m2x, m2y, m2z) = (
// [ 2.493_497, -0.931_383_6, -0.402_710_8 ],
// [ -0.829_489, 1.762_664_1, 0.023_624_687 ],
// [ 0.035_845_83, -0.076_172_39, 0.956_884_5 ],
// );
// let (r, g, b) = (
// r * m2x[0] + g * m2x[1] + b * m2x[2],
// r * m2y[0] + g * m2y[1] + b * m2y[2],
// r * m2z[0] + g * m2z[1] + b * m2z[2],
// );
// // This calculation is similar as above that it is a little faster, but less accurate.
// // let r = 0.8225 * r + 0.1774 * g + 0.0000 * b;
// // let g = 0.0332 * r + 0.9669 * g + 0.0000 * b;
// // let b = 0.0171 * r + 0.0724 * g + 0.9108 * b;
// // Display-P3 (Linear) -> Display-P3
// let (r, g, b) = (
// linear_to_display_p3(r),
// linear_to_display_p3(g),
// linear_to_display_p3(b),
// );
// // unnormalize the value from 0.0 - 1.0
// (
// unnormalize_value_from_0_1(r),
// unnormalize_value_from_0_1(g),
// unnormalize_value_from_0_1(b),
// )
// }
// trait ColorSpaceConversion {
// /// Display P3 Color Encoding (v 1.0)
// /// https://www.color.org/chardata/rgb/DisplayP3.xalter
// fn srgb_to_display_p3(&self) -> Self;
// }
// impl<T: Primitive> ColorSpaceConversion for Rgb<T> {
// fn srgb_to_display_p3(&self) -> Self {
// let (r, g, b) = (self.0[0], self.0[1], self.0[2]);
// let (r, g, b) = apply_conversion_matrix_srgb_to_display_p3(r, g, b);
// Rgb::<T>([r, g, b])
// }
// }
// impl<T: Primitive> ColorSpaceConversion for Rgba<T> {
// fn srgb_to_display_p3(&self) -> Self {
// let (r, g, b, a) = (self.0[0], self.0[1], self.0[2], self.0[3]);
// let (r, g, b) = apply_conversion_matrix_srgb_to_display_p3(r, g, b);
// Rgba::<T>([r, g, b, a])
// }
// }
// // make public if needed
// fn process_srgb_to_display_p3<I, P, S>(image: &I) -> ImageBuffer<P, Vec<S>>
// where
// I: GenericImageView<Pixel = P>,
// P: Pixel<Subpixel = S> + ColorSpaceConversion + 'static,
// S: Primitive + 'static,
// {
// let (width, height) = image.dimensions();
// let mut out = ImageBuffer::new(width, height);
// for (x, y, pixel) in image.pixels() {
// let pixel = pixel.srgb_to_display_p3();
// out.put_pixel(x, y, pixel);
// }
// out
// }
// /// Convert the color space of the image from sRGB to Display-P3.
// fn srgb_to_display_p3(
// image: DynamicImage,
// unmatch_color_handler: fn(
// ColorType,
// DynamicImage,
// ) -> Result<DynamicImage, AnyError>,
// ) -> Result<DynamicImage, AnyError> {
// match image.color() {
// // The conversion of the lumincance color types to the display-p3 color space is meaningless.
// ColorType::L8 => Ok(DynamicImage::ImageLuma8(image.to_luma8())),
// ColorType::L16 => Ok(DynamicImage::ImageLuma16(image.to_luma16())),
// ColorType::La8 => Ok(DynamicImage::ImageLumaA8(image.to_luma_alpha8())),
// ColorType::La16 => Ok(DynamicImage::ImageLumaA16(image.to_luma_alpha16())),
// ColorType::Rgb8 => Ok(DynamicImage::ImageRgb8(process_srgb_to_display_p3(
// &image.to_rgb8(),
// ))),
// ColorType::Rgb16 => Ok(DynamicImage::ImageRgb16(
// process_srgb_to_display_p3(&image.to_rgb16()),
// )),
// ColorType::Rgba8 => Ok(DynamicImage::ImageRgba8(
// process_srgb_to_display_p3(&image.to_rgba8()),
// )),
// ColorType::Rgba16 => Ok(DynamicImage::ImageRgba16(
// process_srgb_to_display_p3(&image.to_rgba16()),
// )),
// x => unmatch_color_handler(x, image),
// }
// }
#[cfg(test)]
mod tests {
use super::*;
@ -616,27 +620,27 @@ mod tests {
assert_eq!(rgba, Rgba::<u8>([255, 0, 0, 127]));
}
#[test]
fn test_apply_conversion_matrix_srgb_to_display_p3() {
let (r, g, b) = apply_conversion_matrix_srgb_to_display_p3(255_u8, 0, 0);
assert_eq!(r, 234);
assert_eq!(g, 51);
assert_eq!(b, 35);
// #[test]
// fn test_apply_conversion_matrix_srgb_to_display_p3() {
// let (r, g, b) = apply_conversion_matrix_srgb_to_display_p3(255_u8, 0, 0);
// assert_eq!(r, 234);
// assert_eq!(g, 51);
// assert_eq!(b, 35);
let (r, g, b) = apply_conversion_matrix_srgb_to_display_p3(0_u8, 255, 0);
assert_eq!(r, 117);
assert_eq!(g, 251);
assert_eq!(b, 76);
// let (r, g, b) = apply_conversion_matrix_srgb_to_display_p3(0_u8, 255, 0);
// assert_eq!(r, 117);
// assert_eq!(g, 251);
// assert_eq!(b, 76);
let (r, g, b) = apply_conversion_matrix_srgb_to_display_p3(0_u8, 0, 255);
assert_eq!(r, 0);
assert_eq!(g, 0);
assert_eq!(b, 245);
// let (r, g, b) = apply_conversion_matrix_srgb_to_display_p3(0_u8, 0, 255);
// assert_eq!(r, 0);
// assert_eq!(g, 0);
// assert_eq!(b, 245);
let (r, g, b) =
apply_conversion_matrix_srgb_to_display_p3(255_u8, 255, 255);
assert_eq!(r, 255);
assert_eq!(g, 255);
assert_eq!(b, 255);
}
// let (r, g, b) =
// apply_conversion_matrix_srgb_to_display_p3(255_u8, 255, 255);
// assert_eq!(r, 255);
// assert_eq!(g, 255);
// assert_eq!(b, 255);
// }
}

View file

@ -3,7 +3,6 @@
use std::path::PathBuf;
pub mod error;
pub mod idl;
mod image_decoder;
mod image_ops;
mod op_create_image_bitmap;

View file

@ -26,11 +26,9 @@ use serde::Serialize;
use crate::error::image_error_message;
use crate::error::DOMExceptionInvalidStateError;
use crate::idl::PredefinedColorSpace;
use crate::image_decoder::ImageDecoderFromReader;
use crate::image_decoder::ImageDecoderFromReaderType;
use crate::image_ops::premultiply_alpha as process_premultiply_alpha;
use crate::image_ops::srgb_to_display_p3;
use crate::image_ops::to_srgb_from_icc_profile;
use crate::image_ops::unpremultiply_alpha;
@ -84,7 +82,6 @@ struct OpCreateImageBitmapArgs {
sh: Option<i32>,
image_orientation: ImageOrientation,
premultiply_alpha: PremultiplyAlpha,
predefined_color_space: PredefinedColorSpace,
color_space_conversion: ColorSpaceConversion,
resize_width: Option<u32>,
resize_height: Option<u32>,
@ -241,44 +238,22 @@ See: https://mimesniff.spec.whatwg.org/#image-type-pattern-matching-algorithm\n"
fn apply_color_space_conversion(
image: DynamicImage,
icc_profile: Option<Vec<u8>>,
image_bitmap_source: &ImageBitmapSource,
color_space_conversion: &ColorSpaceConversion,
predefined_color_space: &PredefinedColorSpace,
) -> Result<DynamicImage, AnyError> {
match color_space_conversion {
// return the decoded image as is.
ColorSpaceConversion::None => Ok(image),
ColorSpaceConversion::Default => {
match image_bitmap_source {
ImageBitmapSource::Blob => {
fn unmatch_color_handler(
x: ColorType,
_: DynamicImage,
) -> Result<DynamicImage, AnyError> {
Err(type_error(image_error_message(
"apply colorspaceConversion: default",
&format!("The color type {:?} is not supported.", x),
)))
}
to_srgb_from_icc_profile(image, icc_profile, unmatch_color_handler)
}
ImageBitmapSource::ImageData => match predefined_color_space {
// If the color space is sRGB, return the image as is.
PredefinedColorSpace::Srgb => Ok(image),
PredefinedColorSpace::DisplayP3 => {
fn unmatch_color_handler(
x: ColorType,
_: DynamicImage,
) -> Result<DynamicImage, AnyError> {
Err(type_error(image_error_message(
"apply colorspace: display-p3",
&format!("The color type {:?} is not supported.", x),
)))
}
srgb_to_display_p3(image, unmatch_color_handler)
}
},
fn unmatch_color_handler(
x: ColorType,
_: DynamicImage,
) -> Result<DynamicImage, AnyError> {
Err(type_error(image_error_message(
"apply colorspaceConversion: default",
&format!("The color type {:?} is not supported.", x),
)))
}
to_srgb_from_icc_profile(image, icc_profile, unmatch_color_handler)
}
}
}
@ -339,7 +314,6 @@ pub(super) fn op_create_image_bitmap(
sy,
image_orientation,
premultiply_alpha,
predefined_color_space,
color_space_conversion,
resize_width,
resize_height,
@ -355,7 +329,6 @@ pub(super) fn op_create_image_bitmap(
sh: args.sh,
image_orientation: args.image_orientation,
premultiply_alpha: args.premultiply_alpha,
predefined_color_space: args.predefined_color_space,
color_space_conversion: args.color_space_conversion,
resize_width: args.resize_width,
resize_height: args.resize_height,
@ -455,13 +428,8 @@ pub(super) fn op_create_image_bitmap(
};
// 9.
let image = apply_color_space_conversion(
image,
icc_profile,
&image_bitmap_source,
&color_space_conversion,
&predefined_color_space,
)?;
let image =
apply_color_space_conversion(image, icc_profile, &color_space_conversion)?;
// 10.
let image =

View file

@ -31,7 +31,6 @@ webidl.converters["ImageDataSettings"] = webidl.createDictionaryConverter(
const _data = Symbol("[[data]]");
const _width = Symbol("[[width]]");
const _height = Symbol("[[height]]");
const _colorSpace = Symbol("[[colorSpace]]");
class ImageData {
/** @type {number} */
[_width];
@ -40,7 +39,7 @@ class ImageData {
/** @type {Uint8Array} */
[_data];
/** @type {'srgb' | 'display-p3'} */
[_colorSpace];
#colorSpace;
constructor(arg0, arg1, arg2 = undefined, arg3 = undefined) {
webidl.requiredArguments(
@ -134,7 +133,7 @@ class ImageData {
this[_height] = sourceHeight;
}
this[_colorSpace] = settings.colorSpace ?? "srgb";
this.#colorSpace = settings.colorSpace ?? "srgb";
this[_width] = sourceWidth;
this[_data] = data;
return;
@ -172,7 +171,7 @@ class ImageData {
);
}
this[_colorSpace] = settings.colorSpace ?? "srgb";
this.#colorSpace = settings.colorSpace ?? "srgb";
this[_width] = sourceWidth;
this[_height] = sourceHeight;
this[_data] = new Uint8ClampedArray(sourceWidth * sourceHeight * 4);
@ -195,7 +194,7 @@ class ImageData {
get colorSpace() {
webidl.assertBranded(this, ImageDataPrototype);
return this[_colorSpace];
return this.#colorSpace;
}
[SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
@ -217,4 +216,4 @@ class ImageData {
const ImageDataPrototype = ImageData.prototype;
export { _colorSpace, _data, _height, _width, ImageData, ImageDataPrototype };
export { _data, _height, _width, ImageData, ImageDataPrototype };

View file

@ -299,26 +299,6 @@ Deno.test("imageBitmapFromBlobAnimatedImage", async (t) => {
});
});
Deno.test(async function imageBitmapImageDataColorspaceConversion() {
const imageData = new ImageData(
new Uint8ClampedArray([
255,
0,
0,
255,
]),
1,
1,
{
colorSpace: "display-p3",
},
);
const imageBitmap = await createImageBitmap(imageData);
// @ts-ignore: Deno[Deno.internal].core allowed
// deno-fmt-ignore
assertEquals(Deno[Deno.internal].getBitmapData(imageBitmap), new Uint8Array([234, 51, 35, 255]));
});
/**
* extract high bytes from Uint16Array
*/