This commit is contained in:
2024-12-17 14:36:15 -08:00
parent b2dbf46d28
commit 06d106de53
17731 changed files with 3037186 additions and 144 deletions

View File

@@ -0,0 +1,30 @@
/* Convert between HSL and HSV
/* ========================================================================== */
export function hsl2hsv(hslH, hslS, hslL) {
const hsv1 = hslS * (hslL < 50 ? hslL : 100 - hslL) / 100;
const hsvS = hsv1 === 0 ? 0 : 2 * hsv1 / (hslL + hsv1) * 100;
const hsvV = hslL + hsv1;
return [ hslH, hsvS, hsvV ];
}
export function hsv2hsl(hsvH, hsvS, hsvV) {
const hslL = (200 - hsvS) * hsvV / 100;
const [ hslS, hslV ] = [
hslL === 0 || hslL === 200 ? 0 : hsvS * hsvV / 100 / (hslL <= 100 ? hslL : 200 - hslL) * 100,
hslL * 5 / 10
];
return [ hsvH, hslS, hslV ];
}
/*
References
----------
- https://gist.github.com/defims/0ca2ef8832833186ed396a2f8a204117
/* ========================================================================== */

View File

@@ -0,0 +1,31 @@
/* Convert between HWB and HSV
/* ========================================================================== */
export function hwb2hsv(hwbH, hwbW, hwbB) {
const [ hsvH, hsvS, hsvV ] = [
hwbH,
hwbB === 100 ? 0 : 100 - hwbW / (100 - hwbB) * 100,
100 - hwbB
];
return [ hsvH, hsvS, hsvV ];
}
export function hsv2hwb(hsvH, hsvS, hsvV) {
const [ hwbH, hwbW, hwbB ] = [
hsvH,
(100 - hsvS) * hsvV / 100,
100 - hsvV
];
return [ hwbH, hwbW, hwbB ];
}
/*
References
----------
- https://en.wikipedia.org/wiki/HWB_color_model#Converting_to_and_from_HSV
/* ========================================================================== */

View File

@@ -0,0 +1,31 @@
/* Convert between Lab and XYZ
/* ========================================================================== */
export function lab2lch(labL, labA, labB) {
const [ lchC, lchH ] = [
Math.sqrt(Math.pow(labA, 2) + Math.pow(labB, 2)), // convert to chroma
Math.atan2(labB, labA) * 180 / Math.PI // convert to hue, in degrees
];
return [ labL, lchC, lchH ];
}
export function lch2lab(lchL, lchC, lchH) {
// convert to Lab a and b from the polar form
const [ labA, labB ] = [
lchC * Math.cos(lchH * Math.PI / 180),
lchC * Math.sin(lchH * Math.PI / 180)
];
return [ lchL, labA, labB ];
}
/*
References
----------
- https://www.w3.org/TR/css-color-4/#lch-to-lab
- https://www.w3.org/TR/css-color-4/#color-conversion-code
/* ========================================================================== */

View File

@@ -0,0 +1,68 @@
import { epsilon, kappa, wd50X, wd50Y, wd50Z, matrix } from './util';
/* Convert between Lab and XYZ
/* ========================================================================== */
export function lab2xyz(labL, labA, labB) {
// compute f, starting with the luminance-related term
const f2 = (labL + 16) / 116;
const f1 = labA / 500 + f2;
const f3 = f2 - labB / 200;
// compute pre-scaled XYZ
const [ initX, initY, initZ ] = [
Math.pow(f1, 3) > epsilon ? Math.pow(f1, 3) : (116 * f1 - 16) / kappa,
labL > kappa * epsilon ? Math.pow((labL + 16) / 116, 3) : labL / kappa,
Math.pow(f3, 3) > epsilon ? Math.pow(f3, 3) : (116 * f3 - 16) / kappa
];
const [ xyzX, xyzY, xyzZ ] = matrix(
// compute XYZ by scaling pre-scaled XYZ by reference white
[ initX * wd50X, initY * wd50Y, initZ * wd50Z ],
// calculate D65 XYZ from D50 XYZ
[
[ 0.9555766, -0.0230393, 0.0631636],
[-0.0282895, 1.0099416, 0.0210077],
[ 0.0122982, -0.0204830, 1.3299098]
]
);
return [ xyzX, xyzY, xyzZ ];
}
export function xyz2lab(xyzX, xyzY, xyzZ) {
// calculate D50 XYZ from D65 XYZ
const [ d50X, d50Y, d50Z ] = matrix([ xyzX, xyzY, xyzZ ], [
[ 1.0478112, 0.0228866, -0.0501270],
[ 0.0295424, 0.9904844, -0.0170491],
[-0.0092345, 0.0150436, 0.7521316]
]);
// calculate f
const [ f1, f2, f3 ] = [
d50X / wd50X,
d50Y / wd50Y,
d50Z / wd50Z
].map(
value => value > epsilon ? Math.cbrt(value) : (kappa * value + 16) / 116
);
const [ labL, labA, labB ] = [
116 * f2 - 16,
500 * (f1 - f2),
200 * (f2 - f3)
];
return [ labL, labA, labB ];
}
/*
References
----------
- https://www.w3.org/TR/css-color-4/#rgb-to-lab
- https://www.w3.org/TR/css-color-4/#color-conversion-code
- https://www.easyrgb.com/en/math.php
/* ========================================================================== */

View File

@@ -0,0 +1,50 @@
import { rgb2hue, rgb2value, rgb2whiteness, hue2rgb } from './util';
/* Convert between RGB and HSL
/* ========================================================================== */
export function rgb2hsl(rgbR, rgbG, rgbB, fallbackhue) {
const hslH = rgb2hue(rgbR, rgbG, rgbB, fallbackhue);
const hslV = rgb2value(rgbR, rgbG, rgbB);
const hslW = rgb2whiteness(rgbR, rgbG, rgbB);
// calculate value/whiteness delta
const hslD = hslV - hslW;
// calculate lightness
const hslL = (hslV + hslW) / 2;
// calculate saturation
const hslS = hslD === 0 ? 0 : hslD / (100 - Math.abs(2 * hslL - 100)) * 100;
return [ hslH, hslS, hslL ];
}
export function hsl2rgb(hslH, hslS, hslL) {
// calcuate t2
const t2 = hslL <= 50 ? hslL * (hslS + 100) / 100 : hslL + hslS - hslL * hslS / 100;
// calcuate t1
const t1 = hslL * 2 - t2;
// calculate rgb
const [ rgbR, rgbG, rgbB ] = [
hue2rgb(t1, t2, hslH + 120),
hue2rgb(t1, t2, hslH),
hue2rgb(t1, t2, hslH - 120)
];
return [ rgbR, rgbG, rgbB ];
}
/*
References
----------
- https://www.w3.org/TR/css-color-3/#hsl-color
- https://www.w3.org/TR/css-color-4/#hsl-to-rgb
- https://www.rapidtables.com/convert/color/rgb-to-hsl.html
- https://www.rapidtables.com/convert/color/hsl-to-rgb.html
/* ========================================================================== */

View File

@@ -0,0 +1,47 @@
import { rgb2value, rgb2whiteness, rgb2hue } from './util';
/* Convert between RGB and HSV
/* ========================================================================== */
export function rgb2hsv(rgbR, rgbG, rgbB, fallbackhue) {
const hsvV = rgb2value(rgbR, rgbG, rgbB);
const hsvW = rgb2whiteness(rgbR, rgbG, rgbB);
const hsvH = rgb2hue(rgbR, rgbG, rgbB, fallbackhue);
// calculate saturation
const hsvS = hsvV === hsvW ? 0 : (hsvV - hsvW) / hsvV * 100;
return [ hsvH, hsvS, hsvV ];
}
export function hsv2rgb(hsvH, hsvS, hsvV) {
const rgbI = Math.floor(hsvH / 60);
// calculate rgb parts
const rgbF = hsvH / 60 - rgbI & 1 ? hsvH / 60 - rgbI : 1 - hsvH / 60 - rgbI;
const rgbM = hsvV * (100 - hsvS) / 100;
const rgbN = hsvV * (100 - hsvS * rgbF) / 100;
const [ rgbR, rgbG, rgbB ] = rgbI === 5
? [ hsvV, rgbM, rgbN ]
: rgbI === 4
? [ rgbN, rgbM, hsvV ]
: rgbI === 3
? [ rgbM, rgbN, hsvV ]
: rgbI === 2
? [ rgbM, hsvV, rgbN ]
: rgbI === 1
? [ rgbN, hsvV, rgbM ]
: [ hsvV, rgbN, rgbM ];
return [ rgbR, rgbG, rgbB ];
}
/*
References
----------
- http://alvyray.com/Papers/CG/hsv2rgb.htm
/* ========================================================================== */

View File

@@ -0,0 +1,32 @@
import { rgb2hue, rgb2whiteness, rgb2value } from './util';
import { hsl2rgb } from './rgb-hsl';
/* Convert between RGB and HWB
/* ========================================================================== */
export function rgb2hwb(rgbR, rgbG, rgbB, fallbackhue) {
const hwbH = rgb2hue(rgbR, rgbG, rgbB, fallbackhue);
const hwbW = rgb2whiteness(rgbR, rgbG, rgbB);
const hwbV = rgb2value(rgbR, rgbG, rgbB);
const hwbB = 100 - hwbV;
return [hwbH, hwbW, hwbB];
}
export function hwb2rgb(hwbH, hwbW, hwbB, fallbackhue) {
const [ rgbR, rgbG, rgbB ] = hsl2rgb(hwbH, 100, 50, fallbackhue).map(
v => v * (100 - hwbW - hwbB) / 100 + hwbW
);
return [ rgbR, rgbG, rgbB ];
}
/*
References
----------
- https://www.w3.org/TR/css-color-4/#hwb-to-rgb
- http://alvyray.com/Papers/CG/hwb2rgb.htm
/* ========================================================================== */

View File

@@ -0,0 +1,42 @@
import { matrix } from './util';
/* Convert between RGB and XYZ
/* ========================================================================== */
export function rgb2xyz(rgbR, rgbG, rgbB) {
const [ lrgbR, lrgbB, lrgbG ] = [ rgbR, rgbG, rgbB ].map(
v => v > 4.045 ? Math.pow((v + 5.5) / 105.5, 2.4) * 100 : v / 12.92
);
const [ xyzX, xyzY, xyzZ ] = matrix([ lrgbR, lrgbB, lrgbG ], [
[0.4124564, 0.3575761, 0.1804375],
[0.2126729, 0.7151522, 0.0721750],
[0.0193339, 0.1191920, 0.9503041]
]);
return [ xyzX, xyzY, xyzZ ];
}
export function xyz2rgb(xyzX, xyzY, xyzZ) {
const [ lrgbR, lrgbB, lrgbG ] = matrix([ xyzX, xyzY, xyzZ ], [
[ 3.2404542, -1.5371385, -0.4985314],
[-0.9692660, 1.8760108, 0.0415560],
[ 0.0556434, -0.2040259, 1.0572252]
]);
const [ rgbR, rgbG, rgbB ] = [ lrgbR, lrgbB, lrgbG ].map(
v => v > 0.31308 ? 1.055 * Math.pow(v / 100, 1 / 2.4) * 100 - 5.5 : 12.92 * v
);
return [ rgbR, rgbG, rgbB ];
}
/*
References
----------
- https://www.w3.org/TR/css-color-4/#rgb-to-lab
- https://www.w3.org/TR/css-color-4/#color-conversion-code
/* ========================================================================== */

View File

@@ -0,0 +1,87 @@
/* Convert between RGB and Hue
/* ========================================================================== */
export function rgb2hue(rgbR, rgbG, rgbB, fallbackhue = 0) {
const value = rgb2value(rgbR, rgbG, rgbB);
const whiteness = rgb2whiteness(rgbR, rgbG, rgbB);
const delta = value - whiteness;
if (delta) {
// calculate segment
const segment = value === rgbR
? (rgbG - rgbB) / delta
: value === rgbG
? (rgbB - rgbR) / delta
: (rgbR - rgbG) / delta;
// calculate shift
const shift = value === rgbR
? segment < 0
? 360 / 60
: 0 / 60
: value === rgbG
? 120 / 60
: 240 / 60;
// calculate hue
const hue = (segment + shift) * 60;
return hue;
} else {
// otherwise return the fallback hue
return fallbackhue;
}
}
export function hue2rgb(t1, t2, hue) {
// calculate the ranged hue
const rhue = hue < 0 ? hue + 360 : hue > 360 ? hue - 360 : hue;
// calculate the rgb value
const rgb = rhue * 6 < 360
? t1 + (t2 - t1) * rhue / 60
: rhue * 2 < 360
? t2
: rhue * 3 < 720
? t1 + (t2 - t1) * (240 - rhue) / 60
: t1;
return rgb;
}
/* RGB tooling
/* ========================================================================== */
export function rgb2value(rgbR, rgbG, rgbB) {
const value = Math.max(rgbR, rgbG, rgbB);
return value;
}
export function rgb2whiteness(rgbR, rgbG, rgbB) {
const whiteness = Math.min(rgbR, rgbG, rgbB);
return whiteness;
}
/* Math matrix
/* ========================================================================== */
export function matrix(params, mats) {
return mats.map(mat => mat.reduce((acc, value, index) => acc + params[index] * value, 0));
}
/* D50 reference white
/* ========================================================================== */
export const [ wd50X, wd50Y, wd50Z ] = [ 96.42, 100, 82.49 ];
/* Epsilon
/* ========================================================================== */
export const epsilon = Math.pow(6, 3) / Math.pow(29, 3);
/* Kappa
/* ========================================================================== */
export const kappa = Math.pow(29, 3) / Math.pow(3, 3);