hdr-canvas 0.0.13 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/hdr-canvas.ts CHANGED
@@ -1,28 +1,72 @@
1
- import { Uint16Image } from "./Uint16Image";
1
+ import { HDRImage } from "./HDRImage";
2
+ import { getBrowserVersion } from "./browser-util";
2
3
 
3
- import type { HDRHTMLCanvasElement } from "./types/HDRCanvas.d.ts";
4
+ import type { HDRHTMLCanvasElement, CanvasRenderingContext2DHDRSettings, CanvasRenderingContext2DHDR } from "./types/HDRCanvas.d.ts";
4
5
 
5
- const hdr_options = { colorSpace: Uint16Image.DEFAULT_COLORSPACE, pixelFormat: "float16" };
6
+ /**
7
+ * Gets a `CanvasRenderingContext2DSettings` object configured for HDR.
8
+ * This function detects the browser version to determine the appropriate `colorType` for HDR support.
9
+ *
10
+ * @returns {CanvasRenderingContext2DHDRSettings} An options object for creating an HDR canvas context.
11
+ */
12
+ export function getHdrOptions(): CanvasRenderingContext2DHDRSettings {
13
+ const hdrOptions: CanvasRenderingContext2DHDRSettings = { colorSpace: HDRImage.DEFAULT_COLORSPACE };
14
+ const browserMajorVersion = getBrowserVersion();
15
+ if (browserMajorVersion == null) {
16
+ console.warn(`Unsupported / untested browser (${navigator.userAgent}) detected - using more modern defaults`);
17
+ hdrOptions["colorType"] = "float16";
18
+ } else {
19
+ if (browserMajorVersion < 134) {
20
+ console.warn("Older Chrome / chromium based browser detected, using older `pixelFormat`");
21
+ } else {
22
+ hdrOptions["pixelFormat"] = "float16";
23
+ }
24
+ }
25
+
26
+ return hdrOptions;
27
+ }
6
28
 
7
- export function initHDRCanvas(canvas: HDRHTMLCanvasElement): RenderingContext | null {
29
+ /**
30
+ * Initializes a given `HTMLCanvasElement` for HDR and returns its 2D rendering context.
31
+ * It first configures the canvas for high dynamic range and then gets the 2D context with HDR options.
32
+ *
33
+ * @param {HDRHTMLCanvasElement} canvas - The canvas element to initialize.
34
+ * @returns {CanvasRenderingContext2DHDR | null} The 2D rendering context, or `null` if the context cannot be created. Can be cast down to `CanvasRenderingContext2D` or `RenderingContext`
35
+ */
36
+ export function initHDRCanvas(canvas: HDRHTMLCanvasElement): CanvasRenderingContext2DHDR | null {
8
37
  canvas.configureHighDynamicRange({ mode: "extended" });
9
- const ctx = canvas.getContext("2d", hdr_options);
10
- return ctx;
38
+ const ctx = canvas.getContext("2d", getHdrOptions());
39
+ return ctx as CanvasRenderingContext2DHDR;
11
40
  }
12
41
 
42
+ /**
43
+ * Patches the `getContext` method of `HTMLCanvasElement` to default to HDR settings.
44
+ * This allows all subsequent calls to `getContext('2d')` to be HDR-enabled without
45
+ * explicitly passing the HDR options.
46
+ *
47
+ * @remarks
48
+ * This function modifies the global `HTMLCanvasElement.prototype` and should be used with caution.
49
+ */
13
50
  /* eslint-disable @typescript-eslint/no-explicit-any */
14
51
  export function defaultGetContextHDR() {
15
52
  (HTMLCanvasElement.prototype as HDRHTMLCanvasElement)._getContext = HTMLCanvasElement.prototype.getContext;
16
53
  (HTMLCanvasElement.prototype as any).getContext = function (type: string, options: object) {
17
54
  if (options !== undefined) {
18
- options = Object.assign({}, options, hdr_options);
55
+ options = Object.assign({}, options, getHdrOptions());
19
56
  } else {
20
- options = hdr_options;
57
+ options = getHdrOptions();
21
58
  }
22
59
  return (this as HDRHTMLCanvasElement)._getContext(type, options);
23
60
  };
24
61
  }
25
62
 
63
+ /**
64
+ * Resets the `getContext` method of `HTMLCanvasElement` to its original behavior.
65
+ * This reverses the changes made by `defaultGetContextHDR`.
66
+ *
67
+ * @remarks
68
+ * This function only works if `defaultGetContextHDR` has been previously called.
69
+ */
26
70
  /* eslint-disable @typescript-eslint/no-explicit-any */
27
71
  export function resetGetContext() {
28
72
  if (typeof (HTMLCanvasElement.prototype as HDRHTMLCanvasElement)._getContext === "function") {
package/src/hdr-check.ts CHANGED
@@ -1,3 +1,26 @@
1
+ import { getHdrOptions } from "./hdr-canvas";
2
+
3
+ // See https://developer.mozilla.org/en-US/docs/Web/CSS/@media/video-dynamic-range
4
+
5
+ /** Check if HDR video is supported using a CSS Media Query.
6
+ * @returns {boolean}
7
+ */
8
+ export function checkHDRVideo(): boolean {
9
+ try {
10
+ const dynamicRangeVideoHighMQ: boolean = window.matchMedia("(video-dynamic-range: high)").matches;
11
+ if (dynamicRangeVideoHighMQ) {
12
+ return true;
13
+ }
14
+ return false;
15
+ } catch {
16
+ return false;
17
+ }
18
+ }
19
+
20
+ /** Check if HDR content (like images) are supported using two CSS Media Queries:
21
+ * One for HDR content itself and another one for the rec2020 colorspace
22
+ * @returns {boolean}
23
+ */
1
24
  export function checkHDR(): boolean {
2
25
  try {
3
26
  const bitsPerChannel: number = screen.colorDepth / 3;
@@ -18,39 +41,34 @@ export function checkHDR(): boolean {
18
41
  }
19
42
  return false;
20
43
  } catch (e) {
21
- /* eslint-disable no-console */
22
- console.error("Bad window.screen test", e);
23
- /* eslint-enable */
44
+ console.error("Exception during check for HDR", e);
24
45
  return false;
25
46
  }
26
47
  }
27
48
 
49
+ /** Check if HDR content is supported in a {HTMLCanvasElement} by tying to get a HDR enabled {CanvasRenderingContext2D}
50
+ * @returns {boolean}
51
+ */
28
52
  export function checkHDRCanvas(): boolean {
29
- const colorSpace: string = "rec2100-pq";
30
-
31
53
  try {
32
54
  const canvas: HTMLCanvasElement = document.createElement("canvas");
33
55
  if (!canvas.getContext) {
34
56
  return false;
35
57
  }
36
- const ctx: CanvasRenderingContext2D | null = <CanvasRenderingContext2D>canvas.getContext("2d", {
37
- colorSpace: colorSpace,
38
- pixelFormat: "float16"
39
- });
58
+
59
+ const options = getHdrOptions();
60
+ const ctx: CanvasRenderingContext2D | null = <CanvasRenderingContext2D>canvas.getContext("2d", options);
40
61
  //canvas.drawingBufferColorSpace = colorSpace;
41
62
  //canvas.unpackColorSpace = colorSpace;
42
63
  if (ctx === null) {
43
64
  return false;
44
65
  }
45
66
  return true;
46
- /* eslint-disable no-console, @typescript-eslint/no-unused-vars */
47
- } catch (e) {
48
- //console.error("Bad canvas ColorSpace test", e);
67
+ } catch {
49
68
  console.error(
50
69
  "Bad canvas ColorSpace test - make sure that the Chromium browser flag 'enable-experimental-web-platform-features' has been enabled"
51
70
  );
52
71
 
53
72
  return false;
54
73
  }
55
- /* eslint-enable */
56
74
  }
package/src/index.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export { Uint16Image } from "./Uint16Image";
2
+ export { Float16Image } from "./Float16Image";
3
+ export { HDRImage } from "./HDRImage";
2
4
  export { checkHDR, checkHDRCanvas } from "./hdr-check";
3
5
  export { initHDRCanvas, defaultGetContextHDR, resetGetContext } from "./hdr-canvas";
@@ -4,6 +4,8 @@
4
4
  type HDRHTMLCanvasOptionsType = "mode";
5
5
  type HDRHTMLCanvasOptions = { [key in HDRHTMLCanvasOptionsType]?: string };
6
6
 
7
+ type HDRImageDataArray = ImageDataArray | Float16Array | Uint16Array;
8
+
7
9
  interface HDRHTMLCanvasElement extends HTMLCanvasElement {
8
10
  configureHighDynamicRange(options: HDRHTMLCanvasOptions): void;
9
11
  _getContext(contextId: string, options?: object): RenderingContext | null;
@@ -11,15 +13,44 @@ interface HDRHTMLCanvasElement extends HTMLCanvasElement {
11
13
 
12
14
  interface HDRImageData {
13
15
  readonly colorSpace: PredefinedColorSpace;
14
- readonly data: Uint8ClampedArray | Uint16Array;
16
+ readonly data: HDRImageDataArray | Uint16Array;
15
17
  readonly height: number;
16
18
  readonly width: number;
17
19
  }
18
20
 
19
21
  // See https://github.com/w3c/ColorWeb-CG/blob/main/hdr_html_canvas_element.md
20
- // "rec2100-display-linear" is left out beacause of mapping issues
22
+ // "rec2100-display-linear" is left out because of mapping issues
21
23
  type HDRPredefinedColorSpace = "display-p3" | "srgb" | "rec2100-hlg" | "rec2100-pq";
22
24
 
23
25
  //enum HDRPredefinedColorSpace {"display-p3", "srgb", "rec2100-hlg", "rec2100-pq", "rec2100-display-linear"};
24
26
 
25
- export { HDRHTMLCanvasElement, HDRPredefinedColorSpace, HDRImageData };
27
+ /**
28
+ * A callback function that receives the red, green, blue, and alpha values of a pixel
29
+ * and returns a new `Float16Array` with the modified values.
30
+ *
31
+ * @callback HDRImagePixelCallback
32
+ * @param {number} red - The red channel value (0-65535).
33
+ * @param {number} green - The green channel value (0-65535).
34
+ * @param {number} blue - The blue channel value (0-65535).
35
+ * @param {number} alpha - The alpha channel value (0-65535).
36
+ * @returns {ImageDataArray} A new `Float16Array` containing the four channel values.
37
+ */
38
+ type HDRImagePixelCallback = (red: number, green: number, blue: number, alpha: number) => HDRImageDataArray;
39
+
40
+ interface CanvasRenderingContext2DHDR extends CanvasRenderingContext2D {}
41
+
42
+ interface CanvasRenderingContext2DHDRSettings {
43
+ colorSpace: HDRPredefinedColorSpace;
44
+ pixelFormat?: "uint8" | "float16";
45
+ colorType?: "unorm8" | "float16";
46
+ }
47
+
48
+ export {
49
+ HDRHTMLCanvasElement,
50
+ HDRPredefinedColorSpace,
51
+ HDRImageDataArray,
52
+ HDRImageData,
53
+ HDRImagePixelCallback,
54
+ CanvasRenderingContext2DHDR,
55
+ CanvasRenderingContext2DHDRSettings
56
+ };
@@ -0,0 +1,17 @@
1
+ export {};
2
+
3
+ declare global {
4
+ var ImageDataSettings: {
5
+ prototype: ImageDataSettings;
6
+ pixelFormat?: ImageDataPixelFormat;
7
+ };
8
+
9
+ var ImageData: {
10
+ prototype: ImageData;
11
+ pixelFormat: ImageDataPixelFormat;
12
+ };
13
+
14
+ type ImageDataArray = Uint8ClampedArray | Float16Array;
15
+
16
+ type ImageDataPixelFormat = "rgba-unorm8" | "rgba-float16";
17
+ }
@@ -1,8 +1,25 @@
1
1
  import WebGPUBackend from 'three/src/renderers/webgpu/WebGPUBackend.js';
2
2
  import { GPUFeatureName, GPUTextureFormat } from 'three/src/renderers/webgpu/utils/WebGPUConstants.js';
3
3
 
4
+ /**
5
+ * An HDR-enabled WebGPU backend for three.js, extending the standard `WebGPUBackend`.
6
+ * This class configures the WebGPU context to support High Dynamic Range rendering
7
+ * by setting the output color space to "rec2100-hlg".
8
+ * **You should never have the need to use this!**
9
+ *
10
+ * @class
11
+ * @augments {WebGPUBackend}
12
+ */
13
+
4
14
  class HDRWebGPUBackend extends WebGPUBackend {
5
15
 
16
+ /**
17
+ * Initializes the backend, including requesting a WebGPU device and configuring the context.
18
+ * This method overrides the parent `init` to set a specific HDR color space.
19
+ *
20
+ * @param {WebGLRenderer} renderer - The three.js renderer instance.
21
+ * @returns {Promise<void>} A promise that resolves when the initialization is complete.
22
+ */
6
23
  // See https://github.com/mrdoob/three.js/blob/master/examples/jsm/renderers/webgpu/WebGPUBackend.js#L123
7
24
  async init( renderer ) {
8
25
 
@@ -67,6 +84,11 @@ class HDRWebGPUBackend extends WebGPUBackend {
67
84
  const alphaMode = parameters.alpha ? 'premultiplied' : 'opaque';
68
85
 
69
86
  // See https://github.com/ccameron-chromium/webgpu-hdr/blob/main/EXPLAINER.md#example-use
87
+ /**
88
+ * Configures the WebGPU context with HDR settings.
89
+ * The `colorSpace` is set to `rec2100-hlg` for High Dynamic Range support.
90
+ * @see {@link https://github.com/ccameron-chromium/webgpu-hdr/blob/main/EXPLAINER.md#example-use | WebGPU HDR Explainer}
91
+ */
70
92
  this.context.configure( {
71
93
  device: this.device,
72
94
  format: GPUTextureFormat.BGRA8Unorm,
@@ -4,7 +4,22 @@ import Renderer from 'three/src/renderers/common/Renderer.js';
4
4
  import WebGLBackend from 'three/src/renderers/webgl-fallback/WebGLBackend.js';
5
5
  import HDRWebGPUBackend from './HDRWebGPUBackend.js';
6
6
 
7
+ /**
8
+ * HDR enabled WebGPURenderer.
9
+ *
10
+ * @class
11
+ * @augments {Renderer}
12
+ * @fires contextrestored
13
+ * @fires contextlost
14
+ * @see {@link https://threejs.org/docs/#api/en/renderers/WebGLRenderer | WebGLRenderer}
15
+ */
7
16
  class HDRWebGPURenderer extends Renderer {
17
+ /**
18
+ * Creates an instance of HDRWebGPURenderer.
19
+ *
20
+ * @constructor
21
+ * @param {object} [parameters={}] - The parameters for the renderer.
22
+ */
8
23
  constructor( parameters = {} ) {
9
24
 
10
25
  let BackendClass;
@@ -31,6 +46,10 @@ class HDRWebGPURenderer extends Renderer {
31
46
 
32
47
  super( backend, parameters );
33
48
 
49
+ /**
50
+ * @type {boolean}
51
+ * @default true
52
+ */
34
53
  this.isWebGPURenderer = true;
35
54
 
36
55
  }
package/tsconfig.json CHANGED
@@ -5,21 +5,27 @@
5
5
  "removeComments": true,
6
6
  "preserveConstEnums": true,
7
7
  "sourceMap": true,
8
- "moduleResolution": "node",
8
+ "moduleResolution": "bundler",
9
9
  "noEmit": false,
10
10
  "noEmitOnError": true,
11
11
  "esModuleInterop": true,
12
12
  "strict": true,
13
13
  "strictNullChecks": true,
14
+ "strictPropertyInitialization": false,
14
15
  "declaration": true,
15
- "allowJs": false,
16
- "skipLibCheck": false,
17
- "target": "es2022",
16
+ "allowJs": true,
17
+ "skipLibCheck": true,
18
+ "target": "es2024",
18
19
  "outDir": "dist",
19
20
  "declarationDir": "dist",
20
21
  "typeRoots": ["./node_modules/@types"],
21
- "lib": ["dom", "es2022"]
22
+ "lib": ["es2024"],
23
+ "paths": {
24
+ "~/hdr-canvas/three/*": ["./three/*"],
25
+ "~/hdr-canvas/*": ["./src/*"],
26
+ "three": ["./node_modules/@types/three"]
27
+ }
22
28
  },
23
- "include": ["src/**/*"],
29
+ "include": ["src/**/*", "tests/site/**/*", "tests/*"],
24
30
  "exclude": ["node_modules", "**/*.spec.ts", "three/**/*"]
25
31
  }
@@ -1,4 +0,0 @@
1
- import type { HDRHTMLCanvasElement } from "./types/HDRCanvas.d.ts";
2
- export declare function initHDRCanvas(canvas: HDRHTMLCanvasElement): RenderingContext | null;
3
- export declare function defaultGetContextHDR(): void;
4
- export declare function resetGetContext(): void;