[csswg-drafts] [css-color-4] How to handle infinite values in color functions? (#10507)

cdoublev has just created a new issue for https://github.com/w3c/csswg-drafts:

== [css-color-4] How to handle infinite values in color functions? ==
Unless otherwise specified, color channel values are not clamped, which applies to values produced by math functions, which can be infinite.

Infinite color channel values produce `NaN` in most conversion functions.

<details>
  <summary>Specifically</summary>

  - `color(xyz-d50) -> color(xyz-d65)`
  - `color(xyz-d65) -> rgb-linear()`
    - when `x` and `y` are the same infinite
    - when `x`/`y` and `z` are infinite
  - `color(xyz-d50) -> lab()`
    - when one or more channels are the same infinite
  - `color(xyz-d65) -> color(xyz-d50)`
    - when `x` and `y` are infinite
    - when `y` and `z` are infinite
    - when `x` and `z` are the same infinite
  - `color(xyz-d65) -> oklab()`
  - `oklab() -> color(xyz-d65)`
  - `rgb() -> hsl()`
    - when one or more channel is infinite
  - `hsl() -> rgb()`
    - when `s` is infinite and `l` is 0/100
    - when `l` is infinite
  - `hwb() -> rgb()`
    - when `w` is infinite
    - when `b` is -Infinity
  - `lab() -> color(xyz-d50)`
    - when `l` is infinite and `a` is the opposite infinite
    - when `l` and `b` are the same infinite
  - `lch() -> lab()`
  - `oklch() -> oklab()`
    - when `c` is infinite and `h` is 0
  - `rgb-linear() -> color(xyz-d65)`
    - when one or more channels are infinite and one or more are the opposite infinite

  This [CodePen](https://codepen.io/creativewave/pen/eYaKyVX) allows to play with conversion functions, which have comments for NaN cases.
</details>

In `colorjs.io@0.5.1`, `NaN` seems to be replaced by `none`, which seems incorrect:

```js
let color = new Color('hsl', [0, 0, Infinity])
color.to('srgb').toString(); // rgb(none none none)
```

Clarification on how to handle infinite values has already been asked in #8629, which was resolved with *no further change* because *if you put an infinite calculation into an rgba(), the behavior is well-defined: clamp to the allowed range*.

At least, this is not true for some channels of other color functions than `rgb()`, and for relative colors.

Following this [comment](https://github.com/w3c/csswg-drafts/issues/8629#issuecomment-1513228530), I tried to guess the color resulting from a color function specified with one infinite channel value. Since I am personally only interested by serialized values, I only did it for `hsl() -> rgb()` and `hwb() -> rgb()`, but it should presumably be done for all conversions producing `NaN`.

<details>
  <summary>When converting <code>hsl()</code> to <code>rgb()</code> and <code>saturation</code> is <code>+Infinity</code> or <code>-Infinity</code>:</summary>

  - when `l === 0`, red/green/blue are `0`
  - when `l < 0`, same as when `l > 0` but with inverted infinite signs
  - when `l > 0`:

  |             `h` |            `r` |            `g` |            `b`
  | --------------- | -------------- | -------------- | --------------
  |  `330 < h < 30` |    `+Infinity` |    `-Infinity` |    `-Infinity`
  |      `h === 30` |    `+Infinity` | depends on `l` |    `-Infinity`
  |   `30 < h < 90` |    `+Infinity` |    `+Infinity` |    `-Infinity`
  |      `h === 90` | depends on `l` |    `+Infinity` |    `-Infinity`
  |  `90 < h < 150` |    `-Infinity` |    `+Infinity` |    `-Infinity`
  |     `h === 150` |    `-Infinity` |    `+Infinity` | depends on `l`
  | `150 < h < 210` |    `-Infinity` |    `+Infinity` |    `+Infinity`
  |     `h === 210` |    `-Infinity` | depends on `l` |    `+Infinity`
  | `210 < h < 270` |    `-Infinity` |    `-Infinity` |    `+Infinity`
  |     `h === 270` | depends on `l` |    `-Infinity` |    `+Infinity`
  | `270 < h < 330` |    `+Infinity` |    `-Infinity` |    `+Infinity`
  |     `h === 330` |    `+Infinity` |    `-Infinity` | depends on `l`

  When converting `hsl()` to `rgb()` and `l === +Infinity`:

    - when `-100 <= saturation <= 100`, r, g, b, are `+Infinity`
    - when `saturation < -100`, same as `saturation > 100` but with inverted infinite signs
    - when `saturation > 100`, r/g/b are `+Infinity`/`-Infinity` depending on `saturation` and `h`
      - there are 6 intervals defined by  `h`
      - the exact `h` interval values depend on `saturation`
      - for each sibling intervals, one of r/g/b has a different sign

  When converting `hsl()` to `rgb()` and `l === -Infinity`:

    - when `-100 <= saturation <= 100`, r/g/b are `-Infinity`
    - when `saturation < -100` or `saturation > 100`, same as when `lightness === +Infinity` but with different intervals
</details>

Let's skip `hwb() -> rgb()` because it becomes more complicated when more than one infinite channel value.

For example, when `a === 0`, `x` tends towards `+Infinity` when `l` tends towards `+Infinity`, or towards `-Infinity` when `b` also tends towards `+Infinity`. Should `z` be `0`? Should some "precedence" between channel values be defined?

Note that channel values do not linearly tend towards an infinite value:

  - `rgb(from hsl(0  -99 calc(infinity)) r g b)` would resolve to `rgb(calc(infinity) calc(infinity) calc(infinity))`
  - `rgb(from hsl(0 -101 calc(infinity)) r g b)` would resolve to `rgb(calc(infinity) calc(-infinity) calc(-infinity))`

Am I missing something?

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/10507 using your GitHub account


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

Received on Thursday, 27 June 2024 17:13:28 UTC