Closed
Description
The CSS gamut mapping algorithm pseudocode can result in infinite looping for some inputs. As an example, gamut mapping the color oklch(104.4% 0 336)
to display-p3
loops indefinitely due to the following steps:
gamut map oklch(104.4% 0 336) to display-p3:
origin_OKLCH -> oklch(104.4% 0 336)
inGamut<display-p3>(origin_OKLCH) -> false
min -> 0
max -> 0
start bisection loop:
chroma -> 0
current -> oklch(104.4% 0 336)
inGamut<display-p3>(current) -> false
current as display-p3 -> color(display-p3 1.058 1.058 1.058)
clipped -> (display-p3 1 1 1)
E -> delta(clipped, current)
-> clipped as oklab(100% 0 5.96E-8)
-> current as oklab(104.4% 0 0)
-> sqrt(((100 / 100) - (104.4 / 100))^2 + (0 - 0)^2 + (5.96E-8 - 0)^2)
-> 0.04
0.04 is not less than JND of 0.02, so we loop forever
I added a condition to step 9.4.3 to also check if chroma was 0, making it something like:
3. if E < JND or chroma is 0 convert clipped to destination and return it as the gamut mapped color
(though, it isn't really necessary to say "convert clipped to destination" since clipped is already in the destination color space by the definition of clip() in step 6.)
I haven't thought too deeply about whether there are any other cases that could infinite loop.