facelessuser / coloraide

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

Pointer gamut #330

Closed facelessuser closed 1 year ago

facelessuser commented 1 year ago

Over on Color.js someone brought up the idea of adding a way to test if a color was within the Pointer gamut. This can be of interest to some people, and probably worth considering. I went ahead and created a POC. Data for the Pointer gamut is found here: https://www.rit-mcsl.org/UsefulData/PointerData.xls.

Below we can test if a color is within the Pointer gamut or fit a color to the Pointer gamut.

>>> Color('red').in_pointer_gamut()
False
>>> Color('red').fit_pointer_gamut().to_string()
'rgb(244 46.539 23.139)'

The idea is simple enough. The data provided gives discrete values for chroma at specific lightness and hues in the LCh color space using an SC illumination. We can simply reuse existing Lab and LCh logic and convert any color to this space. We simply find the closest lightness in the table and the closest hue and interpolate the max chroma for that color's specific lightness and hue. Essentially, we use the data to determine if a point is within the gamut's bounds, and if the color falls between the discrete data available, we interpolate the bounds.

fit

I thought about shoehorning this into existing in_gamut() and fit() logic, but I'm not sure it really belongs there. These two existing functions have a color space-specific context, and the Pointer gamut is more a gamut based on observation, not a color space. It seems it makes sense to maybe expose its own API.

The alternative would be to allow in_gamut() and fit() to have an additional constraint. The example below would fit to the sRGB gamut, but also clamp to the Pointer gamut. I think having a separate API may be more useful and intuitive.

Color('red').fit('srgb', pointer=True)