UNPKG

@texel/color

Version:

a minimal and modern color library

95 lines (86 loc) 3.32 kB
// Oklab and related spaces: OKLCH, OKHSV(sRGB), OKHSL(sRGB) import { constrainAngle, vec3 } from "../util.js"; import { OKHSLToOKLab, OKHSVToOKLab, OKLabToOKHSL, OKLabToOKHSV, } from "../okhsl.js"; import { sRGBGamut } from "./srgb.js"; // based on colorjs.io, could perhaps use a more specific number than this const ACHROMATIC_EPSILON = (0.4 - 0.0) / 100000; /** * The OKLab color space. * @type {ColorSpace} * @category spaces */ export const OKLab = { id: "oklab", }; /** * The OKLCH color space, with Lightness, Chroma, and Hue components. This is the cylindrical form of the {@link OKLab} color space. * @type {ColorSpace} * @category spaces */ export const OKLCH = { id: "oklch", base: OKLab, toBase: (oklch, out = vec3()) => { // Note: newer version of Colorjs.io clamps oklch chroma // However, this means that oklch(0.5, -0.36, 90) -> srgb will result in an in-gamut rgb // which seems a bit odd; you'd expect it to be out of gamut. So we will leave // chroma unclamped for this conversion. // const C = Math.max(0, oklch[1]); const C = oklch[1]; const H = oklch[2]; out[0] = oklch[0]; // L remains the same out[1] = C * Math.cos((H * Math.PI) / 180); // a out[2] = C * Math.sin((H * Math.PI) / 180); // b return out; }, fromBase: (oklab, out = vec3()) => { // These methods are used for other polar forms as well, so we can't hardcode the ε const a = oklab[1]; const b = oklab[2]; let isAchromatic = Math.abs(a) < ACHROMATIC_EPSILON && Math.abs(b) < ACHROMATIC_EPSILON; let hue = isAchromatic ? 0 : constrainAngle((Math.atan2(b, a) * 180) / Math.PI); let C = isAchromatic ? 0 : Math.sqrt(a * a + b * b); out[0] = oklab[0]; // L remains the same out[1] = C; // Chroma out[2] = hue; // Hue, in degrees [0 to 360) return out; }, }; /** * An implementation of the OKHSL color space, fixed to the {@link sRGBGamut}. This is useful for color pickers and other applications where * you wish to work with components in a well-defined and enclosed cylindrical form. If you wish to use OKHSL with a different gamut, you'll * need to use the {@link OKHSLToOKLab} and {@link OKLabToOKHSL} methods directly, passing your desired gamut. * @type {ColorSpace} * @category spaces */ export const OKHSL = { // Note: sRGB gamut only // For other gamuts, use okhsl method directly id: "okhsl", base: OKLab, toBase: (okhsl, out = vec3()) => OKHSLToOKLab(okhsl, sRGBGamut, out), fromBase: (oklab, out = vec3()) => OKLabToOKHSL(oklab, sRGBGamut, out), }; /** * An implementation of the OKHSV color space, fixed to the {@link sRGBGamut}. This is useful for color pickers and other applications where * you wish to work with components in a well-defined and enclosed cylindrical form. If you wish to use OKHSL with a different gamut, you'll * need to use the {@link OKHSLToOKLab} and {@link OKLabToOKHSL} methods directly, passing your desired gamut. * @type {ColorSpace} * @category spaces */ export const OKHSV = { // Note: sRGB gamut only // For other gamuts, use okhsv method directly id: "okhsv", base: OKLab, toBase: (okhsl, out = vec3()) => OKHSVToOKLab(okhsl, sRGBGamut, out), fromBase: (oklab, out = vec3()) => OKLabToOKHSV(oklab, sRGBGamut, out), };