Re: [csswg-drafts] [css-color-4] Gamut mapping pseudocode is confusing (#10226)

I tried implementing it and it got stuck in that `while` loop.

```js
const JND = 0.02;
const EPSILON = 0.0001;

export function mapGamut(
 startOKLCH: Color,
 toDestination: (x: Color) => Color,
 fromDestination: (x: Color) => Color,
): Color {

 // 11. set current to origin_Oklch
 const current = startOKLCH;

 // 12. set clipped to clip(current)
 let clipped = clip(toDestination(current));

 // 13. set E to delta(clipped, current)
 let E = deltaEOK(OKLCH_to_OKLab(fromDestination(clipped)), OKLCH_to_OKLab(current));

 // 14. if E < JND
 if (E < JND) {
  // 14.1. return clipped as the gamut mapped color
  return clipped;
 }

 // 15. set min to zero
 let min = 0.0;
 // 16. set max to the Oklch chroma of origin_Oklch
 const max = current[1];
 // 17. let min_inGamut be a boolean that represents when min is still in gamut, and set it to true
 let min_inGamut = true;

 // 18. while (max - min is greater than epsilon) repeat the following steps
 while ((max - min) > EPSILON) {
  // 18.1. set chroma to (min + max) / 2
  const chroma = (min + max) / 2.0;
  // 18.2. set the chroma component of current to chroma
  current[1] = chroma;
  // 18.3. if min_inGamut is true and also if inGamut(current) is true, set min to chroma and continue to repeat these steps
  if (min_inGamut && inGamut(toDestination(current))) {
   min = chroma;
   continue;
  }

  // 18.4. otherwise, if inGamut(current) is false carry out these steps:
  // 18.4.1. set clipped to clip(current)
  clipped = clip(toDestination(current));
  // 18.4.2. set E to delta(clipped, current)
  E = deltaEOK(OKLCH_to_OKLab(fromDestination(clipped)), OKLCH_to_OKLab(current));

  // 18.4.3. if E < JND
  if (E < JND) {
   // 18.4.3.1 if (JND - E < epsilon) return clipped as the gamut mapped color
   if ((JND - E) < EPSILON) {
    return clipped;
   }

   // 18.4.3.2 otherwise,
   // 18.4.3.2.1 set min_inGamut to false
   min_inGamut = false;
   // 18.4.3.2.2 set min to chroma
   min = chroma;
   continue;
  }
 }

 // 19. return clipped as the gamut mapped color
 return clip(toDestination([...current]));
}
```

Whereas my implementation based on a previous version of the pseudo code seemed to work as expected :

```js
const JND = 0.02;
const EPSILON = 0.00001;

export function mapGamut(
 startOKLCH: Color,
 toDestination: (x: Color) => Color,
 fromDestination: (x: Color) => Color,
): Color {

 const current = startOKLCH;

 let min = 0.0;
 let max = current[1];

 while ((max - min) > EPSILON) {
  const chroma = (min + max) / 2.0;
  current[1] = chroma;

  const converted = toDestination(current);
  if (inGamut(converted)) {
   min = chroma;
   continue;
  }

  const clipped = clip(converted);
  const delta_e = deltaEOK(OKLCH_to_OKLab(fromDestination(clipped)), OKLCH_to_OKLab(current));
  if (delta_e < JND) {
   return clipped;
  }

  max = chroma;
 }

 return clip(toDestination([...current]));
}
```

-- 
GitHub Notification of comment by romainmenke
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/10226#issuecomment-2063180895 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Thursday, 18 April 2024 07:13:33 UTC