-
Notifications
You must be signed in to change notification settings - Fork 711
[css-color-4] Allow out-of-gamut HSL/HWB colors (previously "Move gamut mapping to a future spec") #8444
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I think there are some misunderstandings here.
Do you mean that you met with your team, or was this a more diverse group of UA developers, including developers from other UAs? If it's the latter, I don't understand why the discussion did not happen in the CSS WG; that's what it's for. If the former, it would be helpful to phrase it that way to prevent misunderstandings.
Please note that CSS Color 5 is pretty mature, with all of its current features cleared for shipping.
This is still a form of gamut mapping, just not a very good one; you’re basically doing clipping.
This is incorrect. Relative Color Syntax can also produce out of gamut results, as well as directly specifying values in LCH, Lab, OKLab, OKLCH. I would expect these to be far more common ways of getting OOG than I think part of the confusion lies on the fact that there are two types of out-of-gamut: a) Out of gamut of the specified color format (e.g. When talking about where gamut mapping should be applied, we need to be clear about which OOG we're talking about. |
Section 13.1. An Introduction to Gamut Mapping is a non-normative backgrounder to explain to implementers the design decisions for section 13. In particular it explains the quality differences between a change in chroma (not very visible unless large), a change in lightness (more visible, but acceptable if small) and a change in hue (very visible unless very small). Section 13.2. CSS Gamut Mapping to an RGB Destination is normative, and defines (to a given tolerance, which is much smaller than any visible difference) a specific location on the gamut boundary which intersects with a constant-hue, constant lightness ray from the origin color to the achromatic axis in Oklch. There are two methods to find this point; one is iterative and one is analytical. To the tolerance specified, they give the same result. Describing the implementer choice between these two as "non-normative section" is quite incorrect. They give the same result. Naive clipping finds an entirely different point, with very different properties: for any origin color which is visibly different to the gamut boundary point specified in 13.2, it gives a result with large difference in hue and in lightness.
Private and unminuted meetings are a suboptimal way to reach working group consensus. They are much more likely to come to inaccurate or erroneous conclusions, based on incorrect assumptions or misunderstandings. Such as:
That isn't a change. The specification already requires that intermediate results are retained, not gamut mapped:
and again in 12.1. Color Space for Interpolation If the colors to be interpolated are outside the gamut of the interpolation color space , then once converted to that space, they will contain out of range values. These are not clipped, but the values are interpolated as-is. There are two places where some sort of gamut mapping is required however: 7. If dest is a physical output color space, such as a display, then col2 must be css gamut mapped so that it can be displayed. As @LeaVerou already pointed out, no specification can require that a display emit colors that it can't display. That should be obvious. 8. If dest cannot represent out of gamut colors, for example hsl or hwb, then col2 must be css gamut mapped. That is it. All other color space conversions can, and must, accomodate results which are out of gamut for the color space used. So, bravo on forming a consensus that agrees with what the spec already says. To address one additional inaccurate assumption, which @LeaVerou already mentioned:
Wrong. Out of gamut results are trivially possible from:
|
Thanks so much Lea. This clears things up quite a bit. The crux of the confusion are these tests, which, if I understand correctly are an instance of a) and not b)?
If both these things are true, then the tests are invalid, no? The gamut mapping will happen later in the pipeline, or even not at all if the display device is capable of colors outside the sRGB gamut?
So, we should be doing something similar to the aforementioned tests, but with The concern raised in this issue was primarily about interoperability, since Safari and Chrome are beginning to ship |
They are c) Not expressible in a given color format. HSL and HWB require in-gamut sRGB values as input, and give nonsense values if they get negative number or numbers grester than 100%. So, as the spec says, they need gamut mapped before the sRGB to HSL/HWB conversion step. So (unlike gamut mapping to the used value on the display) this case is observable through script, and the tests are indeed valid. Used values are not exposed through script, the only way to see if the right color is being displayed is to physically measure it or compare it visually to a reference color patch. |
Do they, though? An experiment: The Using the algorithm defined in 7.2 and the srgb equivalent of display-p3 red as input [1.09306 -0.226721 -0.150111] we get the hsl equivalent of [356.51714943615644, 152.34001932269007, 43.31695]. See a demo here. This value round trips fine using the algorithm in 7.1, with some float-precision issues. Is a hue of 356 degrees, a saturation of 152% and a lightness of 43% nonsensical if negative values and values greater than one for srgb are not? Here's a demo for arbitrary values for display-p3 input, including HWB. All resulting values for hsl make sense if we allow saturation > 100%. The highest value I was able to see was about 300%. All values roundtrip to srgb with only rounding errors on the order of 10^-16. I see similar results with HWB, with some negative values for whiteness and blackness when out-of-gamut
What I should have said here is "out-of-gamut results that are observable through script and are expected to be gamut mapped as opposed to left-as-is." Per the discussion here it would appear that we can further narrow this case to be color-mix values in the |
I did not intend to close the issue, apologies. |
Interesting. I don't recall why I thought the values became nonsensical; I would need to look again. If HSL and HWB were made unbounded, we would need to drop statements like this:
|
IIRC, HSL behaves in a fairly sane way when out of gamut within the SDR range, it mainly pushes saturation higher. I think when lightness exceeds the HDR range, either positive or negative, saturation goes into the negative which is a little weird, but it round trips fine. I don't think this is true for all HSL variants out there (OkHSL or HSLuv), but the traditional HSL does round-trip fine. |
I'm in favor! Would you like me to draft a PR to this repo of unbounded HSL and HWB? If HSL and HWB are the only exceptions to the "keep out-of-gamut colors until use-time" paradigm, it would make implementations simpler and less surprising to make them behave like other colorspaces. |
Ah, right. And interpolating in HSL where one component has negative Saturation will give unexpected results. However, I agree that it makes things more consistent if all color spaces can round trip values, and all CSS colors have a unique colorimetric value (which is also why we got rid of preserving hues outside [0..360]).
Sure. I would also like to hear from @emilio and @weinig about this proposed change. |
I assume, if the change to not clamp HSL saturation and/or lightness went into place, it would still be required to gamut map HSL before interpolating in that space to avoid such issues? |
That still needs to be discussed. |
The big benefit of letting HSL/etc go out of gamut is to avoid having to specify the precise gamut mapping right now; if we still have to gamut map before interpolating that defeats the point. I think if you interpolate colors in HSL where the endpoints are out of gamut, you get what you get honestly. |
That's a fair statement. Currently, saturation only goes negative when lightness goes negative or when in the HDR range. The only way it could accidentally go out is if you are converting from one of the newer color spaces like Oklab, OkLCh and maybe Lab and LCh which is not always very precise at the edges. > let color = new Color("oklch", [1, 0,0]).to('hsl').coords.toString();
"223.8135974487593,-172.5242095991637,100.00000421453782" If needed, this could be managed by just clamping lightness to the SDR range during the HSL conversion if it exceeds those limits. None of the current color spaces are meant for HDR use at this time anyways. |
For those curious about "what you get": https://mysterydate.github.io/web-demos/hsl-out-of-gamut-interpolation.html Since Chromium is currently not gamut mapping the inputs (just allowing out-of-gamut values) and Safari TP is gamut mapping, it provides a good side-by-side comparison. Even in with This result is more or less aligned with expectations. Each gradient is more saturated that the previous one. Even though display-p3 is outside of the hsl gamut, the resulting gradient looks fine because my display supports p3 (look at how much more vibrant the green is, for example). For the gradient with xyz endpoints the colors so saturated that the result is out of gamut for my display and as a result we see more saturation throughout the gradient. This is as opposed to gamut mapping the endpoints before generating the gradients: Since the display-p3 gradient's endpoints are being pulled in to srgb, the result is no-longer brighter than using srgb endpoints. The xyz endpoints are pulled so far away from their original place that the gradient now looks totally different, interpolating from a not-quite-red to a not-quite-green.
Is this a library you're using for the
This would be minimally invasive and really quite rare, though I think it would be better if we just left values as-is. This allows everything to rountrip always and keeps the implementation as simple as possible with the only cost being some artifacts when interpolating certain far out of gamut colors in HSL. |
Worth noting that rgb = [0.9999999694343976, 1.000000008665371, 1.000000114856359] but the HSL algorithm seems totally thrown by those fractionally higher than 1.0 numbers. |
The sRGB to HSL code in color.js is the same as the sample code in CSS Color 4 |
This is using the color library that the CSS color spec writers maintain: https://colorjs.io/. Depending on the precision of the matrices, and the precision of the values that are maintained, that value could be slightly different. The exact value is probably not as important, but the point is simply that the algorithm was never designed to give you an exact sRGB |
That seems reasonable to me. If people want perceptually uniform results they should not be interpolating in HSL or HWB anyway. |
Yeah, this is probably the most important point. If you are using OkLCh, why are you converting back to HSL to do interpolation? 🙂 It may be worth noting that you could get odd results if you do though. |
Excellent! I will begin writing a PR on this repo that allows unbounded HSL and HWB in that case. |
w3c/csswg-drafts#8444 (comment) In order to avoid painful and unexpected gamut mapping, color-mix and relative color with all legacy color formats (rgb, rgba, hsl, hwb) will return results in the color(srgb ... ) format, unbounded. This mostly means resetting test expectations. gamut-mapping.html has been renamed as color-mix-out-of-gamut.html to reflect the fact that there is no longer any expectation of "mapping." The test changes are proposed to be a part of interop 2023 web-platform-tests/interop#333 Bug: 1427304 Change-Id: I546619c58b8603acb2879e787c421ab339da3288
w3c/csswg-drafts#8444 (comment) In order to avoid painful and unexpected gamut mapping, color-mix and relative color with all legacy color formats (rgb, rgba, hsl, hwb) will return results in the color(srgb ... ) format, unbounded. This mostly means resetting test expectations. gamut-mapping.html has been renamed as color-mix-out-of-gamut.html to reflect the fact that there is no longer any expectation of "mapping." The test changes are proposed to be a part of interop 2023 web-platform-tests/interop#333 Bug: 1427304 Change-Id: I546619c58b8603acb2879e787c421ab339da3288 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4528835 Reviewed-by: Yi Xu <yiyix@chromium.org> Commit-Queue: Aaron Krajeski <aaronhk@chromium.org> Cr-Commit-Position: refs/heads/main@{#1145537}
w3c/csswg-drafts#8444 (comment) In order to avoid painful and unexpected gamut mapping, color-mix and relative color with all legacy color formats (rgb, rgba, hsl, hwb) will return results in the color(srgb ... ) format, unbounded. This mostly means resetting test expectations. gamut-mapping.html has been renamed as color-mix-out-of-gamut.html to reflect the fact that there is no longer any expectation of "mapping." The test changes are proposed to be a part of interop 2023 web-platform-tests/interop#333 Bug: 1427304 Change-Id: I546619c58b8603acb2879e787c421ab339da3288 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4528835 Reviewed-by: Yi Xu <yiyix@chromium.org> Commit-Queue: Aaron Krajeski <aaronhk@chromium.org> Cr-Commit-Position: refs/heads/main@{#1145537}
w3c/csswg-drafts#8444 (comment) In order to avoid painful and unexpected gamut mapping, color-mix and relative color with all legacy color formats (rgb, rgba, hsl, hwb) will return results in the color(srgb ... ) format, unbounded. This mostly means resetting test expectations. gamut-mapping.html has been renamed as color-mix-out-of-gamut.html to reflect the fact that there is no longer any expectation of "mapping." The test changes are proposed to be a part of interop 2023 web-platform-tests/interop#333 Bug: 1427304 Change-Id: I546619c58b8603acb2879e787c421ab339da3288 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4528835 Reviewed-by: Yi Xu <yiyix@chromium.org> Commit-Queue: Aaron Krajeski <aaronhk@chromium.org> Cr-Commit-Position: refs/heads/main@{#1145537}
… color(srgb ... ), a=testonly Automatic update from web-platform-tests Serialize color-mix with legacy color as color(srgb ... ) w3c/csswg-drafts#8444 (comment) In order to avoid painful and unexpected gamut mapping, color-mix and relative color with all legacy color formats (rgb, rgba, hsl, hwb) will return results in the color(srgb ... ) format, unbounded. This mostly means resetting test expectations. gamut-mapping.html has been renamed as color-mix-out-of-gamut.html to reflect the fact that there is no longer any expectation of "mapping." The test changes are proposed to be a part of interop 2023 web-platform-tests/interop#333 Bug: 1427304 Change-Id: I546619c58b8603acb2879e787c421ab339da3288 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4528835 Reviewed-by: Yi Xu <yiyix@chromium.org> Commit-Queue: Aaron Krajeski <aaronhk@chromium.org> Cr-Commit-Position: refs/heads/main@{#1145537} -- wpt-commits: 7b226a12f55910b4ae6ce873d15de1723d042720 wpt-pr: 40001
… color(srgb ... ), a=testonly Automatic update from web-platform-tests Serialize color-mix with legacy color as color(srgb ... ) w3c/csswg-drafts#8444 (comment) In order to avoid painful and unexpected gamut mapping, color-mix and relative color with all legacy color formats (rgb, rgba, hsl, hwb) will return results in the color(srgb ... ) format, unbounded. This mostly means resetting test expectations. gamut-mapping.html has been renamed as color-mix-out-of-gamut.html to reflect the fact that there is no longer any expectation of "mapping." The test changes are proposed to be a part of interop 2023 web-platform-tests/interop#333 Bug: 1427304 Change-Id: I546619c58b8603acb2879e787c421ab339da3288 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4528835 Reviewed-by: Yi Xu <yiyixchromium.org> Commit-Queue: Aaron Krajeski <aaronhkchromium.org> Cr-Commit-Position: refs/heads/main{#1145537} -- wpt-commits: 7b226a12f55910b4ae6ce873d15de1723d042720 wpt-pr: 40001 UltraBlame original commit: 51a2d6b1e6a1350727da5e2692b040ca9906db1b
… color(srgb ... ), a=testonly Automatic update from web-platform-tests Serialize color-mix with legacy color as color(srgb ... ) w3c/csswg-drafts#8444 (comment) In order to avoid painful and unexpected gamut mapping, color-mix and relative color with all legacy color formats (rgb, rgba, hsl, hwb) will return results in the color(srgb ... ) format, unbounded. This mostly means resetting test expectations. gamut-mapping.html has been renamed as color-mix-out-of-gamut.html to reflect the fact that there is no longer any expectation of "mapping." The test changes are proposed to be a part of interop 2023 web-platform-tests/interop#333 Bug: 1427304 Change-Id: I546619c58b8603acb2879e787c421ab339da3288 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4528835 Reviewed-by: Yi Xu <yiyixchromium.org> Commit-Queue: Aaron Krajeski <aaronhkchromium.org> Cr-Commit-Position: refs/heads/main{#1145537} -- wpt-commits: 7b226a12f55910b4ae6ce873d15de1723d042720 wpt-pr: 40001 UltraBlame original commit: 51a2d6b1e6a1350727da5e2692b040ca9906db1b
… color(srgb ... ), a=testonly Automatic update from web-platform-tests Serialize color-mix with legacy color as color(srgb ... ) w3c/csswg-drafts#8444 (comment) In order to avoid painful and unexpected gamut mapping, color-mix and relative color with all legacy color formats (rgb, rgba, hsl, hwb) will return results in the color(srgb ... ) format, unbounded. This mostly means resetting test expectations. gamut-mapping.html has been renamed as color-mix-out-of-gamut.html to reflect the fact that there is no longer any expectation of "mapping." The test changes are proposed to be a part of interop 2023 web-platform-tests/interop#333 Bug: 1427304 Change-Id: I546619c58b8603acb2879e787c421ab339da3288 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4528835 Reviewed-by: Yi Xu <yiyixchromium.org> Commit-Queue: Aaron Krajeski <aaronhkchromium.org> Cr-Commit-Position: refs/heads/main{#1145537} -- wpt-commits: 7b226a12f55910b4ae6ce873d15de1723d042720 wpt-pr: 40001 UltraBlame original commit: 51a2d6b1e6a1350727da5e2692b040ca9906db1b
Note to self the Color 5 hsl color-mix() example still uses gamut mapping on conversion to hsl, and needs to be recalculated. |
…or syntax r=emilio We convert the resulting colors of a color-mix() to srgb modern syntax. This allows the result of a color-mix to be out of gamut, which is now what the spec requires. Discussion here: w3c/csswg-drafts#8444 Differential Revision: https://phabricator.services.mozilla.com/D183647
…or syntax r=emilio We convert the resulting colors of a color-mix() to srgb modern syntax. This allows the result of a color-mix to be out of gamut, which is now what the spec requires. Discussion here: w3c/csswg-drafts#8444 Differential Revision: https://phabricator.services.mozilla.com/D183647 UltraBlame original commit: 748ea6c2376b2b9ce2eadb53990335f67f0682b5
…or syntax r=emilio We convert the resulting colors of a color-mix() to srgb modern syntax. This allows the result of a color-mix to be out of gamut, which is now what the spec requires. Discussion here: w3c/csswg-drafts#8444 Differential Revision: https://phabricator.services.mozilla.com/D183647 UltraBlame original commit: 748ea6c2376b2b9ce2eadb53990335f67f0682b5
…or syntax r=emilio We convert the resulting colors of a color-mix() to srgb modern syntax. This allows the result of a color-mix to be out of gamut, which is now what the spec requires. Discussion here: w3c/csswg-drafts#8444 Differential Revision: https://phabricator.services.mozilla.com/D183647 UltraBlame original commit: 748ea6c2376b2b9ce2eadb53990335f67f0682b5
Closing this (and other issues where no change to the spec is expected). |
I wrote:
Fixed by 9b3bff8 |
If I’m not mistaken, the resolution to serialize the result of RCS using rgb, hsl, etc. as |
…or syntax r=emilio We convert the resulting colors of a color-mix() to srgb modern syntax. This allows the result of a color-mix to be out of gamut, which is now what the spec requires. Discussion here: w3c/csswg-drafts#8444 Differential Revision: https://phabricator.services.mozilla.com/D183647
Section 13.2 of CSS Color 4 contains a non-normative section describing a binary search gamut-mapping algorithm. Though user agents are beginning to ship CSS Color 4, there is still a lot of ongoing debate over the appropriateness of this algorithm (#7610). Proposed alternatives can be found here: #7653.
Some UA developers met recently to discuss the issue. A consensus was reached that it would be better to allow out-of-gamut color conversions to remain out-of-gamut (as opposed to clipping) while the working group continues to develop a gamut mapping algorithm. To that end, we could move the entire section 13 to a CSS Color 5 or 6 while this work is being done.
For CSS Color 4, step 8 in converting colors could be removed. And since colors are now possibly out-of-gamut Converting sRGB Colors to HSL would need to deal with rgb channels outside of the [0,1] range:
Since
color-mix()
from CSS Color 5 is the only feature for color 4 or 5 that can result in intermediate out-of-gamut results, our exposure to out-of-gamut colors would remain relatively minimal and we can preserve interoperability. Leaving out-of-gamut results and not clipping avoids the worst results, like saturation at the boundaries and different results for canvases. It has the added bonus of color conversions round tripping.The text was updated successfully, but these errors were encountered: