Suggestion to specify target gamut for clampChroma()
danburzo opened this issue · 1 comments
Discussed in #212
Originally posted by dokozero November 20, 2023
Hello @danburzo,
I use Culori for the OkColor Figma plugin and it's quite useful, however, regarding gamut clipping by the chroma only, when I saw clampGamut() for the first time, I thought that it has an option to specify the target gamut to clamp.
As it was not the case, I ended up making a local modified version that uses inGamut() instead of displayable(), as I read from doc that it was equivalent to inGamut('rgb'). However, I had to duplicate culori's main js file into my project.
With this update, I'm able to easily clamp to sRGB or P3 gamut. I don't use toGamut() as I need to keep the same hue and lightness.
I saw previous issue regarding this topic and my suggestion would be to add a new optional target gamut param to clampChroma(), default to 'rgb' for retro-compatibility:
function clampChroma(color, mode = 'lch', targetGamut = 'rgb') {
const isInTargetGamut = inGamut(targetGamut)
color = prepare_default(color)
if (color === void 0 || isInTargetGamut(color)) return color
let conv = converter_default(color.mode)
color = converter_default(mode)(color)
let clamped = { ...color, c: 0 }
if (!isInTargetGamut(clamped)) {
return conv(fixup_rgb(rgb(clamped)))
}
let start = 0
let end = color.c
let range = getMode(mode).ranges.c
let resolution = (range[1] - range[0]) / Math.pow(2, 13)
let _last_good_c
while (end - start > resolution) {
clamped.c = start + (end - start) * 0.5
if (isInTargetGamut(clamped)) {
_last_good_c = clamped.c
start = clamped.c
} else {
end = clamped.c
}
}
return conv(isInTargetGamut(clamped) ? clamped : { ...clamped, c: _last_good_c })
}
For comparison, current code:
function clampChroma(color, mode = 'lch') {
color = prepare_default(color)
if (color === void 0 || displayable(color)) return color
let conv = converter_default(color.mode)
color = converter_default(mode)(color)
let clamped = { ...color, c: 0 }
if (!displayable(clamped)) {
return conv(fixup_rgb(rgb(clamped)))
}
let start = 0
let end = color.c
let range = getMode(mode).ranges.c
let resolution = (range[1] - range[0]) / Math.pow(2, 13)
let _last_good_c
while (end - start > resolution) {
clamped.c = start + (end - start) * 0.5
if (displayable(clamped)) {
_last_good_c = clamped.c
start = clamped.c
} else {
end = clamped.c
}
}
return conv(displayable(clamped) ? clamped : { ...clamped, c: _last_good_c })
}
I saw your answer from #211, with:
oklch(toGamut('p3', 'oklch', differenceEuclidean('oklch'), 0)("oklch(70% 0.4 200)"))
But personally, I think the new param on clampChroma() would be nice.
Thanks for your time.
Fixed in culori@3.3.0