Skip to content

Commit 458efac

Browse files
committed
Allow out-of-gamut premultiplied values with float canvases
1 parent 53e8c77 commit 458efac

1 file changed

Lines changed: 72 additions & 20 deletions

File tree

spec/index.bs

Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2149,20 +2149,77 @@ WebGPU allows all of the color spaces in the {{PredefinedColorSpace}} enum.
21492149
Note, each color space is defined over an extended range, as defined by the referenced CSS definitions,
21502150
to represent color values outside of its space (in both chrominance and luminance).
21512151

2152-
An <dfn dfn>out-of-gamut premultiplied RGBA value</dfn> is one where any of the R/G/B channel values
2153-
exceeds the alpha channel value. For example, the premultiplied sRGB RGBA value [1.0, 0, 0, 0.5]
2154-
represents the (unpremultiplied) color [2, 0, 0] with 50% alpha, written `rgb(srgb 2 0 0 / 50%)` in CSS.
2155-
Just like any color value outside the sRGB color gamut, this is a well defined point in the extended color space
2156-
(except when alpha is 0, in which case there is no color).
2157-
However, when such values are output to a visible canvas, the result is undefined
2158-
(see {{GPUCanvasAlphaMode}} {{GPUCanvasAlphaMode/"premultiplied"}}).
2152+
<h4 id=out-of-gamut-premultiplied-rgba-value data-dfn-type=dfn>Out-of-Gamut Premultiplied RGBA Values
2153+
</h4>
2154+
2155+
An [=out-of-gamut premultiplied RGBA value=] is one where any of the R/G/B channel values
2156+
exceeds the alpha channel value.
2157+
2158+
<div class=example>
2159+
For example, the premultiplied sRGB RGBA value `[0.51, 0, 0, 0.5]`
2160+
represents the (unpremultiplied) color <code><a funcdef>color</a>(srgb 1.02 0 0 / 0.5)</code>.
2161+
</div>
2162+
2163+
Just like any color value outside the sRGB color gamut, any value is a well-defined point in the
2164+
extended color space (regardless of whether the point represents a real color).
2165+
When a canvas containing such a value is
2166+
[$get a copy of the image contents of a context|used as an image source$],
2167+
the color value must not be clamped in the image value given to the calling algorithm
2168+
regardless of the canvas's {{GPUCanvasConfiguration/format}}.
2169+
2170+
However, when a canvas containing such a value is displayed *on screen*, **if and only if** the
2171+
canvas has a range-limited format (like `unorm` formats, which have a range of 0&ndash;1),
2172+
the visible result for the entire canvas is undefined.
2173+
2174+
<div class=example>
2175+
For example, on a display with a color space of exactly Display P3,
2176+
an on-screen, solid-color {{PredefinedColorSpace/"srgb"}} canvas displays as follows:
2177+
2178+
<table class=data>
2179+
<thead>
2180+
<tr><th>{{GPUCanvasConfiguration/alphaMode}},<br>{{GPUCanvasConfiguration/format}}<th>Pixel Value<th>Result
2181+
<tbody>
2182+
<tr><td>{{GPUCanvasAlphaMode/"opaque"}},<br>{{GPUTextureFormat/"rgba16float"}}
2183+
<td rowspan=2>`[1.04, 0, 0, 1.0]`
2184+
<td rowspan=2><code><a funcdef>color</a>(display-p3 0.95 0.21 0.15 / 1.0)</code>,
2185+
<br>because the format is not range-limited.
2186+
<tr><td>{{GPUCanvasAlphaMode/"premultiplied"}},<br>{{GPUTextureFormat/"rgba16float"}}
2187+
<tr><td>{{GPUCanvasAlphaMode/"premultiplied"}},<br>{{GPUTextureFormat/"rgba16float"}}
2188+
<td>`[0.52, 0, 0, 0.5]`
2189+
<td><code><a funcdef>color</a>(display-p3 0.95 0.21 0.15 / 0.5)</code>,
2190+
<br>because the format is not range-limited.
2191+
<tr><td>{{GPUCanvasAlphaMode/"premultiplied"}},<br>{{GPUTextureFormat/"rgba8unorm"}}
2192+
<td>`[0.50, 0, 0, 0.5]`
2193+
<td><code><a funcdef>color</a>(display-p3 0.92 0.20 0.14 / 0.5)</code>,
2194+
<br>because the color is in-gamut for {{PredefinedColorSpace/"srgb"}}.
2195+
<tr><td>{{GPUCanvasAlphaMode/"premultiplied"}},<br>{{GPUTextureFormat/"rgba8unorm"}}
2196+
<td>`[0.52, 0, 0, 0.5]`
2197+
<td>(undefined result),
2198+
<br>because the format is range-limited
2199+
<br>and the color is out-of-gamut for {{PredefinedColorSpace/"srgb"}}.
2200+
</table>
2201+
2202+
The last result is undefined because the color could be:
2203+
2204+
- blended directly to the display buffer as
2205+
<code><a funcdef>color</a>(display-p3 0.95 0.21 0.15 / 0.5)</code>.
2206+
- blended through an intermediate sRGB buffer as `[0.5, 0, 0, 0.5]`,
2207+
then sent to the display buffer resulting in the appearance of
2208+
<code><a funcdef>color</a>(display-p3 0.92 0.2 0.14 / 0.5)</code>.
2209+
- displayed by some other process.
2210+
</div>
2211+
2212+
Note:
2213+
Implementations may defer compositing steps to operating system compositors,
2214+
which often have undefined behavior in these cases. Implementations may not be able to avoid
2215+
these undefined behaviors without significant power usage penalties.
21592216

21602217
### Color Space Conversions ### {#color-space-conversions}
21612218

21622219
A color is converted between spaces by translating its representation in one space to a
21632220
representation in another according to the definitions above.
21642221

2165-
If the source value has fewer than 4 RGBA channels, the missing green/blue/alpha channels are set to
2222+
If the source value has fewer than 4 RGBA channels, any missing green/blue/alpha channels are set to
21662223
`0, 0, 1`, respectively, before converting for color space/encoding and alpha premultiplication.
21672224
After conversion, if the destination needs fewer than 4 channels, the additional channels
21682225
are ignored.
@@ -2174,7 +2231,7 @@ Colors are not lossily clamped during conversion: converting from one color spac
21742231
will result in values outside the range [0, 1] if the source color values were outside the range
21752232
of the destination color space's gamut. For an sRGB destination, for example, this can occur if the
21762233
source is rgba16float, in a wider color space like Display-P3, or is premultiplied and contains
2177-
[=out-of-gamut premultiplied RGBA value|out-of-gamut values=].
2234+
[=out-of-gamut premultiplied RGBA value|out-of-gamut values=]. <!-- FIXME? -->
21782235

21792236
Similarly, if the source value has a high bit depth (e.g. PNG with 16 bits per component) or
21802237
extended range (e.g. canvas with `float16` storage), these colors are preserved through color space
@@ -13748,19 +13805,14 @@ is being composited into (e.g. an HTML page rendering, or a 2D canvas).
1374813805
: <dfn>"premultiplied"</dfn>
1374913806
::
1375013807
Read RGBA as premultiplied: color values are premultiplied by their alpha value.
13751-
100% red at 50% alpha is `[0.5, 0, 0, 0.5]`.
13808+
For example, 100% red at 50% alpha is `[0.5, 0, 0, 0.5]`.
1375213809

13753-
If [=out-of-gamut premultiplied RGBA values=] are output to the canvas, and the canvas is:
13810+
When alpha is 0, no color is represented, regardless of the RGB values.
13811+
(Attempting to compute the color results in division by zero.)
1375413812

13755-
<dl class=switch>
13756-
: [$get a copy of the image contents of a context|used as an image source$]
13757-
:: Values are preserved, as described in [[#color-space-conversions|color space conversion]].
13758-
13759-
: displayed to the screen
13760-
:: Compositing results are undefined.
13761-
This is true even if color space conversion would produce in-gamut values before
13762-
compositing, because the intermediate format for compositing is not specified.
13763-
</dl>
13813+
In this mode, the canvas texture may represent [=out-of-gamut premultiplied RGBA values=].
13814+
If the canvas has a range-limited format and is displayed on screen, out-of-gamut values
13815+
have undefined display results. Refer to that section for details.
1376413816
</dl>
1376513817

1376613818
# Errors &amp; Debugging # {#errors-and-debugging}

0 commit comments

Comments
 (0)