From cc591237f5d513ee5636d368cfa0c46d73aaffdc Mon Sep 17 00:00:00 2001
From: Lim Chee Aun <cheeaun@gmail.com>
Date: Tue, 12 Nov 2024 17:45:28 +0800
Subject: [PATCH] Embrace chroma.js and okLCH

---
 package.json                   |  1 +
 src/components/media-modal.jsx | 47 +++++-----------------------
 src/locales/en.po              | 20 ++++++------
 src/utils/color-utils.js       | 56 +++-------------------------------
 4 files changed, 24 insertions(+), 100 deletions(-)

diff --git a/package.json b/package.json
index 006694b2..5b53c657 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
     "@lingui/macro": "~4.13.0",
     "@lingui/react": "~4.13.0",
     "@szhsin/react-menu": "~4.2.2",
+    "chroma-js": "~3.1.2",
     "compare-versions": "~6.1.1",
     "fast-blurhash": "~1.1.4",
     "fast-equals": "~5.0.1",
diff --git a/src/components/media-modal.jsx b/src/components/media-modal.jsx
index 1d2d1be3..8d277afd 100644
--- a/src/components/media-modal.jsx
+++ b/src/components/media-modal.jsx
@@ -10,7 +10,7 @@ import {
 } from 'preact/hooks';
 import { useHotkeys } from 'react-hotkeys-hook';
 
-import { oklab2rgb, rgb2oklab } from '../utils/color-utils';
+import { oklch2rgb, rgb2oklch } from '../utils/color-utils';
 import isRTL from '../utils/is-rtl';
 import showToast from '../utils/show-toast';
 import states from '../utils/states';
@@ -116,59 +116,28 @@ function MediaModal({
     return () => clearTimeout(timer);
   }, []);
 
-  const mediaOklabColors = useMemo(() => {
+  const mediaOkColors = useMemo(() => {
     return mediaAttachments?.map((media) => {
       const { blurhash } = media;
       if (blurhash) {
         const averageColor = getBlurHashAverageColor(blurhash);
-        return rgb2oklab(averageColor);
+        return rgb2oklch(averageColor);
       }
       return null;
     });
   }, [mediaAttachments]);
-  // const mediaAccentColors = useMemo(() => {
-  //   return mediaOklabColors?.map((labAverageColor) => {
-  //     if (labAverageColor) {
-  //       return oklab2rgb([0.6, labAverageColor[1], labAverageColor[2]]);
-  //     }
-  //     return null;
-  //   });
-  // }, [mediaOklabColors]);
   const mediaAccentColors = useMemo(() => {
-    return mediaOklabColors?.map((labAverageColor) => {
-      if (labAverageColor) {
+    return mediaOkColors?.map((okColor) => {
+      if (okColor) {
         return {
-          light: oklab2rgb([0.95, labAverageColor[1], labAverageColor[2]]),
-          dark: oklab2rgb([0.25, labAverageColor[1], labAverageColor[2]]),
-          default: oklab2rgb([0.6, labAverageColor[1], labAverageColor[2]]),
+          light: oklch2rgb([0.95, 0.01, okColor[2]]),
+          dark: oklch2rgb([0.35, 0.01, okColor[2]]),
+          default: oklch2rgb([0.6, okColor[1], okColor[2]]),
         };
       }
       return {};
     });
   });
-  // const mediaAccentGradient = useMemo(() => {
-  //   const gap = 5;
-  //   const range = 100 / mediaAccentColors.length;
-  //   return (
-  //     mediaAccentColors
-  //       ?.map((color, i) => {
-  //         const start = i * range + gap;
-  //         const end = (i + 1) * range - gap;
-  //         if (color) {
-  //           return `
-  //           rgba(${color?.join(',')}, 0.4) ${start}%,
-  //           rgba(${color?.join(',')}, 0.4) ${end}%
-  //         `;
-  //         }
-
-  //         return `
-  //           transparent ${start}%,
-  //           transparent ${end}%
-  //         `;
-  //       })
-  //       ?.join(', ') || 'transparent'
-  //   );
-  // }, [mediaAccentColors]);
   const mediaAccentGradients = useMemo(() => {
     const gap = 5;
     const range = 100 / mediaAccentColors.length;
diff --git a/src/locales/en.po b/src/locales/en.po
index 2ae08b2e..29a768ab 100644
--- a/src/locales/en.po
+++ b/src/locales/en.po
@@ -106,7 +106,7 @@ msgstr ""
 #: src/components/account-info.jsx:1118
 #: src/components/compose.jsx:2488
 #: src/components/media-alt-modal.jsx:45
-#: src/components/media-modal.jsx:388
+#: src/components/media-modal.jsx:357
 #: src/components/status.jsx:1721
 #: src/components/status.jsx:1738
 #: src/components/status.jsx:1862
@@ -410,7 +410,7 @@ msgstr ""
 #: src/components/keyboard-shortcuts-help.jsx:39
 #: src/components/list-add-edit.jsx:35
 #: src/components/media-alt-modal.jsx:33
-#: src/components/media-modal.jsx:352
+#: src/components/media-modal.jsx:321
 #: src/components/notification-service.jsx:156
 #: src/components/report-modal.jsx:75
 #: src/components/shortcuts-settings.jsx:230
@@ -858,13 +858,13 @@ msgid "Type to search GIFs"
 msgstr ""
 
 #: src/components/compose.jsx:3491
-#: src/components/media-modal.jsx:492
+#: src/components/media-modal.jsx:461
 #: src/components/timeline.jsx:889
 msgid "Previous"
 msgstr ""
 
 #: src/components/compose.jsx:3509
-#: src/components/media-modal.jsx:511
+#: src/components/media-modal.jsx:480
 #: src/components/timeline.jsx:906
 msgid "Next"
 msgstr ""
@@ -1185,27 +1185,27 @@ msgstr ""
 msgid "Speak"
 msgstr ""
 
-#: src/components/media-modal.jsx:399
+#: src/components/media-modal.jsx:368
 msgid "Open original media in new window"
 msgstr ""
 
-#: src/components/media-modal.jsx:403
+#: src/components/media-modal.jsx:372
 msgid "Open original media"
 msgstr ""
 
-#: src/components/media-modal.jsx:419
+#: src/components/media-modal.jsx:388
 msgid "Attempting to describe image. Please wait…"
 msgstr ""
 
-#: src/components/media-modal.jsx:434
+#: src/components/media-modal.jsx:403
 msgid "Failed to describe image"
 msgstr ""
 
-#: src/components/media-modal.jsx:444
+#: src/components/media-modal.jsx:413
 msgid "Describe image…"
 msgstr ""
 
-#: src/components/media-modal.jsx:467
+#: src/components/media-modal.jsx:436
 msgid "View post"
 msgstr ""
 
diff --git a/src/utils/color-utils.js b/src/utils/color-utils.js
index 02f5eed7..52c8e3d8 100644
--- a/src/utils/color-utils.js
+++ b/src/utils/color-utils.js
@@ -1,52 +1,6 @@
-// https://gist.github.com/earthbound19/e7fe15fdf8ca3ef814750a61bc75b5ce
-function clamp(value, min, max) {
-  return Math.max(Math.min(value, max), min);
-}
+import oklab2rgb from 'chroma-js/src/io/oklab/oklab2rgb.js';
+import rgb2oklab from 'chroma-js/src/io/oklab/rgb2oklab.js';
+import oklch2rgb from 'chroma-js/src/io/oklch/oklch2rgb.js';
+import rgb2oklch from 'chroma-js/src/io/oklch/rgb2oklch.js';
 
-const gammaToLinear = (c) =>
-  c >= 0.04045 ? Math.pow((c + 0.055) / 1.055, 2.4) : c / 12.92;
-const linearToGamma = (c) =>
-  c >= 0.0031308 ? 1.055 * Math.pow(c, 1 / 2.4) - 0.055 : 12.92 * c;
-
-export function rgb2oklab([r, g, b]) {
-  r = gammaToLinear(r / 255);
-  g = gammaToLinear(g / 255);
-  b = gammaToLinear(b / 255);
-  var l = 0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b;
-  var m = 0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b;
-  var s = 0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b;
-  l = Math.cbrt(l);
-  m = Math.cbrt(m);
-  s = Math.cbrt(s);
-  return [
-    l * +0.2104542553 + m * +0.793617785 + s * -0.0040720468,
-    l * +1.9779984951 + m * -2.428592205 + s * +0.4505937099,
-    l * +0.0259040371 + m * +0.7827717662 + s * -0.808675766,
-  ];
-}
-
-export function oklab2rgb([L, a, b]) {
-  var l = L + a * +0.3963377774 + b * +0.2158037573;
-  var m = L + a * -0.1055613458 + b * -0.0638541728;
-  var s = L + a * -0.0894841775 + b * -1.291485548;
-  // The ** operator here cubes; same as l_*l_*l_ in the C++ example:
-  l = l ** 3;
-  m = m ** 3;
-  s = s ** 3;
-  var r = l * +4.0767416621 + m * -3.3077115913 + s * +0.2309699292;
-  var g = l * -1.2684380046 + m * +2.6097574011 + s * -0.3413193965;
-  var b = l * -0.0041960863 + m * -0.7034186147 + s * +1.707614701;
-  // Convert linear RGB values returned from oklab math to sRGB for our use before returning them:
-  r = 255 * linearToGamma(r);
-  g = 255 * linearToGamma(g);
-  b = 255 * linearToGamma(b);
-  // OPTION: clamp r g and b values to the range 0-255; but if you use the values immediately to draw, JavaScript clamps them on use:
-  r = clamp(r, 0, 255);
-  g = clamp(g, 0, 255);
-  b = clamp(b, 0, 255);
-  // OPTION: round the values. May not be necessary if you use them immediately for rendering in JavaScript, as JavaScript (also) discards decimals on render:
-  r = Math.round(r);
-  g = Math.round(g);
-  b = Math.round(b);
-  return [r, g, b];
-}
+export { oklab2rgb, rgb2oklab, oklch2rgb, rgb2oklch };