colorizr
Version:
Manipulate colors like a boss
56 lines (45 loc) • 1.82 kB
text/typescript
import { oklch2oklab } from '~/converters';
import { GAMUT_EPSILON, MESSAGES, PRECISION } from '~/modules/constants';
import { invariant } from '~/modules/invariant';
import { isInGamut, oklabToLinearP3 } from '~/modules/linear-rgb';
import { round } from '~/modules/utils';
import { isNumberInRange, isString } from '~/modules/validators';
import parseCSS from '~/parse-css';
import { LCH } from '~/types';
/**
* Get the maximum chroma for a given lightness and hue in the OkLCH color space.
*
* @param input - The input color string or LCH object.
* @param precision - The number of decimal places for the result.
* @returns The maximum chroma value within P3 gamut.
*/
export function getP3MaxChroma(input: string | LCH, precision = PRECISION): number {
const { l, h } = isString(input) ? parseCSS(input, 'oklch') : input;
invariant(isNumberInRange(l, 0, 1), MESSAGES.lightnessRange);
invariant(isNumberInRange(h, 0, 360), MESSAGES.hueRange);
// Binary search parameters
const epsilon = GAMUT_EPSILON;
let low = 0;
let high = 0.5; // Increased max theoretical chroma
while (high - low > epsilon) {
const mid = (low + high) / 2;
const { l: L, a, b } = oklch2oklab({ l, c: mid, h }, 16);
const p3Color = oklabToLinearP3(L, a, b);
if (isInGamut(p3Color)) {
low = mid;
} else {
high = mid;
}
}
return round(low, precision);
}
/**
* Get an OkLCH color with maximum chroma in the P3 color space.
*
* @param input - The input color string or LCH object.
* @returns The OkLCH color string with maximum chroma.
*/
export function getP3MaxColor(input: string | LCH): string {
const lch = isString(input) ? parseCSS(input, 'oklch') : input;
return `oklch(${round(lch.l, PRECISION, false)} ${getP3MaxChroma(lch)} ${round(lch.h, PRECISION, false)})`;
}