From f5e743259f5e14e51d9b834c3e47a999a95a768f Mon Sep 17 00:00:00 2001 From: Gregor Aisch Date: Sat, 17 Aug 2024 13:09:50 +0000 Subject: [PATCH] fix: use oklab implementation from W3C (#350) --- CHANGELOG.md | 2 +- src/io/css/css2rgb.js | 58 ++++++++++++++++++++++++---------- src/io/css/hsl2css.js | 9 +++--- src/io/css/lab2css.js | 9 +++--- src/io/css/lch2css.js | 9 +++--- src/io/css/oklab2css.js | 9 +++--- src/io/css/oklch2css.js | 9 +++--- src/io/lab/lab2rgb.js | 1 + src/io/lab/rgb2lab.js | 1 + src/io/oklab/oklab2rgb.js | 51 +++++++++++++++--------------- src/io/oklab/rgb2oklab.js | 54 ++++++++++++++++--------------- src/io/oklch/oklch2rgb.js | 4 +-- src/utils/index.js | 5 ++- src/utils/multiply-matrices.js | 36 +++++++++++++++++++++ test/io/css2rgb.test.js | 11 ++++--- test/io/oklab2rgb.test.js | 18 +++++------ test/io/oklch2rgb.test.js | 18 +++++------ test/io/rgb2css.test.js | 4 +-- test/io/rgb2oklch.test.js | 6 ++-- 19 files changed, 192 insertions(+), 122 deletions(-) create mode 100644 src/utils/multiply-matrices.js diff --git a/CHANGELOG.md b/CHANGELOG.md index b7f00e6e..f34bd3ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## Changelog -### 3.0.0 +### 3.0.0-0 * 🎉 NEW: add support for modern CSS color spaces `lab()`, `lch()`, `oklab()`, `oklch()`. * 🎉 NEW: you can now control the standard white reference point for the CIE Lab and CIE Lch color spaces via `setLabWhitePoint`. * chroma.css will no longer return legacy CSS colors like `rgb(255, 255, 0)` but modern CSS colors like `rgb(255 255 0)`. diff --git a/src/io/css/css2rgb.js b/src/io/css/css2rgb.js index 5a6a8b3a..1519fe13 100644 --- a/src/io/css/css2rgb.js +++ b/src/io/css/css2rgb.js @@ -1,9 +1,11 @@ import hsl2rgb from '../hsl/hsl2rgb.js'; import lab2rgb from '../lab/lab2rgb.js'; import lch2rgb from '../lch/lch2rgb.js'; -// import oklab2rgb from '../oklab/oklab2rgb.js'; +import oklab2rgb from '../oklab/oklab2rgb.js'; +import oklch2rgb from '../oklch/oklch2rgb.js'; import input from '../input.js'; import limit from '../../utils/limit.js'; +import { getLabWhitePoint, setLabWhitePoint } from '../lab/lab-constants.js'; const RE_RGB = /^rgb\(\s*(-?\d+) \s*(-?\d+)\s* \s*(-?\d+)\s*\)$/; const RE_RGB_LEGACY = /^rgb\(\s*(-?\d+),\s*(-?\d+)\s*,\s*(-?\d+)\s*\)$/; @@ -36,9 +38,11 @@ const RE_HSLA_LEGACY = const RE_LAB = /^lab\(\s*(-?\d+(?:\.\d+)?%?) \s*(-?\d+(?:\.\d+)?%?) \s*(-?\d+(?:\.\d+)?%?)\s*(?:\/\s*(\d+(?:\.\d+)?))?\)?$/; const RE_LCH = - /^lch\(\s*(-?\d+(?:\.\d+)?%?) \s*(?:(-?\d+(?:\.\d+)?%?)|none) \s*(-?\d+(?:\.\d+)?(?:deg)?|none)\s*(?:\/\s*(\d+(?:\.\d+)?))?\)?$/; -// const RE_OKLAB = -// /^oklab\(\s*(-?\d+(?:\.\d+)?%?) \s*(-?\d+(?:\.\d+)?%?) \s*(-?\d+(?:\.\d+)?%?)\s*(?:\/\s*(\d+(?:\.\d+)?))?\)?$/; + /^lch\(\s*(-?\d+(?:\.\d+)?%?) \s*((?:-?\d+(?:\.\d+)?%?)|none) \s*(-?\d+(?:\.\d+)?(?:deg)?|none)\s*(?:\/\s*(\d+(?:\.\d+)?))?\)?$/; +const RE_OKLAB = + /^oklab\(\s*(-?\d+(?:\.\d+)?%?) \s*(-?\d+(?:\.\d+)?%?) \s*(-?\d+(?:\.\d+)?%?)\s*(?:\/\s*(\d+(?:\.\d+)?))?\)?$/; +const RE_OKLCH = + /^oklch\(\s*(-?\d+(?:\.\d+)?%?) \s*(?:(-?\d+(?:\.\d+)?%?)|none) \s*(-?\d+(?:\.\d+)?(?:deg)?|none)\s*(?:\/\s*(\d+(?:\.\d+)?))?\)?$/; const { round } = Math; @@ -47,8 +51,8 @@ const roundRGB = (rgb) => { }; const percentToAbsolute = (pct, min = 0, max = 100, signed = false) => { - if (pct.endsWith('%')) { - pct = parseFloat(pct.substr(0, pct.length - 1)) / 100; + if (typeof pct === 'string' && pct.endsWith('%')) { + pct = parseFloat(pct.substring(0, pct.length - 1)) / 100; if (signed) { // signed percentages are in the range -100% to 100% pct = min + (pct + 1) * 0.5 * (max - min); @@ -144,7 +148,12 @@ const css2rgb = (css) => { lab[0] = percentToAbsolute(lab[0], 0, 100); lab[1] = percentToAbsolute(lab[1], -125, 125, true); lab[2] = percentToAbsolute(lab[2], -125, 125, true); + // convert to D50 Lab whitepoint + const wp = getLabWhitePoint(); + setLabWhitePoint('d50'); const rgb = roundRGB(lab2rgb(lab)); + // convert back to original Lab whitepoint + setLabWhitePoint(wp); rgb[3] = m[4] !== undefined ? +m[4] : 1; return rgb; } @@ -154,20 +163,35 @@ const css2rgb = (css) => { lch[0] = percentToAbsolute(lch[0], 0, 100); lch[1] = percentToAbsolute(noneToValue(lch[1], 0), 0, 150, false); lch[2] = +noneToValue(lch[2].replace('deg', ''), 0); + // convert to D50 Lab whitepoint + const wp = getLabWhitePoint(); + setLabWhitePoint('d50'); const rgb = roundRGB(lch2rgb(lch)); + // convert back to original Lab whitepoint + setLabWhitePoint(wp); rgb[3] = m[4] !== undefined ? +m[4] : 1; return rgb; } - // if ((m = css.match(RE_OKLAB))) { - // const oklab = m.slice(1, 4); - // oklab[0] = percentToAbsolute(oklab[0], 0, 1); - // oklab[1] = percentToAbsolute(oklab[1], -0.4, 0.4, true); - // oklab[2] = percentToAbsolute(oklab[2], -0.4, 0.4, true); - // const rgb = roundRGB(oklab2rgb(oklab)); - // rgb[3] = m[4] !== undefined ? +m[4] : 1; - // return rgb; - // } + if ((m = css.match(RE_OKLAB))) { + const oklab = m.slice(1, 4); + oklab[0] = percentToAbsolute(oklab[0], 0, 1); + oklab[1] = percentToAbsolute(oklab[1], -0.4, 0.4, true); + oklab[2] = percentToAbsolute(oklab[2], -0.4, 0.4, true); + const rgb = roundRGB(oklab2rgb(oklab)); + rgb[3] = m[4] !== undefined ? +m[4] : 1; + return rgb; + } + + if ((m = css.match(RE_OKLCH))) { + const oklch = m.slice(1, 4); + oklch[0] = percentToAbsolute(oklch[0], 0, 1); + oklch[1] = percentToAbsolute(noneToValue(oklch[1], 0), 0, 0.4, false); + oklch[2] = +noneToValue(oklch[2].replace('deg', ''), 0); + const rgb = roundRGB(oklch2rgb(oklch)); + rgb[3] = m[4] !== undefined ? +m[4] : 1; + return rgb; + } }; css2rgb.test = (s) => { @@ -180,7 +204,9 @@ css2rgb.test = (s) => { RE_HSL.test(s) || RE_HSLA.test(s) || RE_LAB.test(s) || - // RE_OKLAB.test(s) || + RE_LCH.test(s) || + RE_OKLAB.test(s) || + RE_OKLCH.test(s) || // legacy RE_RGB_LEGACY.test(s) || RE_RGBA_LEGACY.test(s) || diff --git a/src/io/css/hsl2css.js b/src/io/css/hsl2css.js index 9e0f48eb..b7e196fd 100644 --- a/src/io/css/hsl2css.js +++ b/src/io/css/hsl2css.js @@ -1,5 +1,4 @@ -import { unpack, last } from '../../utils/index.js'; -const rnd = (a) => Math.round(a * 100) / 100; +import { unpack, last, rnd2 } from '../../utils/index.js'; /* * supported arguments: @@ -12,9 +11,9 @@ const rnd = (a) => Math.round(a * 100) / 100; const hsl2css = (...args) => { const hsla = unpack(args, 'hsla'); let mode = last(args) || 'lsa'; - hsla[0] = rnd(hsla[0] || 0) + 'deg'; - hsla[1] = rnd(hsla[1] * 100) + '%'; - hsla[2] = rnd(hsla[2] * 100) + '%'; + hsla[0] = rnd2(hsla[0] || 0) + 'deg'; + hsla[1] = rnd2(hsla[1] * 100) + '%'; + hsla[2] = rnd2(hsla[2] * 100) + '%'; if (mode === 'hsla' || (hsla.length > 3 && hsla[3] < 1)) { hsla[3] = '/ ' + (hsla.length > 3 ? hsla[3] : 1); mode = 'hsla'; diff --git a/src/io/css/lab2css.js b/src/io/css/lab2css.js index 66e7ef2a..567111c7 100644 --- a/src/io/css/lab2css.js +++ b/src/io/css/lab2css.js @@ -1,5 +1,4 @@ -import { unpack, last } from '../../utils/index.js'; -const rnd = (a) => Math.round(a * 100) / 100; +import { unpack, last, rnd2 } from '../../utils/index.js'; /* * supported arguments: @@ -11,9 +10,9 @@ const rnd = (a) => Math.round(a * 100) / 100; const lab2css = (...args) => { const laba = unpack(args, 'lab'); let mode = last(args) || 'lab'; - laba[0] = rnd(laba[0]) + '%'; - laba[1] = rnd(laba[1]); - laba[2] = rnd(laba[2]); + laba[0] = rnd2(laba[0]) + '%'; + laba[1] = rnd2(laba[1]); + laba[2] = rnd2(laba[2]); if (mode === 'laba' || (laba.length > 3 && laba[3] < 1)) { laba[3] = '/ ' + (laba.length > 3 ? laba[3] : 1); } else { diff --git a/src/io/css/lch2css.js b/src/io/css/lch2css.js index 9d74830e..1bc52e25 100644 --- a/src/io/css/lch2css.js +++ b/src/io/css/lch2css.js @@ -1,5 +1,4 @@ -import { unpack, last } from '../../utils/index.js'; -const rnd = (a) => Math.round(a * 100) / 100; +import { unpack, last, rnd2 } from '../../utils/index.js'; /* * supported arguments: @@ -11,9 +10,9 @@ const rnd = (a) => Math.round(a * 100) / 100; const lch2css = (...args) => { const lcha = unpack(args, 'lch'); let mode = last(args) || 'lab'; - lcha[0] = rnd(lcha[0]) + '%'; - lcha[1] = rnd(lcha[1]); - lcha[2] = rnd(lcha[2]) + 'deg'; // add deg unit to hue + lcha[0] = rnd2(lcha[0]) + '%'; + lcha[1] = rnd2(lcha[1]); + lcha[2] = rnd2(lcha[2]) + 'deg'; // add deg unit to hue if (mode === 'lcha' || (lcha.length > 3 && lcha[3] < 1)) { lcha[3] = '/ ' + (lcha.length > 3 ? lcha[3] : 1); } else { diff --git a/src/io/css/oklab2css.js b/src/io/css/oklab2css.js index ae9274bb..78f8525f 100644 --- a/src/io/css/oklab2css.js +++ b/src/io/css/oklab2css.js @@ -1,11 +1,10 @@ -import { unpack } from '../../utils/index.js'; -const rnd = (a) => Math.round(a * 100) / 100; +import { unpack, rnd2, rnd3 } from '../../utils/index.js'; const oklab2css = (...args) => { const laba = unpack(args, 'lab'); - laba[0] = rnd(laba[0] * 100) + '%'; - laba[1] = rnd(laba[1]); - laba[2] = rnd(laba[2]); + laba[0] = rnd2(laba[0] * 100) + '%'; + laba[1] = rnd3(laba[1]); + laba[2] = rnd3(laba[2]); if (laba.length > 3 && laba[3] < 1) { laba[3] = '/ ' + (laba.length > 3 ? laba[3] : 1); } else { diff --git a/src/io/css/oklch2css.js b/src/io/css/oklch2css.js index 8faba956..a1ed7a23 100644 --- a/src/io/css/oklch2css.js +++ b/src/io/css/oklch2css.js @@ -1,11 +1,10 @@ -import { unpack } from '../../utils/index.js'; -const rnd = (a) => Math.round(a * 100) / 100; +import { unpack, rnd2, rnd3 } from '../../utils/index.js'; const oklab2css = (...args) => { const laba = unpack(args, 'lab'); - laba[0] = rnd(laba[0] * 100) + '%'; - laba[1] = rnd(laba[1]); - laba[2] = rnd(laba[2]) + 'deg'; + laba[0] = rnd2(laba[0] * 100) + '%'; + laba[1] = rnd3(laba[1]); + laba[2] = rnd2(laba[2]) + 'deg'; if (laba.length > 3 && laba[3] < 1) { laba[3] = '/ ' + (laba.length > 3 ? laba[3] : 1); } else { diff --git a/src/io/lab/lab2rgb.js b/src/io/lab/lab2rgb.js index b8720bb9..1719bf97 100644 --- a/src/io/lab/lab2rgb.js +++ b/src/io/lab/lab2rgb.js @@ -98,3 +98,4 @@ const xyz2rgb = (x, y, z) => { }; export default lab2rgb; +export { xyz2rgb }; diff --git a/src/io/lab/rgb2lab.js b/src/io/lab/rgb2lab.js index 4e04f441..fd1e651f 100644 --- a/src/io/lab/rgb2lab.js +++ b/src/io/lab/rgb2lab.js @@ -64,3 +64,4 @@ const rgb2xyz = (r, g, b) => { }; export default rgb2lab; +export { rgb2xyz }; diff --git a/src/io/oklab/oklab2rgb.js b/src/io/oklab/oklab2rgb.js index 7334d367..1d888f1e 100644 --- a/src/io/oklab/oklab2rgb.js +++ b/src/io/oklab/oklab2rgb.js @@ -1,33 +1,34 @@ import { unpack } from '../../utils/index.js'; -const { pow, sign } = Math; +import multiplyMatrices from '../../utils/multiply-matrices.js'; +import { xyz2rgb } from '../lab/lab2rgb.js'; -/* - * L* [0..100] - * a [-100..100] - * b [-100..100] - */ const oklab2rgb = (...args) => { args = unpack(args, 'lab'); - const [L, a, b] = args; - - const l = pow(L + 0.3963377774 * a + 0.2158037573 * b, 3); - const m = pow(L - 0.1055613458 * a - 0.0638541728 * b, 3); - const s = pow(L - 0.0894841775 * a - 1.291485548 * b, 3); - - return [ - 255 * lrgb2rgb(+4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s), - 255 * lrgb2rgb(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s), - 255 * lrgb2rgb(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s), - args.length > 3 ? args[3] : 1 - ]; + const [L, a, b, ...rest] = args; + const [X, Y, Z] = OKLab_to_XYZ([L, a, b]); + const [r, g, b_] = xyz2rgb(X, Y, Z); + return [r, g, b_, ...(rest.length > 0 && rest[0] < 1 ? [rest[0]] : [])]; }; -export default oklab2rgb; +// from https://www.w3.org/TR/css-color-4/#color-conversion-code +function OKLab_to_XYZ(OKLab) { + // Given OKLab, convert to XYZ relative to D65 + var LMStoXYZ = [ + [1.2268798758459243, -0.5578149944602171, 0.2813910456659647], + [-0.0405757452148008, 1.112286803280317, -0.0717110580655164], + [-0.0763729366746601, -0.4214933324022432, 1.5869240198367816] + ]; + var OKLabtoLMS = [ + [1.0, 0.3963377773761749, 0.2158037573099136], + [1.0, -0.1055613458156586, -0.0638541728258133], + [1.0, -0.0894841775298119, -1.2914855480194092] + ]; -function lrgb2rgb(c) { - const abs = Math.abs(c); - if (abs > 0.0031308) { - return (sign(c) || 1) * (1.055 * pow(abs, 1 / 2.4) - 0.055); - } - return c * 12.92; + var LMSnl = multiplyMatrices(OKLabtoLMS, OKLab); + return multiplyMatrices( + LMStoXYZ, + LMSnl.map((c) => c ** 3) + ); } + +export default oklab2rgb; diff --git a/src/io/oklab/rgb2oklab.js b/src/io/oklab/rgb2oklab.js index fa9a44b8..c5f05e41 100644 --- a/src/io/oklab/rgb2oklab.js +++ b/src/io/oklab/rgb2oklab.js @@ -1,33 +1,37 @@ import { unpack } from '../../utils/index.js'; -const { cbrt, pow, sign } = Math; +import multiplyMatrices from '../../utils/multiply-matrices.js'; +import { rgb2xyz } from '../lab/rgb2lab.js'; const rgb2oklab = (...args) => { - // OKLab color space implementation taken from - // https://bottosson.github.io/posts/oklab/ const [r, g, b, ...rest] = unpack(args, 'rgb'); - const [lr, lg, lb] = [ - rgb2lrgb(r / 255), - rgb2lrgb(g / 255), - rgb2lrgb(b / 255) - ]; - const l = cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb); - const m = cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb); - const s = cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb); - - return [ - 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s, - 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s, - 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s, - ...(rest.length > 0 && rest[0] < 1 ? [rest[0]] : []) - ]; + const xyz = rgb2xyz(r, g, b); + const oklab = XYZ_to_OKLab(xyz); + return [...oklab, ...(rest.length > 0 && rest[0] < 1 ? [rest[0]] : [])]; }; -export default rgb2oklab; +// from https://www.w3.org/TR/css-color-4/#color-conversion-code +function XYZ_to_OKLab(XYZ) { + // Given XYZ relative to D65, convert to OKLab + const XYZtoLMS = [ + [0.819022437996703, 0.3619062600528904, -0.1288737815209879], + [0.0329836539323885, 0.9292868615863434, 0.0361446663506424], + [0.0481771893596242, 0.2642395317527308, 0.6335478284694309] + ]; + const LMStoOKLab = [ + [0.210454268309314, 0.7936177747023054, -0.0040720430116193], + [1.9779985324311684, -2.4285922420485799, 0.450593709617411], + [0.0259040424655478, 0.7827717124575296, -0.8086757549230774] + ]; -function rgb2lrgb(c) { - const abs = Math.abs(c); - if (abs < 0.04045) { - return c / 12.92; - } - return (sign(c) || 1) * pow((abs + 0.055) / 1.055, 2.4); + const LMS = multiplyMatrices(XYZtoLMS, XYZ); + // JavaScript Math.cbrt returns a sign-matched cube root + // beware if porting to other languages + // especially if tempted to use a general power function + return multiplyMatrices( + LMStoOKLab, + LMS.map((c) => Math.cbrt(c)) + ); + // L in range [0,1]. For use in CSS, multiply by 100 and add a percent } + +export default rgb2oklab; diff --git a/src/io/oklch/oklch2rgb.js b/src/io/oklch/oklch2rgb.js index ab961a60..e1fe3048 100644 --- a/src/io/oklch/oklch2rgb.js +++ b/src/io/oklch/oklch2rgb.js @@ -4,10 +4,10 @@ import oklab2rgb from '../oklab/oklab2rgb.js'; const oklch2rgb = (...args) => { args = unpack(args, 'lch'); - const [l, c, h] = args; + const [l, c, h, ...rest] = args; const [L, a, b_] = lch2lab(l, c, h); const [r, g, b] = oklab2rgb(L, a, b_); - return [r, g, b, args.length > 3 ? args[3] : 1]; + return [r, g, b, ...(rest.length > 0 && rest[0] < 1 ? [rest[0]] : [])]; }; export default oklch2rgb; diff --git a/src/utils/index.js b/src/utils/index.js index c28dd919..4f050845 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,5 +1,8 @@ const { PI, min, max } = Math; +const rnd2 = (a) => Math.round(a * 100) / 100; +const rnd3 = (a) => Math.round(a * 100) / 100; + export { default as clip_rgb } from './clip_rgb.js'; export { default as limit } from './limit.js'; export { default as type } from './type.js'; @@ -11,4 +14,4 @@ const PITHIRD = PI / 3; const DEG2RAD = PI / 180; const RAD2DEG = 180 / PI; -export { PI, TWOPI, PITHIRD, DEG2RAD, RAD2DEG, min, max }; +export { PI, TWOPI, PITHIRD, DEG2RAD, RAD2DEG, min, max, rnd2, rnd3 }; diff --git a/src/utils/multiply-matrices.js b/src/utils/multiply-matrices.js new file mode 100644 index 00000000..75cf3fe9 --- /dev/null +++ b/src/utils/multiply-matrices.js @@ -0,0 +1,36 @@ +// from https://www.w3.org/TR/css-color-4/multiply-matrices.js +export default function multiplyMatrices(A, B) { + let m = A.length; + + if (!Array.isArray(A[0])) { + // A is vector, convert to [[a, b, c, ...]] + A = [A]; + } + + if (!Array.isArray(B[0])) { + // B is vector, convert to [[a], [b], [c], ...]] + B = B.map((x) => [x]); + } + + let p = B[0].length; + let B_cols = B[0].map((_, i) => B.map((x) => x[i])); // transpose B + let product = A.map((row) => + B_cols.map((col) => { + if (!Array.isArray(row)) { + return col.reduce((a, c) => a + c * row, 0); + } + + return row.reduce((a, c, i) => a + c * (col[i] || 0), 0); + }) + ); + + if (m === 1) { + product = product[0]; // Avoid [[a, b, c, ...]] + } + + if (p === 1) { + return product.map((x) => x[0]); // Avoid [[a], [b], [c], ...]] + } + + return product; +} diff --git a/test/io/css2rgb.test.js b/test/io/css2rgb.test.js index 9fe7d1d7..e1a17df8 100644 --- a/test/io/css2rgb.test.js +++ b/test/io/css2rgb.test.js @@ -33,10 +33,13 @@ describe('Testing CSS2RGB color conversions', () => { unknownColor: undefined, 'lab(47.99% -30.39 -8.98)': [0, 128, 128, 1], 'lab(47.99% -30.39 -8.98 / 0.25)': [0, 128, 128, 0.25], - 'lab(47.99% -24.312% -7.13%)': [0, 128, 128, 1] - - // 'oklab(92.83% -0.08 0.13)': [212, 248, 128, 1], - // 'oklab(92.83% -0.08 0.13 / 0.5)': [212, 248, 128, 0.5] + 'lab(47.99% -24.312% -7.18%)': [0, 128, 128, 1], + 'lch(93.12 58.8 115.62)': [212, 248, 128, 1], // #d4f880 + 'lch(93.12% 58.8 115.62deg)': [212, 248, 128, 1], // #d4f880 + 'lch(93.12% 39.2% 115.62deg)': [212, 248, 128, 1], // #d4f880 + 'lch(93.12% none none)': [235, 235, 235, 1], + 'oklch(92.83% 0.15 123.12deg)': [212, 248, 130, 1], + 'oklch(92.83% none none)': [231, 231, 231, 1] }; Object.keys(testCases).forEach((name) => { diff --git a/test/io/oklab2rgb.test.js b/test/io/oklab2rgb.test.js index 92684d8a..0734687a 100644 --- a/test/io/oklab2rgb.test.js +++ b/test/io/oklab2rgb.test.js @@ -6,15 +6,15 @@ const round = (v) => limit(Math.round(v), 0, 255); describe('Testing OKLab to RGB color conversions', () => { const testCases = { - black: { in: [0.0, 0.0, 0.0], out: [0, 0, 0, 1] }, - white: { in: [1.0, 0.0, 0.0], out: [255, 255, 255, 1] }, - gray: { in: [0.59987, 0.0, 0.0], out: [128, 128, 128, 1] }, - red: { in: [0.62796, 0.22486, 0.12585], out: [255, 0, 0, 1] }, - yellow: { in: [0.96798, -0.07137, 0.19857], out: [255, 255, 0, 1] }, - green: { in: [0.51975, -0.1403, 0.10768], out: [0, 128, 0, 1] }, - cyan: { in: [0.9054, -0.14944, -0.0394], out: [0, 255, 255, 1] }, - blue: { in: [0.45201, -0.03246, -0.31153], out: [0, 0, 255, 1] }, - magenta: { in: [0.70167, 0.27457, -0.16916], out: [255, 0, 255, 1] } + black: { in: [0.0, 0.0, 0.0], out: [0, 0, 0] }, + white: { in: [1.0, 0.0, 0.0], out: [255, 255, 255] }, + gray: { in: [0.59987, 0.0, 0.0], out: [128, 128, 128] }, + red: { in: [0.62796, 0.22486, 0.12585], out: [255, 0, 0] }, + yellow: { in: [0.96798, -0.07137, 0.19857], out: [255, 255, 0] }, + green: { in: [0.51975, -0.1403, 0.10768], out: [0, 128, 0] }, + cyan: { in: [0.9054, -0.14944, -0.0394], out: [0, 255, 255] }, + blue: { in: [0.45201, -0.03246, -0.31153], out: [0, 0, 255] }, + magenta: { in: [0.70167, 0.27457, -0.16916], out: [255, 0, 255] } }; Object.keys(testCases).forEach((key) => { diff --git a/test/io/oklch2rgb.test.js b/test/io/oklch2rgb.test.js index f0de157d..6cda32f5 100644 --- a/test/io/oklch2rgb.test.js +++ b/test/io/oklch2rgb.test.js @@ -6,29 +6,29 @@ const round = (v) => limit(Math.round(v), 0, 255); describe('Testing LCH conversions', () => { const testCases = { - black: { in: [0.0, 0.0, NaN], out: [0, 0, 0, 1] }, - white: { in: [1.0, 0.0, NaN], out: [255, 255, 255, 1] }, - gray: { in: [0.59987, 0.0, NaN], out: [128, 128, 128, 1] }, + black: { in: [0.0, 0.0, NaN], out: [0, 0, 0] }, + white: { in: [1.0, 0.0, NaN], out: [255, 255, 255] }, + gray: { in: [0.59987, 0.0, NaN], out: [128, 128, 128] }, red: { in: [0.62796, 0.25768, 29.233885192342633], - out: [255, 0, 0, 1] + out: [255, 0, 0] }, yellow: { in: [0.96798, 0.21101, 109.76923207652125], - out: [255, 255, 0, 1] + out: [255, 255, 0] }, green: { in: [0.51975, 0.17686, 142.49533888780996], - out: [0, 128, 0, 1] + out: [0, 128, 0] }, cyan: { in: [0.9054, 0.15455, 194.76894793196382], - out: [0, 255, 255, 1] + out: [0, 255, 255] }, - blue: { in: [0.45201, 0.31321, 264.052020638055], out: [0, 0, 255, 1] }, + blue: { in: [0.45201, 0.31321, 264.052020638055], out: [0, 0, 255] }, magenta: { in: [0.70167, 0.32249, 328.36341792345144], - out: [255, 0, 255, 1] + out: [255, 0, 255] } }; diff --git a/test/io/rgb2css.test.js b/test/io/rgb2css.test.js index f2594a6f..64b36bd2 100644 --- a/test/io/rgb2css.test.js +++ b/test/io/rgb2css.test.js @@ -42,12 +42,12 @@ const tests = { oklch: { rgb: [212, 248, 128], mode: 'oklch', - css: 'oklch(92.83% 0.15 123.13deg)' + css: 'oklch(92.83% 0.15 123.12deg)' }, oklcha: { rgb: [212, 248, 128, 0.6], mode: 'oklch', - css: 'oklch(92.83% 0.15 123.13deg / 0.6)' + css: 'oklch(92.83% 0.15 123.12deg / 0.6)' } }; diff --git a/test/io/rgb2oklch.test.js b/test/io/rgb2oklch.test.js index 09e8f795..428a7a3e 100644 --- a/test/io/rgb2oklch.test.js +++ b/test/io/rgb2oklch.test.js @@ -6,11 +6,11 @@ const tests = { white: { oklch: [1.0, 0.0, NaN], rgb: [255, 255, 255, 1] }, gray: { oklch: [0.6, 0.0, NaN], rgb: [128, 128, 128, 1] }, red: { oklch: [0.628, 0.258, 29.234], rgb: [255, 0, 0, 1] }, - yellow: { oklch: [0.968, 0.211, 109.769], rgb: [255, 255, 0, 1] }, + yellow: { oklch: [0.968, 0.211, 109.763], rgb: [255, 255, 0, 1] }, green: { oklch: [0.52, 0.177, 142.495], rgb: [0, 128, 0, 1] }, - cyan: { oklch: [0.905, 0.155, 194.769], rgb: [0, 255, 255, 1] }, + cyan: { oklch: [0.905, 0.155, 194.757], rgb: [0, 255, 255, 1] }, blue: { oklch: [0.452, 0.313, 264.052], rgb: [0, 0, 255, 1] }, - magenta: { oklch: [0.702, 0.322, 328.363], rgb: [255, 0, 255, 1] } + magenta: { oklch: [0.702, 0.322, 328.373], rgb: [255, 0, 255, 1] } }; const round = (digits) => {