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

Precise chroma clamping in P3 color space #211

Closed
antiflasher opened this issue Sep 28, 2023 · 2 comments
Closed

Precise chroma clamping in P3 color space #211

antiflasher opened this issue Sep 28, 2023 · 2 comments

Comments

@antiflasher
Copy link

antiflasher commented Sep 28, 2023

It's a specifying of the issue #168.

I need to clamp chroma of an oklch color to a color space with saving it's original lightness and hue. Clamping to srgb works like a charm

let oklch = "oklch(70% 0.4 200)";
let oklchInSrgb = formatCss(clampChroma(oklch, "oklch"));
// oklch(0.7 0.118994140625 200)

Notice the lightness and the hue components being perfectly preserved.

But I couldn't find a way to achieve the same result in P3 color space.

let oklch = "oklch(70% 0.4 200)";
let colorInP3 = toGamut("p3", "oklch")(oklch);
// {"mode":"p3","r":-5.737632591262809e-15,"g":0.7258740626886799,"b":0.780719016756523}
let oklchInP3 = formatCss(converter("oklch")(colorInP3));
// oklch(0.7077162166817106 0.16120532620505068 201.07047721732414)

Notice the lightness and the hue components being mangled.

clampGamut(space) doesn't give the expected result as well:

let oklch = "oklch(70% 0.4 200)";
let colorInP3 = clampGamut("p3")(oklch);
// colorP3InP3: {"mode":"oklch","l":0.7775064298678066,"c":0.18210771244225787,"h":214.39043121337434}
let oklchInP3 = formatCss(colorInP3);
// oklch(0.7775064298678066 0.18210771244225787 214.39043121337434)

Can we have a clampChroma analog for P3 color space that works as perfect as the one for srgb?

@danburzo
Copy link
Collaborator

danburzo commented Oct 1, 2023

Hi @antiflasher!

Indeed, toGamut() was added as an extension of clampChroma() that can be used for any RGB-based color space. However, it implements a slightly different algorithm, based on the one proposed in the CSS Color 4 spec, that allows colors to be "roughly in gamut". This algorithm sacrifices a small bit of hue/lightness in order to obtain a more vivid color, rather than reducing the chroma exclusively.

To make toGamut() work more like clampChroma(), you need to disable this "roughly in gamut" aspect by passing in a JND (just-noticeable difference) argument of zero value:

// Before
oklch(toGamut('p3')("oklch(70% 0.4 200)")) 
Object { mode: "oklch", l: 0.7077162166817105, c: 0.16120532620505054, h: 201.0704772173241 }

// After
oklch(toGamut('p3', 'oklch', differenceEuclidean('oklch'), 0)("oklch(70% 0.4 200)")) 
Object { mode: "oklch", l: 0.7, c: 0.1594726562500001, h: 199.99999999999991 }

Due to floating-point operations in the conversion code the return values for hue and lightness aren’t guaranteed to be identical to the values passed, but as you can see they virtually coincide in the // After version.

@antiflasher
Copy link
Author

Thank you, it works!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants