Gamut mapping: clarification of chroma reduction algorithm
See original GitHub issueHello, I’m coming to the repo from @svgeesus’s comment regarding color clamping in the CSSWG repository. I’ve finally had the chance to look into the proposed algorithm, and I just wanted to confirm some details.
A basic bisection method for reducing the LCH chroma:
let low = 0;
let high = color.chroma;
while (high - low > ε) {
color.chroma = (high + low) / 2;
if (displayable(color)) {
low = color.chroma;
} else {
high = color.chroma;
}
}
color.js
compares the color at each iteration with its clipped version:
while ((high - low > ε) && (error < base_error)) {
let clipped = mappedColor.toGamut({space, method: "clip"});
let deltaE = mappedColor.deltaE(clipped, {method: "2000"});
error = color.deltaE(mappedColor, {method: "2000"});
if (deltaE - 2 < ε) {
low = mappedColor[coordName];
// console.log(++i, "in", mappedColor.chroma, mappedColor.srgb, error);
}
else {
// console.log(++i, "out", mappedColor.chroma, mappedColor.srgb, clipped.srgb, deltaE, error);
if (Math.abs(deltaE - 2) < ε) {
// We've found the boundary
break;
}
high = mappedColor[coordName];
}
mappedColor[coordName] = (high + low) / 2;
}
The current implementation seems to have diverged from the description in the docs:
Instead, the default algorithm reduces chroma (by binary search) and also, at each stage, calculates the deltaE2000 between the current estimate and a channel-clipped version of that color. If the deltaE is less than 2, the clipped color is displayed.
At a first pass through the function, what I see is the definition of displayable()
/ inGamut()
relaxed to include colors at a deltaE2000 distance smaller than 2 to an in-gamut color (which, even by itself, sounds like a great idea). What I’m not seeing, unless I’m missing something, is how the condition Math.abs(deltaE - 2) < ε)
can ever be fulfilled (and the clipped color returned early) without deltaE - 2 < ε
catching it first.
Issue Analytics
- State:
- Created 3 years ago
- Comments:6
Top GitHub Comments
Note that CSS Color 4 now describes gamut mapping. Much of that is just explanation, but it is followed by the actual algorithm which is constant-lightness, constant-hue chroma reduction in OKLCH using deltaE OK as the difference metric.
I would like to prototype the CSS Color 4 gamut mapping algorithm in color.js and have it available as an option. (It should probably the default, as it is clearly better than CIE LCH chroma reduction).