Skip to content
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] How to handle infinite values in color functions? #10507

Open
cdoublev opened this issue Jun 27, 2024 · 2 comments
Open

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

cdoublev opened this issue Jun 27, 2024 · 2 comments
Labels
css-color-4 Current Work

Comments

@cdoublev
Copy link
Collaborator

cdoublev commented Jun 27, 2024

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 colorjs.io@0.5.1, 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 cdoublev added the css-color-4 Current Work label Jun 27, 2024
@svgeesus
Copy link
Contributor

I agree that coercing values to infinity is going to cause no end of problems with color math; most color spaces are not designed to cope with such extreme values.

@tabatkins why are we doing this, again?

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.

For color spaces are clamped, yes. Most are not, including color(srgb ...) and oklab() and so on.

I just re-opened #8629 because the WG discussion apparently consisted of @tabatkins asserting that it was fine (for rgba()) and thus fine everywhere else.

@romainmenke
Copy link
Member

Not sure if this is applicable to browser implementations but I found that clamping to a really large value gives good results : https://github.com/csstools/postcss-plugins/blob/20ccaebde3de250e858adbbd3d519b01b7cf5304/packages/css-color-parser/src/functions/hsl-normalize-channel-values.ts#L111

Having a really large but finite value makes the color math work as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
css-color-4 Current Work
Projects
None yet
Development

No branches or pull requests

3 participants