facelessuser / coloraide

A library to aid in using colors
https://facelessuser.github.io/coloraide
MIT License
195 stars 12 forks source link

CSS has resolved to preserve undefined channels when converting (for interpolation) #247

Closed facelessuser closed 1 year ago

facelessuser commented 2 years ago

CSS has resolved to preserve undefined values when interpolation requires conversion. Red-like channels preserve the none for other Red-like channels, hues are preserved for hue-like channels, chroma/saturation for similar channels, etc.

The thing that troubles me is that just because a color channel shares the same purpose, it does not make them the same. For instance, the red channel in sRGB != red channel in Display P3 etc.

I think the idea is to make the none transfer magically for the user so they don't think about it, but it can change the intent of the color. A change in red in one color space can change multiple channels in another as the definition of red is not entirely the same.

Consider Color('rec2020', [NaN, 1, 0.5]). At render time, both of these cases will have to set NaN to 0 to display them.

Screen Shot 2022-09-03 at 1 27 51 PM

They just aren't the same. Now, with certain colors and spaces maybe the change will not be noticeable, and certainly, this change will preserve the intent of undefined values in interpolation, but it will be at the cost of color intent.


Let's put aside color intent. This will complicate our code a fair bit considering all the different color spaces we support. We now have to link together all like color spaces. We can search for lightness in all cylindrical color spaces, but obviously, HWB doesn't have one, but that's okay. We derive all RGB-ish spaces from an RGB-ish class, but then things like Prismatic are RGB-ish, but also have a lightness. Saturations, chroma, colorfulness, all have to get linked together. I guess intensity and lightness also has to get linked. They seem to ignore a and b in Lab-ish spaces. What about CMY spaces, I guess CMYK should be linked. Generalizing this would be a huge pain. I can foresee us having to add a number of exceptions.

facelessuser commented 2 years ago

I think if we end up going this route, which I'm inclined to not, we would need to make all RGB color spaces (including XYZ) RGB-ish classes. CMY, CMYK, and Prismatic would all be outliers. CMY and CMYK could share coordinates (except for K). While Prismatic is basically an RGB space, it has lightness split out, so that probably means it can't really share with other RGB spaces.

It is very likely there could be other outliers in the future. If we were to go this route, it would be nice to ensure a generalized way so we wouldn't have to update the core every time we wanted to support some new type of color space. Something to think about.

Oh, and since it wasn't explicitly mentioned, this whole thing only pertains to conversions when interpolating, not conversions in general -- at least that is how I interpret it.

facelessuser commented 1 year ago

We are going to do this in 2.0. Exotic color spaces will not be carried forward. HSV will be added as it has many things analogous with other cylindrical spaces, though V (brightness) is not the same as lightness, but different HSV spaces can probably carry forward to each other okay.

facelessuser commented 1 year ago

I'm going to bump this to 2.1 and I want to go ahead and get 2.0 out and I don't feel like fully exploring this until 2.1.

facelessuser commented 1 year ago

I was testing this out...I just think this is maybe not so well thought out. I was warming up to this just as a way to try and handle NaNs in a sane way when someone has specified them in one color space but interpolates in another color space. The idea is to preserve the intent of the powerless channels. I give it two HSL colors to interpolate, but I specify one as none, then if I interpolate in the OkLCh space, we'd interpolate the two colors, but only the lightness.

I think this is what CSS was trying to do, but it just ends up impractical in many cases.

Consider the example below. We have two colors HSL colors: orange and blue. We want to interplate them in the OkLCh color space, but we want to interpolate them with blue's lightness.

  1. If we just interpolate them in OkLCh, they won't be perceptually done with the same lightness.
  2. We could manually convert them to OkLCh and then set one of their lightness to NaN so that it adopts the other color's lightness. This works.
  3. If we try to utilize the carryforward to help us out and just specify the HSL color with a lightness of NaN so that it adopts the lightness of the other color, we get something entirely different.
Screenshot 2023-04-04 at 8 25 09 PM

What ends up happening with the carryforward is that the NaN value is treated as zero during conversion, so the color becomes gray. Then we set the NaN at the end. That isn't what we want. So, the question becomes, is this powerless conversion actually helpful in a real way?

facelessuser commented 1 year ago

Revisiting this by testing it out more. The case above may complain about what expectations of undefined "carry over" might be, but it isn't too different than not using "carry over" at all:

Screenshot 2023-06-15 at 8 59 17 PM

I guess my complaint is mainly related to what the intention is when defining the undefined channel prior to carry-over, but the reality is that the HSL conversion to Okhsl, in this case, is going to yield a monochromatic color no matter what, but in the carry-over case, you do use the lightness of the blue as intended.

I'm not sure if carry-over is necessarily better, but it may be fine to include for compatibility with CSS but as an optional feature, disabled by default. I probably wouldn't make it an official, documented feature until browsers adopt it and it becomes an accepted standard. Right now it is what CSS intends, but if no one implements it, or there are issues found during the trial period, it could still get reverted.

facelessuser commented 1 year ago

Chrome seems to be doing this now. They are the first, and currently the only ones who are doing this. Their results matched our carryforward results. Safari only matched our non-carryforward values. Firefox gave us something different than both.

facelessuser commented 1 year ago

This will be an optional, experimental feature that is disabled by default.