Open cdoublev opened 1 month ago
Converting HWB to sRGB produces NaN
when whiteness is -Infinity
or +Infinity
(produced by a math function) or blackness is -Infinity
.
For example, the current version of Chrome (126) serializes rgb(NaN, NaN, NaN)
from hwb(0 calc(-infinity) 0)
. FF clamps whiteness and blackness before the conversion.
Serializing hwb()
requires converting to rgb()
, therefore resolving math functions (extracting their result) before, as noted in 14.1. Resolving sRGB values:
For historical reasons, when
calc()
in sRGB colors resolves to a single value, the declared value serialises without the "calc(" ")" wrapper.
Note: calc()
should be replaced with a math function.
CSS Color 4 then says:
Also for historical reasons, when
calc()
is simplified down to a single value, the color values are clamped to[0.0, 255.0]
.
But it does not require clamping hwb()
channel values before conversion to rgb()
.
I do not think NaN
cases can be specifically handled in the conversion algorithm.
When whiteness/blackness is < 0 or > 100, the produced color is different depending on which of whiteness/blackness (line in FF) or RGB (like in Chrome) values are clamped:
// whiteness < 0
$el1.style.color = 'hwb(90 -200 0)'
$el2.style.color = 'hwb(90 -100 0)'
$el3.style.color = 'hwb(90 -50 0)'
$el4.style.color = 'hwb(90 0 0)'
getComputedStyle($el1).color; // rgb(128, 255, 0) in FF, rgb(0, 255, 0) in Chrome
getComputedStyle($el2).color; // rgb(128, 255, 0) in FF, rgb(0, 255, 0) in Chrome
getComputedStyle($el3).color; // rgb(128, 255, 0) in FF, rgb(64, 255, 0) in Chrome
getComputedStyle($el4).color; // rgb(128, 255, 0) in FF and Chrome
// whiteness > 100
$el5.style.color = 'hwb(0 100 50)'
$el6.style.color = 'hwb(0 200 50)'
getComputedStyle($el5).color; // rgb(170, 170, 170) in FF and Chrome
getComputedStyle($el6).color; // rgb(170, 170, 170) in FF, rgb(204, 204, 204) in Chrome
When whiteness/blackness is < 0 and not clamped, the hue is shifted: hwb(90 -100 0)
becomes hwb(120 0 0)
.
When whiteness/blackness is > 100 and not clamped, the lightness is shifted: hwb(0 200 50)
becomes hwb(0 100 25)
.
Finally, when (whiteness + blackness) > 1
, I am not sure I understand the current logic. If the algorithm should match the result of mixing paints, I do not understand why the produced color is not white when (whiteness - blackness) > 100
, or black when (blackness - whiteness) > 100
. whiteness + blackness
should probably not be greater than 100
.
I am not sure I understand the reasons, the conditions, the timing, for clamping out of range values.
HSL is off topic but similar observations can be made:
hsl(0 200 -25)
, the hue and lightness are shifted: I would expect black, but I get rgb(0, 64, 64)
in Chromehsl(90 200 25)
, the hue and lightness are shifted: I get rgb(64, 191, 0)
in Chrome, which corresponds to hsl(100 100 37.45)
Currently, channel values that must be clamped are:
ok?lab()
and ok?lch()
They must be clamped at parse time. They must not be clamped in a relative or origin color (and possibly in color-mix()
, #10414).
For RGB, CSS Color 3 optionally requires gamut mapping, without defining how or when. CSS Color 4 required clamping at computed value time before.
For saturation, CSS Color 4 follows CSS Color 3, which requires clamping, without defining when, and lets UAs optionally gamut mapping other channel values (like RGB). CSS Color 4 required clamping saturation and lightness at computed value time before.
For chromaticity and lightness of ok?lab()
and ok?lch()
, CSS Color 4 required clamping at computed value time before.
If the reason for clamping RGB and saturation is backward compatibility, what are the reasons for clamping chromaticity and lightness of ok?lab()
and ok?lch()
?
Browsers serialize
hwb(0 -10 0)
withrgb(255, 0, 0)
. The conversion from hwb to rgb returns[1, -0.1, -0.1]
.Where is clipping now specified in the specification?
I presume it should still apply otherwise resolving
infinity
to255
after the conversion torgb()
would not make sense. Should it apply before or after the conversion torgb()
? Should it only apply when it is not mixed with another color?Whiteness/blackness was clipped:
Note: I asked a similar question for saturation > 100 in
hwb()
but the issue is closed.