export function createOklchToRGBA() { { /** * * @param {readonly [number, number, number, number, number, number, number, number, number]} A * @param {readonly [number, number, number]} B * @returns */ function multiplyMatrices(A, B) { return /** @type {const} */ ([ A[0] * B[0] + A[1] * B[1] + A[2] * B[2], A[3] * B[0] + A[4] * B[1] + A[5] * B[2], A[6] * B[0] + A[7] * B[1] + A[8] * B[2], ]); } /** * @param {readonly [number, number, number]} param0 */ function oklch2oklab([l, c, h]) { return /** @type {const} */ ([ l, isNaN(h) ? 0 : c * Math.cos((h * Math.PI) / 180), isNaN(h) ? 0 : c * Math.sin((h * Math.PI) / 180), ]); } /** * @param {readonly [number, number, number]} rgb */ function srgbLinear2rgb(rgb) { return rgb.map((c) => Math.abs(c) > 0.0031308 ? (c < 0 ? -1 : 1) * (1.055 * Math.abs(c) ** (1 / 2.4) - 0.055) : 12.92 * c, ); } /** * @param {readonly [number, number, number]} lab */ function oklab2xyz(lab) { const LMSg = multiplyMatrices( /** @type {const} */ ([ 1, 0.3963377773761749, 0.2158037573099136, 1, -0.1055613458156586, -0.0638541728258133, 1, -0.0894841775298119, -1.2914855480194092, ]), lab, ); const LMS = /** @type {[number, number, number]} */ ( LMSg.map((val) => val ** 3) ); return multiplyMatrices( /** @type {const} */ ([ 1.2268798758459243, -0.5578149944602171, 0.2813910456659647, -0.0405757452148008, 1.112286803280317, -0.0717110580655164, -0.0763729366746601, -0.4214933324022432, 1.5869240198367816, ]), LMS, ); } /** * @param {readonly [number, number, number]} xyz */ function xyz2rgbLinear(xyz) { return multiplyMatrices( [ 3.2409699419045226, -1.537383177570094, -0.4986107602930034, -0.9692436362808796, 1.8759675015077202, 0.04155505740717559, 0.05563007969699366, -0.20397695888897652, 1.0569715142428786, ], xyz, ); } /** @param {string} oklch */ return function (oklch) { oklch = oklch.replace("oklch(", ""); oklch = oklch.replace(")", ""); let splitOklch = oklch.split(" / "); let alpha = 1; if (splitOklch.length === 2) { alpha = Number(splitOklch.pop()?.replace("%", "")) / 100; } splitOklch = oklch.split(" "); const lch = splitOklch.map((v, i) => { if (!i && v.includes("%")) { return Number(v.replace("%", "")) / 100; } else { return Number(v); } }); const rgb = srgbLinear2rgb( xyz2rgbLinear( oklab2xyz(oklch2oklab(/** @type {[number, number, number]} */ (lch))), ), ).map((v) => { return Math.max(Math.min(Math.round(v * 255), 255), 0); }); return [...rgb, alpha]; }; } }