UNPKG

colorjs.io

Version:

Let’s get serious about color

143 lines (126 loc) 3.13 kB
import RGBColorSpace from "../RGBColorSpace.js"; import sRGBLinear from "./srgb-linear.js"; import KEYWORDS from "../keywords.js"; /** @import { Coords } from "../types.js" */ let coordGrammar = Array(3).fill("<percentage> | <number>[0, 255]"); let coordGrammarNumber = Array(3).fill("<number>[0, 255]"); export default new RGBColorSpace({ id: "srgb", name: "sRGB", base: sRGBLinear, fromBase: rgb => { // convert an array of linear-light sRGB values in the range 0.0-1.0 // to gamma corrected form // https://en.wikipedia.org/wiki/SRGB return rgb.map(val => { let sign = val < 0 ? -1 : 1; let abs = val * sign; if (abs > 0.0031308) { return sign * (1.055 * abs ** (1 / 2.4) - 0.055); } return 12.92 * val; }); }, toBase: rgb => { // convert an array of sRGB values in the range 0.0 - 1.0 // to linear light (un-companded) form. // https://en.wikipedia.org/wiki/SRGB return rgb.map(val => { let sign = val < 0 ? -1 : 1; let abs = val * sign; if (abs <= 0.04045) { return val / 12.92; } return sign * ((abs + 0.055) / 1.055) ** 2.4; }); }, formats: { rgb: { coords: coordGrammar, }, rgb_number: { name: "rgb", commas: true, coords: coordGrammarNumber, alpha: false, }, color: { /* use defaults */ }, rgba: { coords: coordGrammar, commas: true, alpha: true, }, rgba_number: { name: "rgba", commas: true, coords: coordGrammarNumber, }, hex: { type: "custom", toGamut: true, test: str => /^#(([a-f0-9]{2}){3,4}|[a-f0-9]{3,4})$/i.test(str), parse (str) { if (str.length <= 5) { // #rgb or #rgba, duplicate digits str = str.replace(/[a-f0-9]/gi, "$&$&"); } /** @type {number[]} */ let rgba = []; // @ts-expect-error Type 'void' is not assignable to type 'string' str.replace(/[a-f0-9]{2}/gi, component => { rgba.push(parseInt(component, 16) / 255); }); return { spaceId: "srgb", coords: /** @type {Coords} */ (rgba.slice(0, 3)), alpha: /** @type {number} */ (rgba.slice(3)[0]), }; }, serialize: ( coords, alpha, { collapse = true, // collapse to 3-4 digit hex when possible? alpha: alphaFormat, } = {}, ) => { if ((alphaFormat !== false && alpha < 1) || alphaFormat === true) { coords.push(alpha); } coords = /** @type {[number, number, number]} */ ( coords.map(c => Math.round(c * 255)) ); let collapsible = collapse && coords.every(c => c % 17 === 0); let hex = coords .map(c => { if (collapsible) { return (c / 17).toString(16); } return c.toString(16).padStart(2, "0"); }) .join(""); return "#" + hex; }, }, keyword: { type: "custom", test: str => /^[a-z]+$/i.test(str), parse (str) { str = str.toLowerCase(); let ret = { spaceId: "srgb", coords: null, alpha: 1 }; if (str === "transparent") { ret.coords = KEYWORDS.black; ret.alpha = 0; } else { ret.coords = KEYWORDS[str]; } if (ret.coords) { return ret; } }, }, }, });