@soybeanjs/colord 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -0
- package/dist/colord-BV1k1-gC.js +239 -0
- package/dist/colord-xpgrVWRV.d.ts +167 -0
- package/dist/colord.d.ts +2 -0
- package/dist/colord.js +10 -0
- package/dist/extend-DrPfn2Q1.d.ts +7 -0
- package/dist/get-BFaklfVd.js +92 -0
- package/dist/hsv-UWMaaOSN.js +78 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +22 -0
- package/dist/lab-CJHLdqfe.js +128 -0
- package/dist/manipulate-CeAgrUd6.js +119 -0
- package/dist/plugins/a11y.d.ts +41 -0
- package/dist/plugins/a11y.js +35 -0
- package/dist/plugins/cmyk.d.ts +27 -0
- package/dist/plugins/cmyk.js +121 -0
- package/dist/plugins/harmonies.d.ts +19 -0
- package/dist/plugins/harmonies.js +54 -0
- package/dist/plugins/hwb.d.ts +26 -0
- package/dist/plugins/hwb.js +92 -0
- package/dist/plugins/lab.d.ts +26 -0
- package/dist/plugins/lab.js +26 -0
- package/dist/plugins/lch.d.ts +27 -0
- package/dist/plugins/lch.js +122 -0
- package/dist/plugins/minify.d.ts +23 -0
- package/dist/plugins/minify.js +61 -0
- package/dist/plugins/mix.d.ts +30 -0
- package/dist/plugins/mix.js +39 -0
- package/dist/plugins/names.d.ts +22 -0
- package/dist/plugins/names.js +201 -0
- package/dist/plugins/oklab.d.ts +16 -0
- package/dist/plugins/oklab.js +103 -0
- package/dist/plugins/oklch.d.ts +16 -0
- package/dist/plugins/oklch.js +112 -0
- package/dist/plugins/xyz.d.ts +17 -0
- package/dist/plugins/xyz.js +20 -0
- package/dist/rgb-CfVJB1Bj.js +148 -0
- package/dist/utils-BgIyY6bK.js +229 -0
- package/dist/xyz-MII3ndWO.js +73 -0
- package/package.json +81 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { _ as ALPHA_PRECISION, a as mul3x3, c as round, g as OKLAB_M2_INV, h as OKLAB_M2, i as isPresent, m as OKLAB_M1_INV, n as clampHue, o as parseAlpha, p as OKLAB_M1, s as parseHue, t as clamp } from "../utils-BgIyY6bK.js";
|
|
2
|
+
import { n as clampRgb, o as rgbToLinearRgb, r as linearRgbToRgb, t as clampLinearRgb } from "../rgb-CfVJB1Bj.js";
|
|
3
|
+
|
|
4
|
+
//#region src/models/oklch.ts
|
|
5
|
+
const clampOklch = (oklch) => {
|
|
6
|
+
const { l, c, h, alpha } = oklch;
|
|
7
|
+
return {
|
|
8
|
+
l: clamp(l, 1e-4, 1),
|
|
9
|
+
c: clamp(c, 1e-4, .37),
|
|
10
|
+
h: clampHue(h),
|
|
11
|
+
alpha: clamp(alpha)
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
const roundOklch = (oklch) => {
|
|
15
|
+
const { l, c, h, alpha } = oklch;
|
|
16
|
+
return {
|
|
17
|
+
l: round(l, 3),
|
|
18
|
+
c: round(c, 3),
|
|
19
|
+
h: round(h, 3),
|
|
20
|
+
alpha: round(alpha, ALPHA_PRECISION)
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
const oklchToRgb = (oklch) => {
|
|
24
|
+
const { l, c, h, alpha } = oklch;
|
|
25
|
+
const hRad = h * Math.PI / 180;
|
|
26
|
+
const [r, g, b] = mul3x3(OKLAB_M1_INV, mul3x3(OKLAB_M2_INV, [
|
|
27
|
+
l,
|
|
28
|
+
c * Math.cos(hRad),
|
|
29
|
+
c * Math.sin(hRad)
|
|
30
|
+
]).map((v) => v * v * v));
|
|
31
|
+
return clampRgb(linearRgbToRgb(clampLinearRgb({
|
|
32
|
+
r,
|
|
33
|
+
g,
|
|
34
|
+
b,
|
|
35
|
+
alpha
|
|
36
|
+
})));
|
|
37
|
+
};
|
|
38
|
+
const rgbToOklch = (rgb) => {
|
|
39
|
+
const lRgb = rgbToLinearRgb(rgb);
|
|
40
|
+
const [l, a, b] = mul3x3(OKLAB_M2, mul3x3(OKLAB_M1, [
|
|
41
|
+
lRgb.r,
|
|
42
|
+
lRgb.g,
|
|
43
|
+
lRgb.b
|
|
44
|
+
]).map((v) => Math.cbrt(v)));
|
|
45
|
+
const chroma = Math.sqrt(a * a + b * b);
|
|
46
|
+
let hue;
|
|
47
|
+
if (chroma < 1e-4) hue = 0;
|
|
48
|
+
else {
|
|
49
|
+
hue = Math.atan2(b, a) * (180 / Math.PI);
|
|
50
|
+
if (hue < 0) hue += 360;
|
|
51
|
+
}
|
|
52
|
+
return clampOklch({
|
|
53
|
+
l,
|
|
54
|
+
c: chroma,
|
|
55
|
+
h: hue,
|
|
56
|
+
alpha: rgb.alpha
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
const parseOklch = ({ l, c, h, alpha = 1 }) => {
|
|
60
|
+
if (!isPresent(l) || !isPresent(c) || !isPresent(h)) return null;
|
|
61
|
+
return oklchToRgb(clampOklch({
|
|
62
|
+
l: Number(l),
|
|
63
|
+
c: Number(c),
|
|
64
|
+
h: Number(h),
|
|
65
|
+
alpha: Number(alpha)
|
|
66
|
+
}));
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Parsing syntax: oklch(L c h [/ alpha])
|
|
70
|
+
* - L: <number|percentage>
|
|
71
|
+
* - c: <number>
|
|
72
|
+
* - h: <number>
|
|
73
|
+
* - alpha: <number|percentage>
|
|
74
|
+
*/
|
|
75
|
+
const oklchMatcher = /^oklch\(\s*([+-]?[\d.]+)%?\s+([+-]?[\d.]+)\s+([+-]?[\d.]+)(deg|grad|rad|turn)?(?:\s*\/\s*([+-]?[\d.]+%?))?\s*\)$/i;
|
|
76
|
+
/**
|
|
77
|
+
* Parses a valid OKLCH CSS color function/string
|
|
78
|
+
* https://www.w3.org/TR/css-color-4/#specifying-oklch
|
|
79
|
+
* @param input
|
|
80
|
+
* @returns
|
|
81
|
+
*/
|
|
82
|
+
const parseOklchString = (input) => {
|
|
83
|
+
const match = oklchMatcher.exec(input);
|
|
84
|
+
if (!match) return null;
|
|
85
|
+
const [_, L, c, h, unit, alpha] = match;
|
|
86
|
+
let l = Number.parseFloat(L);
|
|
87
|
+
if (L.endsWith("%")) l /= 100;
|
|
88
|
+
return oklchToRgb(clampOklch({
|
|
89
|
+
l,
|
|
90
|
+
c: Number.parseFloat(c),
|
|
91
|
+
h: parseHue(h, unit),
|
|
92
|
+
alpha: parseAlpha(alpha)
|
|
93
|
+
}));
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/plugins/oklch.ts
|
|
98
|
+
/**
|
|
99
|
+
* A plugin adding support for OKLCH colorspace.
|
|
100
|
+
* https://bottosson.github.io/posts/oklab/
|
|
101
|
+
*/
|
|
102
|
+
const oklchPlugin = (ColordClass, parsers) => {
|
|
103
|
+
ColordClass.prototype.toOklch = function toOklch() {
|
|
104
|
+
return roundOklch(rgbToOklch(this.rgb));
|
|
105
|
+
};
|
|
106
|
+
parsers.string.push([parseOklchString, "oklch"]);
|
|
107
|
+
parsers.object.push([parseOklch, "oklch"]);
|
|
108
|
+
};
|
|
109
|
+
var oklch_default = oklchPlugin;
|
|
110
|
+
|
|
111
|
+
//#endregion
|
|
112
|
+
export { oklch_default as default, oklchPlugin };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { g as XyzColor } from "../colord-xpgrVWRV.js";
|
|
2
|
+
import { t as Plugin } from "../extend-DrPfn2Q1.js";
|
|
3
|
+
|
|
4
|
+
//#region src/plugins/xyz.d.ts
|
|
5
|
+
declare module '../colord' {
|
|
6
|
+
interface Colord {
|
|
7
|
+
toXyz(): XyzColor;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* A plugin adding support for CIE XYZ colorspace.
|
|
12
|
+
* Wikipedia: https://en.wikipedia.org/wiki/CIE_1931_color_space
|
|
13
|
+
* Helpful article: https://www.sttmedia.com/colormodel-xyz
|
|
14
|
+
*/
|
|
15
|
+
declare const xyzPlugin: Plugin;
|
|
16
|
+
//#endregion
|
|
17
|
+
export { xyzPlugin as default, xyzPlugin };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import "../utils-BgIyY6bK.js";
|
|
2
|
+
import "../rgb-CfVJB1Bj.js";
|
|
3
|
+
import { n as rgbToXyz, r as roundXyz, t as parseXyz } from "../xyz-MII3ndWO.js";
|
|
4
|
+
|
|
5
|
+
//#region src/plugins/xyz.ts
|
|
6
|
+
/**
|
|
7
|
+
* A plugin adding support for CIE XYZ colorspace.
|
|
8
|
+
* Wikipedia: https://en.wikipedia.org/wiki/CIE_1931_color_space
|
|
9
|
+
* Helpful article: https://www.sttmedia.com/colormodel-xyz
|
|
10
|
+
*/
|
|
11
|
+
const xyzPlugin = (ColordClass, parsers) => {
|
|
12
|
+
ColordClass.prototype.toXyz = function toXyz() {
|
|
13
|
+
return roundXyz(rgbToXyz(this.rgb));
|
|
14
|
+
};
|
|
15
|
+
parsers.object.push([parseXyz, "xyz"]);
|
|
16
|
+
};
|
|
17
|
+
var xyz_default = xyzPlugin;
|
|
18
|
+
|
|
19
|
+
//#endregion
|
|
20
|
+
export { xyz_default as default, xyzPlugin };
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { _ as ALPHA_PRECISION, c as round, i as isPresent, o as parseAlpha, t as clamp } from "./utils-BgIyY6bK.js";
|
|
2
|
+
|
|
3
|
+
//#region src/models/rgb.ts
|
|
4
|
+
const clampRgb = (rgb) => {
|
|
5
|
+
const { r, g, b, alpha } = rgb;
|
|
6
|
+
return {
|
|
7
|
+
r: clamp(r, 0, 255),
|
|
8
|
+
g: clamp(g, 0, 255),
|
|
9
|
+
b: clamp(b, 0, 255),
|
|
10
|
+
alpha: clamp(alpha)
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
const roundRgb = (rgb) => {
|
|
14
|
+
const { r, g, b, alpha } = rgb;
|
|
15
|
+
return {
|
|
16
|
+
r: round(r),
|
|
17
|
+
g: round(g),
|
|
18
|
+
b: round(b),
|
|
19
|
+
alpha: round(alpha, ALPHA_PRECISION)
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
const clampLinearRgb = (rgb) => {
|
|
23
|
+
const { r, g, b, alpha } = rgb;
|
|
24
|
+
return {
|
|
25
|
+
r: clamp(r, 1e-4, 1),
|
|
26
|
+
g: clamp(g, 1e-4, 1),
|
|
27
|
+
b: clamp(b, 1e-4, 1),
|
|
28
|
+
alpha: clamp(alpha)
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
const parseRgb = ({ r, g, b, alpha = 1 }) => {
|
|
32
|
+
if (!isPresent(r) || !isPresent(g) || !isPresent(b)) return null;
|
|
33
|
+
return clampRgb({
|
|
34
|
+
r: Number(r),
|
|
35
|
+
g: Number(g),
|
|
36
|
+
b: Number(b),
|
|
37
|
+
alpha: Number(alpha)
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Converts a single sRGB component value to linear light.
|
|
42
|
+
* This applies gamma expansion (inverse of sRGB transfer function).
|
|
43
|
+
*
|
|
44
|
+
* The sRGB transfer function has two parts:
|
|
45
|
+
* - Linear segment for very dark values (≤ 0.04045)
|
|
46
|
+
* - Power curve with gamma ≈ 2.4 for brighter values
|
|
47
|
+
*
|
|
48
|
+
* @param value - sRGB component value (0-255)
|
|
49
|
+
*/
|
|
50
|
+
const rgbToLinear = (value) => {
|
|
51
|
+
const v = value / 255;
|
|
52
|
+
if (v <= .04045) return v / 12.92;
|
|
53
|
+
return ((v + .055) / 1.055) ** 2.4;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Converts a linear light value to sRGB component.
|
|
57
|
+
* This applies gamma compression (sRGB transfer function).
|
|
58
|
+
*
|
|
59
|
+
* The sRGB transfer function has two parts:
|
|
60
|
+
* - Linear segment for very dark values (≤ 0.0031308)
|
|
61
|
+
* - Power curve with gamma ≈ 1/2.4 for brighter values
|
|
62
|
+
*
|
|
63
|
+
* @param value - Linear light value (0-1)
|
|
64
|
+
*/
|
|
65
|
+
function linearToRgb(value) {
|
|
66
|
+
const v = value <= .0031308 ? value * 12.92 : 1.055 * value ** (1 / 2.4) - .055;
|
|
67
|
+
return Math.round(v * 255);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Converts an RGBA color to Linear RGB color space.
|
|
71
|
+
*
|
|
72
|
+
* This applies gamma expansion to each RGB component,
|
|
73
|
+
* converting from perceptually uniform sRGB to physically
|
|
74
|
+
* linear light values. Alpha channel is passed through unchanged.
|
|
75
|
+
*
|
|
76
|
+
* @param rgb - The RGBA color to convert (r, g, b: 0-255, a: 0-1)
|
|
77
|
+
*/
|
|
78
|
+
function rgbToLinearRgb(rgb) {
|
|
79
|
+
return {
|
|
80
|
+
r: rgbToLinear(rgb.r),
|
|
81
|
+
g: rgbToLinear(rgb.g),
|
|
82
|
+
b: rgbToLinear(rgb.b),
|
|
83
|
+
alpha: rgb.alpha
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Converts a Linear RGB color to RGBA color space.
|
|
88
|
+
*
|
|
89
|
+
* This applies gamma compression to each RGB component,
|
|
90
|
+
* converting from physically linear light values to
|
|
91
|
+
* perceptually uniform sRGB. Alpha channel is passed through unchanged.
|
|
92
|
+
*
|
|
93
|
+
* @param rgb - The LinearRGB color to convert (r, g, b: 0-1 linear, a: 0-1)
|
|
94
|
+
* @returns RGBA color (r, g, b: 0-255, a: 0-1)
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* // Pure red
|
|
99
|
+
* linearRgbToRgb({ r: 1, g: 0, b: 0, a: 1 });
|
|
100
|
+
* // { r: 255, g: 0, b: 0, a: 1 }
|
|
101
|
+
*
|
|
102
|
+
* // Linear mid-point is NOT perceptual mid-gray
|
|
103
|
+
* linearRgbToRgb({ r: 0.5, g: 0.5, b: 0.5, a: 1 });
|
|
104
|
+
* // { r: 188, g: 188, b: 188, a: 1 } - looks quite bright!
|
|
105
|
+
*
|
|
106
|
+
* // With transparency
|
|
107
|
+
* linearRgbToRgb({ r: 1, g: 0.216, b: 0, a: 0.5 });
|
|
108
|
+
* // { r: 255, g: 128, b: 0, a: 0.5 }
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
function linearRgbToRgb(rgb) {
|
|
112
|
+
return {
|
|
113
|
+
r: linearToRgb(rgb.r),
|
|
114
|
+
g: linearToRgb(rgb.g),
|
|
115
|
+
b: linearToRgb(rgb.b),
|
|
116
|
+
alpha: rgb.alpha
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
const rgbMatcher = /^rgb?\(\s*([\d.]+%?)\s*[, ]\s*([\d.]+%?)\s*[, ]\s*([\d.]+%?)(?:\s*[,/]\s*([\d.]+%?))?\s*\)$/i;
|
|
120
|
+
/**
|
|
121
|
+
* Parses a valid RGB[A] CSS color function/string
|
|
122
|
+
* https://www.w3.org/TR/css-color-4/#rgb-functions
|
|
123
|
+
*/
|
|
124
|
+
const parseRgbString = (input) => {
|
|
125
|
+
const match = rgbMatcher.exec(input);
|
|
126
|
+
if (!match) return null;
|
|
127
|
+
const [_, r, g, b, alpha] = match;
|
|
128
|
+
return clampRgb({
|
|
129
|
+
r: parseValue(r),
|
|
130
|
+
g: parseValue(g),
|
|
131
|
+
b: parseValue(b),
|
|
132
|
+
alpha: parseAlpha(alpha)
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
function parseValue(str) {
|
|
136
|
+
if (str.endsWith("%")) {
|
|
137
|
+
const percent = Number.parseFloat(str.slice(0, -1));
|
|
138
|
+
return Math.round(percent / 100 * 255);
|
|
139
|
+
}
|
|
140
|
+
return Number.parseFloat(str);
|
|
141
|
+
}
|
|
142
|
+
const rgbToRgbString = (rgb) => {
|
|
143
|
+
const { r, g, b, alpha } = roundRgb(rgb);
|
|
144
|
+
return alpha < 1 ? `rgb(${r}, ${g}, ${b}, ${alpha})` : `rgb(${r}, ${g}, ${b})`;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
148
|
+
export { parseRgbString as a, roundRgb as c, parseRgb as i, clampRgb as n, rgbToLinearRgb as o, linearRgbToRgb as r, rgbToRgbString as s, clampLinearRgb as t };
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
//#region src/constants/common.ts
|
|
2
|
+
/**
|
|
3
|
+
* We used to work with 2 digits after the decimal point, but it wasn't accurate enough,
|
|
4
|
+
* so the library produced colors that were perceived differently.
|
|
5
|
+
*/
|
|
6
|
+
const ALPHA_PRECISION = 3;
|
|
7
|
+
/**
|
|
8
|
+
* Valid CSS <angle> units.
|
|
9
|
+
* https://developer.mozilla.org/en-US/docs/Web/CSS/angle
|
|
10
|
+
*/
|
|
11
|
+
const ANGLE_UNITS = {
|
|
12
|
+
grad: 360 / 400,
|
|
13
|
+
turn: 360,
|
|
14
|
+
rad: 360 / (Math.PI * 2)
|
|
15
|
+
};
|
|
16
|
+
const D50 = {
|
|
17
|
+
x: .96422,
|
|
18
|
+
y: 1,
|
|
19
|
+
z: .82521
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/constants/matrix.ts
|
|
24
|
+
const OKLAB_M1 = [
|
|
25
|
+
[
|
|
26
|
+
.4122214708,
|
|
27
|
+
.5363325363,
|
|
28
|
+
.0514459929
|
|
29
|
+
],
|
|
30
|
+
[
|
|
31
|
+
.2119034982,
|
|
32
|
+
.6806995451,
|
|
33
|
+
.1073969566
|
|
34
|
+
],
|
|
35
|
+
[
|
|
36
|
+
.0883024619,
|
|
37
|
+
.2817188376,
|
|
38
|
+
.6299787005
|
|
39
|
+
]
|
|
40
|
+
];
|
|
41
|
+
const OKLAB_M1_INV = [
|
|
42
|
+
[
|
|
43
|
+
4.076741661347993,
|
|
44
|
+
-3.3077115904081937,
|
|
45
|
+
.2309699287294278
|
|
46
|
+
],
|
|
47
|
+
[
|
|
48
|
+
-1.2684380040921763,
|
|
49
|
+
2.609757400663372,
|
|
50
|
+
-.3413193963102196
|
|
51
|
+
],
|
|
52
|
+
[
|
|
53
|
+
-.0041960865418371,
|
|
54
|
+
-.7034186144594495,
|
|
55
|
+
1.7076147009309446
|
|
56
|
+
]
|
|
57
|
+
];
|
|
58
|
+
const OKLAB_M2 = [
|
|
59
|
+
[
|
|
60
|
+
.2104542553,
|
|
61
|
+
.793617785,
|
|
62
|
+
-.0040720468
|
|
63
|
+
],
|
|
64
|
+
[
|
|
65
|
+
1.9779984951,
|
|
66
|
+
-2.428592205,
|
|
67
|
+
.4505937099
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
.0259040371,
|
|
71
|
+
.7827717662,
|
|
72
|
+
-.808675766
|
|
73
|
+
]
|
|
74
|
+
];
|
|
75
|
+
const OKLAB_M2_INV = [
|
|
76
|
+
[
|
|
77
|
+
.9999999984505198,
|
|
78
|
+
.3963377921737678,
|
|
79
|
+
.2158037580607588
|
|
80
|
+
],
|
|
81
|
+
[
|
|
82
|
+
1.0000000088817607,
|
|
83
|
+
-.1055613423236563,
|
|
84
|
+
-.0638541747717059
|
|
85
|
+
],
|
|
86
|
+
[
|
|
87
|
+
1.0000000546724108,
|
|
88
|
+
-.0894841820949657,
|
|
89
|
+
-1.2914855378640917
|
|
90
|
+
]
|
|
91
|
+
];
|
|
92
|
+
const M_SRGB_TO_XYZ_D65 = [
|
|
93
|
+
[
|
|
94
|
+
.4124564391,
|
|
95
|
+
.3575760776,
|
|
96
|
+
.1804374833
|
|
97
|
+
],
|
|
98
|
+
[
|
|
99
|
+
.2126728514,
|
|
100
|
+
.7151521552,
|
|
101
|
+
.0721749934
|
|
102
|
+
],
|
|
103
|
+
[
|
|
104
|
+
.0193338956,
|
|
105
|
+
.1191920259,
|
|
106
|
+
.9503040785
|
|
107
|
+
]
|
|
108
|
+
];
|
|
109
|
+
const M_XYZ_D65_TO_SRGB = [
|
|
110
|
+
[
|
|
111
|
+
3.2404541621,
|
|
112
|
+
-1.5371385127,
|
|
113
|
+
-.4985314095
|
|
114
|
+
],
|
|
115
|
+
[
|
|
116
|
+
-.9692660305,
|
|
117
|
+
1.8760108454,
|
|
118
|
+
.0415560175
|
|
119
|
+
],
|
|
120
|
+
[
|
|
121
|
+
.055643431,
|
|
122
|
+
-.2040259135,
|
|
123
|
+
1.0572251882
|
|
124
|
+
]
|
|
125
|
+
];
|
|
126
|
+
const M_D65_TO_D50 = [
|
|
127
|
+
[
|
|
128
|
+
1.0478112,
|
|
129
|
+
.0228866,
|
|
130
|
+
-.050127
|
|
131
|
+
],
|
|
132
|
+
[
|
|
133
|
+
.0295424,
|
|
134
|
+
.9904844,
|
|
135
|
+
-.0170491
|
|
136
|
+
],
|
|
137
|
+
[
|
|
138
|
+
-.0092345,
|
|
139
|
+
.0150436,
|
|
140
|
+
.7521316
|
|
141
|
+
]
|
|
142
|
+
];
|
|
143
|
+
const M_D50_TO_D65 = [
|
|
144
|
+
[
|
|
145
|
+
.9555766,
|
|
146
|
+
-.0230393,
|
|
147
|
+
.0631636
|
|
148
|
+
],
|
|
149
|
+
[
|
|
150
|
+
-.0282895,
|
|
151
|
+
1.0099416,
|
|
152
|
+
.0210077
|
|
153
|
+
],
|
|
154
|
+
[
|
|
155
|
+
.0122982,
|
|
156
|
+
-.020483,
|
|
157
|
+
1.3299098
|
|
158
|
+
]
|
|
159
|
+
];
|
|
160
|
+
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region src/utils/index.ts
|
|
163
|
+
const isPresent = (value) => {
|
|
164
|
+
if (typeof value === "string") return value.length > 0;
|
|
165
|
+
if (typeof value === "number") return true;
|
|
166
|
+
return false;
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Rounds a number to the specified number of decimal places.
|
|
170
|
+
* @param number - The number to round
|
|
171
|
+
* @param digits - The number of decimal places (default: 0)
|
|
172
|
+
* @returns The rounded number (converts -0 to 0)
|
|
173
|
+
*/
|
|
174
|
+
const round = (number, digits = 0) => {
|
|
175
|
+
const base = 10 ** digits;
|
|
176
|
+
return Math.round(base * number) / base + 0;
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Floors a number to the specified number of decimal places.
|
|
180
|
+
* @param number - The number to floor
|
|
181
|
+
* @param digits - The number of decimal places (default: 0)
|
|
182
|
+
* @returns The floored number (converts -0 to 0)
|
|
183
|
+
*/
|
|
184
|
+
const floor = (number, digits = 0) => {
|
|
185
|
+
const base = 10 ** digits;
|
|
186
|
+
return Math.floor(base * number) / base + 0;
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Clamps a value between an upper and lower bound.
|
|
190
|
+
* We use ternary operators because it makes the minified code
|
|
191
|
+
* is 2 times shorter then `Math.min(Math.max(a,b),c)`
|
|
192
|
+
* NaN is clamped to the lower bound
|
|
193
|
+
*/
|
|
194
|
+
const clamp = (number, min = 0, max = 1) => {
|
|
195
|
+
if (number > max) return max;
|
|
196
|
+
if (number < min) return min;
|
|
197
|
+
return number;
|
|
198
|
+
};
|
|
199
|
+
/**
|
|
200
|
+
* Processes and clamps a degree (angle) value properly.
|
|
201
|
+
* Any `NaN` or `Infinity` will be converted to `0`.
|
|
202
|
+
* Examples: -1 => 359, 361 => 1
|
|
203
|
+
*/
|
|
204
|
+
const clampHue = (degrees) => {
|
|
205
|
+
const degree = Number.isFinite(degrees) ? degrees % 360 : 0;
|
|
206
|
+
return degree > 0 ? degree : degree + 360;
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Converts a hue value to degrees from 0 to 360 inclusive.
|
|
210
|
+
*/
|
|
211
|
+
const parseHue = (value, unit = "deg") => {
|
|
212
|
+
return Number.parseFloat(value) * (ANGLE_UNITS[unit] || 1);
|
|
213
|
+
};
|
|
214
|
+
function mul3x3(m, v) {
|
|
215
|
+
return [
|
|
216
|
+
m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2],
|
|
217
|
+
m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2],
|
|
218
|
+
m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2]
|
|
219
|
+
];
|
|
220
|
+
}
|
|
221
|
+
function parseAlpha(alpha) {
|
|
222
|
+
if (!alpha) return 1;
|
|
223
|
+
let value = Number.parseFloat(alpha);
|
|
224
|
+
if (alpha.endsWith("%")) value /= 100;
|
|
225
|
+
return value;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
//#endregion
|
|
229
|
+
export { ALPHA_PRECISION as _, mul3x3 as a, round as c, M_SRGB_TO_XYZ_D65 as d, M_XYZ_D65_TO_SRGB as f, OKLAB_M2_INV as g, OKLAB_M2 as h, isPresent as i, M_D50_TO_D65 as l, OKLAB_M1_INV as m, clampHue as n, parseAlpha as o, OKLAB_M1 as p, floor as r, parseHue as s, clamp as t, M_D65_TO_D50 as u, D50 as v };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { _ as ALPHA_PRECISION, a as mul3x3, c as round, d as M_SRGB_TO_XYZ_D65, f as M_XYZ_D65_TO_SRGB, i as isPresent, t as clamp, v as D50 } from "./utils-BgIyY6bK.js";
|
|
2
|
+
import { n as clampRgb, o as rgbToLinearRgb, r as linearRgbToRgb } from "./rgb-CfVJB1Bj.js";
|
|
3
|
+
|
|
4
|
+
//#region src/models/xyz.ts
|
|
5
|
+
/**
|
|
6
|
+
* Limits XYZ axis values assuming XYZ is relative to D50.
|
|
7
|
+
*/
|
|
8
|
+
const clampXyz = (xyz) => {
|
|
9
|
+
const { x, y, z, alpha } = xyz;
|
|
10
|
+
return {
|
|
11
|
+
x: clamp(x, 1e-4, D50.x),
|
|
12
|
+
y: clamp(y, 1e-4, D50.y),
|
|
13
|
+
z: clamp(z, 1e-4, D50.z),
|
|
14
|
+
alpha: clamp(alpha)
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
const roundXyz = (xyz) => {
|
|
18
|
+
const { x, y, z, alpha } = xyz;
|
|
19
|
+
return {
|
|
20
|
+
x: round(x, 4),
|
|
21
|
+
y: round(y, 4),
|
|
22
|
+
z: round(z, 4),
|
|
23
|
+
alpha: round(alpha, ALPHA_PRECISION)
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Converts an CIE XYZ color (D50) to RGBA color space (D65)
|
|
28
|
+
* https://www.w3.org/TR/css-color-4/#color-conversion-code
|
|
29
|
+
*/
|
|
30
|
+
const xyzToRgb = (xyz) => {
|
|
31
|
+
const { x, y, z, alpha } = xyz;
|
|
32
|
+
const [r, g, b] = mul3x3(M_XYZ_D65_TO_SRGB, [
|
|
33
|
+
x,
|
|
34
|
+
y,
|
|
35
|
+
z
|
|
36
|
+
]);
|
|
37
|
+
return clampRgb(linearRgbToRgb({
|
|
38
|
+
r,
|
|
39
|
+
g,
|
|
40
|
+
b,
|
|
41
|
+
alpha
|
|
42
|
+
}));
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Converts an RGB color (D65) to CIE XYZ (D50)
|
|
46
|
+
* https://image-engineering.de/library/technotes/958-how-to-convert-between-srgb-and-ciexyz
|
|
47
|
+
*/
|
|
48
|
+
const rgbToXyz = (rgb) => {
|
|
49
|
+
const { r, g, b, alpha } = rgbToLinearRgb(rgb);
|
|
50
|
+
const [x, y, z] = mul3x3(M_SRGB_TO_XYZ_D65, [
|
|
51
|
+
r,
|
|
52
|
+
g,
|
|
53
|
+
b
|
|
54
|
+
]);
|
|
55
|
+
return clampXyz({
|
|
56
|
+
x,
|
|
57
|
+
y,
|
|
58
|
+
z,
|
|
59
|
+
alpha
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
const parseXyz = ({ x, y, z, alpha = 1 }) => {
|
|
63
|
+
if (!isPresent(x) || !isPresent(y) || !isPresent(z)) return null;
|
|
64
|
+
return xyzToRgb(clampXyz({
|
|
65
|
+
x: Number(x),
|
|
66
|
+
y: Number(y),
|
|
67
|
+
z: Number(z),
|
|
68
|
+
alpha: Number(alpha)
|
|
69
|
+
}));
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
//#endregion
|
|
73
|
+
export { xyzToRgb as i, rgbToXyz as n, roundXyz as r, parseXyz as t };
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@soybeanjs/colord",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"description": "A tiny yet powerful tool for high-performance color manipulations and conversions",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Soybean",
|
|
8
|
+
"email": "soybeanjs@outlook.com",
|
|
9
|
+
"url": "https://github.com/soybeanjs"
|
|
10
|
+
},
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"homepage": "https://github.com/soybeanjs/colord",
|
|
13
|
+
"repository": {
|
|
14
|
+
"url": "https://github.com/soybeanjs/colord.git"
|
|
15
|
+
},
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/soybeanjs/colord/issues"
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"registry": "https://registry.npmjs.org/"
|
|
21
|
+
},
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"import": "./dist/index.js",
|
|
26
|
+
"require": "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./plugins/*": {
|
|
29
|
+
"types": "./dist/plugins/*.d.ts",
|
|
30
|
+
"import": "./dist/plugins/*.js",
|
|
31
|
+
"require": "./dist/plugins/*.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"main": "./dist/index.js",
|
|
35
|
+
"module": "./dist/index.js",
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"files": [
|
|
38
|
+
"dist"
|
|
39
|
+
],
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"cli-progress": "3.12.0",
|
|
42
|
+
"consola": "3.4.2",
|
|
43
|
+
"dayjs": "1.11.19",
|
|
44
|
+
"execa": "9.6.1",
|
|
45
|
+
"kolorist": "1.8.0",
|
|
46
|
+
"ofetch": "1.5.1"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@soybeanjs/cli": "1.4.4",
|
|
50
|
+
"@soybeanjs/eslint-config": "1.7.5",
|
|
51
|
+
"@types/cli-progress": "3.11.6",
|
|
52
|
+
"@types/node": "25.0.8",
|
|
53
|
+
"@types/tinycolor2": "^1.4.6",
|
|
54
|
+
"colord": "^2.9.3",
|
|
55
|
+
"eslint": "9.39.2",
|
|
56
|
+
"lint-staged": "16.2.7",
|
|
57
|
+
"simple-git-hooks": "2.13.1",
|
|
58
|
+
"tinycolor2": "^1.6.0",
|
|
59
|
+
"tsdown": "0.19.0",
|
|
60
|
+
"tsx": "4.21.0",
|
|
61
|
+
"typescript": "5.9.3"
|
|
62
|
+
},
|
|
63
|
+
"simple-git-hooks": {
|
|
64
|
+
"commit-msg": "pnpm soy git-commit-verify",
|
|
65
|
+
"pre-commit": "pnpm typecheck && pnpm lint-staged"
|
|
66
|
+
},
|
|
67
|
+
"lint-staged": {
|
|
68
|
+
"*": "eslint --fix"
|
|
69
|
+
},
|
|
70
|
+
"scripts": {
|
|
71
|
+
"build": "tsdown",
|
|
72
|
+
"cleanup": "soy cleanup",
|
|
73
|
+
"commit": "soy git-commit",
|
|
74
|
+
"dev": "tsup --watch",
|
|
75
|
+
"lint": "eslint . --fix",
|
|
76
|
+
"publish-pkg": "pnpm publish --access public",
|
|
77
|
+
"release": "soy release",
|
|
78
|
+
"typecheck": "tsc --noEmit --skipLibCheck",
|
|
79
|
+
"update-pkg": "soy ncu"
|
|
80
|
+
}
|
|
81
|
+
}
|