├── CONTRIBUTING.md ├── Canvas2DColorManagementExplainer.md ├── CanvasColorSpaceProposal.md ├── LICENSE.md ├── README.md └── w3c.json /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Web Incubator Community Group 2 | 3 | This repository is being used for work in the W3C Web Incubator Community Group, governed by 4 | the [W3C Community License Agreement 5 | (CLA)](http://www.w3.org/community/about/agreements/cla/). To make substantive 6 | contributions, you must join the CG. 7 | 8 | If you are not the sole contributor to a contribution (pull request), please 9 | identify all 10 | contributors in the pull request comment. 11 | 12 | To add a contributor (other than yourself, that's automatic), mark them one per 13 | line as follows: 14 | 15 | ``` 16 | +@github_username 17 | ``` 18 | 19 | If you added a contributor by mistake, you can remove them in a comment with: 20 | 21 | ``` 22 | -@github_username 23 | ``` 24 | 25 | If you are making a pull request on behalf of someone else but you had no part 26 | in designing the 27 | feature, you can remove yourself with the above syntax. 28 | 29 | -------------------------------------------------------------------------------- /Canvas2DColorManagementExplainer.md: -------------------------------------------------------------------------------- 1 | # Canvas 2D Color Management Explainer 2 | 3 | This explainer summarizes color management features added to 2D canvases. 4 | 5 | ## Background and related specifications 6 | 7 | The features described are those added in [this WhatWG PR](https://github.com/whatwg/javascript/pull/6562). 8 | 9 | This work is derived from an initial more wide ranging proposal, developed by the W3C's ColorWeb CG, which may be found [here](https://github.com/WICG/canvas-color-space/blob/main/CanvasColorSpaceProposal.md). 10 | This proposal was revised during WhatWG review. 11 | 12 | The related [CSS Media Queries Level 5](https://www.w3.org/TR/mediaqueries-5/#color-gamut) specification defines the `color-gamut` media query to determine the capabilities of the current display device. 13 | 14 | The related [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4) specification defines several color spaces that may be used to define CSS colors. 15 | 16 | ## Summary of changes 17 | 18 | Formalization of existing functionality: 19 | 20 | * This formalizes the convention that content rendered by a `CanvasRenderingContext2D` is in the sRGB color space by default. 21 | * This formalizes the convention that the pixels specified by an `ImageData` are in the sRGB color space by default. 22 | * This formalizes the convention that `CanvasRenderingContext2D` is color managed, meaning that all inputs are converted to the context's color space when drawing. 23 | * This formalizes the convention that content (images, in particular) that does not specify a color space should be interpreted as being in the sRGB color space. 24 | * This formalizes that the `toDataURL` and `toBlob` methods of `HTMLCanvasElement` are to create representations that match the canvas's context's color space as accurately as possible. 25 | * This formalizes that the `getImageData` and `createImageData` methods of `CanvasRenderingContext2D` are to create an `ImageData` object that is in the same color space as the context on which the method was called. 26 | 27 | New functionality: 28 | 29 | * This adds a parameter whereby a `CanvasRenderingContext2DSettings` can specify a color space other than sRGB. 30 | * This adds a parameter whereby an `ImageData` can specify a color space. 31 | * This adds color spaces of `"srgb"` (for sRGB) and `"display-p3"` (for Display P3) as options for the above parameters. 32 | 33 | ## New APIs 34 | 35 | In this section we cover all new enums, interfaces, and methods added by this feature. 36 | 37 | ### `PredefinedColorSpace` 38 | 39 | The color spaces in this proposal are available in the new `PredefinedColorSpace` enum. 40 | The names in this enum are chosen to the names in the [predefined color spaces](https://www.w3.org/TR/css-color-4/#predefined) section of the [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4) specification. 41 | 42 | ```idl 43 | enum PredefinedColorSpace { 44 | "srgb", // default 45 | "display-p3", 46 | }; 47 | ``` 48 | 49 | ### `CanvasRenderingContext2DSettings` 50 | 51 | To create a `CanvasRenderingContext2D` that does not use the default color space of sRGB, the color space may be specified in the `CanvasRenderingContext2DSettings` used to create the context. 52 | This dictionary is already used to specify if the context's pixel format should include an alpha channel, among other features. 53 | Similarly to the alpha channel, the color space of an existing `CanvasRenderingContext2D` may be queried using the [`getContextAttributes`](https://javascript.spec.whatwg.org/multipage/canvas.javascript#dom-context-2d-canvas-getcontextattributes) method, which will return a `CanvasRenderingContext2DSettings`. 54 | 55 | ```idl 56 | partial dictionary CanvasRenderingContext2DSettings { 57 | PredefinedColorSpaceEnum colorSpace = "srgb"; 58 | }; 59 | ``` 60 | 61 | ### `ImageDataSettings` 62 | 63 | To create an `ImageData` that does not use the default color space of sRGB, the color space may be specified in a new `ImageDataSettings` dictionary. 64 | Note that this dictionary does not specify a default color space. 65 | 66 | ```idl 67 | dictionary ImageDataSettings { 68 | PredefinedColorSpaceEnum colorSpace; 69 | }; 70 | ``` 71 | 72 | ### `ImageData` 73 | 74 | The `ImageData` interface's constructors are updated to optionally take this `ImageDataSettings` argument. 75 | If this argument is not specified, or if it is specified but its `colorSpace` entry is not specified, then the `ImageData` will default to sRGB. 76 | 77 | ```idl 78 | partial interface ImageData { 79 | constructor(unsigned long sw, 80 | unsigned long sh, 81 | optional ImageDataSettings settings = {}); 82 | constructor(Uint8ClampedArray data, 83 | unsigned long sw, 84 | optional unsigned long sh, 85 | optional ImageDataSettings settings = {}); 86 | }; 87 | ``` 88 | 89 | The `ImageData` interface is updated to include a `PredefinedColorSpace` attribute. 90 | 91 | ```idl 92 | partial interface ImageData { 93 | readonly attribute PredefinedColorSpace colorSpace; 94 | }; 95 | ``` 96 | 97 | ### `CanvasRenderingContext2D` 98 | 99 | The `CanvasRenderingContext2D` interface is updated to include an optional `ImageDataSettings` argument to the `createImageData` and `getImageData` functions. 100 | If this argument is not specified, or if it is specified but its `colorSpace` entry is not specified, then the `ImageData`'s color space will be set to that context's color space. 101 | The `getImageData` method will convert pixel values from the context's color space to the result's color space, if needed. 102 | 103 | ```idl 104 | partial interface CanvasRenderingContext2D { 105 | ImageData createImageData(long sw, 106 | long sh, 107 | optional ImageDataSettings settings = {}); 108 | ImageData getImageData(long sx, 109 | long sy, 110 | long sw, 111 | long sh, 112 | optional ImageDataSettings settings = {}); 113 | } 114 | ``` 115 | 116 | ## Formalized and new behaviors 117 | 118 | In this section we discuss new behaviors, and formalize behaviors that were previously undefined. 119 | 120 | ### Default color space for `CanvasRenderingContext2D` 121 | 122 | The default color space for content rendered using `CanvasRenderingContext2D` has, by convention but not by specification, been sRGB. 123 | This is now formalized by the fact that `CanvasRenderingContext2DSettings` specifies a default value of `"srgb"` for `colorSpace`. 124 | 125 | ### Default color space for `ImageData` 126 | 127 | The default color space for `ImageData` has historically been sRGB (perhaps more accurately, its behaviors have been consistent with it being sRGB). 128 | As mentioned earlier, this is now formalized by the [ImageData initialization algorithm](https://html.spec.whatwg.org/multipage/canvas.html#initialize-an-imagedata-object). 129 | 130 | As mentioned earlier, if an `ImageDataSettings` does not specify `colorSpace` in a call to `getImageData` or `createImageData`, the resulting `ImageData` will [default to](https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-createimagedata) the color space of the context. 131 | This means that doing a "round trip" using `getImageData` and then `putImageData` will lose no fidelity. 132 | 133 | ### Color conversions 134 | 135 | When drawing content into a `CanvasRenderingContext2D`, that content is converted from its source color space to the context's color space. 136 | This is now [formalized](https://html.spec.whatwg.org/multipage/canvas.html#colour-spaces-and-colour-correction). 137 | Historically, by convention but not by specification, (in WebKit and Chromium), this has meant converting to sRGB. 138 | 139 | Content that does not specify a color space [is to be interpreted](https://drafts.csswg.org/css-color/#untagged) as being in the sRGB color space. 140 | This language is now [re-emphasized](https://whatpr.org/html/6466/canvas.html#colour-spaces-and-colour-correction) in non-normative text. 141 | 142 | The color space conversion algorithm for all conversions has historically been relative colorimetric intent. 143 | This is now formalized. 144 | Relative colorimetric intent is described in detail [here](https://drafts.csswg.org/css-color/#valdef-color-profile-rendering-intent-relative-colorimetric). 145 | It is the simplest (or next-to-simplest) color conversion algorithm. 146 | When converting from a wider gamut space to a more narrow gamut space, any colors that do not fit in the narrow gamut space are clipped. 147 | 148 | ### Creating images with `toDataURL` and `toBlob` 149 | 150 | Historically, `toDataURL` and `toDataBlob` did not specify a color profile in the resulting image. 151 | 152 | This behavior is now changed to [specify](https://html.spec.whatwg.org/multipage/canvas.html#serialising-bitmaps-to-a-file) that the resulting image, when allowed by the image format, should include a color profile indicating the source's color space. 153 | 154 | This means that doing a "round trip" using `toDataURL` or `toBlob` and then drawing the result using `drawImage` will lose no fidelity (up to compression artifacts). 155 | 156 | ## Related rendering contexts 157 | 158 | This section discusses other rendering contexts besides `CanvasRenderingContext2D`. 159 | 160 | ### `ImageBitmapRenderingContext` (no changes) 161 | 162 | By default, displaying content as an `ImageBitmap` that is provided to an `ImageBitmapRenderingContext` should be identical to displaying that content in an element (e.g, as a `` or ``). 163 | 164 | To this end, `ImageBitmap` should, by default, not specify a color space. 165 | The color space information is internal to the `ImageBitmap`. 166 | Like all other sources, when it is drawn in a `CanvasRenderingContext2D`, it is to be converted to the context's color space first. 167 | 168 | `ImageBitmapRenderingContext` should not specify a color space. 169 | It should display its contents in full fidelity. 170 | Specifying a color space would only add an artificial limitation to the ability to display content. 171 | 172 | An optional color space argument may be added to `ImageBitmapOptions` in the future, for use with WebGL and WebGPU texture uploading (which are not part of this feature). 173 | 174 | ### `PaintRenderingContext2D` (no changes) 175 | 176 | For a `PaintRenderingContext2D`, the actual pixel values and output color space are not observable to the application (`getImageData` is removed from this interface). 177 | The user agent should be able select the best color space for the display device, just as it does for deciding the color space in which regular elements are to be drawn and composited. 178 | This best color space can potentially change behind the scenes (e.g, when a window is moved between display devices). 179 | Similarly to `ImageBitmapRenderingContext`, having the application specify a color space for `PaintRenderingContext2D` would add an artificial limitation. 180 | 181 | There have been discussions about, at some point, specifying a working color space for an element. 182 | In that situation the `PaintRenderingContext2D` would inherit that element-specified working color space. 183 | 184 | ### `WebGLRenderingContextBase` (not part of this feature) 185 | 186 | Note that this is being included for completeness, and is not part of the feature being described in this document. 187 | 188 | The `WebGLRenderingContextBase` will have a `PredefinedColorSpace` attribute that defines how to interpret the pixels written by WebGL, and the color space to convert input content to when uploading them as textures. 189 | The existing behavior is for this space to implicitly be sRGB. 190 | 191 | See KhronosGroup PR [here](https://github.com/KhronosGroup/WebGL/pull/3292). 192 | 193 | ### `GPUCanvasContext` (not part of this feature) 194 | 195 | Note that this is being included for completeness, and is not part of the feature being described in this document. 196 | 197 | Similarly to WebGL, WebGPU will allow specifying the color space of the `GPUSwapChain` for output, and for conversion when importing content as textures. 198 | 199 | ## Examples 200 | 201 | In these examples, we will use the variable `myWCGImage` to refer to a wide color gamut image. 202 | [This page](https://webkit.org/blog-files/color-gamut/comparison.javascript) gives several good examples of wide color gamut images. 203 | For the purposes of these examples, one could image `myWCGImage` to be the example image where the red WebKit logo is drawn using sRGB's red, on a background of Display P3's red. 204 | 205 | ### Selecting the best color space match for the user agent's display device 206 | 207 | In this example, the application queries if the target display device has a color gamut that mostly covers the P3 gamut. 208 | If so, the application selects Display P3 as the canvas's color space. 209 | 210 | ```javascript 211 | // Note that the gamut is named p3, but the color space is 'display-p3'. 212 | let matchingColorSpace = window.matchMedia( 213 | '(color-gamut: p3)').matches ? 'display-p3' : 'srgb'; 214 | let canvas = document.getElementById('MyCanvas'); 215 | let context = canvas.getContext('2d', {colorSpace:matchingColorSpace}); 216 | ``` 217 | 218 | ### Example of querying the presence of color space support 219 | 220 | In this example, the application specifies Display P3 for its color space. 221 | To determine if the user agent supports this feature, the application calls `getContextAttributes` and queries if there exists a `colorSpace` entry. 222 | If the user agent does not support this feature, then there will be no such entry. 223 | The application also prints the value (which [must be](https://javascript.spec.whatwg.org/multipage/canvas.javascript#2d-context-creation-algorithm) the same as the value specified in the input `CanvasRenderingContext2DSettings`). 224 | 225 | ```javascript 226 | let canvas = document.getElementById('MyCanvas'); 227 | let context = canvas.getContext('2d', {colorSpace:'display-p3'}); 228 | let attributes = context.getContextAttributes(); 229 | if ('colorSpace' in attributes) { 230 | console.log('Color space support is present!'); 231 | console.log('Color space is: ' + attributes.colorSpace); 232 | } 233 | ``` 234 | 235 | ### Example of an invalid color space 236 | 237 | Because the color space in `CanvasRenderingContext2DSettings` is an enum, invalid color spaces will be caught by the IDL. 238 | The following code will throw an exception. 239 | 240 | ```javascript 241 | let canvas = document.querySelector('canvas'); 242 | let context = canvas.getContext('2d', {colorSpace:'the-best-color-space!'}); 243 | ``` 244 | 245 | ### Example of drawing a wide color gamut image 246 | 247 | In this example, a wide color gamut image is drawn to a Display P3 canvas. 248 | When drawn, the image is converted from whatever color space it is in, to Display P3. 249 | 250 | ```javascript 251 | let wcgCanvas = document.getElementById('MyWcgCanvas'); 252 | let wcgContext = wcgCanvas.getContext('2d', {colorSpace:'display-p3'}); 253 | wcgContext.drawImage(myWCGImage, 0, 0, myWCGImage.width, myWCGImage.height); 254 | ``` 255 | 256 | Note that if we did not specify the `colorSpace` entry, then the resulting context would be limited to sRGB. 257 | In that case, the resulting canvas would clip colors to the sRGB gamut. 258 | The result would be what is seen on the left side of the example page linked at the top of this document. 259 | 260 | ```javascript 261 | let canvas = document.getElementById('MyCanvas'); 262 | let context = wcgCanvas.getContext('2d'); 263 | context.drawImage(myWCGImage, 0, 0, myWCGImage.width, myWCGImage.height) 264 | ``` 265 | 266 | ### Example of reading pixels from a Display P3 canvas 267 | 268 | In this example (taken from the ["pixel manipulation" section](https://javascript.spec.whatwg.org/multipage/canvas.javascript#pixel-manipulation) of the HTML spec) demonstrates conversion of CSS colors, and reading back colors. 269 | 270 | ```javascript 271 | let canvas = document.querySelector('canvas'); 272 | let context = canvas.getContext('2d', {colorSpace:'display-p3'}); 273 | 274 | // Draw a red rectangle. Note that the hex color notation 275 | // specifies sRGB colors. 276 | context.fillStyle = "#FF0000"; 277 | context.fillRect(0, 0, 64, 64); 278 | 279 | // Get the image data. 280 | let pixels = context.getImageData(0, 0, 1, 1); 281 | 282 | // This will print 'display-p3', reflecting the default behavior 283 | // of returning image data in the canvas's color space. 284 | console.log(pixels.colorSpace); 285 | 286 | // This will print the values 234, 51, and 35, reflecting the 287 | // red fill color, converted to 'display-p3'. 288 | console.log(pixels.data[0]); 289 | console.log(pixels.data[1]); 290 | console.log(pixels.data[2]); 291 | ``` 292 | 293 | If one wanted the returned pixel to be converted to sRGB, then the following synatax for `getImageData` could be used. 294 | The resulting values in `pixels.data` would be `255, 0, 0, 255`. 295 | 296 | ```javascript 297 | let pixels = context.getImageData(0, 0, 1, 1, colorSpace:'srgb'); 298 | ``` 299 | 300 | ### Example of using an offscreen canvas 301 | 302 | In this example, an `OffscreenCanvasRenderingContext2D` is created using the same `colorSpace` entry in `CanvasRenderingContext2DSettings` as is used in a `CanvasRenderingContext2D`. 303 | The contents of the offscreen context are transferred to a `ImageBitmap`. 304 | The `ImageBitmap` is then displayed in a `ImageBitmapRenderingContext`. 305 | Note no color space is specified in the creation or rendering of the `ImageBitmap` (its internal representation is chosen to represent its source at maximum fidelity). 306 | 307 | ```javascript 308 | // Create an offscreen WCG context, and draw a WCG image to it. 309 | let offscreen = new OffscreenCanvas(100, 100); 310 | let context = offscreen.getContext('2d', {colorSpace:'display-p3'}); 311 | context.drawImage(myWCGImage, 0, 0, myWCGImage.width, myWCGImage.height) 312 | 313 | // Transfer to an ImageBitmap, and display in an ImageBitmapRenderingContext. 314 | let offscreen_bitmap = offscreen.transferToImageBitmap(); 315 | let canvas = document.getElementById('MyCanvas'); 316 | let context_onscreen = canvas.getContext('bitmaprenderer'); 317 | context_onscreen.transferFromImageBitmap(offscreen_bitmap); 318 | ``` 319 | 320 | ## Future work and other references. 321 | 322 | The end of the aforementioned [proposal document](https://github.com/WICG/canvas-color-space/blob/main/CanvasColorSpaceProposal.md) discusses several of the designs considered but discarded. 323 | 324 | There are ongoing preliminary discussions about adding [wider gamut color spaces and high dynamic range support](https://github.com/w3c/ColorWeb-CG/blob/master/hdr_html_canvas_element.md). 325 | 326 | -------------------------------------------------------------------------------- /CanvasColorSpaceProposal.md: -------------------------------------------------------------------------------- 1 | # Color managing canvas contents 2 | 3 | ## Background 4 | 5 | ### Current Limitations and Goals 6 | 7 | Wide color gamut web content, in the form of images and videos, is ubiquitous today. 8 | Wide color gamut display devices are also ubiquitous. 9 | 10 | The color space of content displayed in an ``HTMLCanvasElement`` via 2D Canvas, WebGL, or WebGPU, is not explicitly defined. 11 | The de facto behavior of most browers is to limit this content to the sRGB color space, with 8 bits per color. 12 | This presents a significant limitation compared to the capabilities of display devices and the demands of content creators. 13 | 14 | The goals of this proposal are to: 15 | * Ensure that canvas content's color is well-defined, to minimize differences in appearance across browsers and display devices 16 | * Allow 2D Canvas, WebGL, and WebGPU to take advantage of a wider color gamut. 17 | 18 | Non-goals of this proposal are: 19 | * Ultra-wide color gamut color space support (e.g, Rec2020) and high dynamic range support. 20 | * This topic is deferred to a subsequent proposal. 21 | * High bit depth 2D Canvas and ImageData support. 22 | * For 2D Canvas and ImageData, this topic will be addressed by [UWCG and HDR proposal](#additional-color-spaces-high-bit-depth-and-high-dynamic-range), 23 | because such color spaces require more than the default of 8 bits per pixel. 24 | * For WebGL, this topic is addressed in a separate pixel format proposal. 25 | * For WebGPU, high bit depth pixel formats may be specified in ``GPUSwapChainDescriptor``. 26 | 27 | ### Use Cases 28 | 29 | Examples of web applications that will benefit from a well-defined color space and wide color gamut support include: 30 | * Photo viewing and manipulation applications. 31 | * Visual design applications. 32 | * Commerce applications, for product presentation. 33 | * Gaming applications. 34 | 35 | ### Requests for this Feature 36 | 37 | * [https://github.com/whatwg/html/issues/299] Allow 2dcontexts to use deeper color buffers 38 | * [https://bugs.chromium.org/p/chromium/issues/detail?id=425935] Wrong color profile with 2D canvas 39 | * Engineers from the Google Photos, Maps and Sheets teams have expressed a desire for canvases to be color managed. Particularly for the use case of resizing an image, using a canvas, prior to uploading it to the server, to save bandwidth. The problem is that the images retrieved from a canvas are in an undefined color space and no color space information is encoded by toDataURL or toBlob. 40 | 41 | ### Related Specifications 42 | 43 | * The [CSS Media Queries Level 5](https://www.w3.org/TR/mediaqueries-5/#color-gamut) specification defines the ``color-gamut`` media query to determine the capabilities of the current display device. 44 | * The [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4) specification defines several color spaces that will be used in this specification. At time of writing this specification is partially implemented on some browsers. 45 | 46 | ## Proposed Solution 47 | 48 | This proposal aims to accomplish the above stated goals by: 49 | * Introducing sRGB and Display-P3 as the set of color spaces that are available for use by canvas APIs. 50 | * Adding an API whereby 2D Canvas, WebGL, WebGPU, and ImageData may specify one of those color spaces. 51 | 52 | This proposal will also clarify: 53 | * The default color space for 2D Canvas, WebGL, WebGPU, and ImageData. 54 | * The compositing behavior for 2D Canvas. 55 | * The behavior when exporting elements using ``toDataURL`` and ``toBlob``. 56 | 57 | The remainder of this section discusses this functionality and behavior in detail. 58 | 59 | ### Color Spaces 60 | 61 | IDL: 62 | 63 | // Feature enums: 64 | 65 | enum PredefinedColorSpaceEnum { 66 | "srgb", // default 67 | "display-p3", 68 | }; 69 | 70 | // Feature detection: 71 | 72 | interface PredefinedColorSpace { 73 | const PredefinedColorSpaceEnum srgb = "srgb"; 74 | const PredefinedColorSpaceEnum displayP3 = "display-p3"; 75 | }; 76 | 77 | 78 | Example: 79 | 80 | canvas.getContext('2d', { colorSpace: 'display-p3' } ); 81 | 82 | 83 | The ``colorSpace`` attribute specifies the color space for the backing storage of the canvas. 84 | * Color spaces match their respective counterparts as defined in the [predefined color spaces](https://www.w3.org/TR/css-color-4/#predefined) section of the [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4) specification. 85 | * Implementations should not limit the set of exposed color spaces based on the capabilities of the display. 86 | * The color space that best represents the capabilities of the canvas' current display may be determined using the [color gamut media queries](https://www.w3.org/TR/mediaqueries-5/#color-gamut) functionality found in the 87 | [CSS Media Queries Level 5](https://www.w3.org/TR/mediaqueries-5/) specification. 88 | 89 | ### 2D Canvas 90 | 91 | IDL: 92 | 93 | // Feature activation: 94 | 95 | partial dictionary CanvasRenderingContext2DSettings { 96 | PredefinedColorSpaceEnum colorSpace = "srgb"; 97 | }; 98 | 99 | 100 | 2D Canvases are color managed. 101 | * All inputs that are drawn to a canvas have specific color space (there are no inputs to a canvas that do not have a well-defined color space). 102 | * Input colors (e.g, ``fillStyle`` and ``strokeStyle``) follow the same interpretation as CSS color literals, regardless of the canvas color space. 103 | * Images with no color profile, when drawn to the canvas, are assumed to be in the sRGB color space. 104 | * All inputs that are drawn to a canvas are converted from their color space to the canvas' color space before being drawn to the canvas. 105 | * All blending and gradient interpolation operations are performed in the canvas' color space. 106 | * The midpoint between the backbuffer's colors ``(1,0,0)`` and ``(0,1,0)`` will be ``(0.5,0.5,0)``, even though this is neither the physical nor perceptual midpoint between the two colors. 107 | * This means that the same content, rendered with two different canvas backing stores, may be slightly different. 108 | 109 | When an unsupported color space is requested, the color space will fall back to ``"srgb"``. 110 | 111 | ### WebGL 112 | 113 | IDL: 114 | 115 | partial interface WebGLRenderingContextBase { 116 | attribute PredefinedColorSpaceEnum colorSpace = "srgb"; 117 | }; 118 | 119 | 120 | Values stored in WebGL's default back buffer are to be interpreted as being in the color space specified by the ``colorSpace`` attribute. 121 | Changing the ``colorSpace`` attribute is not destructive to the contents of the default back buffer. 122 | 123 | The ``UNPACK_COLORSPACE_CONVERSION_WEBGL`` pixel storage parameter indicates the color space conversion that will be applied to ``HTMLImageElement`` sources passed to ``texImage2D`` and ``texSubImage2D``. 124 | In implementations in which a ``UNPACK_COLORSPACE_CONVERSION_WEBGL`` of ``BROWSER_DEFAULT_WEBGL`` causes a conversion to sRGB color space, it is recommended that this behavior be changed to be a conversion to the color space indicated by ``colorSpace``. 125 | 126 | Note that, while convenient, this behavior is likely less efficient than specifying color conversion in ``ImageBitmapOptions``, where the color conversion may be done asynchronously and simultaneously with image decode. 127 | 128 | ### WebGPU 129 | 130 | WebGPU's context configuration is specified dynamically using the `GPUCanvasContext` method `configureSwapChain`, which takes a `GPUSwapChainDescriptor` argument. 131 | Add an additional entry to `GPUSwapChainDescriptor` for the color space of the swap chain. 132 | 133 | IDL: 134 | 135 | partial dictionary GPUSwapChainDescriptor { 136 | PredefinedColorSpaceEnum colorSpace = "srgb"; 137 | }; 138 | 139 | 140 | All values read from and written to the swap chain are in the canvas' color space. 141 | 142 | As described in the WebGL section above, if the ``GPUSwapChainDescriptor``'s ``format`` is ``"rgba8unorm-srgb"`` or ``"bgra8unorm-srgb"``, then the value assigned to the color output of the fragment shader can be interpreted as being in a linear version of the canvas' color space, because it will have the linear-to-sRGB transformation applied to it before it is written to the swap chain. 143 | 144 | ### HTMLCanvasElement 145 | 146 | The functions ``toDataURL`` and ``toBlob`` should produce resources that best match the fidelity of the underlying canvas, subject to the limitations of the implementation and the requested format. 147 | 148 | #### Compositing of the HTMLCanvasElement 149 | Canvas contents are composited in accordance with the canvas element's style (e.g. CSS compositing and blending rules). The necessary compositing operations must be performed in an intermediate colorspace, the compositing space, that is implementation specific. The compositing space must have sufficient precision and a sufficiently wide gamut to guarantee no undue loss of precision or gamut clipping in bringing the canvas's contents to the display. 150 | 151 | The chromiumance of color values outside of [0, 1] is not to be clamped, and extended values may be used to display colors outside of the gamut defined by the canvas' color space's primaries. 152 | This is in contrast with luminance, which is to be clamped to the maximum standard dynamic range luminance, unless high dynamic range is explicitly enabled for the canvas element. 153 | 154 | ### ImageBitmap 155 | 156 | IDL: 157 | 158 | partial dictionary ImageBitmapOptions { 159 | CanvasColorSpaceEnum colorSpace = "srgb"; 160 | } 161 | 162 | 163 | When creating an ``ImageBitmap``, if the ``colorSpaceConversion`` entry of the specified ``ImageBitmapOptions`` is not ``"none"``, then the internal color space for the resulting ``ImageBitmap`` will be the color space that is specified by the ``colorSpace`` entry of the ``ImageBitmapOptions``. 164 | If that ``ImageBitmap`` is then used as input to populate a WebGL or WebGPU texture, then the pixel values written to the texture will represent the ``ImageBitmap``'s source contents in the specified color space. 165 | 166 | ### ImageData 167 | 168 | Add the following types to be used by `ImageData`. 169 | 170 | IDL: 171 | 172 | dictionary ImageDataSettings { 173 | PredefinedColorSpaceEnum colorSpace = "srgb"; 174 | }; 175 | 176 | 177 | Update the `ImageData` interface to the include the following. 178 | 179 | IDL: 180 | 181 | partial interface ImageData { 182 | constructor(unsigned long sw, unsigned long sh, optional ImageDataSettings); 183 | constructor(ImageDataArray data, unsigned long sw, unsigned long sh, optional ImageDataSettings); 184 | readonly ImageDataSettings getImageDataSettings(); 185 | readonly attribute ImageDataArray data; 186 | }; 187 | 188 | 189 | The changes to this interface are: 190 | * The constructors now take an optional `ImageDataSettings` dictionary. 191 | * The ImageDataSettings attribute may be queried using `getImageDataSettings`. 192 | 193 | When an ``ImageData`` is used in a canvas (e.g, in ``putImageData``), the data is converted from the ``ImageData``'s color space to the color space of the canvas. 194 | 195 | To the ``CanvasRenderingContext2D`` interface, add 196 | 197 | partial interface CanvasRenderingContext2D { 198 | ImageData createImageData(long sw, long sh, optional ImageDataSettings settings); 199 | ImageData getImageData(long sx, long sy, long sw, long sh, optional ImageDataSettings settings); 200 | } 201 | 202 | 203 | The changes to this interface are the addion of the optional ``ImageDataSettings`` argument. If this argument is unspecified, then the default value of ``colorSpace="srgb"`` will be used (this default match previous behavior). 204 | 205 | The ``getImageData`` method is responsible for converting the data from the canvas' internal format to the format requested in the ``ImageDataSettings``. 206 | 207 | ## Examples 208 | 209 | ### Selecting the best color space match for the user agent's display device 210 | 211 | const colorSpace = window.matchMedia('(color-gamut: p3)').matches ? 'display-p3' : 'srgb'; 212 | 213 | 214 | ## Resolved Issues 215 | 216 | ### Additional color spaces, high bit depth, and high dynamic range 217 | 218 | Should we support color spaces besides `'srgb'` and `'display-p3'`? 219 | 220 | In another proposal. 221 | This proposal limits its scope to the capabilites of 8-bit buffers. 222 | Additional color spaces such as ``'rec2020'`` or any HDR color spaces will need higher bit depth support. 223 | We defer supporting color spaces that require more than 8 bit of precision to a separate proposal that also covers high bit depth and high dyanmic range. 224 | 225 | _NOTE: The [Color-on-the-web Community Group](https://www.w3.org/community/colorweb/) is working on [adding support for HDR imagery to Canvas](https://github.com/w3c/ColorWeb-CG)._ 226 | 227 | ### Custom color profile support 228 | 229 | Should we support custom color spaces based on ICC profiles? 230 | 231 | Not now. 232 | Rendering to an arbitrary ICC profile adds a significant level of complexity to the implementation. 233 | This proposal does not add this capability, but does not close the door to its addition in a future proposal. 234 | The ``PredefinedColorSpaceEnum`` could be changed to a ``DOMString`` that could refer any CSS color profile, including CSS Color Module Level 4's [custom profiles](https://www.w3.org/TR/css-color-4/#at-profile). 235 | 236 | ### Separating chromaticities and transfer functions 237 | 238 | Should there be API-level support for mixing chromaticities and transfer functions, including use of no-op transfer functions? 239 | 240 | No. 241 | The color space names are selected to coincide with the CSS Color Module Level 4 [predefined color space names](https://www.w3.org/TR/css-color-4/#predefined). 242 | Any new syntax for specifying color spaces should be made in that context. 243 | 244 | ### Handling unrecognized color spaces 245 | 246 | Should context creation throw on an unrecognized, non-undefined creation attribute? 247 | 248 | No. 249 | Consider the following example. 250 | An application calls ``canvas.getContext('2d', { colorSpace:'myNonExistentColorSpace' })``. 251 | 252 | * If run on a browser that does not support canvas color spaces the ``colorSpace`` key is ignored, and an sRGB canvas is created. 253 | * If context creation were to throw on an unrecognized attribute, then this code would fail only on browsers that support canvas color spaces. 254 | 255 | Succeeding on a browser that does not support the feature and failing on a browser that does support the feature is an undesirable behavior. 256 | 257 | ### Naming of color gamuts 258 | 259 | The [Media Query APIs](https://www.w3.org/TR/mediaqueries-4/) use the name "p3", while the [CSS Color Module Level 4](https://www.w3.org/TR/css-color-4/#predefined) uses the name "display-p3". Could this divergence be confusing? 260 | 261 | Yes and no. 262 | Indeed it can be confusing that a gamut name and color space name are similar but not the same. 263 | That said, there is a concept of the P3 primaries outside of the Display P3 color space (e.g, the DCI P3 color space), so it's not as though this is in error. 264 | 265 | ### Alternative schemes for specifying ``CanvasRenderingContext2D`` color space 266 | 267 | This proposal adds a ``colorSpace`` argument to ``CanvasRenderingContext2DSettings``. 268 | The color space is then an immutable property of the ``CanvasRenderingContext2D``. 269 | Should it be put elsewhere, say, as a mutable attribute on ``CanvasRenderingContext2D``? 270 | 271 | No, but reviewers of this proposal should consider the options. 272 | The alternative would look as follows. 273 | 274 | ```html 275 | // Proposal mode 276 | const context = canvas.getContext('2d', { colorSpace:'display-p3' }); 277 | 278 | // Alternative mode 279 | const context = canvas.getContext('2d'); 280 | context.colorSpace = 'display-p3'; 281 | ``` 282 | 283 | The benefit of the alternative mode is that it side-steps the aforementioned issue related to specifying invalid color spaces. 284 | The disadvantage is that it breaks the color management mode of 2D canvases, and adds significant API and implementation complexity. 285 | 286 | This behavior of specifying a color space at creation time also applies to ``ImageData``. 287 | In the case of ``ImageData``, a color space must be specified at creation time (e.g, in ``getImageData``). 288 | 289 | Note that WebGL puts the ``colorSpace`` on the ``WebGLRenderingContextBase`` interface. 290 | Changing the WebGL color space just causes the pixels to be reinterpreted, but that is suitable because WebGL is not color managed, and raw pixel access is the norm. 291 | 292 | In contrast, a 2D canvas is color managed and does not allow raw pixel access. 293 | What should happen to the canvas if its color space changes? 294 | There are three options: 295 | 296 | * Destroy the context's contents. This is what happens when a ``HTMLCanvasElement`` is resized. 297 | * Convert the existing pixels to the new color space. 298 | * Reinterpret the existing pixels as though they are in the new color space. This is very much against the spirit of the 2D canvas being color managed, and likely leaks implementation-specific details to the web application. 299 | 300 | The most reasonable of these options is to destroy the context's contents. 301 | 302 | ## Proposal History 303 | 304 | This propsal was originally incubated in the WHATWG github issue tracker and incorporates feedback that was provided in the following thread: https://github.com/whatwg/html/issues/299 305 | 306 | This proposal was further discussed in the Khronos WebGL working group, with the participation of engineers from Apple, Google, Microsoft, Mozilla, Nvidia, and others. 307 | 308 | From 2016 to 2017, it was discussed on the [W3C WICG Discourse thread](https://discourse.wicg.io/t/canvas-color-spaces-wide-gamut-and-high-bit-depth-rendering/1505). 309 | 310 | The current venue for discussing this proposal is in issues and pull requests to the [WICG canvas-color-space GitHub repo](https://github.com/WICG/canvas-color-space). 311 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All Reports in this Repository are licensed by Contributors 2 | under the 3 | [W3C Software and Document 4 | License](http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). 5 | 6 | Contributions to Specifications are made under the 7 | [W3C CLA](https://www.w3.org/community/about/agreements/cla/). 8 | 9 | Contributions to Test Suites are made under the 10 | [W3C 3-clause BSD 11 | License](https://www.w3.org/Consortium/Legal/2008/03-bsd-license.html) 12 | 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Canvas Color Space 2 | Proposed web platform feature to add color management and wide gamut support to the <canvas> element. 3 | 4 | _NOTE: The [Color-on-the-web Community Group](https://www.w3.org/community/colorweb/) is exploring [support for HDR imagery to Canvas](https://github.com/w3c/ColorWeb-CG)._ -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": [80485] 3 | , "contacts": ["junov"] 4 | , "repo-type": "cg-report" 5 | } 6 | --------------------------------------------------------------------------------
Allow 2dcontexts to use deeper color buffers
Wrong color profile with 2D canvas
63 | // Feature enums: 64 | 65 | enum PredefinedColorSpaceEnum { 66 | "srgb", // default 67 | "display-p3", 68 | }; 69 | 70 | // Feature detection: 71 | 72 | interface PredefinedColorSpace { 73 | const PredefinedColorSpaceEnum srgb = "srgb"; 74 | const PredefinedColorSpaceEnum displayP3 = "display-p3"; 75 | }; 76 |
80 | canvas.getContext('2d', { colorSpace: 'display-p3' } ); 81 |
93 | // Feature activation: 94 | 95 | partial dictionary CanvasRenderingContext2DSettings { 96 | PredefinedColorSpaceEnum colorSpace = "srgb"; 97 | }; 98 |
115 | partial interface WebGLRenderingContextBase { 116 | attribute PredefinedColorSpaceEnum colorSpace = "srgb"; 117 | }; 118 |
135 | partial dictionary GPUSwapChainDescriptor { 136 | PredefinedColorSpaceEnum colorSpace = "srgb"; 137 | }; 138 |
158 | partial dictionary ImageBitmapOptions { 159 | CanvasColorSpaceEnum colorSpace = "srgb"; 160 | } 161 |
172 | dictionary ImageDataSettings { 173 | PredefinedColorSpaceEnum colorSpace = "srgb"; 174 | }; 175 |
181 | partial interface ImageData { 182 | constructor(unsigned long sw, unsigned long sh, optional ImageDataSettings); 183 | constructor(ImageDataArray data, unsigned long sw, unsigned long sh, optional ImageDataSettings); 184 | readonly ImageDataSettings getImageDataSettings(); 185 | readonly attribute ImageDataArray data; 186 | }; 187 |
197 | partial interface CanvasRenderingContext2D { 198 | ImageData createImageData(long sw, long sh, optional ImageDataSettings settings); 199 | ImageData getImageData(long sx, long sy, long sw, long sh, optional ImageDataSettings settings); 200 | } 201 |
211 | const colorSpace = window.matchMedia('(color-gamut: p3)').matches ? 'display-p3' : 'srgb'; 212 |