Re: [csswg-drafts] [css-colors-4] Clip negative whiteness/blackness of `<hwb()>`? (#10368)

[Converting HWB to sRGB](https://drafts.csswg.org/css-color-4/#hwb-to-rgb) 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](https://drafts.csswg.org/css-color-4/#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:

  ```js
  // 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:

  - when saturation is > 100 and lightness < 0, eg. `hsl(0 200 -25)`, the hue and lightness are shifted: I would expect black, but I get `rgb(0, 64, 64)` in Chrome
  - when saturation is > 100 and 0 < lightness < 100, eg. `hsl(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:

  - RGB
  - saturation at lower boundary
  - chromaticity at lower boundary
  - lightness of `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()`?

-- 
GitHub Notification of comment by cdoublev
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/10368#issuecomment-2169392747 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Saturday, 15 June 2024 11:57:58 UTC