@voryx/color 1.0.0
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/dist/Color.d.ts +47 -0
- package/dist/Color.d.ts.map +1 -0
- package/dist/Color.js +551 -0
- package/dist/encode.d.ts +9 -0
- package/dist/encode.d.ts.map +1 -0
- package/dist/encode.js +60 -0
- package/dist/gamut/inGamut.d.ts +6 -0
- package/dist/gamut/inGamut.d.ts.map +1 -0
- package/dist/gamut/inGamut.js +25 -0
- package/dist/gamut/map.d.ts +8 -0
- package/dist/gamut/map.d.ts.map +1 -0
- package/dist/gamut/map.js +70 -0
- package/dist/gamut/types.d.ts +2 -0
- package/dist/gamut/types.d.ts.map +1 -0
- package/dist/gamut/types.js +0 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/misc.d.ts +24 -0
- package/dist/misc.d.ts.map +1 -0
- package/dist/misc.js +38 -0
- package/dist/spaces/HSL.d.ts +9 -0
- package/dist/spaces/HSL.d.ts.map +1 -0
- package/dist/spaces/HSL.js +69 -0
- package/dist/spaces/LMS.d.ts +7 -0
- package/dist/spaces/LMS.d.ts.map +1 -0
- package/dist/spaces/LMS.js +0 -0
- package/dist/spaces/OKLAB.d.ts +9 -0
- package/dist/spaces/OKLAB.d.ts.map +1 -0
- package/dist/spaces/OKLAB.js +62 -0
- package/dist/spaces/OKLCH.d.ts +9 -0
- package/dist/spaces/OKLCH.d.ts.map +1 -0
- package/dist/spaces/OKLCH.js +35 -0
- package/dist/spaces/P3.d.ts +12 -0
- package/dist/spaces/P3.d.ts.map +1 -0
- package/dist/spaces/P3.js +58 -0
- package/dist/spaces/RGB.d.ts +9 -0
- package/dist/spaces/RGB.d.ts.map +1 -0
- package/dist/spaces/RGB.js +21 -0
- package/dist/spaces/SRGB.d.ts +11 -0
- package/dist/spaces/SRGB.d.ts.map +1 -0
- package/dist/spaces/SRGB.js +63 -0
- package/dist/spaces/types.d.ts +6 -0
- package/dist/spaces/types.d.ts.map +1 -0
- package/dist/spaces/types.js +0 -0
- package/dist/transforms.d.ts +13 -0
- package/dist/transforms.d.ts.map +1 -0
- package/dist/transforms.js +25 -0
- package/package.json +36 -0
package/dist/Color.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ColorSpace } from './spaces/types';
|
|
2
|
+
import { type SRGBComponents } from './spaces/SRGB';
|
|
3
|
+
import { type HSLComponents } from './spaces/HSL';
|
|
4
|
+
import { type RGBComponents } from './spaces/RGB';
|
|
5
|
+
import { type P3Components } from './spaces/P3';
|
|
6
|
+
import { type OKLCHComponents } from './spaces/OKLCH';
|
|
7
|
+
import { type OKLABComponents } from './spaces/OKLAB';
|
|
8
|
+
import { type LMSComponents } from './spaces/LMS';
|
|
9
|
+
export type ColorComponents<S extends ColorSpace = ColorSpace> = S extends 'srgb' ? SRGBComponents : S extends 'hsl' ? HSLComponents : S extends 'rgb' ? RGBComponents : S extends 'p3' ? P3Components : S extends 'oklch' ? OKLCHComponents : S extends 'oklab' ? OKLABComponents : S extends 'lms' ? LMSComponents : never;
|
|
10
|
+
export declare class Color {
|
|
11
|
+
#private;
|
|
12
|
+
static fromSRGB(components: SRGBComponents): Color;
|
|
13
|
+
static fromHSL(components: HSLComponents): Color;
|
|
14
|
+
static fromRGB(components: RGBComponents): Color;
|
|
15
|
+
static fromP3(components: P3Components): Color;
|
|
16
|
+
static fromOKLCH(components: OKLCHComponents): Color;
|
|
17
|
+
static fromOKLAB(components: OKLABComponents): Color;
|
|
18
|
+
static fromLMS(components: LMSComponents): Color;
|
|
19
|
+
static fromColorCode(code: string): Color;
|
|
20
|
+
static from(item: Color | ColorComponents | string): Color;
|
|
21
|
+
constructor(view: [number, number, number, number]);
|
|
22
|
+
rotate(degrees: number, space?: 'oklch' | 'hsl'): Color;
|
|
23
|
+
to<S extends ColorSpace>(space: S): ColorComponents<S>;
|
|
24
|
+
get srgb(): SRGBComponents;
|
|
25
|
+
get hsl(): HSLComponents;
|
|
26
|
+
get rgb(): RGBComponents;
|
|
27
|
+
get p3(): P3Components;
|
|
28
|
+
get oklch(): OKLCHComponents;
|
|
29
|
+
get oklab(): OKLABComponents;
|
|
30
|
+
get lms(): LMSComponents;
|
|
31
|
+
get long(): number;
|
|
32
|
+
get medium(): number;
|
|
33
|
+
get short(): number;
|
|
34
|
+
get alpha(): number;
|
|
35
|
+
get luminance(): number;
|
|
36
|
+
get foregroundColor(): Color;
|
|
37
|
+
withAlpha(alpha: number): Color;
|
|
38
|
+
contrastRatio(other: Color): number;
|
|
39
|
+
inGamut(gamut: 'srgb' | 'p3', epsilon?: number): boolean;
|
|
40
|
+
toGamut(gamut: 'srgb' | 'p3', epsilon?: number): Color;
|
|
41
|
+
toCSS<S extends ColorSpace>({ space, precision }?: {
|
|
42
|
+
space?: S;
|
|
43
|
+
precision?: number;
|
|
44
|
+
}): string;
|
|
45
|
+
toColorCode(): string;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=Color.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Color.d.ts","sourceRoot":"","sources":["../src/Color.ts"],"names":[],"mappings":"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"}
|
package/dist/Color.js
ADDED
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
import { SRGB } from "./spaces/SRGB.js";
|
|
2
|
+
import { HSL } from "./spaces/HSL.js";
|
|
3
|
+
import { RGB } from "./spaces/RGB.js";
|
|
4
|
+
import { P3 } from "./spaces/P3.js";
|
|
5
|
+
import { OKLCH } from "./spaces/OKLCH.js";
|
|
6
|
+
import { OKLAB } from "./spaces/OKLAB.js";
|
|
7
|
+
import { inGamut } from "./gamut/inGamut.js";
|
|
8
|
+
import { mapToGamut } from "./gamut/map.js";
|
|
9
|
+
import { normalizeAngle, roundToPrecision } from "./misc.js";
|
|
10
|
+
import { ObjectCache } from "@voryx/object-cache";
|
|
11
|
+
import { decodeColorCode, encodeColorCode } from "./encode.js";
|
|
12
|
+
class Color {
|
|
13
|
+
static fromSRGB(components) {
|
|
14
|
+
const lms = SRGB.toLMS([
|
|
15
|
+
components.sRed,
|
|
16
|
+
components.green,
|
|
17
|
+
components.blue,
|
|
18
|
+
components.alpha
|
|
19
|
+
]);
|
|
20
|
+
return new Color(lms);
|
|
21
|
+
}
|
|
22
|
+
static fromHSL(components) {
|
|
23
|
+
const lms = HSL.toLMS([
|
|
24
|
+
components.hue,
|
|
25
|
+
components.saturation,
|
|
26
|
+
components.lightness,
|
|
27
|
+
components.alpha
|
|
28
|
+
]);
|
|
29
|
+
return new Color(lms);
|
|
30
|
+
}
|
|
31
|
+
static fromRGB(components) {
|
|
32
|
+
const lms = RGB.toLMS([
|
|
33
|
+
components.red,
|
|
34
|
+
components.green,
|
|
35
|
+
components.blue,
|
|
36
|
+
components.alpha
|
|
37
|
+
]);
|
|
38
|
+
return new Color(lms);
|
|
39
|
+
}
|
|
40
|
+
static fromP3(components) {
|
|
41
|
+
const lms = P3.toLMS([
|
|
42
|
+
components.p3Red,
|
|
43
|
+
components.green,
|
|
44
|
+
components.blue,
|
|
45
|
+
components.alpha
|
|
46
|
+
]);
|
|
47
|
+
return new Color(lms);
|
|
48
|
+
}
|
|
49
|
+
static fromOKLCH(components) {
|
|
50
|
+
const lms = OKLCH.toLMS([
|
|
51
|
+
components.lightness,
|
|
52
|
+
components.chroma,
|
|
53
|
+
components.hue,
|
|
54
|
+
components.alpha
|
|
55
|
+
]);
|
|
56
|
+
return new Color(lms);
|
|
57
|
+
}
|
|
58
|
+
static fromOKLAB(components) {
|
|
59
|
+
const lms = OKLAB.toLMS([
|
|
60
|
+
components.lightness,
|
|
61
|
+
components.a,
|
|
62
|
+
components.b,
|
|
63
|
+
components.alpha
|
|
64
|
+
]);
|
|
65
|
+
return new Color(lms);
|
|
66
|
+
}
|
|
67
|
+
static fromLMS(components) {
|
|
68
|
+
return new Color([
|
|
69
|
+
components.long,
|
|
70
|
+
components.medium,
|
|
71
|
+
components.short,
|
|
72
|
+
components.alpha
|
|
73
|
+
]);
|
|
74
|
+
}
|
|
75
|
+
static fromColorCode(code) {
|
|
76
|
+
const decoded = decodeColorCode(code);
|
|
77
|
+
return new Color([
|
|
78
|
+
decoded.l,
|
|
79
|
+
decoded.m,
|
|
80
|
+
decoded.s,
|
|
81
|
+
decoded.alpha
|
|
82
|
+
]);
|
|
83
|
+
}
|
|
84
|
+
static from(item) {
|
|
85
|
+
let components;
|
|
86
|
+
if (item instanceof Color) return new Color([
|
|
87
|
+
item.lms.long,
|
|
88
|
+
item.lms.medium,
|
|
89
|
+
item.lms.short,
|
|
90
|
+
item.lms.alpha
|
|
91
|
+
]);
|
|
92
|
+
if ('string' == typeof item) {
|
|
93
|
+
const comp = parseColorString(item);
|
|
94
|
+
if (!comp) throw new Error(`Invalid color string: ${item}`);
|
|
95
|
+
components = comp;
|
|
96
|
+
} else components = item;
|
|
97
|
+
if ('sRed' in components) {
|
|
98
|
+
const lms = SRGB.toLMS([
|
|
99
|
+
components.sRed,
|
|
100
|
+
components.green,
|
|
101
|
+
components.blue,
|
|
102
|
+
components.alpha
|
|
103
|
+
]);
|
|
104
|
+
return new Color(lms);
|
|
105
|
+
}
|
|
106
|
+
if ('p3Red' in components) {
|
|
107
|
+
const lms = P3.toLMS([
|
|
108
|
+
components.p3Red,
|
|
109
|
+
components.green,
|
|
110
|
+
components.blue,
|
|
111
|
+
components.alpha
|
|
112
|
+
]);
|
|
113
|
+
return new Color(lms);
|
|
114
|
+
}
|
|
115
|
+
if ('chroma' in components) {
|
|
116
|
+
const lms = OKLCH.toLMS([
|
|
117
|
+
components.lightness,
|
|
118
|
+
components.chroma,
|
|
119
|
+
components.hue,
|
|
120
|
+
components.alpha
|
|
121
|
+
]);
|
|
122
|
+
return new Color(lms);
|
|
123
|
+
}
|
|
124
|
+
if ('a' in components) {
|
|
125
|
+
const lms = OKLAB.toLMS([
|
|
126
|
+
components.lightness,
|
|
127
|
+
components.a,
|
|
128
|
+
components.b,
|
|
129
|
+
components.alpha
|
|
130
|
+
]);
|
|
131
|
+
return new Color(lms);
|
|
132
|
+
}
|
|
133
|
+
if ('hue' in components) {
|
|
134
|
+
const lms = HSL.toLMS([
|
|
135
|
+
components.hue,
|
|
136
|
+
components.saturation,
|
|
137
|
+
components.lightness,
|
|
138
|
+
components.alpha
|
|
139
|
+
]);
|
|
140
|
+
return new Color(lms);
|
|
141
|
+
}
|
|
142
|
+
if ('red' in components) {
|
|
143
|
+
const lms = RGB.toLMS([
|
|
144
|
+
components.red,
|
|
145
|
+
components.green,
|
|
146
|
+
components.blue,
|
|
147
|
+
components.alpha
|
|
148
|
+
]);
|
|
149
|
+
return new Color(lms);
|
|
150
|
+
}
|
|
151
|
+
if ('long' in components) return new Color([
|
|
152
|
+
components.long,
|
|
153
|
+
components.medium,
|
|
154
|
+
components.short,
|
|
155
|
+
components.alpha
|
|
156
|
+
]);
|
|
157
|
+
throw new Error('Invalid color components');
|
|
158
|
+
}
|
|
159
|
+
#view;
|
|
160
|
+
constructor(view){
|
|
161
|
+
this.#view = view;
|
|
162
|
+
}
|
|
163
|
+
rotate(degrees, space = 'oklch') {
|
|
164
|
+
if ('oklch' === space) {
|
|
165
|
+
const oklch = OKLCH.fromLMS([
|
|
166
|
+
this.#view[0],
|
|
167
|
+
this.#view[1],
|
|
168
|
+
this.#view[2],
|
|
169
|
+
this.#view[3]
|
|
170
|
+
]);
|
|
171
|
+
const newHue = normalizeAngle(oklch[2] + degrees);
|
|
172
|
+
const newOklch = [
|
|
173
|
+
oklch[0],
|
|
174
|
+
oklch[1],
|
|
175
|
+
newHue,
|
|
176
|
+
oklch[3]
|
|
177
|
+
];
|
|
178
|
+
const newLMS = OKLCH.toLMS(newOklch);
|
|
179
|
+
return new Color(newLMS);
|
|
180
|
+
}
|
|
181
|
+
if ('hsl' === space) {
|
|
182
|
+
const hsl = HSL.fromLMS([
|
|
183
|
+
this.#view[0],
|
|
184
|
+
this.#view[1],
|
|
185
|
+
this.#view[2],
|
|
186
|
+
this.#view[3]
|
|
187
|
+
]);
|
|
188
|
+
const newHue = normalizeAngle(hsl[0] + degrees);
|
|
189
|
+
const newHsl = [
|
|
190
|
+
newHue,
|
|
191
|
+
hsl[1],
|
|
192
|
+
hsl[2],
|
|
193
|
+
hsl[3]
|
|
194
|
+
];
|
|
195
|
+
const newLMS = HSL.toLMS(newHsl);
|
|
196
|
+
return new Color(newLMS);
|
|
197
|
+
}
|
|
198
|
+
throw new Error('Invalid rotation space');
|
|
199
|
+
}
|
|
200
|
+
to(space) {
|
|
201
|
+
return ObjectCache.getOrSet(this, `to-${space}`, ()=>{
|
|
202
|
+
switch(space){
|
|
203
|
+
case 'srgb':
|
|
204
|
+
const srgb = SRGB.fromLMS([
|
|
205
|
+
this.#view[0],
|
|
206
|
+
this.#view[1],
|
|
207
|
+
this.#view[2],
|
|
208
|
+
this.#view[3]
|
|
209
|
+
]);
|
|
210
|
+
return {
|
|
211
|
+
sRed: srgb[0],
|
|
212
|
+
green: srgb[1],
|
|
213
|
+
blue: srgb[2],
|
|
214
|
+
alpha: srgb[3]
|
|
215
|
+
};
|
|
216
|
+
case 'hsl':
|
|
217
|
+
const hsl = HSL.fromLMS([
|
|
218
|
+
this.#view[0],
|
|
219
|
+
this.#view[1],
|
|
220
|
+
this.#view[2],
|
|
221
|
+
this.#view[3]
|
|
222
|
+
]);
|
|
223
|
+
return {
|
|
224
|
+
hue: hsl[0],
|
|
225
|
+
saturation: hsl[1],
|
|
226
|
+
lightness: hsl[2],
|
|
227
|
+
alpha: hsl[3]
|
|
228
|
+
};
|
|
229
|
+
case 'rgb':
|
|
230
|
+
const rgb = RGB.fromLMS([
|
|
231
|
+
this.#view[0],
|
|
232
|
+
this.#view[1],
|
|
233
|
+
this.#view[2],
|
|
234
|
+
this.#view[3]
|
|
235
|
+
]);
|
|
236
|
+
return {
|
|
237
|
+
red: rgb[0],
|
|
238
|
+
green: rgb[1],
|
|
239
|
+
blue: rgb[2],
|
|
240
|
+
alpha: rgb[3]
|
|
241
|
+
};
|
|
242
|
+
case 'p3':
|
|
243
|
+
const p3 = P3.fromLMS([
|
|
244
|
+
this.#view[0],
|
|
245
|
+
this.#view[1],
|
|
246
|
+
this.#view[2],
|
|
247
|
+
this.#view[3]
|
|
248
|
+
]);
|
|
249
|
+
return {
|
|
250
|
+
p3Red: p3[0],
|
|
251
|
+
green: p3[1],
|
|
252
|
+
blue: p3[2],
|
|
253
|
+
alpha: p3[3]
|
|
254
|
+
};
|
|
255
|
+
case 'oklch':
|
|
256
|
+
const oklch = OKLCH.fromLMS([
|
|
257
|
+
this.#view[0],
|
|
258
|
+
this.#view[1],
|
|
259
|
+
this.#view[2],
|
|
260
|
+
this.#view[3]
|
|
261
|
+
]);
|
|
262
|
+
return {
|
|
263
|
+
lightness: oklch[0],
|
|
264
|
+
chroma: oklch[1],
|
|
265
|
+
hue: oklch[2],
|
|
266
|
+
alpha: oklch[3]
|
|
267
|
+
};
|
|
268
|
+
case 'oklab':
|
|
269
|
+
const oklab = OKLAB.fromLMS([
|
|
270
|
+
this.#view[0],
|
|
271
|
+
this.#view[1],
|
|
272
|
+
this.#view[2],
|
|
273
|
+
this.#view[3]
|
|
274
|
+
]);
|
|
275
|
+
return {
|
|
276
|
+
lightness: oklab[0],
|
|
277
|
+
a: oklab[1],
|
|
278
|
+
b: oklab[2],
|
|
279
|
+
alpha: oklab[3]
|
|
280
|
+
};
|
|
281
|
+
case 'lms':
|
|
282
|
+
return {
|
|
283
|
+
long: this.#view[0],
|
|
284
|
+
medium: this.#view[1],
|
|
285
|
+
short: this.#view[2],
|
|
286
|
+
alpha: this.#view[3]
|
|
287
|
+
};
|
|
288
|
+
default:
|
|
289
|
+
throw new Error(`Unsupported color space: ${space}`);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
get srgb() {
|
|
294
|
+
return this.to('srgb');
|
|
295
|
+
}
|
|
296
|
+
get hsl() {
|
|
297
|
+
return this.to('hsl');
|
|
298
|
+
}
|
|
299
|
+
get rgb() {
|
|
300
|
+
return this.to('rgb');
|
|
301
|
+
}
|
|
302
|
+
get p3() {
|
|
303
|
+
return this.to('p3');
|
|
304
|
+
}
|
|
305
|
+
get oklch() {
|
|
306
|
+
return this.to('oklch');
|
|
307
|
+
}
|
|
308
|
+
get oklab() {
|
|
309
|
+
return this.to('oklab');
|
|
310
|
+
}
|
|
311
|
+
get lms() {
|
|
312
|
+
return this.to('lms');
|
|
313
|
+
}
|
|
314
|
+
get long() {
|
|
315
|
+
return this.#view[0];
|
|
316
|
+
}
|
|
317
|
+
get medium() {
|
|
318
|
+
return this.#view[1];
|
|
319
|
+
}
|
|
320
|
+
get short() {
|
|
321
|
+
return this.#view[2];
|
|
322
|
+
}
|
|
323
|
+
get alpha() {
|
|
324
|
+
return this.#view[3];
|
|
325
|
+
}
|
|
326
|
+
get luminance() {
|
|
327
|
+
return 0.15514 * this.long + 0.53121 * this.medium + 0.04618 * this.short;
|
|
328
|
+
}
|
|
329
|
+
get foregroundColor() {
|
|
330
|
+
const contrastWithBlack = this.contrastRatio(Color.fromSRGB({
|
|
331
|
+
sRed: 0,
|
|
332
|
+
green: 0,
|
|
333
|
+
blue: 0,
|
|
334
|
+
alpha: 1
|
|
335
|
+
}));
|
|
336
|
+
const contrastWithWhite = this.contrastRatio(Color.fromSRGB({
|
|
337
|
+
sRed: 1,
|
|
338
|
+
green: 1,
|
|
339
|
+
blue: 1,
|
|
340
|
+
alpha: 1
|
|
341
|
+
}));
|
|
342
|
+
return contrastWithBlack > contrastWithWhite ? Color.fromSRGB({
|
|
343
|
+
sRed: 0,
|
|
344
|
+
green: 0,
|
|
345
|
+
blue: 0,
|
|
346
|
+
alpha: 1
|
|
347
|
+
}) : Color.fromSRGB({
|
|
348
|
+
sRed: 1,
|
|
349
|
+
green: 1,
|
|
350
|
+
blue: 1,
|
|
351
|
+
alpha: 1
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
withAlpha(alpha) {
|
|
355
|
+
return new Color([
|
|
356
|
+
this.#view[0],
|
|
357
|
+
this.#view[1],
|
|
358
|
+
this.#view[2],
|
|
359
|
+
alpha
|
|
360
|
+
]);
|
|
361
|
+
}
|
|
362
|
+
contrastRatio(other) {
|
|
363
|
+
const lum1 = this.luminance;
|
|
364
|
+
const lum2 = other.luminance;
|
|
365
|
+
const brightest = Math.max(lum1, lum2);
|
|
366
|
+
const darkest = Math.min(lum1, lum2);
|
|
367
|
+
return (brightest + 0.05) / (darkest + 0.05);
|
|
368
|
+
}
|
|
369
|
+
inGamut(gamut, epsilon) {
|
|
370
|
+
return inGamut([
|
|
371
|
+
this.#view[0],
|
|
372
|
+
this.#view[1],
|
|
373
|
+
this.#view[2],
|
|
374
|
+
this.#view[3]
|
|
375
|
+
], gamut, epsilon);
|
|
376
|
+
}
|
|
377
|
+
toGamut(gamut, epsilon) {
|
|
378
|
+
const mapped = mapToGamut([
|
|
379
|
+
this.#view[0],
|
|
380
|
+
this.#view[1],
|
|
381
|
+
this.#view[2],
|
|
382
|
+
this.#view[3]
|
|
383
|
+
], gamut, {
|
|
384
|
+
epsilon
|
|
385
|
+
});
|
|
386
|
+
return new Color(mapped);
|
|
387
|
+
}
|
|
388
|
+
toCSS({ space, precision = 3 } = {}) {
|
|
389
|
+
return ObjectCache.getOrSet(this, `toCSS-${space}-${precision}`, ()=>{
|
|
390
|
+
if (!space) space = 'rgb';
|
|
391
|
+
const components = this.to(space);
|
|
392
|
+
switch(space){
|
|
393
|
+
case 'srgb':
|
|
394
|
+
return `color(srgb ${roundToPrecision(components.sRed, precision)} ${roundToPrecision(components.green, precision)} ${roundToPrecision(components.blue, precision)} / ${roundToPrecision(components.alpha, 2)})`;
|
|
395
|
+
case 'hsl':
|
|
396
|
+
return `hsla(${roundToPrecision(components.hue, precision)}, ${roundToPrecision(components.saturation, precision)}%, ${roundToPrecision(components.lightness, precision)}%, ${roundToPrecision(components.alpha, 2)})`;
|
|
397
|
+
case 'rgb':
|
|
398
|
+
return `rgba(${roundToPrecision(components.red, precision)}, ${roundToPrecision(components.green, precision)}, ${roundToPrecision(components.blue, precision)}, ${roundToPrecision(components.alpha, 2)})`;
|
|
399
|
+
case 'p3':
|
|
400
|
+
return `color(display-p3 ${roundToPrecision(components.p3Red, precision)} ${roundToPrecision(components.green, precision)} ${roundToPrecision(components.blue, precision)} / ${roundToPrecision(components.alpha, 2)})`;
|
|
401
|
+
case 'oklch':
|
|
402
|
+
return `oklch(${roundToPrecision(components.lightness, precision)} ${roundToPrecision(components.chroma, precision)} ${roundToPrecision(components.hue, precision)} / ${roundToPrecision(components.alpha, 2)})`;
|
|
403
|
+
case 'oklab':
|
|
404
|
+
return `oklab(${roundToPrecision(components.lightness, precision)} ${roundToPrecision(components.a, precision)} ${roundToPrecision(components.b, precision)} / ${roundToPrecision(components.alpha, 2)})`;
|
|
405
|
+
case 'lms':
|
|
406
|
+
return `lms(${roundToPrecision(components.long, precision)} ${roundToPrecision(components.medium, precision)} ${roundToPrecision(components.short, precision)} / ${roundToPrecision(components.alpha, 2)})`;
|
|
407
|
+
default:
|
|
408
|
+
throw new Error(`Unsupported color space: ${space}`);
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
toColorCode() {
|
|
413
|
+
return encodeColorCode({
|
|
414
|
+
l: this.#view[0],
|
|
415
|
+
m: this.#view[1],
|
|
416
|
+
s: this.#view[2],
|
|
417
|
+
alpha: this.#view[3]
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
function parseColorString(colorString) {
|
|
422
|
+
const trimmed = colorString.trim().toLowerCase();
|
|
423
|
+
if (trimmed.startsWith('rgba')) {
|
|
424
|
+
const match = trimmed.match(/rgba\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*\)/);
|
|
425
|
+
if (match) return {
|
|
426
|
+
red: parseFloat(match[1]),
|
|
427
|
+
green: parseFloat(match[2]),
|
|
428
|
+
blue: parseFloat(match[3]),
|
|
429
|
+
alpha: parseFloat(match[4])
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
if (trimmed.startsWith('rgb')) {
|
|
433
|
+
const match = trimmed.match(/rgb\(\s*([\d.]+)\s*,\s*([\d.]+)\s*,\s*([\d.]+)\s*\)/);
|
|
434
|
+
if (match) return {
|
|
435
|
+
red: parseFloat(match[1]),
|
|
436
|
+
green: parseFloat(match[2]),
|
|
437
|
+
blue: parseFloat(match[3]),
|
|
438
|
+
alpha: 1
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
if (trimmed.startsWith('hsla')) {
|
|
442
|
+
const match = trimmed.match(/hsla\(\s*([\d.]+)\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%\s*,\s*([\d.]+)\s*\)/);
|
|
443
|
+
if (match) return {
|
|
444
|
+
hue: parseFloat(match[1]),
|
|
445
|
+
saturation: parseFloat(match[2]),
|
|
446
|
+
lightness: parseFloat(match[3]),
|
|
447
|
+
alpha: parseFloat(match[4])
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
if (trimmed.startsWith('hsl')) {
|
|
451
|
+
const match = trimmed.match(/hsl\(\s*([\d.]+)\s*,\s*([\d.]+)%\s*,\s*([\d.]+)%\s*\)/);
|
|
452
|
+
if (match) return {
|
|
453
|
+
hue: parseFloat(match[1]),
|
|
454
|
+
saturation: parseFloat(match[2]),
|
|
455
|
+
lightness: parseFloat(match[3]),
|
|
456
|
+
alpha: 1
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
if (trimmed.startsWith('color(')) {
|
|
460
|
+
const match = trimmed.match(/color\(\s*([\w-]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*([\d.]+))?\s*\)/);
|
|
461
|
+
if (match) {
|
|
462
|
+
const space = match[1];
|
|
463
|
+
const c1 = parseFloat(match[2]);
|
|
464
|
+
const c2 = parseFloat(match[3]);
|
|
465
|
+
const c3 = parseFloat(match[4]);
|
|
466
|
+
const alpha = match[5] ? parseFloat(match[5]) : 1;
|
|
467
|
+
switch(space){
|
|
468
|
+
case 'srgb':
|
|
469
|
+
return {
|
|
470
|
+
sRed: c1,
|
|
471
|
+
green: c2,
|
|
472
|
+
blue: c3,
|
|
473
|
+
alpha
|
|
474
|
+
};
|
|
475
|
+
case 'display-p3':
|
|
476
|
+
return {
|
|
477
|
+
p3Red: c1,
|
|
478
|
+
green: c2,
|
|
479
|
+
blue: c3,
|
|
480
|
+
alpha
|
|
481
|
+
};
|
|
482
|
+
default:
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (trimmed.startsWith('oklch')) {
|
|
488
|
+
const match = trimmed.match(/oklch\(\s*([\d.]+)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*([\d.]+))?\s*\)/);
|
|
489
|
+
if (match) return {
|
|
490
|
+
lightness: parseFloat(match[1]),
|
|
491
|
+
chroma: parseFloat(match[2]),
|
|
492
|
+
hue: parseFloat(match[3]),
|
|
493
|
+
alpha: match[4] ? parseFloat(match[4]) : 1
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
if (trimmed.startsWith('oklab')) {
|
|
497
|
+
const match = trimmed.match(/oklab\(\s*([\d.]+)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*([\d.]+))?\s*\)/);
|
|
498
|
+
if (match) return {
|
|
499
|
+
lightness: parseFloat(match[1]),
|
|
500
|
+
a: parseFloat(match[2]),
|
|
501
|
+
b: parseFloat(match[3]),
|
|
502
|
+
alpha: match[4] ? parseFloat(match[4]) : 1
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
if (trimmed.startsWith('lms')) {
|
|
506
|
+
const match = trimmed.match(/lms\(\s*([\d.]+)\s+([\d.]+)\s+([\d.]+)(?:\s*\/\s*([\d.]+))?\s*\)/);
|
|
507
|
+
if (match) return {
|
|
508
|
+
long: parseFloat(match[1]),
|
|
509
|
+
medium: parseFloat(match[2]),
|
|
510
|
+
short: parseFloat(match[3]),
|
|
511
|
+
alpha: match[4] ? parseFloat(match[4]) : 1
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
if (trimmed.startsWith('#')) {
|
|
515
|
+
const hex = trimmed.slice(1);
|
|
516
|
+
if (6 === hex.length) {
|
|
517
|
+
const r = parseInt(hex.slice(0, 2), 16) / 255;
|
|
518
|
+
const g = parseInt(hex.slice(2, 4), 16) / 255;
|
|
519
|
+
const b = parseInt(hex.slice(4, 6), 16) / 255;
|
|
520
|
+
return {
|
|
521
|
+
sRed: r,
|
|
522
|
+
green: g,
|
|
523
|
+
blue: b,
|
|
524
|
+
alpha: 1
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
if (8 === hex.length) {
|
|
528
|
+
const r = parseInt(hex.slice(0, 2), 16) / 255;
|
|
529
|
+
const g = parseInt(hex.slice(2, 4), 16) / 255;
|
|
530
|
+
const b = parseInt(hex.slice(4, 6), 16) / 255;
|
|
531
|
+
const a = parseInt(hex.slice(6, 8), 16) / 255;
|
|
532
|
+
return {
|
|
533
|
+
sRed: r,
|
|
534
|
+
green: g,
|
|
535
|
+
blue: b,
|
|
536
|
+
alpha: a
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
if (trimmed.startsWith('L')) {
|
|
541
|
+
const decoded = decodeColorCode(trimmed);
|
|
542
|
+
return {
|
|
543
|
+
long: decoded.l,
|
|
544
|
+
medium: decoded.m,
|
|
545
|
+
short: decoded.s,
|
|
546
|
+
alpha: decoded.alpha
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
export { Color };
|
package/dist/encode.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encode.d.ts","sourceRoot":"","sources":["../src/encode.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,IAAI;IACnB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;CACd;AAoGD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,IAAI,GACV,MAAM,CASR;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,GACX,IAAI,CAsBN"}
|
package/dist/encode.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const LMS_MIN = -2;
|
|
2
|
+
const LMS_MAX = 2;
|
|
3
|
+
const LMS_BITS = 16;
|
|
4
|
+
const LMS_INT_MAX = 2 ** LMS_BITS - 1;
|
|
5
|
+
const ALPHA_BITS = 8;
|
|
6
|
+
const ALPHA_INT_MAX = 2 ** ALPHA_BITS - 1;
|
|
7
|
+
function clamp(value, min, max) {
|
|
8
|
+
return Math.min(max, Math.max(min, value));
|
|
9
|
+
}
|
|
10
|
+
function encodeLMSChannel(value) {
|
|
11
|
+
const normalized = (clamp(value, LMS_MIN, LMS_MAX) - LMS_MIN) / (LMS_MAX - LMS_MIN);
|
|
12
|
+
return Math.round(normalized * LMS_INT_MAX);
|
|
13
|
+
}
|
|
14
|
+
function decodeLMSChannel(value) {
|
|
15
|
+
const normalized = value / LMS_INT_MAX;
|
|
16
|
+
return LMS_MIN + normalized * (LMS_MAX - LMS_MIN);
|
|
17
|
+
}
|
|
18
|
+
function encodeAlpha(alpha) {
|
|
19
|
+
return Math.round(clamp(alpha, 0, 1) * ALPHA_INT_MAX);
|
|
20
|
+
}
|
|
21
|
+
function decodeAlpha(alpha) {
|
|
22
|
+
return alpha / ALPHA_INT_MAX;
|
|
23
|
+
}
|
|
24
|
+
function encodeLMSA({ l, m, s, alpha }) {
|
|
25
|
+
const L = BigInt(encodeLMSChannel(l));
|
|
26
|
+
const M = BigInt(encodeLMSChannel(m));
|
|
27
|
+
const S = BigInt(encodeLMSChannel(s));
|
|
28
|
+
const A = BigInt(encodeAlpha(alpha));
|
|
29
|
+
return L << 40n | M << 24n | S << 8n | A;
|
|
30
|
+
}
|
|
31
|
+
function decodeLMSA(value) {
|
|
32
|
+
const L = Number(value >> 40n & 0xffffn);
|
|
33
|
+
const M = Number(value >> 24n & 0xffffn);
|
|
34
|
+
const S = Number(value >> 8n & 0xffffn);
|
|
35
|
+
const A = Number(0xffn & value);
|
|
36
|
+
return {
|
|
37
|
+
l: decodeLMSChannel(L),
|
|
38
|
+
m: decodeLMSChannel(M),
|
|
39
|
+
s: decodeLMSChannel(S),
|
|
40
|
+
alpha: decodeAlpha(A)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const VERSION = '1';
|
|
44
|
+
function encodeColorCode(color) {
|
|
45
|
+
const encoded = encodeLMSA(color);
|
|
46
|
+
const hex = encoded.toString(16).padStart(14, '0').toUpperCase();
|
|
47
|
+
return `L${VERSION}${hex}`;
|
|
48
|
+
}
|
|
49
|
+
function decodeColorCode(code) {
|
|
50
|
+
const match = /^L(\d)([0-9A-Fa-f]{14})$/.exec(code);
|
|
51
|
+
if (!match) throw new Error(`Invalid color code: ${code}`);
|
|
52
|
+
const [, version, hex] = match;
|
|
53
|
+
switch(version){
|
|
54
|
+
case '1':
|
|
55
|
+
return decodeLMSA(BigInt(`0x${hex}`));
|
|
56
|
+
default:
|
|
57
|
+
throw new Error(`Unsupported color code version: ${version}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export { decodeColorCode, encodeColorCode };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RGBGamut } from './types';
|
|
2
|
+
export declare function isRGBInGamut(rgb: [number, number, number, number], epsilon?: number): boolean;
|
|
3
|
+
export declare function lmsToGamutRGB(lms: [number, number, number, number], gamut: RGBGamut): [number, number, number, number];
|
|
4
|
+
export declare function gamutRGBToLMS(rgb: [number, number, number, number], gamut: RGBGamut): [number, number, number, number];
|
|
5
|
+
export declare function inGamut(lms: [number, number, number, number], gamut: RGBGamut, epsilon?: number): boolean;
|
|
6
|
+
//# sourceMappingURL=inGamut.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inGamut.d.ts","sourceRoot":"","sources":["../../src/gamut/inGamut.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAKvC,wBAAgB,YAAY,CAC1B,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EACrC,OAAO,SAAO,GACb,OAAO,CAST;AAED,wBAAgB,aAAa,CAC3B,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EACrC,KAAK,EAAE,QAAQ,GACd,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAQlC;AAED,wBAAgB,aAAa,CAC3B,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EACrC,KAAK,EAAE,QAAQ,GACd,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAQlC;AAED,wBAAgB,OAAO,CACrB,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EACrC,KAAK,EAAE,QAAQ,EACf,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAKT"}
|