colorjs.io
Version:
Let’s get serious about color
86 lines (70 loc) • 1.93 kB
JavaScript
import ColorSpace from "../space.js";
import {WHITES} from "../adapt.js";
import xyz_d65 from "./xyz-d65.js";
import {uv} from "../chromaticity.js";
import {isNone, skipNone} from "../util.js";
let white = WHITES.D65;
const ε = 216 / 24389; // 6^3/29^3 == (24/116)^3
const κ = 24389 / 27; // 29^3/3^3
const [U_PRIME_WHITE, V_PRIME_WHITE] = uv({space: xyz_d65, coords: white});
export default new ColorSpace({
id: "luv",
name: "Luv",
coords: {
l: {
refRange: [0, 100],
name: "Lightness",
},
// Reference ranges from https://facelessuser.github.io/coloraide/colors/luv/
u: {
refRange: [-215, 215],
},
v: {
refRange: [-215, 215],
},
},
white: white,
base: xyz_d65,
// Convert D65-adapted XYZ to Luv
// https://en.wikipedia.org/wiki/CIELUV#The_forward_transformation
fromBase (XYZ) {
let xyz = [skipNone(XYZ[0]), skipNone(XYZ[1]), skipNone(XYZ[2])];
let y = xyz[1];
let [up, vp] = uv({space: xyz_d65, coords: xyz});
// Protect against XYZ of [0, 0, 0]
if (!Number.isFinite(up) || !Number.isFinite(vp)) {
return [0, 0, 0];
}
let L = y <= ε ? κ * y : 116 * Math.cbrt(y) - 16;
return [
L,
13 * L * (up - U_PRIME_WHITE),
13 * L * (vp - V_PRIME_WHITE),
];
},
// Convert Luv to D65-adapted XYZ
// https://en.wikipedia.org/wiki/CIELUV#The_reverse_transformation
toBase (Luv) {
let [L, u, v] = Luv;
// Protect against division by zero and NaN Lightness
if (L === 0 || isNone(L)) {
return [0, 0, 0];
}
u = skipNone(u);
v = skipNone(v);
let up = (u / (13 * L)) + U_PRIME_WHITE;
let vp = (v / (13 * L)) + V_PRIME_WHITE;
let y = L <= 8 ? L / κ : Math.pow((L + 16) / 116, 3);
return [
y * ((9 * up) / (4 * vp)),
y,
y * ((12 - 3 * up - 20 * vp) / (4 * vp)),
];
},
formats: {
color: {
id: "--luv",
coords: ["<number> | <percentage>", "<number> | <percentage>[-1,1]", "<number> | <percentage>[-1,1]"],
},
},
});