csswg-drafts icon indicating copy to clipboard operation
csswg-drafts copied to clipboard

[css-color-4] How to handle infinite values in color functions?

Open cdoublev opened this issue 8 months ago • 2 comments

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.

Specifically...
  • color(srgb-linear) -> color(xyz-d65)
    • when one or more channels are infinite and one or more are the opposite infinite
  • color(xyz-d50) -> color(xyz-d65)
  • color(xyz-d65) -> color(srgb-linear)
    • when x and y are the same infinite
    • when x/y and z are infinite
  • color(xyz-d50) -> lab()
    • when x and y are the same infinite
    • when y and z 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

This CodePen allows to play with conversion functions, which have comments for NaN cases.

In [email protected], NaN seems to be replaced by none, which seems incorrect:

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, 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.

When converting hsl() to rgb() and saturation is +Infinity or -Infinity: [...]
  • when l === 0, r, g, b, 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

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

For example, when a === 0, z tends towards +Infinity when l tends towards +Infinity, but z tends 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 always tend linearly towards an infinite value:

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

Am I missing something?

cdoublev avatar Jun 27 '24 17:06 cdoublev