Skip to content
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

[css-color-4] Channel clipping breaks author expectations, especially when using 'perceptually uniform' spaces #9449

Open
mirisuzanne opened this issue Oct 9, 2023 · 209 comments
Labels
css-color-4 Current Work

Comments

@mirisuzanne
Copy link
Contributor

mirisuzanne commented Oct 9, 2023

This is not an issue in the css-color-4 spec, but in all the implementations. While issues have been filed on the individual bug trackers, I wanted to raise the issue with the CSSWG since it seems like this was an intentional decision agreed to by the browser vendors.

Here are the individual bug reports:

And, as I understand, the decision was made in these CSSWG issues:

I'm opening a separate issue because I don't have strong feelings about all the details of a gamut mapping algorithm, but I'm pretty frustrated about the state of what browsers shipped here, and I think we need to do something to fix it asap. From an authoring perspective it's entirely unusable, and it breaks the fundamental promise of the format: providing perceptually-uniform lightness.

  • Here's a codepen demo showing two colors with the same hue and lightness values, but vastly different perceptual lightness in the results.
  • Here's a tool for comparing gamut-mapping options - set the lightness low (eg 0.25) and clip gives colors which are over-saturated/too light, set the lightness high (eg .85) and clip gives over-saturated and too dark.

This is the format that authors were most excited about, and it doesn't do what we told them it does. I really wish this feature hadn't shipped at all, since it clearly wasn't ready to ship. Adding agenda+ because I think this deserves more eyes on it, and more urgency in fixing it.

@romainmenke
Copy link
Member

I really wish this feature hadn't shipped at all, since it clearly wasn't ready to ship.

An important aspect is that there is no feature detection for gamut mapping.
Authors can't write a supports query that will make it possible to progressively use (extremely) wide gamut colors safely in browser versions that do not support gamut mapping.

@facelessuser
Copy link

It's probably important to note that there likely isn't a perfect gamut mapping approach, each will probably have some quirks and can be useful if their limits are understood, but anything is better than clipping, which currently is what all browsers do.

OkLCh generally does well for gamut mapping when colors are within the model's ideal range. OkLCh seems to do decent up through rec2020 as the color space maintains a reasonable geometric shape. It also avoids the purple shift that occurs when gamut mapping with LCh.

rec200_in_oklch
https://facelessuser.github.io/coloraide/demos/3d_models.html?space=oklch&gamut=rec2020&edges=false&aspect=false&ortho=false

But we can see that the geometry of the OkLCh space becomes quite distorted for a space like ProPhoto RGB which extends past the visible gamut. This distortion helps contribute to issues like #7071.

prophoto_in_oklch
https://facelessuser.github.io/coloraide/demos/3d_models.html?space=oklch&gamut=prophoto-rgb&edges=false&aspect=false&ortho=false

Gamut mapping with LCh has its own issues, purple shift in the blue range as an example, but the space does hold its shape much better with extreme gamuts allowing for more consistent mapping, but still, some corner cases exists, like with bright yellows due to the geometry in that hue region.

prophoto_in_lch

https://facelessuser.github.io/coloraide/demos/3d_models.html?space=lch&gamut=prophoto-rgb&edges=false&aspect=false&ortho=false

Clipping is still probably worse than either of these options:

Screenshot 2023-10-09 at 6 08 20 PM

@jamesnw
Copy link

jamesnw commented Oct 11, 2023

The same perceptual lightness shift is also present in (ok)lab. Here's a Codepen demo showing (ok)lab and (ok)lch with consistent lightness values, and changing only the a or chroma channels, respectively.

Ok(lab) bug reports-

Both these browser bug reports and in the issue description itself also talk about the separate but connected issue around powerless components. No browsers have implemented this portion of the spec (which is also present on LCH, Oklab, and Oklch specs).

If the lightness of a Lab color is 0%, or 100% both the a and b components are [powerless](https://www.w3.org/TR/css-color-4/#powerless-color-component) and the color represents black, or white, respectively.

These all should be black and white-

There is also related conversation here- #8794

@svgeesus
Copy link
Contributor

I'm pretty frustrated about the state of what browsers shipped here, and I think we need to do something to fix it asap. From an authoring perspective it's entirely unusable, and it breaks the fundamental promise of the format: providing perceptually-uniform lightness.

I agree. After all the work that went into finding a good Gamut Mapping Algorithm that was hue-preserving, lightness-preserving, and thus allowed the closest approximation to a specified color that was out of gamut of the display device, we end up with naive clip shipping in browsers which gives massive hue shifts and even bigger lightness shifts.

And this was done out of a misguided attempt to make 2D Canvas (which is drawing millions of pixels) align with displayed images (which will be using a perceptual gamut mapping, to preserve overall look and image detail) and with CSS (where you have maybe a hundred or so colors in all the stylesheets on a page). Trading off authoring complexity and frustration for minimal gains in computing efficiency of the implementation,

Here is an example: the CSS Color 4 GMA with Oklch on the left, the (old) CSS Color 4 GMA with CIE LCH and DeltaE2000 on the right, and in the middle naive clip which, as cal clearly be seen, for these light colors gives a much darker result quite unlike the requested color.

oklab-gma-clip

Its a screen shot from this demo with OK lightness set to 0.95.

Which is why we see preprocessor plugings like this which take your CSS and auto-generate sRGB fallbacks (using the CSS Color 4 GMA)

Browsers don't support this part of CSS Color 4 yet.
So if you want to have correct colors on all displays you should include both narrow and wide gamut color values. This new plugin helps you to do just that.https://t.co/FyYQOElo90 pic.twitter.com/JSMD2ddEzT

— CSS Tools (@css_tools_) October 9, 2023

image

@svgeesus
Copy link
Contributor

Here is that PostCSS GMA plugin btw

@svgeesus
Copy link
Contributor

svgeesus commented Oct 11, 2023

If the lightness of a Lab color is 0%, or 100% both the a and b components are powerless and the color represents black, or white, respectively.

That portion of the spec has changed because of

it now says:

If the lightness of a Lab color (after clamping) is 0%, or 100% the color will be displayed as black, or white, respectively due to gamut mapping to the display.

which is more correct - the specified color does have chroma, but because of the lightness it will be out of gamut of any SDR display (where the brightest color that can be displayed is media white).

@jamesnw
Copy link

jamesnw commented Oct 12, 2023

Sorry- I was looking at an outdated version of the spec.

I see that L=0 is black and L=1 is white is now covered in the CSS Gamut Mapping to an RGB Destination section-

For colors which are out of range on the Lightness axis, white is returned in the destination color space if the Lightness is greater than or equal to 1.0, while black is returned in the destination color space if the Lightness is less than or equal to 0.0.

@svgeesus
Copy link
Contributor

Sorry- I was looking at an outdated version of the spec.

No problem, we should update the official TR version more often (I keep meaning to but then there is always more to do). But the Editor's Draft is the right place to look for the latest version.

@svgeesus
Copy link
Contributor

@jamesnw wrote:

Here's a Codepen demo showing (ok)lab and (ok)lch with consistent lightness values, and changing only the a or chroma channels, respectively.

So that demo has oklab(90% 0.36 0) which is out of gamut for all RGB colorspaces (even prophoto-rgb) and is rgb(152.937% 10.3745% 83.2625%).

Because Chrome and Firefox do naive clipping, that becomes rgb(100% 10.3745% 83.2625%) which is a much darker fuchsia and is oklch(0.6836 0.29009 338.36). Lightness changed from 90% to 68% because of the clip; oklab(90% 0.36 0) is oklch(0.9 0.36 0) so we also see the hue shifted 21.64 degrees because of the clip.

A CSS gamut mapped version of oklab(90% 0.36 0) to the sRGB gamut is rgb(100% 73.3771% 82.2121%) and taking that back to oklch is oklch(0.861 0.08294 357.323). We still have a lightness shift (to avoid excessive chroma loss), but less so: 90% became 86.1% and a small hue shift too, 2.677 degrees. Much better than the naive clip though.

On a P3 screen, we start from color(display-p3 1.40598 0.3464 0.8253) which CSS gamut mapped to P3 is color(display-p3 1 0.72344 0.82079) and taking that back to Oklch it is oklch(0.86331 0.10669 357.684). Lightness and he shifts similar to the sRGB case, but a better chroma due to the wider gamut P3 screen.

@mirisuzanne wrote:

Here's a codepen demo showing two colors with the same hue and lightness values, but vastly different perceptual lightness in the results.

Similarly this has [oklch(90% 90% 0deg)] which is rgb(152.937% 10.3745% 83.2625%) so naive clip gives rgb(100% 10.3745% 83.2625%) which is oklch(0.6836 0.29009 338.36), a change in lightness from 90% to 68.36% and a change in hue of 21.64deg.

TLDR; clip is a terrible gamut mapping replacement (unless the colors to be clipped are barely out of gamut)

@jamesnw
Copy link

jamesnw commented Nov 29, 2023

So that demo has oklab(90% 0.36 0) which is out of gamut for all RGB colorspaces (even prophoto-rgb) and is rgb(152.937% 10.3745% 83.2625%).

Thanks for the walkthrough of the issue here. I made a Codepen that compares the CSS Algorithm outputs for sRGB and display-p3 with a naive clip (and a comparison with the browser's adjustment, so we can compare when that is fixed).

@ccameron-chromium
Copy link

I agree that the oklab and oklch spaces work best when paired with gamut mapping, and I think that the CSS gamut mapping algorithm produces good results for these spaces.

However, I do believe that the CSS gamut mapping algorithm can be inappropriate to apply to other things like display-p3 colors, because doing so can produce results that are undesirable (e.g, this example with reds). The CSS gamut mapping algorithm is really built for oklab and oklch (I might be tempted to call it something like "okl gamut mapping")

I think that the best way forward would be to "bake" CSS gamut mapping in to the definitions of oklab and oklch.

The difficulty is to define exactly what "baking" to do. Mapping to the display's gamut might be okay, but on sRGB-ish devices, it might do surprising things. When drawing to a canvas, the mapping cannot depend on the device's color space (ignoring fingerprinting, we just wouldn't want the non-determinism), and I don't think that mapping to the canvas' space would be that good (sRGB is the default and is very narrow).

One scheme would be something where we bake a well-known gamut into oklab and oklch, so we end up in effect having oklab-srgb or oklab-p3 or oklab-rec2020 (and the vanilla oklab defaults to one of those). I'm not a huge fan of this. The resulting geometries in oklab are very nonconvex and can have some sharp edges.

A better scheme could be to define a standard polyhedron to always do gamut mapping to, then I think that would be a really good way forward. This polyhedron should be big -- maybe as big as the spectral colors. And it could be made to be convex. (And maybe we could define it as being smooth).

I've been using this tool to visualize some of these options.

@svgeesus
Copy link
Contributor

The CSS gamut mapping algorithm is really built for oklab and oklch (I might be tempted to call it something like "okl gamut mapping")

No, it really isn't. It doesn't care whether the out of gamut color came from prophoto-rgb() or whatever. It is built to use oklch as the color space in which gamut mapping happens, yes. And so your conclusion that

I think that the best way forward would be to "bake" CSS gamut mapping in to the definitions of oklab and oklch.

is entirely unjustified.

@mirisuzanne
Copy link
Contributor Author

mirisuzanne commented Nov 30, 2023

The linked ('red/redder') example demonstrates to me is that rgb clipping works well when there is only one rgb channel in use. That seems like the extreme special case to me. Maybe there could be special handing of single-channel rgb in a gamut mapping algorithm? But as soon as you start combining channels in any color space, channel-clipping will cause hue-shift. That's the 99% case, and the case that gamut mapping is designed to solve.

(and while it may be better to have the 'redder' red in that case, even a slightly desaturated red is a much closer to user-intent than we get from the channel-clipping failure cases. At least it's still red!)

@romainmenke
Copy link
Member

romainmenke commented Nov 30, 2023

I think the red/redder example is a sidetrack because it starts from an incorrect assumption.

It makes the assumption that color(display-p3 1 0 0) is a redder red but that is untrue.

  • red is color(display-p3 0.92 0.2 0.14) (notice that the green and blue channels have different non-zero values)
  • color(display-p3 1 0 0) is a slightly different hue and is also brighter than red

Gamut mapping from color(display-p3 1 0 0) to srgb color space does not result in red because it never was a redder red. It will contain traces of the source being a slightly different hue and being brighter.


  • red is the purest "red" in the srgb color space
  • color(display-p3 1 0 0) is the purest "red" in the display-p3 color space

These both happen to have the maximum value in a single channel and zero values in the others in their respective color space.

Connecting these two values and assuming that one gamut maps to the other is incorrect.
It is seeing a pattern where there is none.

@ccameron-chromium
Copy link

ccameron-chromium commented Dec 1, 2023

With respect to the "red redder", it is not obvious to me that this (the gamut mapped result) is a desirable representation of this (the original, needs a P3 monitor). I don't think that projecting along constant luminance is the right thing to do in the general case, but we can drop that for now.

I would really like to apply gamut mapping to oklab and oklch spaces.

The difficulty with the oklab and oklch space is that, when used correctly, they produce extremely out-of-gamut colors. For example, suppose I like this particular blue (I think it's oklab(46.64% 0 -0.32)), and I want to do a gradient from black to white through that blue. Here's that blue in oklab (sorry, I wrote oklch in the images...).

Visualization of the P3 gamut in oklab space, with a blue color circled

This is what the gradient looks like, with CSS gamut mapping, going from oklab(0% 0 -0.32) through oklab(100% 0 -0.32). It looks great!

Gamut with slicing plane showing gradient from black to white through the previous blue

But there is a problem here! There is a real mapping from points in this 3D space to chromaticity values, and this gradient does not accurately represent those colors.

At the top, at L=99%, the true color is a brilliant blue, but what we see here is almost-white. In the demo app, set gamut mapping to "none" (and enable the various flags), and you'll see this true color.

At the bottom, at L=1%, the chromaticity of the colors is a physically impossible color. In this picture I've added the spectral colors -- everything outside of the convex hull of the spectral colors is not a physically realizable color.

Gamut with spectral colors

The problem with oklab and oklch is that they can produce extremely out of gamut and physically impossible colors. A designer working with these colors, say, on a modern P3 display, may like what they see, but:

  • The author of the content has no way to visualize what they've truly specified.
  • The author may not like the representation on a more powerful display than the one they authored the content on.
  • The author has no way to constrain what they've authored to be exactly what they see on their authoring display.

The simplest solution that I see to this problem is to bake gamut mapping into the definitions of oklab and oklch.

@romainmenke
Copy link
Member

romainmenke commented Dec 1, 2023

@ccameron-chromium I am sorry, but I am not really following.

To me it seems that there are some misconceptions leading to incorrect conclusions about the new color notations, interpolation and gamut mapping.

I am not sure if this particular issue is the best place to answer these questions.

The focus of this issue is that browsers shipped color notations that can express wide gamut colors without implementing gamut mapping.

New issues for your specific questions would be easier to resolve without causing noise in this issue :)

@facelessuser
Copy link

facelessuser commented Dec 1, 2023

I am sorry, but I am not really following.

@romainmenke I think what is being suggested is just to map the points more 3 dimensionally instead of just mapping by reducing chroma in one dimension. It's mapping the geometry of a wider gamut surface down to a smaller gamut surface. This sacrifices preserving lightness, chroma, and hue to gain something the suggester thinks would be more intuitive. What is better can be subjective based on what your intent is.

CSS has chosen to preserve as much of the original color intent as possible by only reducing chroma (though some minor hue and lightness are sacrificed with the clipping via MINDE).

It is like what is expressed in this Oklab gamut mapping article. Do you just project along the chroma dimension? Or do you project in the lightness dimension as well, and if so, then by what degree do you project in the lightness dimension, or how much original lightness are you willing to sacrifice to get what you think is a "better" color? The suggestion being made is to do this more in 3 dimensions, most likely sacrificing more hue and lightness than CSS currently does.

EDIT: I do realize I am oversimplifying the 3-dimensional transform being suggested.

The focus of this issue is that browsers shipped color notations that can express wide gamut colors without implementing gamut mapping.

New issues for your specific questions would be easier to resolve without causing noise in this issue :)

I agree, this probably deserves a separate topic if a different algorithm wants to be discussed.

@svgeesus
Copy link
Contributor

svgeesus commented Dec 1, 2023

The difficulty with the oklab and oklch space is that, when used correctly, they produce extremely out-of-gamut colors.

This is factually incorrect. Highly out of gamut colors can be produced in pretty much any colorspace, including sRGB.

@w3c w3c deleted a comment from sobotka Dec 15, 2023
@ccameron-chromium
Copy link

The difficulty with the oklab and oklch space is that, when used correctly, they produce extremely out-of-gamut colors.

This is factually incorrect. Highly out of gamut colors can be produced in pretty much any colorspace, including sRGB.

I think that the difference is whether or not it is obvious to the content author when they are entering the danger zone.

If specifying colors in an RGB color space, there's the clear signal that "if the parameters are outside of the [0,1] interval, then I'm playing with fire". True, one can specify color(srgb 0 -1 999), but it's clear that that is to set oneself up for a bad day.

Meanwhile, in something like oklab and oklch, the guardrails are less obvious. In the example from the codepen, the endpoints are oklch(90% 10% 0deg) and oklch(90% 90% 0deg). One of these is in the sRGB gamut and the other one is way outside of the the gamut of any existing monitor. Which is which? It's not obvious just by reading them.

It turns out that oklch(90% 90% 0deg) is the one that is way far outside of the gamut of what any existing monitor can produce. It's equivalent to color(rec2020 1.295 0.434 0.797).

For this reason, I think that we should provide content authors with a space that is perceptually uniform but does not suffer from this problem. A cylindrical space something like okhsl would be nice. (That particular formulation is very tightly tied to the sRGB gamut, so it won't do as a verbatim drop-in). The CSS gamut mapping algorithm has the effect of cylinder-ifying the oklab and oklch spaces.

@mirisuzanne
Copy link
Contributor Author

mirisuzanne commented Jan 24, 2024

Sure, there might be an even more perfect color space down the road. It's totally theoretical, but the theory is great. And if that perfect space ever ships in CSS, colors will continue to go out-of-gamut. And authors should get better behavior than random clipping when that happens.

We can't put this on authors, and expect them to just keep all their colors in the gamut. Because the web (by design) is an unreliable context. A cylindrical space with integer boundaries won't change that. We can't expect authors to carefully manage their colors across a web for everyone, on everything. Even the most carefully crafted rec2020 colors will continue to go outside some remaining sRGB monitors. When that happens, CSS should try and help provide a 'close match' for the majority of use-cases, rather than throwing up our hands.

When clipping is anywhere close to author intent, it's pure luck. That's not a solution, it's a stopped clock. We need an approach to out-of-gamut colors that attempts to maintain author intent. Now that browsers have shipped color-mix() and 'perceptually uniform' spaces, that need is even more urgent.

The default behavior should help get 90% of use-cases close-enough. And then we can provide additional tools for authors that need additional precision around the edges.

@mirisuzanne mirisuzanne changed the title [css-color-4] (ok)lch implementations break the entire purpose for authors [css-color-4] Channel clipping breaks author expectations, especially when using 'perceptually uniform' spaces Jan 24, 2024
@ccameron-chromium
Copy link

We can't put this on authors, and expect them to just keep all their colors in the gamut.

I agree with this statement. It shouldn't be expected that an author ensure that all colors be in the gamut of the target device.

What concerns me is something slightly different: Should an author specify a color when they do not (or physically cannot) know what that color actually looks like?

Should an author specify colors that are very far outside of the gamut of any existing display (including the one that the author is using)?

Should an author specify colors that do not physically exist?

@eeeps
Copy link
Contributor

eeeps commented Jan 24, 2024

Should an author specify a color when they do not (or physically cannot) know what that color actually looks like?

"How do we help authors make sensible choices?" is a good question, but it's not the question that's being posed here, which is "how do we best adapt arbitrary content for users on varied hardware"?

And doing worse things for users – especially users on less-capable devices – is not a good answer to the "how do we help authors" question.

@mirisuzanne
Copy link
Contributor Author

If the concern is about color formats that give easy access to a very wide gamut, rather than a concern with adapting those colors for display, then browsers shipped the wrong half of the spec. Now authors are encouraged to use the fully interop/supported wide gamut formats, and there's absolutely no safety net in place to ensure those formats work for people on the other end. This is what has me confused.

We can't put this on authors, and expect them to just keep all their colors in the gamut.

I agree with this statement. It shouldn't be expected that an author ensure that all colors be in the gamut of the target device.

In that case, a solution that is specific to (ok)l** formats is not a solution to this issue. We can't 'bake gamut mapping' into a few wide-gamut formats and be done with it. We would still need gamut mapping for other wide-gamut formats, which may still render on narrower-gamut displays. If we want to also change how a few formats work, that should be a separate issue for discussion.

Trying to move gamut mapping forward, I see a few options on the table.

  • If the priority is to preserve lightness and hue, at the expense of chroma (this matches my experience in the field, but my experience is not universal) the current spec does that decently well using oklch and ΔEOK.
    • In the thread above, @facelessuser points out some tradeoffs with oklch vs lch as the base model, which would warrant a a side-by-side comparison and decision.
  • Is there a solid counter-proposal for a multi-channel mapping algorithm? Can we bring examples to a telecon, and discuss the tradeoffs that we're willing to make?
  • The other hinted-at suggestion, which I may be reading incorrectly, is that different formats represent different authors intents in some heuristic way? Which might lead us to different mapping for different color formats? I can see some logic to that - I would reach for different formats to achieve different goals, but it leads to some strange outcomes:
    • The same out-of-gamut color, on the same narrower-gamut device, is mapped to a different result for reasons that may not be obvious.
    • On the plus side, we're providing tools to change the format of a color, so authors would have an escape hatch?
    • Browsers have to maintain several mapping algorithms? Is that reasonable?

Did I miss something? Can we narrow in on a path forward?

@romainmenke
Copy link
Member

With respect to contradicting arguments:

As I read the points:

  • As hardware advances the displayed colors will radically change.
  • It is fine to clip to rec2020 because hardware won't advance beyond it in the near future or at a rapid pace.

These contradict each other. Or at least to me they are conflicting :)

@tabatkins
Copy link
Member

These contradict each other. Or at least to me they are conflicting :)

Ah, they don't!

On the one hand, as hardware advances, a color well outside of Rec2020 will radically change between how it looks on legacy monitors and how it looks on new monitors. Your pictures of tropical parrots and flowers will just get prettier and prettier.

On the other hand, extremely vivid colors aren't actually needed (or even desired) for most use-cases; Rec2020 is (we think) a decent compromise space that covers the likely near-future usage of colors, and generally contains most colors people actually want to use. You don't actually want to render tropical-macaw red on your website most of the time, it's kinda blinding.

The problem is that while extremely vivid colors aren't needed for most things, they're very easy to specify, including accidentally via innocent-looking color modification (hue rotation, lightening, etc). Someone designing their page, today, on a monitor that is Rec2020 or smaller, can very easily specify a completely wacky color that requires lasers to render, but they'll see a much more reasonable color on their screen, and they'll design against that more reasonable color they see, not what they theoretically specified. Ensuring that the color stays reasonable as monitor tech advances future-proofs the color, and eagerly mapping that into the rec2020 gamut accomplishes this.

@LeaVerou
Copy link
Member

LeaVerou commented Jun 12, 2024

Lots of things to process, so this is not an exhaustive response, but more some thoughts in no particular order:

  • I do agree that it’s ok not to gamut map when using the RGB forms. Author intent seems clear there. This way we can also avoid introducing things like oklch-extended() — just use RCS to convert to one of the RGB forms if you want to avoid gamut mapping. I would prefer to scope it to specific color spaces, rather than "everything that uses color().
  • I’m not a huge fan to gamut mapping to a particular gamut and getting stuck with it for time immemorial. We have already made this mistake once. I do think it could be ok if the screen gamut is considered. E.g. you gamut map to Rec.2020/P3 OR the device gamut, whatever is larger for that particular color.
  • I think one thing many people have not realized is that we would not wake up one day and suddenly we'd have displays capable of ProPhoto colors. Any gamut evolution happens incredibly gradually — going from sRGB to P3 took over a decade. So I don’t think the problem @ccameron-chromium is worrying about of extremely bright colors will be as prominent in practice, especially if dev tools evolve quickly to show gamut (it could be as simple as a gamut indicator like the one here).
  • I think it's quite important to give authors control of what to prioritize: hue, chroma, lightness, relationships between colors, etc. since use cases have drastically different demands. For accessibility, I think preserving lightness should probably be the default.
  • Preserving chroma is not a stable goal and thus should not be the default. There is always a color with the same L or H that is in gamut, for any gamut. This guarantee does not exist for chroma.
  • I still maintain that centering CSS gamut mapping around the use case of color matching with images is the wrong order of priorities. I understand this use case is important to certain folks, but it is not the top priority when working with CSS color. I would prefer we design around authors' actual use cases and offer a switch for the cases where matching image color is a priority.
  • GMAs that change in-gamut colors are not an option for CSS, due to web compat if nothing else.
  • Do note that we could use different GM strategies for gradients and colors used in other contexts.

@mirisuzanne
Copy link
Contributor Author

Doing a quick comparison with the Chrome css gamut mapping feature flag turned on – which I'm told matches this proposal – I'm pretty happy with the results. It is much better than the current clipping behavior.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-color-4] Channel clipping breaks author expectations, especially when using 'perceptually uniform' spaces, and agreed to the following:

  • RESOLVED: Gamut map all device independent color spaces to Rec.2020 for now, transforming from there to device gamut is undefined. Exact gamut-mapping algorithm TBD. Continue discussing in the issue.
  • RESOLVED: RGB color spaces (srgb, display-p3, rec2020, a98rgb) do not gamut map.
  • ACTION ccameron: review gamut mapping algorithms
  • ACTION lea, chris, mia to review Chrome proposal discussed today
The full IRC log of that discussion <TabAtkins> https://github.com//issues/9449#issuecomment-2162288182
<jarhar> miriam: i opneed this issue because theres a number fo new colro sapces that are intended to be perceptually uniform and wide gamut in fa ct unbounded gamut. and in order for a color space to be perceptually uniform, it cant be spatially uniform. they dont have nice cylindrical edges like hsl. you cant have both of those things. these color spaces
<jarhar> are exceiting to authors for being able to move around in color space in ways thatfeel perceptually reliable. if i add a certain amount of lightness it should be a certain amount lighter which is not possible in hsl
<jarhar> miriam: the problem is, because we dont have uniform edges, we end up outside of them quite easily, at that point we have to decide what to do. the spec tries to handle that by maintining lightness and sacrificing chroma with an algorithm, proposal s for preserve lightness aprpoach. in browsers, corpping rgb channels can give you strange changes in
<jarhar> lightness and huge, its the worst of all cases
<jarhar> miriam: thats the summary of the issue. browsers ship the problem and not the solution, so some of the arguments should be that we shouldn't have color spaces that allow you to do this, we shipped the way to easily make mistakes but did not guardrail, we need some sort of guardrail in these spaces, to me that is preserving lightness
<lea> q?
<jarhar> miriam: that is starting to get into other things, ok bye
<jarhar> fantasai: tab you have a proposal?
<lea> q+
<una> (As a non-color expert I can +1 the pain points of using modern color syntax and getting unexpected results)
<jarhar> TabAtkins: ok, so as miriam said if you use a color far outside your gamut its going to be mapped to your gamut. the current proposal makes that depend on your monitor which can result in different colors displayed on the screen, especially since lightness preserving tends to sacrifice chroma, when you look at your screen and see low chroma they
<jarhar> design your color, they use that in their design, in the better monitor they get an insanely vivid pink instead of light coral
<jarhar> TabAtkins: current approach isnt future compatible with monitor technology if you specify colors outside of gamut when designing the page
<ChrisL> The "insane pink" is due to switching into HDR mode, so the comparison is invalid
<ChrisL> the claim of "no perceptual mapping" in ICC profiles is invalid
<ChrisL> q+
<jarhar> TabAtkins: second thing is that ever since css2, css colors and image colors have been magical. via the introduction of - color profile stuff, you can have an ff0000 red and and ff000000 in your page look the same, and its important for having your page colors match up with images used on the page and dont want color drops
<lea> q-
<lea> q++
<lea> q+
<jarhar> TabAtkins: make sure that images and css colors work in the same way by default even if the fact that images and videos need to use cheap mapping methods rather than good mapping methods. were ok with authors opt into breaking that consistency even if the final result is not as ideal
<jarhar> TabAtkins: these altogether, whenever youre specifying a color with oklab or oklch functions, we eagerly map that into direct2020 gamut. somewhat arbitrary choice, we can accept others, fairly light gamut likely to encompass monitors for the future, small enough that even when you do cheap easy clipping into srgb you still get a color close to the
<jarhar> design intent. if designer is direct2020 and user srgb or vice versa it will still work out pretty well
<jarhar> TabAtkins: we dont care very much what the mapping method is to take the far out of gamut oklab color into 2020. if the spec wants to preserve lightness were ok with that
<jarhar> TabAtkins: do it eagerly so its future proof, once its in that gamut we want to make sure its allowed by the spec to match up with how we handle images and videos etc. because the quality mapping is fairly critical in that region. we do expect there to be an opt out method alternate function what have you that lets you specify oklab or oklch thats
<jarhar> not evenly mapped
<kbabbitt> s/direct2020/rec2020/
<ChrisL> s/evenly/eagerly/
<ChrisL> s/direct2020/rec2020/g
<jarhar> TabAtkins: ??? and you know its going to be played on a quality monitor, and then the other methods of specifying colors, that use arbitrary color schemes we prefer to leave those alone, as the browser sees fit video in that color profile, those arent designed for easy author useage on the page, they are sdesigned for hardware ? matching what they
<jarhar> might be used in an image for video we dont need to worry about authors to see a weird color unless theyre trying to match an image win which case it should match
<jarhar> TabAtkins: eagerly map oklab into 2020 gamut and if you need to reduce further from there provide methods for that, dont touch other colors, render as accurately as it can
<florian> q?
<florian> q+
<jarhar> TabAtkins: apple and mozilla folks are broadly supportive of that. simon finds it reasonable
<jarhar> TabAtkins: he also supports the ability to give authors choice of gamut mapping
<jarhar> TabAtkins: ?? from mozilla says that 2020 might be iffy, but doesn't think its a blocker and seems reasonable
<jarhar> TabAtkins: other thoughts?
<TabAtkins> s/??/Tiann Louw/
<jarhar> ChrisL: i want to address two inaccurate statements. first is about colors that were wildly different vivid pinks, that was done by sdr color imagery and then throwing in an hdr color
<jarhar> ChrisL: so yeah ok thats a very different color, the other one which is frequently stated but not demonstrated that all colros inside gamut will never change. since i just mentioned hdr, hdr has sigmoid curve you will bring the darks down and lights up which means colors in gamut will change
<TabAtkins> q+ ccameron
<jarhar> ChrisL: if you have a perceptual rendering intent then colors inside the gamut will chagne so smooth gradients continue to look smooth. the other - thats perceptual rednering intent, relative ? will guarantee colors wont change, thats not the only option
<jarhar> ChrisL: its depends on what the ? is
<jarhar> ChrisL: firstly ill point out that this proposal droped an hour before the meeting, i have not had time to play with it and see what its properties are, its an interesting proposal but it hasn't been played with
<jarhar> ChrisL: cheap gamut mapping methods: people have proposed 5 or 6 methods of cheap gamut mapping which haven't received comments
<jarhar> ChrisL: some of them are very cheap like linearized ?? and then clip or compress down, single step process no loops very efficient could probably be done in hardware, no comments
<miriam> q+
<jarhar> ChrisL: hardware can do this because games work in linear light, so could be quite efficient but no comment on that
<ChrisL> https://apps.colorjs.io/gamut-mapping/?
<jarhar> ChrisL: theres a gamut mapping playground where all o fthese have been implemented, you can put different colors in
<jarhar> ChrisL: theres been no comment from the chrome team about these proposals
<jarhar> ChrisL: some aspects are interesting, some aspects are concerning. this eager converting to 2020 gamut. if i take something out of gamut in oklch and gamut is 30 and this eager conversion, what happens when i get back the computed value? are they wildly different? has the chroma reduction happened? its saying that you specify one value and you use
<jarhar> a different value - how observable is that? it has changed, this seems odd to me
<jarhar> ChrisL: in terms of the 2020 reasonable target, id say, the reason i say that is because all the primaries are in spectral lockers, you can only get them with 100% ? but then youd have greater than 100% ?? which is why youll never have a pro monitor because ?? the triangle can cover as much of the area as possible
<lea> s/lockers/locus/
<dbaron> s/pro monitor/ProPhoto monitor/
<jarhar> ChrisL: was the point clear? im agreeing with the proposal there are real colors outside it but not very many, you have to look for real world examples. as a - if we have to have this as a boundary as a reasonable or unreasonable color, direct 2020 seems good ot me but im worried about the impact if this conversion is done behind the scenes, how do
<jarhar> authors work with this
<jarhar> TabAtkins: exact timing is - exactly what we want for gradients to work well is unclear, it would show up by computed value time, the conversion might happen at a point - is still a detail to be woked out. it should be good for authors
<jarhar> florian: i wanted to ask what do you mean by eagerly convert. is it parsing time? computing time? do you bring in direct2020 first and then do the color math? if you have functions do you convert before or after that?
<ChrisL> s/outside it/outside rec2020/
<jarhar> florian: i think im hearing you say its up in the air, but maybe youve done that already
<ChrisL> s/lockers/locus/
<jarhar> ccameron-chromium: i think the way - within the general frame we are flexible, my way that im thinkig about it is to view oklab as having th egamupt mapping be a part of the definition of the coordinate spaces
<ChrisL> s/100% ?/100% of one wavelength/
<jarhar> ccameron-chromium: it would only be observable if you round to a different color space. it would be withini that definition of srgb or xyz or whatever your connection space is. in implementation terms this is done per pixel
<jarhar> ccameron-chromium: does that answer the question? its in the definnition of how the coordinates are defined in the coordinate space
<lea> q?
<jarhar> florian: maybe? what im wondering about for example is as ? was saying, the boundaries are not uniform so if you tahke a color that - if youre gpoing to rotate it around, whether its in or out of gamut will depend on the view. so do you bring it into gamut first and then rotate or vice versa? would make a difference. i suspect what we want is
<jarhar> predictability so if its done late thats fine, that would preserve uniformity, but i havent thought deeply about this
<jarhar> ccameron-chromium: that sounds good if one is ?? doing this rotation, it would be that you rotate first then map, it may be that if one is doing a rotation in the context of some other operation then maybe we talk about ahaving an escape hatch to get out ?? i think thats particular case is smalll
<florian> q-
<jarhar> ccameron-chromium: rotate then map vs other is rotate then map because youre working within the coordinate system
<jarhar> ccameron-chromium: how to implement it, its really thinking of it as a coordinate system rather than a mapping on all colors
<jarhar> florian: makes sense, thanks
<RRSAgent> I have made the request to generate https://www.w3.org/2024/06/12-css-minutes.html fantasai
<jarhar> lea: so lots of things to process. i left a comment but im going to summarize my points here. i do agree that its ok not to gamut map for rgb formats. i would rather not spec it as anything using the color funciton and scope it down to color spaces like display p3
<fantasai> s/Topic:/Subtopic:/
<fantasai> s/Topic:/Subtopic:/
<jarhar> lea: especially since down the line we plan to add custmo color spaces and we dont know what they would be or if they woul dbenfit from gamut mapping
<RRSAgent> I have made the request to generate https://www.w3.org/2024/06/12-css-minutes.html fantasai
<jarhar> lea: ??? once rgb formats dont gamut map you need relative color syntax to map to relative syntax and then you dont have gamut mapping
<jarhar> lea: i am not a huge fan of tying down css to a color space, id rather not repeat the same mistake, i think it woudl lbe ok to do that as long as we concisder the devide and map to whatever is larger for that color, but counter to what the proposal was trying to do. what many people have not realized is that ccameron said that people can specify
<jarhar> imaginary colors and then monitors can do it and then you have a color thats burning through your eyes, but they are happening gradually
<jarhar> lea: were talking about extremely bright colors on unmaintained websites, but these websites tend to not be the ones that what most users are using
<jarhar> lea: and it happens so gradually that people do have time to adapt. its not like suddenly a display comes out that that can display ?
<TabAtkins> We do not, usually, intentionally design for slow breakage when we have a way to avoid it.
<jarhar> lea: worry is overprounounced compared to reality. i do think its important to let authors control
<jarhar> lea: different use cases prioritize different things. some cases propritize color mapping between image and videos. others abou tlightness preservation or hue or chroma
<jarhar> lea: having a control for this would be the best of all worlds. by default it should be lightness were preserving for ax. anything esely you preserve could have readable for developers but not readable for other peopls monitors
<jarhar> lea: and preserving chroma should not be the default because its not a stable stragegy.
<jarhar> lea: if we preserve lightness or hue then theres always a color in gamut for any gamut. for chroma theres no such garauantee because its usually chroma that gets you out of gamut. centering design aroung images and videos is wron gpriori8ty. i dont think its as common as preserving author intent all over ui, far more important use case than images
<jarhar> and videos, which there should be some way to get it but we shouldn't be designing entire css color sysntax around this one use case.
<jarhar> lea: people mentioned perceptual rendering intents that end up changing colors that are already in gamut, i dont think thats an optipn for css, we should preserve colors in gamut
<jarhar> lea: if we start changing colros in gamut we could be changing srgb colors that are widely employed. we are not bound to using the same gamut mapping stragety geverywhere
<jarhar> lea: we scould use one for gradients and use another one for background and borders or text colors or anything like that.
<ChrisL> Here is a chromaticity diagram for ProPhoto, showing primaries well outside the visible limits
<ChrisL> https://drafts.csswg.org/css-color-4/images/UCS-prophoto.svg
<ChrisL> q+
<dbaron> +1 to perceptual rendering intents that change colors in gamut not being a reasonable option for CSS
<florian> q+
<jarhar> miriam: thought overlap with lea's, like chris said i havent played with this so i dont know the results of it. i am frustreated in the proposal that all of the prioritizeds are around image and video matching whent his entire discussion started out of author requests for some other prioritizes
<TabAtkins> That's... not at all true? It's part, but the future-hostility is the biggest part.
<jarhar> miriam: feels absurd that those get dropped every time we come to it
<TabAtkins> It's *the first thin in my list*
<jarhar> miriam: yes image and video matching are important but please stop dropping all the other author use cases
<TabAtkins> whooops i'm out, my uber is here in two minutes
<lea> +infinity to what miriam just said
<ccameron-chromium> q+
<lea> 👏🏼👏🏼👏🏼
<jarhar> miriam: the other frustration there is when i proivde example to hue shift has problems for other cases im told thats an edge case, but then the counter example is this bright pink that required jumping into hdr rendering, and that is somehow not a strange edge case, so just frustrated about how this is presented, but maybe its fine i should go
<jarhar> play with it
<lea> q?
<jarhar> ccameron-chromium: playing with it, theres an experimental flag in chromium that we published a while ago called css gamut mapping. do give that a try, unfortuantely its been a while so its there in stable right now but its expired so we have to make sure its still kept alive
<lea> qq+ to ask ccameron-chromium if the current behavior in stable Chrome is still clipping
<jarhar> ccameron-chromium: that is roughly the place and time of where it would be applied. in terms of priories, its important to have prioritises - important to address use cases
<jarhar> ccameron-chromium: the main thing of the proposal is that it doesn't break color matching, if color matching is to be broken we want users to say this is my intention. that flag in chromium modula o the xact math it does what the proposal is proposing
<jarhar> lea: is clipping the default behavior in stable chrome/? i remember experiments with other behaviors without a flag
<jarhar> ccameron-chromium: its always been that we will clip it at a certain point, near or above the monitor. there will be some clipping
<jarhar> ccameron-chromium: in some circumstances the clipping is done further down the pipeline than us, it will be done either by windows or macos, and the os has the option to do something mroe but chrome does clipping of all colors specificed except for that flag, ?? will be gamut mapped with that flag
<jarhar> lea: so if that flag is not set then it just passes it to the os without clipping?
<jarhar> ccameron-chromium: yes
<Zakim> lea, you wanted to react to ccameron to ask ccameron-chromium if the current behavior in stable Chrome is still clipping
<lea> q?
<jarhar> florian: for a while without having thought of the eact proposal that something resembling it is likely part of the solution set that we want to look after. whether that should be dthe default behaiovri is another question. which use case tdo we prioritize? we have mu;ltiple use cases and this or something like this probably should be available.
<jarhar> wehther matchig videos and images is the thing we prioritize or whether we go closer to some other things that mirian was talking about and opt into this
<ChrisL> https://drafts.csswg.org/css-color-4/images/UCS-prophoto.svg vs https://drafts.csswg.org/css-color-4/images/UCS-rec2020.svg
<jarhar> ChrisL: so firstly i want to drop two links to images
<jarhar> ChrisL: one shows primaries are outsid ethe primaries of the shape, the 10% pure wavelength there are no colors outside that. if you compare that to direct 2020 it is exactly on the edge, you cant get bigger, it sthe largest gamma you can get if you have three primaries ant ehpeimriare s have to be real were not going to get biger than that unless
<jarhar> we ? primaries
<jarhar> ChrisL: question: when chrome does hdr mode for image and video is any tone mapping applied?
<astearns> s/?/use more /
<lea> ChrisL: These diagrams have two triangles. What's the inner one? sRGB?
<jarhar> ccameron-chromium: videos are passed to the os and ? mappings apply, for images tone mappi9ng apply to get you into the tone range of the monitor. there are the vast majority 99.999% of images that are hdr are done using the ? map format, with that format it is possible to specify color match with css
<jarhar> ccameron-chromium: you can specify that colors reamin gifxed, a specific pixel remains fixed, so you can color match wih css or with video or anything else
<jarhar> ChrisL: so youre saying that if you have a gain map image you have to tailer the gain map so that the pixels that you want to be unchagned remain unchanged right?
<Zakim> florian, you wanted to react to florian
<lea> q+ to ask, if we do adopt the proposal to gamut map to Rec.2020, can we change it later, as screen gamuts evolve? Or will we be stuck with Rec.2020 forever?
<jarhar> ccameron-chromium: ? has to specify ? so if you have an emoji that you put into your image then ? will do tone mapping, otherwise people use ? to determine what that will be, but every pixel comes with tone mapping, i think tab or someone suggested that css could have an equivalent thing where you specify theres the max of what i would want this to
<jarhar> be, and im supportive
<jarhar> ChrisL: so if i go into current builds of photoshop and export a gainmap image, heres my hdr photo i just took and i export i t out it calculates the gainmap there will be no clor match because the gainmap software will interpolable between sdr and hdr and that tone mapping operation will chagne all of the in gamut colors unless i specificaly edit
<jarhar> the gainmap unless i say these are the ones i dont want to chagne. im pushing on this because as mia said we are moving to a model where mapping between images and colors an ddoing perceptual manipulations is low priority and yet in the gainmap image speac eyou have to identify pixels you dont want to chagne otherwise they will change
<jarhar> ccameron-chromium: with the gainmap images in particular, if one exports it from photoshop, they key thing is the determinism there. is that its well defiend how - there are two points you move between. the key takeaway is to look at whats oign gon in the world of hdr video where hdr video color matching is not possible and i would characterisze
<jarhar> hdr video as when viewed in desktop computers and mobile devices as a sdisaseter zone. every application that tries to integrate hdr video into their app is frustrating and gives up and goes to sdr because the interaction is underdefined in there
<jarhar> ccameron-chromium: in terms of color matching gainmap images give a ? where that is possible. if you dont need to colo rmatch to any color of that image thn thats fine if you go into ps and export and sdr image you can ? color matchi f you want to,
<jarhar> ccameron-chromium: ?? color matching between images and videos it was behind every corner, the key thing is to think of it as a parameter space. better to give users parameter spaces where theyre going to bget a specific behavior. would like heres a ? preserving space, heres a hsl kind of space whereits more chroma perserving, heres something where
<jarhar> its more perceptual. color matchihng is the cornerstone of color management and its very hard to make a system that works twithout it
<jarhar> ccameron-chromium: the other different options are - im a fan of ? a different option to have them, ? from the pserspective of color matching, i like it woudl be wonderful to support css colo s that have some sort of gainmap like scheme ? and were ok with anything in between them
<jarhar> dbaron: repeating what tab said on irc before he left, which is that what he said is that his top motivation in response to the argument that e primary motivation was to do image and ivdeo matching, tab had said that his primary motivation was about the future compat issues and not about image
<jarhar> dbaron: i dont want to put words in his mouth but it soudned like it was a motivation but the biggest was future compat
<jarhar> ccameron-chromium: in terms of the compatability thing in 2020 vs other spaces, i would emphazie that im fine with other options as long as its well defined. there is this difficult that its hard to define whats a wide color sdr color vs hdr color, in some discussions were discussiong we can do it based on ??? sheet of paper and that kind of thing
<jarhar> as sdr
<jarhar> ccameron-chromium: srgb colors go outside of that gamut, so its very hard to find a dividing line between sdr and hdr, that will always be some boundary in place and map to that, any definition is fine, 2020 was what ??
<miriam> (doing only a quick comparison, I'm pretty happy with the results of the chrome algo so far)
<lea> q?
<Zakim> lea, you wanted to ask, if we do adopt the proposal to gamut map to Rec.2020, can we change it later, as screen gamuts evolve? Or will we be stuck with Rec.2020 forever?
<jarhar> lea: if that is the case, if we can change it alter then gtiven that tehde current situation is the worst of all words,, ,right now we have the terrible sitation the me and mia have describes and that these colors are unusuable, and given the fact that - i think there is even if we dont have consensus about everything we do have consensus baout
<jarhar> some things. we have consensus around rgb spaces not gamut mapping, author intent is clear there, hard to ?? without intending to, given taht the blink folks are willing to gamut mapping with 2020, and open to changingi it alter, we could resolve to do that while exploring better solutions because the longer we go without any mapping the harder it
<jarhar> willb eto change, not hard to go to 2020 to p3 than the current situation where verything is clipped
<jarhar> lea: i would be in favor of resolving for that as long as its not the end of gamut mapping and that this is just lets resolve on this because its better. once we resolve on this then foolks wil give us feedback because weve been trying to get this feedback for months or years with no response
<jarhar> lea: as chris described earlier
<jarhar> fantasai: proposal on the table to do gamut mapping by ok stuff to rec2020 and continue discussing the issue
<jarhar> lea: this should not be framed around spaces like oklch but device and ? color spaces. right now they are oklab oklch ? and ?
<jarhar> lea: should also apply to future spaces, they will have the same problem
<ccameron-chromium> with respect various gamut mapping algorithms, it's an issue that the gamut mapping is applied at a time that breaks color matching.
<jarhar> fantasai: i would like the chrome reps and peole in this issue what are the action items you want to assign to each other because we are going to assiign actin items
<jarhar> lea: is the proposal that we gamut map to 2020 and then its normative to clip or that they re allowed to clip
<jarhar> fantasai: leave it undefined and continue discussing
<lea> PROPOSED RESOLUTION: Gamut map all device independent color spaces to Rec.2020 for now, UAs are allowed to clip or gamut map from there to device gamut. Exact GMA TBD
<jarhar> ccameron-chromium: would it be ok to respond to whether ? algorithms or are we short on time?
<dbaron> ccameron: I agree with that [leave it undefined]. [earlier]
<jarhar> fserb: what about having a way to opt out of this?
<lea> PROPOSED RESOLUTION: RGB color spaces do not gamut map (srgb, display-p3, rec2020, a98rgb).
<jarhar> ccameron-chromium: chromium is ok with ok spaces, for lab and lch were a bit more afraid of that because lab is used more for ?? color matching, were a little bit uncomfortable with that but ??
<jarhar> lea: about escape hatch, relative color syntax to go to one of the rgb spaces
<jarhar> ChrisL: resolution doesn't say when it happens so we can timplement it
<fantasai> PROPOSED: Gamut map all device independent color spaces to Rec.2020 for now, transforming from there to device gamut is undefined. Exact gamut-mapping algorithm TBD. Continue discussing in the issue.
<dbaron> s/more for ??/as a canonical color space for ICC/ [??]
<jarhar> fantasai: continue discussing in issue
<fantasai> RESOLVED: Gamut map all device independent color spaces to Rec.2020 for now, transforming from there to device gamut is undefined. Exact gamut-mapping algorithm TBD. Continue discussing in the issue.
<jarhar> lea: im not sure about xyz ones
<jarhar> fantasai: does anyone want to discuss that resolution further?
<jarhar> dbaron: just to reword so its clear what its a list of
<fantasai> PROPOSED: RGB color spaces (srgb, display-p3, rec2020, a98rgb) do not gamut map.
<jarhar> ccameron-chromium: is the resolution oklab oklch maybe some other things question mark and then not gamut map rgb?
<jarhar> ccameron-chromium: sounds good
<jarhar> ccameron-chromium: rest is still under discussion?
<jarhar> fantasai: uyes
<fantasai> RESOLVED: RGB color spaces (srgb, display-p3, rec2020, a98rgb) do not gamut map.
<jarhar> fantasai: now i want to ask people what action items should be taken away from here? lea wants feedback on gamut mapping algos so we can pick a good one. action item for?
<jarhar> lea: ccameron
<jarhar> ccameron-chromium: i think the core issue ive been failing to communicate is the breaking of color matching and specifying far out of gamut colors and particular choide of algo is separate to that. its about where the algos fit but now which one it is
<lea> q+
<jarhar> ccameron-chromium: earlier i had issues with the algos perf characteristics, more the problem with the approach of where mapping is being placed
<jarhar> fantasai: will you review the gamut mapping algos?
<jarhar> lea: we need feedback from someone
<jarhar> ccameron-chromium: i can look at them again
<jarhar> fantasai: then chris lea and ? to look at chrome proposal
<astearns> s/?/mia/
<fantasai> ACTION ccameron: review gamut mapping algorithms
<fantasai> ACTION lea, chris, mia to review Chrome proposal discussed today

@svgeesus
Copy link
Contributor

CSS and images/videos have had color-matching in sRGB and P3 for many years. (This wasn't always true, but we were able to pull it off in CSS2.) This is still a common, important use-case for authors, and\ as images are now being deployed in the wild using P3-compatible gamuts or wider, we want to preserve it.

* We should ensure that, _by default_, CSS colors and image/video colors map 
   in the same way so color-matching works when both are using the same gamut, 
   at least within realistic image/video gamuts.

Pushing some more on this assertion that in-gamut colors in images never change, here are a few useful diagrams from Ján Morovič "Color Gamut Mapping". These are all using typical RGB to CMYK gamuts so absolutely no "unreasonable" colors.

The first shows a variety of input-to-output transfer functions used in gamut mapping; only one (here, labelled clipping) has a straight line where output=input for in-gamut colors. The others use a soft knee, or a sigmoidal function, to ease the transition and thus, some or all in-gamut colors change.

CGM-54

Next, a division of gamut volume into core colors which will not change, destination colors (the intersection of source and destination, minus core) where gamut compression is applied, and source (outside destination; all colors will change):

CGM-1011

And lastly, a constant hue and lightness-compressing GMA which uses that core/destination division to perform gamut mapping into the destination:

CGM-1018

@romainmenke
Copy link
Member

Wasn't clear from the proposal or the minutes, but is gamut mapping still the last step?

Current steps around interpolation, color space conversions, relative colors,... are quite complex and depend on gamut mapping happening at a later stage, right before display.

Would this still be true? Or would it happen earlier?


Has the prototype in Chrome advanced enough to be able to test color mixing, relative colors, gradients, ...?

Simple color declarations aren't the best way to validate the proposed solution, more advanced values allows us to stress test this a bit more.

@eeeps
Copy link
Contributor

eeeps commented Jun 12, 2024

@tabatkins:

(2) We propose that, when mapping a used color into the monitor gamut, by default it is done by naive r/g/b channel clipping, to match existing image/video gamut reduction behavior.

Clipping Rec.2020 colors to, e.g., sRGB sometimes produces significant lightness shifts vs doing what's in the spec now (which tries to preserve lightness above all else). Here's a ~worst-case, real-world example of how different results can be, even post-mapping-to-Rec.2020: https://codepen.io/eeeps/pen/zYQPqKM

@LeaVerou:

I think it's quite important to give authors control of what to prioritize: hue, chroma, lightness, relationships between colors, etc. since use cases have drastically different demands. For accessibility, I think preserving lightness should probably be the default.

I agree; "match the image processing pipeline" should be an option, but I would prefer if "preserve lightness" were the default – not only for the proposed first stage, where wildly out-of-gamut OkLCH colors are mapped to Rec.2020, but also when doing the proposed second stage: mapping Rec.2020 colors to physical display gamuts.

@jamesnw
Copy link

jamesnw commented Jun 13, 2024

@eeeps:

Clipping Rec.2020 colors to, e.g., sRGB sometimes produces significant lightness shifts vs doing what's in the spec now (which tries to preserve lightness above all else). Here's a ~worst-case, real-world example of how different results can be, even post-mapping-to-Rec.2020: https://codepen.io/eeeps/pen/zYQPqKM

An alternative could be that we map to either rec2020 or p3, choosing the smallest that has a gamut larger than the user's monitor, and then clip from there. Here's a fork of your codepen, with the addition of an example where we first map to p3, then clip to sRGB. https://codepen.io/jamessw/pen/abrVLvQ

I added the Lightness Delta in oklab, and for that specific super red, this method would reduce the lightness shift by about half. For colors starting outside of rec2020, the lightness preservation by mapping to p3 before clipping would be even more drastic.

@jamesnw
Copy link

jamesnw commented Jun 13, 2024

@romainmenke:

Has the prototype in Chrome advanced enough to be able to test color mixing, relative colors, gradients, ...?

Simple color declarations aren't the best way to validate the proposed solution, more advanced values allows us to stress test this a bit more.

There is a port of the Chromium algorithm in the Color.js Gamut Mapping apps at https://apps.colorjs.io/gamut-mapping/ and https://apps.colorjs.io/gamut-mapping/gradients which may help with some exploration.

@mirisuzanne
Copy link
Contributor Author

The goal of the two-step process (as I understand) was to "bake step 1 into the format" so it doesn't change over time. So I don't think a contextual first step (either rec2020 or p3) meets the requirement.

@romainmenke
Copy link
Member

romainmenke commented Jul 10, 2024

colorjs doesn't support relative color syntax.

What I am mostly interested in is how colors behave depending on the notation.

With relative color syntax you can change the notation.
A value first described with oklch might end up being used in color(from ... srgb r g b).

https://codepen.io/romainmenke/pen/GRbgMPj

Screenshot 2024-07-10 at 16 55 09

I would still prefer that gamut mapping was applied to all CSS color values and that it doesn't depend on how a value is declared.

With another way to opt-in/out of clipping/gamut mapping for all CSS colors.


Also concerned about mapping to rec2020 and then clipping.

Is there any clipping or gamut mapping that happens before any other interpolation?
Or is clipping/gamut mapping always the last step as intended by the current specification text?

Clipping literally throws away information and in a way that does not preserve the balance between channels.

i.e. these gradients should render differently as they should render the hue circle in opposite directions.

#a {
  background: linear-gradient(to right in oklch longer hue, oklch(from color(srgb 1 -0.01 0) l c h), red 100%);
}

#b {
  background: linear-gradient(to right in oklch longer hue, oklch(from color(srgb 1 0 -0.01) l c h), red 100%);
}

Chrome (with the flag) shows pure red, but maybe not by design given that the implementation is experimental?

https://codepen.io/romainmenke/pen/dyBPZoG?editors=1100

@ccameron-chromium
Copy link

  • ACTION ccameron: review gamut mapping algorithms

I've opened #10579 on this topic (with a request for algorithms to consider).

@romainmenke
Copy link
Member

Are we losing the window where we can still make changes?

Given that there is pretty good interop for most of css-color-4 and css-color-5 I am worried that authors are increasingly likely to depend on the current clipping behavior.

It also seems that the experimental implementation was removed from Chrome?

@LeaVerou
Copy link
Member

Chatting with @jyasskin today, it looks like there has been a miscommunication: @ccameron-chromium has been expecting a gamut mapping algorithm from the CSS WG, and the CSS WG has been waiting for him to review existing algorithms per action item in #9449 (comment) .

We need to resolve this ASAP and move forwards before the window for changes closes. @astearns could we slot this in the TPAC agenda somewhere? 🙏🏼

@astearns
Copy link
Member

@LeaVerou is there a set of existing algorithms that could be listed in #10579?

@LeaVerou
Copy link
Member

@astearns not in a comment, but here: https://apps.colorjs.io/gamut-mapping/
@svgeesus are there any others? Is this an exhaustive list?

@astearns
Copy link
Member

@LeaVerou are all the options in https://apps.colorjs.io/gamut-mapping/ still in play, or could the list be reduced further? A comment in 10579 instead of this one would be useful.

@argyleink
Copy link
Contributor

here's a newer one that may not be tracked https://github.com/texel-org/color

@facelessuser
Copy link

here's a newer one that may not be tracked https://github.com/texel-org/color

IIRC, that is just implementing Björn's gamut mapping algorithm with constant lightness as described here: https://bottosson.github.io/posts/gamutclipping/.

You can see it compared against all the others here: https://deploy-preview-7--color-apps.netlify.app/gamut-mapping/gradients?from=oklch%2890%25+.4+250%29&to=oklch%2840%25+.1+20%29.

I've had a PR to merge it open for a while, but I don't have the power to merge it officially, so the preview is the best way to view it currently.

@svgeesus
Copy link
Contributor

Slightly off topic but

I've had a PR to merge it open for a while, but I don't have the power to merge it officially, so the preview is the best way to view it currently.

Not seeing it?

@svgeesus
Copy link
Contributor

If we give the comparison a bit more work to do then Edge Seeker is the fastest at 2ms, (same speed as clip!) while CSS_Rec2020 is slowest at 10ms (in Firefox on my machine). Times in Chrome are similar, Edge Seeker at 0.6ms while CSS_Rec2020 is 3.3ms.

@LeaVerou
Copy link
Member

here's a newer one that may not be tracked texel-org/color

IIRC, that is just implementing Björn's gamut mapping algorithm with constant lightness as described here: bottosson.github.io/posts/gamutclipping.

You can see it compared against all the others here: deploy-preview-7--color-apps.netlify.app/gamut-mapping/gradients?from=oklch%2890%25+.4+250%29&to=oklch%2840%25+.1+20%29.

I've had a PR to merge it open for a while, but I don't have the power to merge it officially, so the preview is the best way to view it currently.

Merged now, sorry for the delay!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-color-4 Current Work
Projects
Status: Wednesday morning
Development

No branches or pull requests