@spellix/magic-color-transformer
Version:
Magic color transform library
141 lines (112 loc) • 3.82 kB
text/typescript
/* eslint-disable -- allow for magic-color */
import type { HexColor, HsbColor, HslColor, LabColor, RgbColor } from '../types';
const rgbRegex = /^rgba?\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(0|0?\.\d+|1(?:\.0)?))?\)$/;
const newRgbRegex = /^rgb\((\d+)\s+(\d+)\s+(\d+)(?:\s*\/\s*(0|0?\.\d+|1(?:\.0)?))?\s*\)$/;
export function isRgb(color: string): boolean {
return rgbRegex.test(color) || newRgbRegex.test(color);
}
export function parseRgb(color: string) {
const match = rgbRegex.exec(color) || newRgbRegex.exec(color);
if (!match) throw new Error('Invalid RGB or RGBA color format.');
const rgb = [match[1], match[2], match[3]].map(Number) as RgbColor;
const alpha = match[4] ? Number.parseFloat(match[4]) : 1;
return { values: rgb, alpha };
}
export function rgbToHex(color: RgbColor): HexColor {
const [r, g, b] = color.map(Math.round);
// @ts-expect-error - allow for magic-color
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
}
export function rgbToHsl(color: RgbColor): HslColor {
const [r, g, b] = color.map((i) => i / 255);
// @ts-expect-error - allow for magic-color
const max = Math.max(r, g, b);
// @ts-expect-error - allow for magic-color
const min = Math.min(r, g, b);
let h = 0;
let s = 0;
const l = (max + min) / 2;
if (max !== min) {
const delta = max - min;
s = l > 0.5 ? delta / (2 - max - min) : delta / (max + min);
switch (max) {
case r: {
// @ts-expect-error - allow for magic-color
h = (g - b) / delta + (g < b ? 6 : 0);
break;
}
case g: {
// @ts-expect-error - allow for magic-color
h = (b - r) / delta + 2;
break;
}
case b: {
// @ts-expect-error - allow for magic-color
h = (r - g) / delta + 4;
break;
}
}
h *= 60;
}
return [h, s * 100, l * 100];
}
export function rgbToHsb(color: RgbColor): HsbColor {
const [r, g, b] = color.map((i) => i / 255);
// @ts-expect-error - allow for magic-color
const max = Math.max(r, g, b);
// @ts-expect-error - allow for magic-color
const min = Math.min(r, g, b);
let h = 0;
const v = max;
const d = max - min;
const s = max === 0 ? 0 : d / max;
if (max === min) {
h = 0; // achromatic
} else {
switch (max) {
case r: {
// @ts-expect-error - allow for magic-color
h = (g - b) / d + (g < b ? 6 : 0);
break;
}
case g: {
// @ts-expect-error - allow for magic-color
h = (b - r) / d + 2;
break;
}
case b: {
// @ts-expect-error - allow for magic-color
h = (r - g) / d + 4;
break;
}
}
h /= 6;
}
return [h * 360, s * 100, v * 100];
}
export function rgbToLab(color: RgbColor): LabColor {
function rgb_xyz(rgb: number) {
const r = rgb / 255;
if (r <= 0.040_45) return r / 12.92;
return ((r + 0.055) / 1.055) ** 2.4;
}
function xyz_lab(t: number) {
if (t > 0.008_856_452) return t ** (1 / 3);
return t / 0.128_418_55 + 0.137_931_034;
}
function rgb2xyz(color: RgbColor) {
const [r, g, b] = color.map(rgb_xyz);
// @ts-expect-error - allow for magic-color
const x = xyz_lab((0.412_456_4 * r + 0.357_576_1 * g + 0.180_437_5 * b) / 0.950_47);
// @ts-expect-error - allow for magic-color
const y = xyz_lab((0.212_672_9 * r + 0.715_152_2 * g + 0.072_175 * b) / 1);
// @ts-expect-error - allow for magic-color
const z = xyz_lab((0.019_333_9 * r + 0.119_192 * g + 0.950_304_1 * b) / 1.088_83);
return [x, y, z];
}
const [x, y, z] = rgb2xyz(color);
// @ts-expect-error - allow for magic-color
const l = 116 * y - 16;
// @ts-expect-error - allow for magic-color
return [l < 0 ? 0 : l, 500 * (x - y), 200 * (y - z)] as LabColor;
}