drwpow / better-color-tools

Better color tools for JS, TS, and Sass. Supports Oklab and CSS Color Module 4.
https://better-color-tools.pages.dev/
MIT License
78 stars 7 forks source link

Issues when converting OKLCH back to RGB #16

Closed drgarlic closed 2 years ago

drgarlic commented 2 years ago

Hi !

First of all, awesome library it does exactly what I need and it's very lightweight.

But I found some issues and results quite different from https://oklch.evilmartians.io/ when using better.from(oklchString).rgb

For example:

Black:

oklch(0% 0.08567819015077803 221.11457081307586/1)

Returns: rgb(0, 0, 3) Should be: rgb(0, 0, 0) link

oklch(0% 2.248544052636656e-8 89.8755635095349/1)

Returns: rgb(255, 0, 38) Should be: rgb(0, 0, 0) link

White:

oklch(100% 0.12801375474374593 168.89572110316647)

Returns: rgb(159, 255, 240) Should be: rgb(255, 255, 255) link

Other:

oklch(85% 0.17463268857418898 54.10799672454288)

Returns: rgb(255, 170, 80) Should be: rgb(255, 189.4, 146.73) link

Anyway,

Thank you so much for this library, it saved me quite some time !

drwpow commented 2 years ago

Thank you for finding this bug! Will look into the discrepancy here. The Oklch calculations are based off of Oklab; hopefully it’s just Oklch that’s the problem.

drgarlic commented 2 years ago

I just checked their code and they do some additional checks, both return the same rgb when removing them.

Maybe you could add an option for a 'safe mode' that would do the same checks ?

drwpow commented 2 years ago

Yes I found what you were talking about. It seems that the issue is when converting between color spaces, you often get out-of-bound numbers (e.g. RGB values > 255) that are important to maintain. The problem was clamping the values too early (I found a great blog post about it).

Like you pointed out, the library behind https://oklch.evilmartians.io uses Culori, which has a similar check in place.

I was able to see great improvements in just not clamping values during conversion. This was the cause of most of the weirdness. But I’m still seeing a few unexpected values now in testing that I’m still looking into (trying to see if there’s something in Björn Ottosen’s research that is an improvement on what Culori is doing here).

drwpow commented 2 years ago

So I was able to improve this! The colors you gave as examples were all colors that aren’t in the sRGB gamut and therefore can’t be displayed, so it’s really open to interpretation how they get converted.

17 adds Björn Ottosen’s method of sRGB gamut clipping for good results. It should be noted that this DOES NOT match https://oklch.evilmartians.io/, which uses Culori under-the-hood. Nor does this library match Color.js’ implementation. Both employ crude, simple gamut clipping that don’t follow the Oklab space very well. I think Björn’s method yields better results. And that is the name of this library, after all 🙂

Extra info: gamut clipping is quite an interesting rabbit hole, and there’s never a “right answer,” because it depends on usecase (see Björn’s comparison of different clipping algorithms).

All that said, with this being subjective, there still may be improvements to be made on the gamut clipping here. Open to all that feedback, but #17 at least adds this handling that was missing before.

drgarlic commented 2 years ago

Thank you so much ! I'll take a look at all of this asap !

drwpow commented 2 years ago

Going to close the issue, but let me know if there is any additional weirdness 🙂