diff --git a/ext/canvas/01_image.js b/ext/canvas/01_image.js index 3018e9e2ac..844650dcb0 100644 --- a/ext/canvas/01_image.js +++ b/ext/canvas/01_image.js @@ -13,30 +13,18 @@ const { SymbolFor, TypeError, TypedArrayPrototypeGetBuffer, - TypedArrayPrototypeGetLength, - TypedArrayPrototypeGetSymbolToStringTag, Uint8Array, - Uint8ClampedArray, MathCeil, PromiseResolve, PromiseReject, RangeError, } = primordials; - -webidl.converters["PredefinedColorSpace"] = webidl.createEnumConverter( - "PredefinedColorSpace", - [ - "srgb", - "display-p3", - ], -); - -webidl.converters["ImageDataSettings"] = webidl.createDictionaryConverter( - "ImageDataSettings", - [ - { key: "colorSpace", converter: webidl.converters["PredefinedColorSpace"] }, - ], -); +import { + _data, + _height, + _width, + ImageDataPrototype, +} from "ext:deno_web/16_image_data.js"; webidl.converters["ImageOrientation"] = webidl.createEnumConverter( "ImageOrientation", @@ -115,194 +103,6 @@ webidl.converters["ImageBitmapOptions"] = webidl.createDictionaryConverter( ], ); -const _data = Symbol("[[data]]"); -const _width = Symbol("[[width]]"); -const _height = Symbol("[[height]]"); -class ImageData { - /** @type {number} */ - [_width]; - /** @type {height} */ - [_height]; - /** @type {Uint8Array} */ - [_data]; - /** @type {'srgb' | 'display-p3'} */ - #colorSpace; - - constructor(arg0, arg1, arg2 = undefined, arg3 = undefined) { - webidl.requiredArguments( - arguments.length, - 2, - 'Failed to construct "ImageData"', - ); - this[webidl.brand] = webidl.brand; - - let sourceWidth; - let sourceHeight; - let data; - let settings; - const prefix = "Failed to construct 'ImageData'"; - - // Overload: new ImageData(data, sw [, sh [, settings ] ]) - if ( - arguments.length > 3 || - TypedArrayPrototypeGetSymbolToStringTag(arg0) === "Uint8ClampedArray" - ) { - data = webidl.converters.Uint8ClampedArray(arg0, prefix, "Argument 1"); - sourceWidth = webidl.converters["unsigned long"]( - arg1, - prefix, - "Argument 2", - ); - const dataLength = TypedArrayPrototypeGetLength(data); - - if (webidl.type(arg2) !== "Undefined") { - sourceHeight = webidl.converters["unsigned long"]( - arg2, - prefix, - "Argument 3", - ); - } - - settings = webidl.converters["ImageDataSettings"]( - arg3, - prefix, - "Argument 4", - ); - - if (dataLength === 0) { - throw new DOMException( - "Failed to construct 'ImageData': The input data has zero elements.", - "InvalidStateError", - ); - } - - if (dataLength % 4 !== 0) { - throw new DOMException( - "Failed to construct 'ImageData': The input data length is not a multiple of 4.", - "InvalidStateError", - ); - } - - if (sourceWidth < 1) { - throw new DOMException( - "Failed to construct 'ImageData': The source width is zero or not a number.", - "IndexSizeError", - ); - } - - if (webidl.type(sourceHeight) !== "Undefined" && sourceHeight < 1) { - throw new DOMException( - "Failed to construct 'ImageData': The source height is zero or not a number.", - "IndexSizeError", - ); - } - - if (dataLength / 4 % sourceWidth !== 0) { - throw new DOMException( - "Failed to construct 'ImageData': The input data length is not a multiple of (4 * width).", - "IndexSizeError", - ); - } - - if ( - webidl.type(sourceHeight) !== "Undefined" && - (sourceWidth * sourceHeight * 4 !== dataLength) - ) { - throw new DOMException( - "Failed to construct 'ImageData': The input data length is not equal to (4 * width * height).", - "IndexSizeError", - ); - } - - if (webidl.type(sourceHeight) === "Undefined") { - this[_height] = dataLength / 4 / sourceWidth; - } else { - this[_height] = sourceHeight; - } - - this.#colorSpace = settings.colorSpace ?? "srgb"; - this[_width] = sourceWidth; - this[_data] = data; - return; - } - - // Overload: new ImageData(sw, sh [, settings]) - sourceWidth = webidl.converters["unsigned long"]( - arg0, - prefix, - "Argument 1", - ); - sourceHeight = webidl.converters["unsigned long"]( - arg1, - prefix, - "Argument 2", - ); - - settings = webidl.converters["ImageDataSettings"]( - arg2, - prefix, - "Argument 3", - ); - - if (sourceWidth < 1) { - throw new DOMException( - "Failed to construct 'ImageData': The source width is zero or not a number.", - "IndexSizeError", - ); - } - - if (sourceHeight < 1) { - throw new DOMException( - "Failed to construct 'ImageData': The source height is zero or not a number.", - "IndexSizeError", - ); - } - - this.#colorSpace = settings.colorSpace ?? "srgb"; - this[_width] = sourceWidth; - this[_height] = sourceHeight; - this[_data] = new Uint8ClampedArray(sourceWidth * sourceHeight * 4); - } - - get width() { - webidl.assertBranded(this, ImageDataPrototype); - return this[_width]; - } - - get height() { - webidl.assertBranded(this, ImageDataPrototype); - return this[_height]; - } - - get data() { - webidl.assertBranded(this, ImageDataPrototype); - return this[_data]; - } - - get colorSpace() { - webidl.assertBranded(this, ImageDataPrototype); - return this.#colorSpace; - } - - [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { - return inspect( - createFilteredInspectProxy({ - object: this, - evaluate: ObjectPrototypeIsPrototypeOf(ImageDataPrototype, this), - keys: [ - "data", - "width", - "height", - "colorSpace", - ], - }), - inspectOptions, - ); - } -} - -const ImageDataPrototype = ImageData.prototype; - const _bitmapData = Symbol("[[bitmapData]]"); const _detached = Symbol("[[detached]]"); class ImageBitmap { @@ -549,4 +349,4 @@ function getBitmapData(imageBitmap) { internals.getBitmapData = getBitmapData; -export { _bitmapData, _detached, createImageBitmap, ImageBitmap, ImageData }; +export { _bitmapData, _detached, createImageBitmap, ImageBitmap }; diff --git a/ext/canvas/lib.deno_canvas.d.ts b/ext/canvas/lib.deno_canvas.d.ts index 28d57d583a..7498429c6e 100644 --- a/ext/canvas/lib.deno_canvas.d.ts +++ b/ext/canvas/lib.deno_canvas.d.ts @@ -5,34 +5,6 @@ /// /// -/** @category Web APIs */ -declare type PredefinedColorSpace = "srgb" | "display-p3"; - -/** @category Web APIs */ -declare interface ImageDataSettings { - readonly colorSpace?: PredefinedColorSpace; -} - -/** @category Web APIs */ -declare interface ImageData { - readonly colorSpace: PredefinedColorSpace; - readonly data: Uint8ClampedArray; - readonly height: number; - readonly width: number; -} - -/** @category Web APIs */ -declare var ImageData: { - prototype: ImageData; - new (sw: number, sh: number, settings?: ImageDataSettings): ImageData; - new ( - data: Uint8ClampedArray, - sw: number, - sh?: number, - settings?: ImageDataSettings, - ): ImageData; -}; - /** @category Web APIs */ declare type ColorSpaceConversion = "default" | "none"; diff --git a/ext/web/16_image_data.js b/ext/web/16_image_data.js new file mode 100644 index 0000000000..43f297a5c4 --- /dev/null +++ b/ext/web/16_image_data.js @@ -0,0 +1,219 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +import { primordials } from "ext:core/mod.js"; +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"; +const { + ObjectPrototypeIsPrototypeOf, + Symbol, + SymbolFor, + TypedArrayPrototypeGetLength, + TypedArrayPrototypeGetSymbolToStringTag, + Uint8ClampedArray, +} = primordials; + +webidl.converters["PredefinedColorSpace"] = webidl.createEnumConverter( + "PredefinedColorSpace", + [ + "srgb", + "display-p3", + ], +); + +webidl.converters["ImageDataSettings"] = webidl.createDictionaryConverter( + "ImageDataSettings", + [ + { key: "colorSpace", converter: webidl.converters["PredefinedColorSpace"] }, + ], +); + +const _data = Symbol("[[data]]"); +const _width = Symbol("[[width]]"); +const _height = Symbol("[[height]]"); +class ImageData { + /** @type {number} */ + [_width]; + /** @type {height} */ + [_height]; + /** @type {Uint8Array} */ + [_data]; + /** @type {'srgb' | 'display-p3'} */ + #colorSpace; + + constructor(arg0, arg1, arg2 = undefined, arg3 = undefined) { + webidl.requiredArguments( + arguments.length, + 2, + 'Failed to construct "ImageData"', + ); + this[webidl.brand] = webidl.brand; + + let sourceWidth; + let sourceHeight; + let data; + let settings; + const prefix = "Failed to construct 'ImageData'"; + + // Overload: new ImageData(data, sw [, sh [, settings ] ]) + if ( + arguments.length > 3 || + TypedArrayPrototypeGetSymbolToStringTag(arg0) === "Uint8ClampedArray" + ) { + data = webidl.converters.Uint8ClampedArray(arg0, prefix, "Argument 1"); + sourceWidth = webidl.converters["unsigned long"]( + arg1, + prefix, + "Argument 2", + ); + const dataLength = TypedArrayPrototypeGetLength(data); + + if (webidl.type(arg2) !== "Undefined") { + sourceHeight = webidl.converters["unsigned long"]( + arg2, + prefix, + "Argument 3", + ); + } + + settings = webidl.converters["ImageDataSettings"]( + arg3, + prefix, + "Argument 4", + ); + + if (dataLength === 0) { + throw new DOMException( + "Failed to construct 'ImageData': The input data has zero elements.", + "InvalidStateError", + ); + } + + if (dataLength % 4 !== 0) { + throw new DOMException( + "Failed to construct 'ImageData': The input data length is not a multiple of 4.", + "InvalidStateError", + ); + } + + if (sourceWidth < 1) { + throw new DOMException( + "Failed to construct 'ImageData': The source width is zero or not a number.", + "IndexSizeError", + ); + } + + if (webidl.type(sourceHeight) !== "Undefined" && sourceHeight < 1) { + throw new DOMException( + "Failed to construct 'ImageData': The source height is zero or not a number.", + "IndexSizeError", + ); + } + + if (dataLength / 4 % sourceWidth !== 0) { + throw new DOMException( + "Failed to construct 'ImageData': The input data length is not a multiple of (4 * width).", + "IndexSizeError", + ); + } + + if ( + webidl.type(sourceHeight) !== "Undefined" && + (sourceWidth * sourceHeight * 4 !== dataLength) + ) { + throw new DOMException( + "Failed to construct 'ImageData': The input data length is not equal to (4 * width * height).", + "IndexSizeError", + ); + } + + if (webidl.type(sourceHeight) === "Undefined") { + this[_height] = dataLength / 4 / sourceWidth; + } else { + this[_height] = sourceHeight; + } + + this.#colorSpace = settings.colorSpace ?? "srgb"; + this[_width] = sourceWidth; + this[_data] = data; + return; + } + + // Overload: new ImageData(sw, sh [, settings]) + sourceWidth = webidl.converters["unsigned long"]( + arg0, + prefix, + "Argument 1", + ); + sourceHeight = webidl.converters["unsigned long"]( + arg1, + prefix, + "Argument 2", + ); + + settings = webidl.converters["ImageDataSettings"]( + arg2, + prefix, + "Argument 3", + ); + + if (sourceWidth < 1) { + throw new DOMException( + "Failed to construct 'ImageData': The source width is zero or not a number.", + "IndexSizeError", + ); + } + + if (sourceHeight < 1) { + throw new DOMException( + "Failed to construct 'ImageData': The source height is zero or not a number.", + "IndexSizeError", + ); + } + + this.#colorSpace = settings.colorSpace ?? "srgb"; + this[_width] = sourceWidth; + this[_height] = sourceHeight; + this[_data] = new Uint8ClampedArray(sourceWidth * sourceHeight * 4); + } + + get width() { + webidl.assertBranded(this, ImageDataPrototype); + return this[_width]; + } + + get height() { + webidl.assertBranded(this, ImageDataPrototype); + return this[_height]; + } + + get data() { + webidl.assertBranded(this, ImageDataPrototype); + return this[_data]; + } + + get colorSpace() { + webidl.assertBranded(this, ImageDataPrototype); + return this.#colorSpace; + } + + [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { + return inspect( + createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf(ImageDataPrototype, this), + keys: [ + "data", + "width", + "height", + "colorSpace", + ], + }), + inspectOptions, + ); + } +} + +const ImageDataPrototype = ImageData.prototype; + +export { _data, _height, _width, ImageData, ImageDataPrototype }; diff --git a/ext/web/lib.deno_web.d.ts b/ext/web/lib.deno_web.d.ts index 55048e14e5..d28e33f58d 100644 --- a/ext/web/lib.deno_web.d.ts +++ b/ext/web/lib.deno_web.d.ts @@ -1237,3 +1237,31 @@ declare var DecompressionStream: { declare function reportError( error: any, ): void; + +/** @category Web APIs */ +declare type PredefinedColorSpace = "srgb" | "display-p3"; + +/** @category Web APIs */ +declare interface ImageDataSettings { + readonly colorSpace?: PredefinedColorSpace; +} + +/** @category Web APIs */ +declare interface ImageData { + readonly colorSpace: PredefinedColorSpace; + readonly data: Uint8ClampedArray; + readonly height: number; + readonly width: number; +} + +/** @category Web APIs */ +declare var ImageData: { + prototype: ImageData; + new (sw: number, sh: number, settings?: ImageDataSettings): ImageData; + new ( + data: Uint8ClampedArray, + sw: number, + sh?: number, + settings?: ImageDataSettings, + ): ImageData; +}; diff --git a/ext/web/lib.rs b/ext/web/lib.rs index 2792212ae3..acac78f562 100644 --- a/ext/web/lib.rs +++ b/ext/web/lib.rs @@ -117,6 +117,7 @@ deno_core::extension!(deno_web, "13_message_port.js", "14_compression.js", "15_performance.js", + "16_image_data.js", ], options = { blob_store: Arc, diff --git a/runtime/js/98_global_scope_shared.js b/runtime/js/98_global_scope_shared.js index d139a438b2..aba0f3710f 100644 --- a/runtime/js/98_global_scope_shared.js +++ b/runtime/js/98_global_scope_shared.js @@ -30,6 +30,7 @@ import * as messagePort from "ext:deno_web/13_message_port.js"; import * as webidl from "ext:deno_webidl/00_webidl.js"; import { DOMException } from "ext:deno_web/01_dom_exception.js"; import * as abortSignal from "ext:deno_web/03_abort_signal.js"; +import * as imageData from "ext:deno_web/16_image_data.js"; import { loadWebGPU } from "ext:deno_webgpu/00_init.js"; import * as webgpuSurface from "ext:deno_webgpu/02_surface.js"; import { unstableIds } from "ext:runtime/90_deno_ns.js"; @@ -60,10 +61,7 @@ const windowOrWorkerGlobalScope = { FileReader: core.propNonEnumerable(fileReader.FileReader), FormData: core.propNonEnumerable(formData.FormData), Headers: core.propNonEnumerable(headers.Headers), - ImageData: core.propNonEnumerableLazyLoaded( - (image) => image.ImageData, - loadImage, - ), + ImageData: core.propNonEnumerable(imageData.ImageData), ImageBitmap: core.propNonEnumerableLazyLoaded( (image) => image.ImageBitmap, loadImage, diff --git a/tools/core_import_map.json b/tools/core_import_map.json index cbae323edb..9e70f52f7f 100644 --- a/tools/core_import_map.json +++ b/tools/core_import_map.json @@ -223,6 +223,7 @@ "ext:deno_web/13_message_port.js": "../ext/web/13_message_port.js", "ext:deno_web/14_compression.js": "../ext/web/14_compression.js", "ext:deno_web/15_performance.js": "../ext/web/15_performance.js", + "ext:deno_web/16_image_data.js": "../ext/web/16_image_data.js", "ext:deno_webidl/00_webidl.js": "../ext/webidl/00_webidl.js", "ext:deno_websocket/01_websocket.js": "../ext/websocket/01_websocket.js", "ext:deno_websocket/02_websocketstream.js": "../ext/websocket/02_websocketstream.js",