From 3227ae6a0fb5f8c06b8eaa2f18837358951d1052 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 20 Dec 2021 15:56:56 +0100 Subject: [PATCH 1/5] The third argument to DOM.context2d can be an options object with {scale, colorSpace}, allowing the creation of wide-gamut graphics on supported hardware and software. Note: this necessitates a try/catch because Safari will throw if the os requirements are not met. Tests/demo: https://observablehq.com/@fil/colorspace-display-p3-context2d --- README.md | 4 ++-- src/dom/context2d.mjs | 24 ++++++++++++++++++------ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a07ca81f..d74fec89 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,9 @@ html`` If you are using [2D Canvas](https://www.w3.org/TR/2dcontext/) (rather than [WebGL](https://webglfundamentals.org/)), you should use [DOM.context2d](#DOM_context2d) instead of DOM.canvas for automatic pixel density scaling. -# DOM.context2d(width, height[, dpi]) [<>](https://github.com/observablehq/stdlib/blob/main/src/dom/context2d.mjs "Source") +# DOM.context2d(width, height[, options]) [<>](https://github.com/observablehq/stdlib/blob/main/src/dom/context2d.mjs "Source") -Returns a new canvas context with the specified *width* and *height* and the specified device pixel ratio *dpi*. If *dpi* is not specified, it defaults to [*window*.devicePixelRatio](https://developer.mozilla.org/docs/Web/API/Window/devicePixelRatio). To access the context’s canvas, use [*context*.canvas](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/canvas). For example, to create a 960×500 canvas: +Returns a new canvas context with the specified *width* and *height* and the specified *options*. If the options are specified as an object, its scale and colorSpace properties are considered. A string or number value specifies the scale. The scale defaults to [*window*.devicePixelRatio](https://developer.mozilla.org/docs/Web/API/Window/devicePixelRatio). The colorSpace defaults to srgb—the other acceptable value is display-p3. To access the context’s canvas, use [*context*.canvas](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/canvas). For example, to create a 960×500 canvas: ```js { diff --git a/src/dom/context2d.mjs b/src/dom/context2d.mjs index 4b36ac89..846ab083 100644 --- a/src/dom/context2d.mjs +++ b/src/dom/context2d.mjs @@ -1,10 +1,22 @@ -export default function(width, height, dpi) { - if (dpi == null) dpi = devicePixelRatio; +export default function(width, height, options) { + var colorSpace = "srgb"; + var scale = devicePixelRatio; + if (options == +options) { + scale = +options; + } else { + if (options.scale) scale = +options.scale; + if (options.colorSpace === "display-p3") colorSpace = options.colorSpace; + } var canvas = document.createElement("canvas"); - canvas.width = width * dpi; - canvas.height = height * dpi; + canvas.width = width * scale; + canvas.height = height * scale; canvas.style.width = width + "px"; - var context = canvas.getContext("2d"); - context.scale(dpi, dpi); + var context; + try { + context = canvas.getContext("2d", {colorSpace}); + } catch(e) { + context = canvas.getContext("2d"); + } + context.scale(scale, scale); return context; } From c82eaa871200a1825d90cc03da158db4e01058a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 20 Dec 2021 18:36:24 +0100 Subject: [PATCH 2/5] pass-through --- README.md | 2 +- src/dom/context2d.mjs | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index d74fec89..1b688834 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ If you are using [2D Canvas](https://www.w3.org/TR/2dcontext/) (rather than [Web # DOM.context2d(width, height[, options]) [<>](https://github.com/observablehq/stdlib/blob/main/src/dom/context2d.mjs "Source") -Returns a new canvas context with the specified *width* and *height* and the specified *options*. If the options are specified as an object, its scale and colorSpace properties are considered. A string or number value specifies the scale. The scale defaults to [*window*.devicePixelRatio](https://developer.mozilla.org/docs/Web/API/Window/devicePixelRatio). The colorSpace defaults to srgb—the other acceptable value is display-p3. To access the context’s canvas, use [*context*.canvas](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/canvas). For example, to create a 960×500 canvas: +Returns a new canvas context with the specified *width* and *height* and the specified *options*. The scale property of the options object defines the device pixel ratio, and defaults to [*window*.devicePixelRatio](https://developer.mozilla.org/docs/Web/API/Window/devicePixelRatio). The remaining options are passed through as contextAttributes to [getContext](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext). As a shorthand notation, *options* having only scale can be specified as a number. To access the context’s canvas, use [*context*.canvas](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/canvas). For example, to create a 960×500 canvas: ```js { diff --git a/src/dom/context2d.mjs b/src/dom/context2d.mjs index 846ab083..4ad005cd 100644 --- a/src/dom/context2d.mjs +++ b/src/dom/context2d.mjs @@ -1,22 +1,20 @@ export default function(width, height, options) { - var colorSpace = "srgb"; var scale = devicePixelRatio; - if (options == +options) { + if (options == null) { + options = {}; + } else if (options == +options) { scale = +options; + options = {}; } else { if (options.scale) scale = +options.scale; - if (options.colorSpace === "display-p3") colorSpace = options.colorSpace; + delete options.scale; } var canvas = document.createElement("canvas"); canvas.width = width * scale; canvas.height = height * scale; canvas.style.width = width + "px"; var context; - try { - context = canvas.getContext("2d", {colorSpace}); - } catch(e) { - context = canvas.getContext("2d"); - } + context = canvas.getContext("2d", options); context.scale(scale, scale); return context; } From 4e72641b93466bbc01000ee3bb0047b218b3b7f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Mon, 20 Dec 2021 19:14:24 +0100 Subject: [PATCH 3/5] cleaner --- src/dom/context2d.mjs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/dom/context2d.mjs b/src/dom/context2d.mjs index 4ad005cd..28a9647b 100644 --- a/src/dom/context2d.mjs +++ b/src/dom/context2d.mjs @@ -1,20 +1,19 @@ export default function(width, height, options) { - var scale = devicePixelRatio; + let scale; if (options == null) { - options = {}; - } else if (options == +options) { - scale = +options; - options = {}; + options = undefined; + } else if (typeof options === "number") { + scale = options; + options = undefined; } else { - if (options.scale) scale = +options.scale; - delete options.scale; + ({scale, ...options} = options); } - var canvas = document.createElement("canvas"); + if (scale === undefined) scale = devicePixelRatio; + const canvas = document.createElement("canvas"); canvas.width = width * scale; canvas.height = height * scale; canvas.style.width = width + "px"; - var context; - context = canvas.getContext("2d", options); + const context = canvas.getContext("2d", options); context.scale(scale, scale); return context; } From 76e1b11ab5db69b9e01249a7d4ef81238079cb95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Wed, 5 Oct 2022 18:03:53 +0200 Subject: [PATCH 4/5] =?UTF-8?q?Apply=20Fabian=E2=80=99s=20suggestion,=20ha?= =?UTF-8?q?ndle=20null=20options?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dom/context2d.mjs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/dom/context2d.mjs b/src/dom/context2d.mjs index 28a9647b..8a25d8f1 100644 --- a/src/dom/context2d.mjs +++ b/src/dom/context2d.mjs @@ -1,19 +1,12 @@ -export default function(width, height, options) { - let scale; - if (options == null) { - options = undefined; - } else if (typeof options === "number") { - scale = options; - options = undefined; - } else { - ({scale, ...options} = options); - } - if (scale === undefined) scale = devicePixelRatio; +export default function (width, height, options = {}) { + const {scale = devicePixelRatio, ...contextOptions} = !isNaN(options) + ? {...(options != null && {scale: options})} + : options; const canvas = document.createElement("canvas"); canvas.width = width * scale; canvas.height = height * scale; canvas.style.width = width + "px"; - const context = canvas.getContext("2d", options); + const context = canvas.getContext("2d", !options ? options : contextOptions); context.scale(scale, scale); return context; } From e1b6ea9b21479ee42517e702f759a9b700623c2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Rivi=C3=A8re?= Date: Wed, 5 Oct 2022 18:25:16 +0200 Subject: [PATCH 5/5] options === null --- src/dom/context2d.mjs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/dom/context2d.mjs b/src/dom/context2d.mjs index 8a25d8f1..5590091b 100644 --- a/src/dom/context2d.mjs +++ b/src/dom/context2d.mjs @@ -6,7 +6,10 @@ export default function (width, height, options = {}) { canvas.width = width * scale; canvas.height = height * scale; canvas.style.width = width + "px"; - const context = canvas.getContext("2d", !options ? options : contextOptions); + const context = canvas.getContext( + "2d", + options === null ? options : contextOptions + ); context.scale(scale, scale); return context; }