@texel/color 1.1.0 → 1.1.2
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/package.json +1 -1
- package/src/core.js +30 -18
- package/src/util.js +1 -1
- package/test/spaces/hsl.js +5 -0
- package/test/spaces/lab.js +3 -1
- package/test/test.js +78 -36
package/package.json
CHANGED
package/src/core.js
CHANGED
|
@@ -64,6 +64,7 @@ export const serialize = (input, inputSpace, outputSpace = inputSpace) => {
|
|
|
64
64
|
}
|
|
65
65
|
const id = outputSpace.id;
|
|
66
66
|
if (id == "srgb") {
|
|
67
|
+
// uses the legacy rgb() format
|
|
67
68
|
const r = floatToByte(tmp3[0]);
|
|
68
69
|
const g = floatToByte(tmp3[1]);
|
|
69
70
|
const b = floatToByte(tmp3[2]);
|
|
@@ -72,7 +73,8 @@ export const serialize = (input, inputSpace, outputSpace = inputSpace) => {
|
|
|
72
73
|
} else {
|
|
73
74
|
const alphaSuffix = alpha === 1 ? "" : ` / ${alpha}`;
|
|
74
75
|
if (id == "oklab" || id == "oklch") {
|
|
75
|
-
|
|
76
|
+
// older versions of Safari don't support oklch with 0..1 L but do support %
|
|
77
|
+
return `${id}(${tmp3[0] * 100}% ${tmp3[1]} ${tmp3[2]}${alphaSuffix})`;
|
|
76
78
|
} else {
|
|
77
79
|
return `color(${id} ${tmp3[0]} ${tmp3[1]} ${tmp3[2]}${alphaSuffix})`;
|
|
78
80
|
}
|
|
@@ -84,6 +86,13 @@ const stripAlpha = (coords) => {
|
|
|
84
86
|
return coords;
|
|
85
87
|
};
|
|
86
88
|
|
|
89
|
+
const parseFloatValue = str => parseFloat(str) || 0;
|
|
90
|
+
|
|
91
|
+
const parseColorValue = (str, is255 = false) => {
|
|
92
|
+
if (is255) return clamp(parseFloatValue(str) / 0xff, 0, 0xff);
|
|
93
|
+
else return str.includes('%') ? parseFloatValue(str) / 100 : parseFloatValue(str);
|
|
94
|
+
};
|
|
95
|
+
|
|
87
96
|
export const deserialize = (input) => {
|
|
88
97
|
if (typeof input !== "string") {
|
|
89
98
|
throw new Error(`expected a string as input`);
|
|
@@ -105,26 +114,19 @@ export const deserialize = (input) => {
|
|
|
105
114
|
throw new Error(`could not parse color string ${input}`);
|
|
106
115
|
}
|
|
107
116
|
const fn = parts[1].toLowerCase();
|
|
108
|
-
if (/^rgba?$/i.test(fn)) {
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
}
|
|
117
|
+
if (/^rgba?$/i.test(fn) && parts[2].includes(',')) {
|
|
118
|
+
const coords = parts[2].split(',').map((v, i) => {
|
|
119
|
+
return parseColorValue(v.trim(), i < 3)
|
|
120
|
+
});
|
|
121
121
|
return {
|
|
122
122
|
id: "srgb",
|
|
123
123
|
coords: stripAlpha(coords),
|
|
124
124
|
};
|
|
125
125
|
} else {
|
|
126
126
|
let id, coordsStrings;
|
|
127
|
-
|
|
127
|
+
let div255 = false;
|
|
128
|
+
|
|
129
|
+
if (/^color$/i.test(fn)) {
|
|
128
130
|
const params =
|
|
129
131
|
/([^\s]+)\s+([^\s]+)\s+([^\s]+)\s+([^\s/]+)(?:\s?\/\s?([^\s]+))?/.exec(
|
|
130
132
|
parts[2]
|
|
@@ -133,8 +135,15 @@ export const deserialize = (input) => {
|
|
|
133
135
|
throw new Error(`could not parse color() function ${input}`);
|
|
134
136
|
id = params[1].toLowerCase();
|
|
135
137
|
coordsStrings = params.slice(2, 6);
|
|
136
|
-
} else
|
|
137
|
-
|
|
138
|
+
} else {
|
|
139
|
+
if (/^(oklab|oklch)$/i.test(fn)) {
|
|
140
|
+
id = fn;
|
|
141
|
+
} else if (/rgba?/i.test(fn)) {
|
|
142
|
+
id = 'srgb';
|
|
143
|
+
div255 = true;
|
|
144
|
+
} else {
|
|
145
|
+
throw new Error(`unknown color function ${fn}`);
|
|
146
|
+
}
|
|
138
147
|
const params =
|
|
139
148
|
/([^\s]+)\s+([^\s]+)\s+([^\s/]+)(?:\s?\/\s?([^\s]+))?/.exec(parts[2]);
|
|
140
149
|
if (!params)
|
|
@@ -146,7 +155,9 @@ export const deserialize = (input) => {
|
|
|
146
155
|
coordsStrings = coordsStrings.slice(0, 3);
|
|
147
156
|
}
|
|
148
157
|
|
|
149
|
-
const coords = coordsStrings.map((f) =>
|
|
158
|
+
const coords = coordsStrings.map((f, i) => {
|
|
159
|
+
return parseColorValue(f.trim(), div255 && i < 3);
|
|
160
|
+
});
|
|
150
161
|
if (coords.length < 3 || coords.length > 4)
|
|
151
162
|
throw new Error(`invalid number of coordinates`);
|
|
152
163
|
return {
|
|
@@ -156,6 +167,7 @@ export const deserialize = (input) => {
|
|
|
156
167
|
}
|
|
157
168
|
}
|
|
158
169
|
};
|
|
170
|
+
|
|
159
171
|
export const parse = (input, targetSpace, out = vec3()) => {
|
|
160
172
|
if (!targetSpace)
|
|
161
173
|
throw new Error(`must specify a target space to parse into`);
|
package/src/util.js
CHANGED
|
@@ -30,7 +30,7 @@ export const RGBToHex = (rgb) =>
|
|
|
30
30
|
`#${rgb.map((n) => floatToByte(n).toString(16).padStart(2, "0")).join("")}`;
|
|
31
31
|
|
|
32
32
|
/** @deprecated use RGBToHex */
|
|
33
|
-
export const RGBtoHex =
|
|
33
|
+
export const RGBtoHex = RGBToHex;
|
|
34
34
|
|
|
35
35
|
export const isRGBInGamut = (lrgb, ep = GAMUT_EPSILON) => {
|
|
36
36
|
const r = lrgb[0];
|
package/test/spaces/hsl.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
// HSL space (hue, saturation, lightness within sRGB gamut)
|
|
2
|
+
|
|
3
|
+
// Reference:
|
|
4
|
+
// https://github.com/color-js/color.js/blob/cfe55d358adb6c2e23c8a897282adf42904fd32d/src/spaces/hsl.js
|
|
5
|
+
|
|
1
6
|
import { sRGB, sRGBLinear } from "../../src/index.js";
|
|
2
7
|
|
|
3
8
|
export const HSL = {
|
package/test/spaces/lab.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Lab aka CIELAB aka L*a*b* (uses a D50 WHITE_D50 point and has to be adapted)
|
|
2
2
|
// refer to CSS Color Module Level 4 Spec for more details
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
// Reference:
|
|
5
|
+
// https://github.com/color-js/color.js/blob/cfe55d358adb6c2e23c8a897282adf42904fd32d/src/spaces/lab.js
|
|
4
6
|
import { D50_to_D65_M, D65_to_D50_M } from "../../src/index.js";
|
|
5
7
|
|
|
6
8
|
// K * e = 2^3 = 8
|
package/test/test.js
CHANGED
|
@@ -230,61 +230,95 @@ test("should serialize", async (t) => {
|
|
|
230
230
|
t.deepEqual(serialize([0, 0.5, 1], sRGB), "rgb(0, 128, 255)");
|
|
231
231
|
t.deepEqual(serialize([0, 0.5, 1], sRGBLinear), "color(srgb-linear 0 0.5 1)");
|
|
232
232
|
t.deepEqual(serialize([1, 0, 0], OKLCH, sRGB), "rgb(255, 255, 255)");
|
|
233
|
-
t.deepEqual(serialize([1, 0, 0], OKLCH), "oklch(
|
|
234
|
-
t.deepEqual(serialize([1, 0, 0], OKLab), "oklab(
|
|
233
|
+
t.deepEqual(serialize([1, 0, 0], OKLCH), "oklch(100% 0 0)");
|
|
234
|
+
t.deepEqual(serialize([1, 0, 0], OKLab), "oklab(100% 0 0)");
|
|
235
235
|
t.deepEqual(
|
|
236
236
|
serialize([1, 0, 0, 0.4523], OKLCH, sRGB),
|
|
237
237
|
"rgba(255, 255, 255, 0.4523)"
|
|
238
238
|
);
|
|
239
239
|
t.deepEqual(
|
|
240
240
|
serialize([1, 0, 0, 0.4523], OKLCH, OKLCH),
|
|
241
|
-
"oklch(
|
|
241
|
+
"oklch(100% 0 0 / 0.4523)"
|
|
242
242
|
);
|
|
243
|
-
t.deepEqual(serialize([1, 0, 0, 0.4523], OKLCH), "oklch(
|
|
243
|
+
t.deepEqual(serialize([1, 0, 0, 0.4523], OKLCH), "oklch(100% 0 0 / 0.4523)");
|
|
244
244
|
t.deepEqual(
|
|
245
245
|
serialize([1, 0, 0, 0.4523], DisplayP3),
|
|
246
246
|
"color(display-p3 1 0 0 / 0.4523)"
|
|
247
247
|
);
|
|
248
248
|
});
|
|
249
249
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
250
|
+
// not yet finished
|
|
251
|
+
// test("should parse to a color coord", async (t) => {
|
|
252
|
+
// t.deepEqual(parse("rgb(0, 128, 255)", sRGB), [0, 128 / 0xff, 255 / 0xff]);
|
|
253
|
+
// t.deepEqual(parse("rgba(0, 128, 255, .25)", sRGB), [
|
|
254
|
+
// 0,
|
|
255
|
+
// 128 / 0xff,
|
|
256
|
+
// 255 / 0xff,
|
|
257
|
+
// 0.25,
|
|
258
|
+
// ]);
|
|
259
|
+
// let outVec = [0, 0, 0];
|
|
260
|
+
// t.deepEqual(parse("rgba(0, 128, 255, 1)", sRGB), [0, 128 / 0xff, 255 / 0xff]);
|
|
261
|
+
// let out;
|
|
262
|
+
// out = parse("rgba(0, 128, 255, 1)", sRGB, outVec);
|
|
263
|
+
// t.deepEqual(out, [0, 128 / 0xff, 255 / 0xff]);
|
|
264
|
+
// t.equal(out, outVec);
|
|
265
|
+
|
|
266
|
+
// // trims to 3
|
|
267
|
+
// outVec = [0, 0, 0, 0];
|
|
268
|
+
// out = parse("rgba(0, 128, 255, 1)", sRGB, outVec);
|
|
269
|
+
// t.deepEqual(out, [0, 128 / 0xff, 255 / 0xff]);
|
|
270
|
+
// t.equal(out, outVec);
|
|
271
|
+
|
|
272
|
+
// // ensures 4
|
|
273
|
+
// outVec = [0, 0, 0, 0];
|
|
274
|
+
// out = parse("rgba(0, 128, 255, 0.91)", sRGB, outVec);
|
|
275
|
+
// t.deepEqual(out, [0, 128 / 0xff, 255 / 0xff, 0.91]);
|
|
276
|
+
// t.equal(out, outVec);
|
|
277
|
+
|
|
278
|
+
// t.deepEqual(
|
|
279
|
+
// serialize(parse("oklch(1 0 0)", sRGB), sRGB),
|
|
280
|
+
// "rgb(255, 255, 255)"
|
|
281
|
+
// );
|
|
282
|
+
// });
|
|
282
283
|
|
|
283
284
|
test("should deserialize color string information", async (t) => {
|
|
285
|
+
|
|
284
286
|
t.deepEqual(deserialize("rgb(0, 128, 255)"), {
|
|
285
287
|
coords: [0, 128 / 0xff, 255 / 0xff],
|
|
286
288
|
id: "srgb",
|
|
287
289
|
});
|
|
290
|
+
t.deepEqual(deserialize("rgba(0, 128, 255)"), {
|
|
291
|
+
coords: [0, 128 / 0xff, 255 / 0xff],
|
|
292
|
+
id: "srgb",
|
|
293
|
+
});
|
|
294
|
+
t.deepEqual(deserialize("rgba(0, 128, 255, 50%)"), {
|
|
295
|
+
coords: [0, 128 / 0xff, 255 / 0xff, 0.5],
|
|
296
|
+
id: "srgb",
|
|
297
|
+
});
|
|
298
|
+
t.deepEqual(deserialize("rgb(0, 128, 255, 0.5)"), {
|
|
299
|
+
coords: [0, 128 / 0xff, 255 / 0xff, 0.5],
|
|
300
|
+
id: "srgb",
|
|
301
|
+
});
|
|
302
|
+
t.deepEqual(deserialize("rgb(0 128 255)"), {
|
|
303
|
+
coords: [0, 128 / 0xff, 255 / 0xff],
|
|
304
|
+
id: "srgb",
|
|
305
|
+
});
|
|
306
|
+
t.deepEqual(deserialize("rgb(0 128 255 / 0.5)"), {
|
|
307
|
+
coords: [0, 128 / 0xff, 255 / 0xff, 0.5],
|
|
308
|
+
id: "srgb",
|
|
309
|
+
});
|
|
310
|
+
t.deepEqual(deserialize("rgb(0 128 255 / 50%)"), {
|
|
311
|
+
coords: [0, 128 / 0xff, 255 / 0xff, 0.5],
|
|
312
|
+
id: "srgb",
|
|
313
|
+
});
|
|
314
|
+
t.deepEqual(deserialize("rgb(0 128 255 / 0.35)"), {
|
|
315
|
+
coords: [0, 128 / 0xff, 255 / 0xff, 0.35],
|
|
316
|
+
id: "srgb",
|
|
317
|
+
});
|
|
318
|
+
t.deepEqual(deserialize("RGBA(0 128 255 / 0.35)"), {
|
|
319
|
+
coords: [0, 128 / 0xff, 255 / 0xff, 0.35],
|
|
320
|
+
id: "srgb",
|
|
321
|
+
});
|
|
288
322
|
t.deepEqual(deserialize("rgba(0, 128, 255, 0.35)"), {
|
|
289
323
|
coords: [0, 128 / 0xff, 255 / 0xff, 0.35],
|
|
290
324
|
id: "srgb",
|
|
@@ -297,6 +331,14 @@ test("should deserialize color string information", async (t) => {
|
|
|
297
331
|
id: "srgb",
|
|
298
332
|
coords: [1, 0, 0.8, 0.8],
|
|
299
333
|
});
|
|
334
|
+
t.deepEqual(deserialize("COLOR(sRGB-Linear 0 0.5 1)"), {
|
|
335
|
+
id: "srgb-linear",
|
|
336
|
+
coords: [0, 0.5, 1],
|
|
337
|
+
});
|
|
338
|
+
t.deepEqual(deserialize("COLOR(sRGB-Linear 0 50% 1)"), {
|
|
339
|
+
id: "srgb-linear",
|
|
340
|
+
coords: [0, 0.5, 1],
|
|
341
|
+
});
|
|
300
342
|
t.deepEqual(deserialize("color(srgb-linear 0 0.5 1)"), {
|
|
301
343
|
id: "srgb-linear",
|
|
302
344
|
coords: [0, 0.5, 1],
|