@texel/color 1.0.4 → 1.0.6
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 +28 -5
- package/package.json +1 -1
- package/src/core.js +115 -9
- package/src/gamut.js +1 -1
- package/src/util.js +4 -1
- package/test/test.js +90 -2
package/README.md
CHANGED
|
@@ -85,7 +85,7 @@ The return value is the new coordinates in the destination space; such as `[r,g,
|
|
|
85
85
|
|
|
86
86
|
#### `output = gamutMapOKLCH(oklch, gamut = sRGBGamut, targetSpace = gamut.space, out = [0, 0, 0], mapping = MapToCuspL, [cusp])`
|
|
87
87
|
|
|
88
|
-
Performs fast gamut mapping in OKLCH as [described by Björn Ottoson](https://bottosson.github.io/posts/gamutclipping/) (2021). This takes an input `[l,c,h]` coords in OKLCH space, and ensures the final result will lie within the specified color `gamut` (default `sRGBGamut`). You can further specify a different target space (which default's
|
|
88
|
+
Performs fast gamut mapping in OKLCH as [described by Björn Ottoson](https://bottosson.github.io/posts/gamutclipping/) (2021). This takes an input `[l,c,h]` coords in OKLCH space, and ensures the final result will lie within the specified color `gamut` (default `sRGBGamut`). You can further specify a different target space (which default's to the gamut's space), for example to get a linear-light sRGB and avoid the transfer function, or to keep the result in OKLCH:
|
|
89
89
|
|
|
90
90
|
```js
|
|
91
91
|
import { gamutMapOKLCH, sRGBGamut, sRGBLinear, OKLCH } from "@texel/color";
|
|
@@ -116,7 +116,7 @@ gamutMapOKLCH(oklch, sRGBGamut, sRGB, rgb, MapToL);
|
|
|
116
116
|
|
|
117
117
|
The `cusp` can also be passed as the last parameter, allowing for faster evaluation for known hues. See below for calculating the cusp.
|
|
118
118
|
|
|
119
|
-
>
|
|
119
|
+
> **Note:** If you map to an OKLab-based target (OKLCH, OKHSL etc), the final step of RGB clipping will be skipped. This produces more predictable OKLab and OKLCH based results, but you will likely want to perform a final clampedRGB() step when converting to a displayable color.
|
|
120
120
|
|
|
121
121
|
#### `LC = findCuspOKLCH(a, b, gamut, out = [0, 0])`
|
|
122
122
|
|
|
@@ -142,24 +142,41 @@ const cuspLC = findCuspOKLCH(a, b, gamut);
|
|
|
142
142
|
|
|
143
143
|
// ... somewhere else in your program ...
|
|
144
144
|
// pass 'cusp' parameter for faster evaluation
|
|
145
|
+
// expected that your OKLCH coord has the same hue as the cusp (H)
|
|
145
146
|
gamutMapOKLCH(oklch, gamut, gamut.space, out, MapToCuspL, cuspLC);
|
|
146
147
|
```
|
|
147
148
|
|
|
148
149
|
The `a` and `b` can also be from OKLab coordinates, but must be normalized so `a^2 + b^2 == 1`.
|
|
149
150
|
|
|
150
|
-
#### `str = serialize(coords, inputSpace
|
|
151
|
+
#### `str = serialize(coords, inputSpace, outputSpace = inputSpace)`
|
|
151
152
|
|
|
152
|
-
Turns the specified `coords` (assumed to be in `inputSpace`) into a string, first converting if needed to the specified `outputSpace`. If the space is sRGB, a plain `rgb(r,g,b)` string (in bytes) will be used for browser compatibility and performance, otherwise a CSS color string will be returned. Note that not all spaces, such as certain linear spaces, are currently supported by CSS.
|
|
153
|
+
Turns the specified `coords` (assumed to be in `inputSpace`) into a string, first converting if needed to the specified `outputSpace`. If the space is sRGB, a plain `rgb(r,g,b)` string (in bytes) will be used for browser compatibility and performance, otherwise a CSS color string will be returned. Note that not all spaces, such as certain linear spaces, are currently supported by CSS. You can optionally pass an `alpha` component (0..1 range) as the fourth element in the `coords` array for it to be considered.
|
|
153
154
|
|
|
154
155
|
```js
|
|
155
156
|
import { serialize, sRGB, DisplayP3, OKLCH } from "@texel/color";
|
|
156
157
|
|
|
157
158
|
serialize([0, 0.5, 1], sRGB); // "rgb(0, 128, 255)"
|
|
159
|
+
serialize([0, 0.5, 1, 0.5], sRGB); // "rgba(0, 128, 255, 0.5)"
|
|
158
160
|
serialize([0, 0.5, 1], DisplayP3); // "color(display-p3 0 0.5 1)"
|
|
161
|
+
serialize([0, 0.5, 1, 0.35], DisplayP3); // "color(display-p3 0 0.5 1 / 0.35)"
|
|
159
162
|
serialize([1, 0, 0], OKLCH, sRGB); // "rgb(255, 255, 255)"
|
|
160
163
|
serialize([1, 0, 0], OKLCH); // "oklch(1 0 0)"
|
|
161
164
|
```
|
|
162
165
|
|
|
166
|
+
#### `info = deserialize(colorString)`
|
|
167
|
+
|
|
168
|
+
The inverse of `serialize`, this will take a string and determine the color space `id` it is referencing, and the 3 or 4 (for alpha) `coords`. This is intentionally limited in functionality, only supporting hex RGB, `rgb()` and `rgba()` bytes, and `oklch()`, `oklab()`, and plain `color()` functions with no modifiers.
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
import { deserialize } from "@texel/color";
|
|
172
|
+
|
|
173
|
+
const { coords, id } = deserialize("color(display-p3 0 0.5 1 / 0.35)");
|
|
174
|
+
console.log(id); // "display-p3"
|
|
175
|
+
console.log(coords); // [ 0, 0.5, 1, 0.35 ]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
> **Note:** Parsing is still a WIP area of API design, and complex CSS color string handling is not within the scope of this library.
|
|
179
|
+
|
|
163
180
|
#### `delta = deltaEOK(oklabA, oklabB)`
|
|
164
181
|
|
|
165
182
|
Performs a color difference in OKLab space between two coordinates. As this is a perceptually uniform color space that improves upon CIELAB and its flaws, it should be suitable as a replacement for the CIEDE2000 color difference equation in many situations.
|
|
@@ -293,6 +310,8 @@ import {
|
|
|
293
310
|
LMS_to_XYZ_M,
|
|
294
311
|
XYZ_to_LMS_M,
|
|
295
312
|
sRGB,
|
|
313
|
+
OKHSLToOKLab,
|
|
314
|
+
DisplayP3Gamut,
|
|
296
315
|
} from "@texel/color";
|
|
297
316
|
|
|
298
317
|
console.log(XYZ_to_linear_sRGB_M); // [ [a,b,c], ... ]
|
|
@@ -301,6 +320,10 @@ OKLab_from(xyzD65, XYZ_to_LMS_M); // XYZ D65 -> OKLab
|
|
|
301
320
|
transform(xyzD65, XYZ_to_linear_sRGB_M); // XYZ D65 -> sRGBLinear
|
|
302
321
|
sRGB.fromBase(in_linear_sRGB, out_sRGB); // linear to gamma transfer function
|
|
303
322
|
sRGB.toBase(in_sRGB, out_linear_sRGB); // linear to gamma transfer function
|
|
323
|
+
|
|
324
|
+
// OKHSL in a non-sRGB gamut
|
|
325
|
+
// also see OKHSVToOKLab and their inverse functions
|
|
326
|
+
OKHSLToOKLab([h, s, l], DisplayP3Gamut, optionalOutVec);
|
|
304
327
|
```
|
|
305
328
|
|
|
306
329
|
## Notes
|
|
@@ -309,7 +332,7 @@ sRGB.toBase(in_sRGB, out_linear_sRGB); // linear to gamma transfer function
|
|
|
309
332
|
|
|
310
333
|
[Colorjs](https://colorjs.io/) is fantastic and perhaps the current leading standard in JavaScript, but it's not very practical for creative coding and real-time web applications, where the requirements are often (1) leaner codebases, (2) highly optimized, and (3) minimal GC thrashing.
|
|
311
334
|
|
|
312
|
-
Colorjs, and simialrly, [Culori](https://culorijs.org/)
|
|
335
|
+
Colorjs, and simialrly, [Culori](https://culorijs.org/), are focused on matching CSS spec, which means it will very likely continue to grow in complexity over time, and performance will often be marred (for example, `@texel/color` cusp intersection gamut mapping is ~125 times faster than Colorjs and ~60 times faster than culori).
|
|
313
336
|
|
|
314
337
|
There are many other options such as [color-space](https://www.npmjs.com/package/color-space) or [color-convert](https://www.npmjs.com/package/color-convert), however, these do not support modern spacse such as OKLab and OKHSL, and/or have dubious levels of accuracy (many libraries, for example, do not distinguish between D50 and D65 whitepoints in XYZ).
|
|
315
338
|
|
package/package.json
CHANGED
package/src/core.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { floatToByte, vec3 } from "./util.js";
|
|
1
|
+
import { clamp, floatToByte, hexToRGB, vec3 } from "./util.js";
|
|
2
2
|
import { LMS_to_OKLab_M, OKLab_to_LMS_M } from "./conversion_matrices.js";
|
|
3
|
-
import { XYZ } from "./spaces.js";
|
|
3
|
+
import { listColorSpaces, sRGB, XYZ } from "./spaces.js";
|
|
4
4
|
|
|
5
5
|
const tmp3 = vec3();
|
|
6
6
|
|
|
@@ -51,25 +51,131 @@ const vec3Copy = (input, output) => {
|
|
|
51
51
|
|
|
52
52
|
export const serialize = (input, inputSpace, outputSpace = inputSpace) => {
|
|
53
53
|
if (!inputSpace) throw new Error(`must specify an input space`);
|
|
54
|
+
// extract alpha if present
|
|
55
|
+
let alpha = 1;
|
|
56
|
+
if (input.length > 3) {
|
|
57
|
+
alpha = input[3];
|
|
58
|
+
}
|
|
59
|
+
// copy into temp
|
|
60
|
+
vec3Copy(input, tmp3);
|
|
61
|
+
// convert if needed
|
|
54
62
|
if (inputSpace !== outputSpace) {
|
|
55
63
|
convert(input, inputSpace, outputSpace, tmp3);
|
|
56
|
-
} else {
|
|
57
|
-
vec3Copy(input, tmp3);
|
|
58
64
|
}
|
|
59
|
-
|
|
60
65
|
const id = outputSpace.id;
|
|
61
66
|
if (id == "srgb") {
|
|
62
67
|
const r = floatToByte(tmp3[0]);
|
|
63
68
|
const g = floatToByte(tmp3[1]);
|
|
64
69
|
const b = floatToByte(tmp3[2]);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return `${id}(${tmp3[0]} ${tmp3[1]} ${tmp3[2]})`;
|
|
70
|
+
const rgb = `${r}, ${g}, ${b}`;
|
|
71
|
+
return alpha === 1 ? `rgb(${rgb})` : `rgba(${rgb}, ${alpha})`;
|
|
68
72
|
} else {
|
|
69
|
-
|
|
73
|
+
const alphaSuffix = alpha === 1 ? "" : ` / ${alpha}`;
|
|
74
|
+
if (id == "oklab" || id == "oklch") {
|
|
75
|
+
return `${id}(${tmp3[0]} ${tmp3[1]} ${tmp3[2]}${alphaSuffix})`;
|
|
76
|
+
} else {
|
|
77
|
+
return `color(${id} ${tmp3[0]} ${tmp3[1]} ${tmp3[2]}${alphaSuffix})`;
|
|
78
|
+
}
|
|
70
79
|
}
|
|
71
80
|
};
|
|
72
81
|
|
|
82
|
+
const stripAlpha = (coords) => {
|
|
83
|
+
if (coords.length >= 4 && coords[3] === 1) return coords.slice(0, 3);
|
|
84
|
+
return coords;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const deserialize = (input) => {
|
|
88
|
+
if (typeof input !== "string") {
|
|
89
|
+
throw new Error(`expected a string as input`);
|
|
90
|
+
}
|
|
91
|
+
input = input.trim();
|
|
92
|
+
if (input.charAt(0) === "#") {
|
|
93
|
+
const rgbIn = input.slice(0, 7);
|
|
94
|
+
let alphaByte = input.length > 7 ? parseInt(input.slice(7, 9), 16) : 255;
|
|
95
|
+
let alpha = isNaN(alphaByte) ? 1 : alphaByte / 255;
|
|
96
|
+
const coords = hexToRGB(rgbIn);
|
|
97
|
+
if (alpha !== 1) coords.push(alpha);
|
|
98
|
+
return {
|
|
99
|
+
id: "srgb",
|
|
100
|
+
coords,
|
|
101
|
+
};
|
|
102
|
+
} else {
|
|
103
|
+
const parts = /^(rgb|rgba|oklab|oklch|color)\((.+)\)$/i.exec(input);
|
|
104
|
+
if (!parts) {
|
|
105
|
+
throw new Error(`could not parse color string ${input}`);
|
|
106
|
+
}
|
|
107
|
+
const fn = parts[1].toLowerCase();
|
|
108
|
+
if (/^rgba?$/i.test(fn)) {
|
|
109
|
+
const hasAlpha = fn == "rgba";
|
|
110
|
+
const coords = parts[2]
|
|
111
|
+
.split(",")
|
|
112
|
+
.map((v, i) =>
|
|
113
|
+
i < 3 ? clamp(parseInt(v, 10) || 0, 0, 255) / 255 : parseFloat(v)
|
|
114
|
+
);
|
|
115
|
+
const expectedLen = hasAlpha ? 4 : 3;
|
|
116
|
+
if (coords.length !== expectedLen) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`got ${fn} with incorrect number of coords, expected ${expectedLen}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
id: "srgb",
|
|
123
|
+
coords: stripAlpha(coords),
|
|
124
|
+
};
|
|
125
|
+
} else {
|
|
126
|
+
let id, coordsStrings;
|
|
127
|
+
if (fn === "color") {
|
|
128
|
+
const params =
|
|
129
|
+
/([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s/]+)(?:\s?\/\s?([^\s]+))?/.exec(
|
|
130
|
+
parts[2]
|
|
131
|
+
);
|
|
132
|
+
if (!params)
|
|
133
|
+
throw new Error(`could not parse color() function ${input}`);
|
|
134
|
+
id = params[1].toLowerCase();
|
|
135
|
+
coordsStrings = params.slice(2, 6);
|
|
136
|
+
} else if (/^(oklab|oklch)$/.test(fn)) {
|
|
137
|
+
id = fn;
|
|
138
|
+
const params =
|
|
139
|
+
/([^\s]+)\s+([^\s]+)\s+([^\s/]+)(?:\s?\/\s?([^\s]+))?/.exec(parts[2]);
|
|
140
|
+
if (!params)
|
|
141
|
+
throw new Error(`could not parse color() function ${input}`);
|
|
142
|
+
coordsStrings = params.slice(1, 6);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (coordsStrings[3] == null) {
|
|
146
|
+
coordsStrings = coordsStrings.slice(0, 3);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const coords = coordsStrings.map((f) => parseFloat(f));
|
|
150
|
+
if (coords.length < 3 || coords.length > 4)
|
|
151
|
+
throw new Error(`invalid number of coordinates`);
|
|
152
|
+
return {
|
|
153
|
+
id,
|
|
154
|
+
coords: stripAlpha(coords),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
export const parse = (input, targetSpace, out = vec3()) => {
|
|
160
|
+
if (!targetSpace)
|
|
161
|
+
throw new Error(`must specify a target space to parse into`);
|
|
162
|
+
|
|
163
|
+
const { coords, id } = deserialize(input);
|
|
164
|
+
const space = listColorSpaces().find((f) => id === f.id);
|
|
165
|
+
if (!space) throw new Error(`could not find space with the id ${id}`);
|
|
166
|
+
const alpha = coords.length === 4 ? coords[3] : 1;
|
|
167
|
+
|
|
168
|
+
// copy 3D coords to output and convert
|
|
169
|
+
vec3Copy(coords, out);
|
|
170
|
+
convert(out, space, targetSpace, out);
|
|
171
|
+
|
|
172
|
+
// store alpha
|
|
173
|
+
if (alpha !== 1) out[3] = alpha;
|
|
174
|
+
// reduce to 3D
|
|
175
|
+
if (alpha == 1 && out.length === 4) out.pop();
|
|
176
|
+
return out;
|
|
177
|
+
};
|
|
178
|
+
|
|
73
179
|
export const convert = (input, fromSpace, toSpace, out = vec3()) => {
|
|
74
180
|
// place into output
|
|
75
181
|
vec3Copy(input, out);
|
package/src/gamut.js
CHANGED
|
@@ -334,7 +334,7 @@ export const gamutMapOKLCH = (
|
|
|
334
334
|
// will be applied. The OKLCH result is _basically_ in gamut, but not exactly; you'll need to clip at final stage.
|
|
335
335
|
// I've documented this behaviour in the readme.
|
|
336
336
|
const targetSpaceBase = targetSpace.base ?? targetSpace;
|
|
337
|
-
if (targetSpaceBase ==
|
|
337
|
+
if (targetSpaceBase.id == "oklab") {
|
|
338
338
|
return convert(out, OKLCH, targetSpace, out);
|
|
339
339
|
}
|
|
340
340
|
|
package/src/util.js
CHANGED
|
@@ -26,9 +26,12 @@ export const hexToRGB = (str, out = vec3()) => {
|
|
|
26
26
|
return out;
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
export const
|
|
29
|
+
export const RGBToHex = (rgb) =>
|
|
30
30
|
`#${rgb.map((n) => floatToByte(n).toString(16).padStart(2, "0")).join("")}`;
|
|
31
31
|
|
|
32
|
+
/** @deprecated use RGBToHex */
|
|
33
|
+
export const RGBtoHex = (rgb) => RGBToHex;
|
|
34
|
+
|
|
32
35
|
export const isRGBInGamut = (lrgb, ep = GAMUT_EPSILON) => {
|
|
33
36
|
const r = lrgb[0];
|
|
34
37
|
const g = lrgb[1];
|
package/test/test.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
floatToByte,
|
|
6
6
|
hexToRGB,
|
|
7
7
|
isRGBInGamut,
|
|
8
|
-
|
|
8
|
+
RGBToHex,
|
|
9
9
|
linear_sRGB_to_LMS_M,
|
|
10
10
|
LMS_to_linear_sRGB_M,
|
|
11
11
|
LMS_to_XYZ_M,
|
|
@@ -42,6 +42,8 @@ import {
|
|
|
42
42
|
A98RGBLinear,
|
|
43
43
|
XYZ_to_linear_A98RGB_M,
|
|
44
44
|
DisplayP3Gamut,
|
|
45
|
+
deserialize,
|
|
46
|
+
parse,
|
|
45
47
|
} from "../src/index.js";
|
|
46
48
|
|
|
47
49
|
test("should convert XYZ in different whitepoints", async (t) => {
|
|
@@ -229,10 +231,96 @@ test("should serialize", async (t) => {
|
|
|
229
231
|
t.deepEqual(serialize([0, 0.5, 1], sRGBLinear), "color(srgb-linear 0 0.5 1)");
|
|
230
232
|
t.deepEqual(serialize([1, 0, 0], OKLCH, sRGB), "rgb(255, 255, 255)");
|
|
231
233
|
t.deepEqual(serialize([1, 0, 0], OKLCH), "oklch(1 0 0)");
|
|
234
|
+
t.deepEqual(serialize([1, 0, 0], OKLab), "oklab(1 0 0)");
|
|
235
|
+
t.deepEqual(
|
|
236
|
+
serialize([1, 0, 0, 0.4523], OKLCH, sRGB),
|
|
237
|
+
"rgba(255, 255, 255, 0.4523)"
|
|
238
|
+
);
|
|
239
|
+
t.deepEqual(
|
|
240
|
+
serialize([1, 0, 0, 0.4523], OKLCH, OKLCH),
|
|
241
|
+
"oklch(1 0 0 / 0.4523)"
|
|
242
|
+
);
|
|
243
|
+
t.deepEqual(serialize([1, 0, 0, 0.4523], OKLCH), "oklch(1 0 0 / 0.4523)");
|
|
244
|
+
t.deepEqual(
|
|
245
|
+
serialize([1, 0, 0, 0.4523], DisplayP3),
|
|
246
|
+
"color(display-p3 1 0 0 / 0.4523)"
|
|
247
|
+
);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("should parse to a color coord", async (t) => {
|
|
251
|
+
t.deepEqual(parse("rgb(0, 128, 255)", sRGB), [0, 128 / 0xff, 255 / 0xff]);
|
|
252
|
+
t.deepEqual(parse("rgba(0, 128, 255, .25)", sRGB), [
|
|
253
|
+
0,
|
|
254
|
+
128 / 0xff,
|
|
255
|
+
255 / 0xff,
|
|
256
|
+
0.25,
|
|
257
|
+
]);
|
|
258
|
+
let outVec = [0, 0, 0];
|
|
259
|
+
t.deepEqual(parse("rgba(0, 128, 255, 1)", sRGB), [0, 128 / 0xff, 255 / 0xff]);
|
|
260
|
+
let out;
|
|
261
|
+
out = parse("rgba(0, 128, 255, 1)", sRGB, outVec);
|
|
262
|
+
t.deepEqual(out, [0, 128 / 0xff, 255 / 0xff]);
|
|
263
|
+
t.equal(out, outVec);
|
|
264
|
+
|
|
265
|
+
// trims to 3
|
|
266
|
+
outVec = [0, 0, 0, 0];
|
|
267
|
+
out = parse("rgba(0, 128, 255, 1)", sRGB, outVec);
|
|
268
|
+
t.deepEqual(out, [0, 128 / 0xff, 255 / 0xff]);
|
|
269
|
+
t.equal(out, outVec);
|
|
270
|
+
|
|
271
|
+
// ensures 4
|
|
272
|
+
outVec = [0, 0, 0, 0];
|
|
273
|
+
out = parse("rgba(0, 128, 255, 0.91)", sRGB, outVec);
|
|
274
|
+
t.deepEqual(out, [0, 128 / 0xff, 255 / 0xff, 0.91]);
|
|
275
|
+
t.equal(out, outVec);
|
|
276
|
+
|
|
277
|
+
t.deepEqual(
|
|
278
|
+
serialize(parse("oklch(1 0 0)", sRGB), sRGB),
|
|
279
|
+
"rgb(255, 255, 255)"
|
|
280
|
+
);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("should deserialize color string information", async (t) => {
|
|
284
|
+
t.deepEqual(deserialize("rgb(0, 128, 255)"), {
|
|
285
|
+
coords: [0, 128 / 0xff, 255 / 0xff],
|
|
286
|
+
id: "srgb",
|
|
287
|
+
});
|
|
288
|
+
t.deepEqual(deserialize("rgba(0, 128, 255, 0.35)"), {
|
|
289
|
+
coords: [0, 128 / 0xff, 255 / 0xff, 0.35],
|
|
290
|
+
id: "srgb",
|
|
291
|
+
});
|
|
292
|
+
t.deepEqual(deserialize("#ff00cc"), {
|
|
293
|
+
id: "srgb",
|
|
294
|
+
coords: [1, 0, 0.8],
|
|
295
|
+
});
|
|
296
|
+
t.deepEqual(deserialize("#ff00cccc"), {
|
|
297
|
+
id: "srgb",
|
|
298
|
+
coords: [1, 0, 0.8, 0.8],
|
|
299
|
+
});
|
|
300
|
+
t.deepEqual(deserialize("color(srgb-linear 0 0.5 1)"), {
|
|
301
|
+
id: "srgb-linear",
|
|
302
|
+
coords: [0, 0.5, 1],
|
|
303
|
+
});
|
|
304
|
+
t.deepEqual(deserialize("color(srgb-linear 0 0.5 1/0.25)"), {
|
|
305
|
+
id: "srgb-linear",
|
|
306
|
+
coords: [0, 0.5, 1, 0.25],
|
|
307
|
+
});
|
|
308
|
+
t.deepEqual(deserialize("color(srgb-linear 0 0.5 1 / 0.25)"), {
|
|
309
|
+
id: "srgb-linear",
|
|
310
|
+
coords: [0, 0.5, 1, 0.25],
|
|
311
|
+
});
|
|
312
|
+
t.deepEqual(deserialize("oklch(1 0 0)"), {
|
|
313
|
+
id: "oklch",
|
|
314
|
+
coords: [1, 0, 0],
|
|
315
|
+
});
|
|
316
|
+
t.deepEqual(deserialize("oklch(1 0 0/0.25)"), {
|
|
317
|
+
id: "oklch",
|
|
318
|
+
coords: [1, 0, 0, 0.25],
|
|
319
|
+
});
|
|
232
320
|
});
|
|
233
321
|
|
|
234
322
|
test("utils", async (t) => {
|
|
235
|
-
t.deepEqual(
|
|
323
|
+
t.deepEqual(RGBToHex([0, 0.5, 1]), "#0080ff");
|
|
236
324
|
t.deepEqual(hexToRGB("#0080ff"), [0, 0.5019607843137255, 1]);
|
|
237
325
|
const tmp = [0, 0, 0];
|
|
238
326
|
hexToRGB("#0080ff", tmp);
|