Closed samweinig closed 2 years ago
Thank you. This gap in functionality is one I have been meaning to address once I have a concrete syntax proposal You are right though that at least raising it as an issue is the first step!
And yes, both Lab and LCH are useful here. Both are (approximately) perceptually uniform; LCH is also chroma-preserving so a gradient between two very different, saturated hues will go around the hue circle rather than in a straight line, which will get greyish near the middle.
Both options avoid the un-necessary darkening that happens when calculations are done directly on the gamma-encoded RGB values rather than converting them to linear-light first.
CSS Color 4 has a section on color interpolation which can be used as a basis (and allows an individual CSS feature to pick a default colorspace for a given operation, as well as encouraging ssntax so authors can pick other colorspaces). In particular it defines interpolating with alpha, and hue interpolation for polar spaces with a hue angle. And unlike the legacy parts of CSS, CSS Color 4 does not make assumptions like "all colors are sRGB".
As a first step, in the new year I want to make some examples with pairs of colors interpolated in various spaces, so people can see the practical effect of doing so.
sRGB references light. Seems like a muddle to be mixing perceptual notions with light transport here.
I think everyone here is aware of the SVG color-interpolation property. Having such a setting that alters the default interpolation is helpful too, although not as versatile as directly making it a part of the gradient properties.
Re "muddle": CSS doesn't care what the colorspace is intended to model. It just needs know a space and a way to draw a line through the space.
PS: the markdown is a bit broken in the color-4 interpolation…
I think I am going to straw a proposal here. Here goes.
<interpolation-space> = <colorspace> | default
<interpolation-hue> = shorter | longer | increasing | decreasing | specified
<interpolation> = <interpolation-space> [<interpolation-hue>]? | linearRGB
color-interpolation
has value <interpolation>
. It defaults to default
. It defines how any blending is done by default.
linearRGB
indicates that color interpolation occurs in a linear sRGB color space. This also gives SVG compatibility.color-interpolation-space
has value <interpolation-space>
. It defaults to default
.
<colorspace>
indicates a color space as in css-color-5.1default
specifies the behavior in css-color-4 section 13, where Lab is used except for colors specified in a legacy syntax, which would be interpreted in sRGB.color-interpolation-hue
has value <interpolation-hue>
. It defaults to some weird empty value that acts like shorter
. It controls the hue interpolation mode as in css-color-4 section 13.1 Maybe <ident> | <dashed-ident>
for more choices, but then we need a linear/gamma switch for the RGB ones. Moving "linearRGB" to the bigger interpolation thing is in consideration of that.
Maybe just add a , [<interpolation>]?
after the position specification. I don't know.
Previous discussion on this: https://github.com/w3c/csswg-drafts/issues/4647
I'm all for this, of course, and have been mulling it over as we implemented it.
mask-image: radial-gradient(...)
needs the option of using linearRGB
even if the other parts of the element use another strategy.So I'm broadly in agreement with the above proposal, and in particular think we should definitely extend the existing color-interpolation
property to make it apply universally, not just to SVG. I would suggest adding two new values to that property:
color-interpolation: auto | srgb | linearrgb | match | lch
The "match if we can, lch if we can't" approach was roughly where we got to in https://github.com/w3c/csswg-drafts/issues/4647, but that's obviously subject to revision. And I'd keep the list of options to a bare minimum - there's no need to blend Lab colors in display-p3.
My only worry is that the default value is currently "sRGB" not "auto". I'm not really sure why auto exists, actually - it doesn't seem to do anything except introduce ambiguity. As it currently gives the user agent the option of choosing between srgb and linearrgb, making it the default and redefining it to mean "always choose srgb for colors defined prior to color-4" doesn't seem to be a breaking change to me.
The "shorter, longer" hue issue was discussed here: https://github.com/w3c/csswg-drafts/issues/4735.
- "auto" is currently defined as "the user agent can choose either the sRGB or linearRGB spaces for color interpolation. This option indicates that the author doesn't require that color interpolation occur in a particular color space". I'd propose changing this to be be "Choose "srgb" for colors specified as sRGB or HSL, and otherwise act as "match".
- "match" means use the color-space of the colors being interpolated. So two sRGB colors interpolate in sRGB, two HSL colors in HSL, two LCH colors in LCH. If the colors have different spaces, the interpolation is in LCH.
- "lch", "srgb", "linearrgb" - interpolation always happens in that space.
What would the intended use case of something like auto
or match
be for something like gradient.
In general, I think we should try to avoid defaults and automatic picking of color spaces, especially based on input colors, as I think the results can be quite confusing. By always requiring a specific color space (or color-interpolation), (except for legacy case of sRGB where it is excluded), I think it makes it easier to explain the concept to new users.
It certainly has been the case for me that the more explicit tagging of input and output color spaces in our code (that is, in WebKit internals, so a bit different I will admit) has made things easier to understand and follow.
I'm not quite sure I follow what you're proposing - dropping auto
or match
? So presumably all interpolation would default to sRGB unless the author explicitly specified (for example) lch or lab?
I suspect we agree that sRGB has to remain the default for sRGB
and HSL
. But I don't believe sRGB
should be the default for LCH
or device-cmyk
color. Two reasons.
For me, the least confusing default choice is "the same colorspace as the input colors". That's match
. But we can't do that for HSL for compat reasons, hence auto
.
This all covered in https://github.com/w3c/csswg-drafts/issues/4647 - do read it through if you haven't already. I should note I also didn't see the value in LCH originally, but was won over in the discussion in that issue.
(edit: also, auto
exists now for color-interpolation
, so if that's the property we're using for this, it has to stay)
I'm not quite sure I follow what you're proposing - dropping
auto
ormatch
? So presumably all interpolation would default to sRGB unless the author explicitly specified (for example) lch or lab?
Yes, I proposing that for gradients (I am not making a point on any other type of interpolation, nor am I proposing we use the existing color-interpolation concept) we keep the current behavior that no specified color space means sRGB, and extend it to allow specifying a specific color space.
I am also expressing my opinion that I don't think match
as a concept is good one, as I don't think it should matter how a color was specified when creating things like gradient. Namely, I think it would be confusing that a gradient specified as:
background: linear-gradient(90deg, rgba(255,0,0,1) 0%, rgba(0,0,255,1) 100%);
(that is a linear gradient from sRGB red to sRGB blue)
would be different that a gradient specified as:
background: linear-gradient(90deg, lch(54.29% 106.83 40.85) 0%, lch(29.57% 131.22 301.37) 100%);
(that is a linear gradient from sRGB red to sRGB blue, but the input colors use lch() syntax to describe the color)
especially given that the input colors can be, and often are, css variables. I think this would lead to confusing behavior as people start to adopt new (to CSS) ways of specifying color like lab(...) and lch(...).
I'll counter your second example with this:
#div1 { background: lch(75% 67 180deg); }
#div2 { background: linear-gradient(lch(75% 67 180deg) lch(75% 101 82deg));
#div3 { background: lch(75% 101 82deg); }
Placed together, an author might reasonably expect the middle div to interpolate smoothly between div1 and div3 - the end colors are the same, after all. But if interpolation is in sRGB, it won't - both colors are outside the sRGB gamut, although within the display-p3
gamut. So if displayed on an iPhone or Macbook, there would be a sharp jump in color as div2
immediately converts the colors used in the gradient - including the colors at both ends - to sRGB.
I can't demonstrate this in a browser, but if you go to https://bfo.com/publisher/?job:I2eInQj00J2BBJIv#pdf, download the generated PDF and view it in Acrobat (which correctly interpolates gradients in Lab - you won't see this in other PDF viewers, including the one in your browser or macOS Preview) on a wide-gamut display such as a macbook, you'll see the gradient on the right has a hard boundary 1/3 and 2/3 of the way down. That's the effect I'm talking about.
(note the interpolation is otherwise fairly similar, despite the left gradient being LCH and the right sRGB, and the gradient covering a full 100° of hue. I just picked two random out-of-gamut colors, so no idea how representative this is)
Clearly neither solution is perfect and obviously there's no question this will be configurable - the only issue is which default is the least surprising.
I view sRGB is the option of last resort - it's guaranteed to be supported, but it's gamut is well below the majority (caution: assumption detected) of devices in use today. I just don't think we should be designing around it at this stage.
Either way I think we've both staked our respective positions out quite clearly, which is a good start. I'd welcome some other viewpoints on this.
sRGB references light. Seems like a muddle to be mixing perceptual notions with light transport here.
All colors are produced by light, whether reflected or generated.
In general, I think we should try to avoid defaults and automatic picking of color spaces, especially based on input colors, as I think the results can be quite confusing.
Yes. I'm thinking particularly of gradients where all the stops are in some form of sRGB and then, one stop gets changed to display-p3 or rec2020 or whatever.
In addition, we previously resolved to treat legacy-sRGB syntaxes and the new, as yet undeployed color(sRGB ...)
differently, precisely to allow an sRGB opt-in opt-in to whatever better space we have for interpolating the new colors.
By always requiring a specific color space (or color-interpolation), (except for legacy case of sRGB where it is excluded), I think it makes it easier to explain the concept to new users.
I agree.
@weinig wrote
we keep the current behavior that no specified color space means sRGB, and extend it to allow specifying a specific color space.
We have to keep the current behavior, for legacy syntax colors, for Web compat.
We don't have to require gamut-mapping to sRGB for gradients without an explicit color space. We could, and it simplifies some things while giving annoying and unexpected results in other cases.
We certainly need an opt-in to specify a better interpolation colorspace for gradients, going forward. (We also need the same for transitions and for animations, which are basically gradients except over time instead of space).
In general, I think we should try to avoid defaults and automatic picking of color spaces, especially based on input colors, as I think the results can be quite confusing.
Yes. I'm thinking particularly of gradients where all the stops are in some form of sRGB and then, one stop gets changed to display-p3 or rec2020 or whatever.
Oh, one thing I learned recently is that WebKit is actually already a bit non-compliant in this case. WebKit interpolates a gradient in the so-called "Extended sRGB" color-space, which is just sRGB without the clamping and in the negative it is sign-reversed in the negative. So, if you do:
background: linear-gradient(90deg, color(display-p3 1 0 0) 0%, color(display-p3 0 1 0) 100%);
(or any other color that has a wider gamut than sRGB), we produce a gradient that includes those colors, but uses the sRGB curves.
I kind of doubt anyone is relying on this, but I don't know for sure.
@Artoria2e5 wrote
PS: the markdown is a bit broken in the color-4 interpolation…
Yeah I am having a problem with Bikeshed picking up inline GH issues like
Issue(#3088):
It doesn't insert the issue title or link, and it just runs on to the following paragraph.
Edit: it was my fault, unclosed <pre>
in an example, now fixed.
WebKit interpolates a gradient in the so-called "Extended sRGB" color-space, which is just sRGB without the clamping and in the negative it is sign-reversed in the negative.
Yes. As a means to store colors internally, for example, that is totally fine and avoids gamut mapping (and there is hardware support for it).
In general though interpolating on gamma-encoded colors will give you muddy midpoint colors. Legacy sRGB gradients have that already. I'd like to avoid it for non-legacy colors which don't have the Web-compat issue.
The better options are:
My thoughts, most of which I have expressed before in the various discussions about this:
color-interpolation
property is a good idea for a variety of reasons. a) Setting color interpolation on a per-element basis is too coarse. A single element may have rules that come from different stylesheets, with different ages and therefore assumptions. Given custom properties, even a single declaration could come from multiple stylesheets. b) What happens when color-interpolation
is set to narrower spaces than the specified colors? How do you interpolate P3 colors in sRGB or Lab colors in P3 if they fall outside these gamuts?auto
. text-decoration-skip-ink
by default. Similarly, when we switched gradient interpolation to premultiplied RGBA, gradients had already been widely deployed at that point. Note that any undesirable interpolation can always be overridden by adding more stops, so there is an escape hatch for the handful of authors in the world that may prefer the old behavior.The BEST colorspace for any blend or mix,
.....completely dependent on the desired result and use case.
You can not say that such and such colorspace is the most ideal for a gradient, not even for compositing. Not without knowing the full case and desired result.
Here are some examples in a GIST: https://gist.github.com/Myndex/10caff6a68e844591e83eadeebfb4347#straight-talk-about-linear
More examples: https://www.myndex.com/WEB/Gradients
And more more examples: https://www.myndex.com/WEB/GradientsPartTwo
And the space/mode colors are being defined in is not necessarily the best space for performing a blend or mix or gradient.
And the space/mode colors are being defined in is not necessarily the best space for performing a blend or mix or gradient.
Agreed; for the non-legacy stuff we are careful to clearly separate what colorspace is being used for interpolation, from what is being used for specifying the colors (and to not require the two colors to be specified the same way or in the same colorspace).
This has now been done, see CSS Color 4: 13.1. Color space for interpolation which defines
<color-interpolation-method> = in [ <<ectangular-color-space> | <polar-color-space> <hue-interpolation-method>? ]
which is used in CSS Images 4:
3.1. Linear Gradients: the linear-gradient() notation 3.2. Radial Gradients: the radial-gradient() notation 3.3. Conic Gradients: the conic-gradient() notation
Currently, all gradient interpolation takes place in pre-multiplied sRGB space. Now that we have the some predefined color spaces from css-color (https://drafts.csswg.org/css-color-4/#predefined) it would be nice to allow gradients to use those color spaces to produce nicer looking results (I have heard Lab is a color space people particular like using for gradients due to the perceptually uniformity characteristics).
One potential way to introduce this would be to all the color space to be specified as a new optional first parameter to the gradient functions.