-
Notifications
You must be signed in to change notification settings - Fork 664
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] [css-color-5] Mixing with transparent
seems broken in implementations
#8612
Comments
Have been looking at this a bit more. As far as I can tell any mix with a color that has For When mixing with I think the main issue here is a lack of test coverage in WPT and some implementation bugs. Missing tests :
There are a few tests with |
Agree that these tests would help. I believe at this point the specification is correct (but perhaps more examples would help). |
I've added some tests which I think fully cover this issue : web-platform-tests/wpt#39139 |
@emilio Given that Firefox intends to ship I've opened more issues around |
Interesting, did those land very recently? I looked at all the tests just yesterday (on our tree) and I didn't see any extra failures. I'll look into this tho. |
These tests were merged into WPT last week. I've mainly noticed that there are some inconsistencies between the specification and implementations around handling of powerless components, missing components and color space conversions. I don't have anything useful to say about how blocking all this is, I'll leave that up to @svgeesus and @tabatkins. |
I think what's going on in implementations is that |
Hmm, ok, so it might be due to the color conversion, it seems at least on Gecko we do end up with |
Yes, color conversion is relevant here as that is when powerless components become missing and missing components in turn can adopt analogous components from the other color. |
So the spec should be clearer on how to perform the conversion for Oklch etc. In Gecko, the HSL case works out because we end up doing https://drafts.csswg.org/css-color-4/#rgb-to-hsl explicitly, which accounts for the The spec isn't super-clear on what operations should happen when converting between these color-spaces (unless I've missed it). So it seems that should be clearer. |
Agreed, the order of these steps is also undefined : #8602
We have a separate step where we explicitly go from powerless to missing components : https://github.com/csstools/postcss-plugins/blob/main/packages/css-color-parser/src/color-data.ts#L288-L343 Note that |
That description in CSS Color 5 should link to CSS Color 4 Converting Colors. Do you still feel it is underspecified? |
Oh, and I notice the list of color conversion steps should note that steps can be skipped if a sequence of them is no-op (eg converting sRGB to HSL, where src is identical to dest-rect) |
The order op operations when carry-forward and powerless components both apply has now been specified - you do the carry-forward first, which in the case of color interpolation in color-mix means the component value for the other color is used. |
It currently says:
which seems fairly specific, as CSS 4 Color interpolation is pretty detailed. Is there something specific which is unclear or missing? But I guess that
could link "converting them" to Converting Colors which is the immediately preceding section. And I could always add some worked examples. |
I clarified: Interpolation between <<color>> values
occurs by first
checking the two colors for analogous components
which will be carried forward;
then (if required) converting them to a given color space
which will be referred to as the <dfn export>interpolation color space</dfn> below,
and then
linearly interpolating each component of the computed value of the color
separately. |
That is confusing to me 🤔 The steps I have :
|
what does "before and after color space conversion" mean? |
That is indeed poorly worded. This is more accurate :
|
So I was seeing it like this (re-using your wording):
|
In other words the first step is basically setting flags, before the original color is lost; and the third and fourth stages are overwriting parts of the converted colors (first with |
I assigned some values to the steps to make sure I am not getting lost somewhere. For
no analogous components ->
no missing components -> nothing is filled in
->
In my implementation this becomes : I expect My interpolation function : function interpolate(start: number, end: number, p: number): number {
if (Number.isNaN(start)) {
return end;
}
if (Number.isNaN(end)) {
return start;
}
return (start * p) + end * (1 - p);
}
divide components by interpolated alpha ->
becomes:
With the interpolated alpha the outcome is I think using this order just pushed the problem further down. Then it becomes an issue of what happens during linear interpolation. Footnotes
|
Well, there was no resolution to make anything other than hue powerless in the first place; and we are now seeing a bunch of breakage and discontinuity as a result of forcing chroma/saturation to be missing at exactly white and black. Compare |
Sorry, I actually meant : I haven't updated my implementation to match that change because it didn't seem final. Which components are or aren't powerless doesn't have any effect on this thread. I do seem to have made a mistake in my previous comment. |
I thought I had a mistake because doublechecking lead to different results, but was a typo in the second attempt :) My expectation remains that any mix with This is something that just worked for me with the order I described in : #8612 (comment) I think it now depends on the exact definition of linear interpolation. |
@tabatkins I guess it is too late (not Web compatible) to redefine |
That would not really help as far as I can tell. |
I am a bit confused what the issue is, because as far as I can tell this is just an implementation bug. These are different linear gradients between The background is also This illustrates that a linear gradient between https://codepen.io/romainmenke/pen/dyQzxKP Blue is worse because a hue of https://codepen.io/romainmenke/pen/zYMdgQV We only get weird results when color space conversions are part of the equation (in current implementations) |
@emilio wrote:
Correct, it needs to go through XYZ (that isn't needed when sRGB colors (rgb, hsl, hwb) are interpolated in an sRGB colorspace). But the final polar conversion step, Oklab to Oklch or Lab to LCH, should be producing a In color.js, Either way though, when the alpha is zero, the premultiplied |
I agree that at this point the specifications are clear. On WPT parsing/color-computed-color-mix-function.html is also clear and has tests for both Chrome (and Edge) pass all 476 subtests. Firefox (117) and Safari (173 preview) both pass 350/476 subtests and they both fail all the polar-space tests with |
Very happy to see that Chrome is now interpolation in But I think we need more coverage for gradients : #a {
background: color-mix(in oklch, blue, transparent);
height: 50px;
}
#b {
background: linear-gradient(to right in oklch, transparent, blue);
height: 50px;
} https://codepen.io/romainmenke/pen/NWEaKpL Unless I am mistaken there should be no difference between https://bugs.chromium.org/p/chromium/issues/detail?id=1462612 |
Bugzilla bug for this: https://bugzilla.mozilla.org/show_bug.cgi?id=1836557 |
We do, although reftests for gradients are hard because of things like stochastic dithering to break up mach banding. |
Oh absolutely,
The trick is that gradients in rectangular spaces are defined to interpolate in premultiplied rectangular space, which has similar "basically act like you take channels from the other color" behavior, except it also works gradually as you move away from pure transparent. (Actually it changes the 4d hypercube of the rectangular space+alpha into a hypercone with a cubic base, so all We don't have similar geometric tricks that can be done in polar spaces, tho, because the hue dimension isn't linear so it can't just be scaled down like that. So that's why we need this extra handling, which is also all-or-nothing. |
https://drafts.csswg.org/css-color-4/#interpolation-alpha
https://drafts.csswg.org/css-color-5/#color-mix-with-alpha
As best as I can work out this algorithm will always cause any mix with
transparent
to be the other color but with a different alpha value.It should be impossible to get a color change from mixing with
transparent
.Yet in browsers you can get very large hue shifts by mixing with
transparent
.https://codepen.io/romainmenke/pen/zYJLRQJ
If I am reading all relevant bits correctly the steps should be :
Unless
transparent
isn't zero in all components in all colorspaces we always interpolate with[0 0 0]
The ratio between the two colors is the inverse of the value used when un premultiplying.
So you always end up with the non
transparent
input color.This might also be the result of implementations not handling powerless/missing components correctly : #8609
Questions :
transparent
instead ofwhite
?transparent
always0
ornone
for all components in all color spaces?The text was updated successfully, but these errors were encountered: