@texel/color 1.1.2 → 1.1.4

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 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 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:
88
+ Performs fast gamut mapping in OKLCH as [described by Björn Ottosson](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";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@texel/color",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "a minimal and modern color library",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/gamut.js CHANGED
@@ -182,13 +182,15 @@ export const findGamutIntersectionOKLCH = (a, b, l1, c1, l0, cusp, gamut) => {
182
182
 
183
183
  // Find the intersection for upper and lower half separately
184
184
  if ((l1 - l0) * cusp[1] - (cusp[0] - l0) * c1 <= 0.0) {
185
+ const denom = (c1 * cusp[0] + cusp[1] * (l0 - l1));
185
186
  // Lower half
186
- t = (cusp[1] * l0) / (c1 * cusp[0] + cusp[1] * (l0 - l1));
187
+ t = denom === 0 ? 0 : (cusp[1] * l0) / denom;
187
188
  } else {
188
189
  // Upper half
189
190
 
190
191
  // First intersect with triangle
191
- t = (cusp[1] * (l0 - 1.0)) / (c1 * (cusp[0] - 1.0) + cusp[1] * (l0 - l1));
192
+ const denom = (c1 * (cusp[0] - 1.0) + cusp[1] * (l0 - l1));
193
+ t = denom === 0 ? 0 : (cusp[1] * (l0 - 1.0)) / denom;
192
194
 
193
195
  // Then one step Halley's method
194
196
  let dl = l1 - l0;
@@ -1,15 +0,0 @@
1
- const EPSILON = Math.pow(2, -33); // ~= 0.0000000001
2
-
3
- export default function arrayAlmostEqual(a, b, tolerance = EPSILON) {
4
- if (!a || !b) return false;
5
- if (a.length !== b.length) return false;
6
- for (let i = 0; i < a.length; i++) {
7
- const p0 = a[i];
8
- const p1 = b[i];
9
- if (p0 !== p1) {
10
- const diff = Math.abs(p1 - p0);
11
- if (diff > tolerance) return false;
12
- }
13
- }
14
- return true;
15
- }
package/test/banner.png DELETED
Binary file
@@ -1,227 +0,0 @@
1
- import Color from "colorjs.io";
2
- import {
3
- sRGB as sRGB_ColorJS,
4
- OKLCH as OKLCH_ColorJS,
5
- P3 as DisplayP3_ColorJS,
6
- toGamut,
7
- } from "colorjs.io/fn";
8
- import {
9
- convert,
10
- OKLCH,
11
- OKLab,
12
- sRGB,
13
- sRGBGamut,
14
- DisplayP3Gamut,
15
- DisplayP3,
16
- gamutMapOKLCH,
17
- constrainAngle,
18
- findCuspOKLCH,
19
- degToRad,
20
- MapToCuspL,
21
- } from "../src/index.js";
22
-
23
- import { getSupportedColorJSSpaces } from "./colorjs-fn.js";
24
-
25
- const supportedSpaces = getSupportedColorJSSpaces();
26
-
27
- // @texel/color space interfaces
28
- const spaces = supportedSpaces.map((s) => s.space);
29
-
30
- // Colorjs.io space interfaces & IDs
31
- const colorJSSpaces = supportedSpaces.map((s) => s.colorJSSpace);
32
- const colorJSSpaceIDs = supportedSpaces.map((s) => s.colorJSSpace.id);
33
-
34
- const N = 128 * 128;
35
-
36
- // sampling variables within OKLab and converting to OKLCH
37
- const vecs = Array(N)
38
- .fill()
39
- .map((_, i, lst) => {
40
- const t = i / (lst.length - 1);
41
- return convert([t, t * 2 - 1, t * 2 - 1], OKLab, OKLCH);
42
- });
43
-
44
- const tmp = [0, 0, 0];
45
-
46
- let now, elapsedColorjs, elapsedOurs;
47
-
48
- //// OKLCH to sRGB with gamut mapping (direct path)
49
-
50
- const hueCusps = Array(360).fill(null);
51
- const oklchVecs = Array(512 * 256)
52
- .fill()
53
- .map((_, i, lst) => {
54
- const t0 = i / (lst.length - 1);
55
- const t1 = i / lst.length;
56
- const H = constrainAngle(Math.round(t1 * 360));
57
- if (!hueCusps[H]) {
58
- const Hr = degToRad(H);
59
- const a = Math.cos(Hr);
60
- const b = Math.sin(Hr);
61
- hueCusps[H] = findCuspOKLCH(a, b, sRGBGamut);
62
- }
63
- return [t0, t0, H];
64
- });
65
-
66
- compare(
67
- "conversion (Colorjs.io procedural API)",
68
- () => {
69
- for (let vec of vecs) {
70
- for (let i = 0; i < colorJSSpaces.length; i++) {
71
- for (let j = 0; j < colorJSSpaces.length; j++) {
72
- const a = colorJSSpaces[i];
73
- const b = colorJSSpaces[j];
74
- const ret = b.from(a, vec);
75
- }
76
- }
77
- }
78
- },
79
- () => {
80
- for (let vec of vecs) {
81
- for (let i = 0; i < spaces.length; i++) {
82
- for (let j = 0; j < spaces.length; j++) {
83
- const a = spaces[i];
84
- const b = spaces[j];
85
- convert(vec, a, b, tmp);
86
- }
87
- }
88
- }
89
- }
90
- );
91
-
92
- compare(
93
- "conversion (Colorjs.io main API)",
94
- () => {
95
- for (let vec of vecs) {
96
- for (let i = 0; i < colorJSSpaceIDs.length; i++) {
97
- for (let j = 0; j < colorJSSpaceIDs.length; j++) {
98
- const a = colorJSSpaceIDs[i];
99
- const b = colorJSSpaceIDs[j];
100
- new Color(a, vec).to(b);
101
- }
102
- }
103
- }
104
- },
105
- () => {
106
- for (let vec of vecs) {
107
- for (let i = 0; i < spaces.length; i++) {
108
- for (let j = 0; j < spaces.length; j++) {
109
- const a = spaces[i];
110
- const b = spaces[j];
111
- convert(vec, a, b, tmp);
112
- }
113
- }
114
- }
115
- }
116
- );
117
-
118
- compare(
119
- "gamut mapping OKLCH - sRGB (Colorjs.io procedural API)",
120
- () => {
121
- let tmpColor = { space: sRGB_ColorJS, coords: [0, 0, 0], alpha: 1 };
122
- for (let vec of oklchVecs) {
123
- tmpColor.coords = sRGB_ColorJS.from(OKLCH_ColorJS, vec);
124
- toGamut(tmpColor);
125
- }
126
- },
127
- () => {
128
- for (let vec of oklchVecs) {
129
- // you can omit the cusp and it will be found on the fly,
130
- // however the test will run slightly slower
131
- const cusp = hueCusps[vec[2]];
132
- gamutMapOKLCH(vec, sRGBGamut, sRGB, tmp, MapToCuspL, cusp);
133
- }
134
- }
135
- );
136
-
137
- compare(
138
- "gamut mapping OKLCH - sRGB (Colorjs.io main API)",
139
- () => {
140
- for (let vec of oklchVecs) {
141
- new Color("oklch", vec)
142
- .to("srgb")
143
- .toGamut({ space: "srgb", method: "css" });
144
- }
145
- },
146
- () => {
147
- for (let vec of oklchVecs) {
148
- // you can omit the cusp and it will be found on the fly,
149
- // however the test will run slightly slower
150
- const cusp = hueCusps[vec[2]];
151
- gamutMapOKLCH(vec, sRGBGamut, sRGB, tmp, MapToCuspL, cusp);
152
- }
153
- }
154
- );
155
-
156
- compare(
157
- "gamut mapping all spaces to P3 (Colorjs.io procedural API)",
158
- () => {
159
- let tmpColor = { space: DisplayP3_ColorJS, coords: [0, 0, 0], alpha: 1 };
160
- for (let vec of vecs) {
161
- for (let i = 0; i < colorJSSpaces.length; i++) {
162
- const a = colorJSSpaces[i];
163
- tmpColor.coords = DisplayP3_ColorJS.from(a, vec);
164
- toGamut(tmpColor);
165
- }
166
- }
167
- },
168
- () => {
169
- for (let vec of vecs) {
170
- for (let i = 0; i < spaces.length; i++) {
171
- const a = spaces[i];
172
- convert(vec, a, OKLCH, tmp);
173
- gamutMapOKLCH(tmp, DisplayP3Gamut, DisplayP3, tmp);
174
- }
175
- }
176
- }
177
- );
178
-
179
- compare(
180
- "gamut mapping all spaces to P3 (Colorjs.io main API)",
181
- () => {
182
- for (let vec of vecs) {
183
- for (let i = 0; i < colorJSSpaceIDs.length; i++) {
184
- const a = colorJSSpaceIDs[i];
185
- new Color(a, vec).to("p3").toGamut({ space: "p3", method: "css" });
186
- }
187
- }
188
- },
189
- () => {
190
- for (let vec of vecs) {
191
- for (let i = 0; i < spaces.length; i++) {
192
- const a = spaces[i];
193
- convert(vec, a, OKLCH, tmp);
194
- gamutMapOKLCH(tmp, DisplayP3Gamut, DisplayP3, tmp);
195
- }
196
- }
197
- }
198
- );
199
-
200
- function print(label) {
201
- console.log("%s --", label);
202
- console.log("Colorjs.io: %s ms", elapsedColorjs.toFixed(2));
203
- console.log("Ours: %s ms", elapsedOurs.toFixed(2));
204
- if (elapsedColorjs > elapsedOurs)
205
- console.log(
206
- "Speedup: %sx faster",
207
- (elapsedColorjs / elapsedOurs).toFixed(1)
208
- );
209
- else
210
- console.log(
211
- "Slowdown: %sx slower",
212
- (elapsedOurs / elapsedColorjs).toFixed(1)
213
- );
214
- console.log();
215
- }
216
-
217
- function compare(label, colorJSFn, ourFn) {
218
- now = performance.now();
219
- colorJSFn();
220
- elapsedColorjs = performance.now() - now;
221
-
222
- now = performance.now();
223
- ourFn();
224
- elapsedOurs = performance.now() - now;
225
-
226
- print(label);
227
- }
@@ -1,148 +0,0 @@
1
- import {
2
- convert,
3
- OKLCH,
4
- sRGB,
5
- sRGBGamut,
6
- listColorSpaces,
7
- DisplayP3Gamut,
8
- DisplayP3,
9
- gamutMapOKLCH,
10
- constrainAngle,
11
- findCuspOKLCH,
12
- degToRad,
13
- MapToCuspL,
14
- OKHSL,
15
- OKLab,
16
- OKHSLToOKLab,
17
- } from "../src/index.js";
18
-
19
- import { p3, toGamut, oklch, okhsl, converter } from "culori";
20
-
21
- const N = 256 * 256;
22
- const gamut = DisplayP3Gamut;
23
- const target = gamut.space;
24
-
25
- // get N OKLCH in-gamut pixels by sampling uniformly from OKHSL cube
26
- const oklchPixelsInGamut = Array(N)
27
- .fill()
28
- .map((_, i, lst) => {
29
- const t = i / (lst.length - 1);
30
- // note if we use the standard OKHSL space, it is bound to sRGB gamut...
31
- // so instead we use the OKHSLToOKLab function and pass P3 gamut
32
- const okhsl = [t * 360, t, t];
33
- const oklab = OKHSLToOKLab(okhsl, gamut);
34
- return convert(oklab, OKLab, OKLCH);
35
- });
36
-
37
- // get N OKLCH pixels by sampling OKLab, many will be out of srgb gamut
38
- const oklchPixelsRandom = Array(N)
39
- .fill()
40
- .map((_, i, lst) => {
41
- const t = i / (lst.length - 1);
42
- return convert([t, t * 2 - 1, t * 2 - 1], OKLab, OKLCH);
43
- });
44
-
45
- const toP3Gamut = toGamut("p3", "oklch");
46
- // same perf as p3() it seems
47
- // const p3Converter = converter("p3");
48
-
49
- test(oklchPixelsInGamut, "Random Sampling in P3 Gamut");
50
- test(oklchPixelsRandom, "Random Sampling in OKLab L Planes");
51
- test(oklchPixelsRandom, "Random Sampling in OKLab L Planes", true);
52
-
53
- function test(inputPixelsOKLCH, label, fixedCusp) {
54
- let cuspMap;
55
- if (fixedCusp) {
56
- cuspMap = new Array(360).fill(null);
57
- inputPixelsOKLCH = inputPixelsOKLCH.map((oklch) => {
58
- const H = constrainAngle(Math.round(oklch[2]));
59
- oklch = oklch.slice();
60
- oklch[2] = H;
61
- if (!cuspMap[H]) {
62
- const Hr = degToRad(H);
63
- const a = Math.cos(Hr);
64
- const b = Math.sin(Hr);
65
- cuspMap[H] = findCuspOKLCH(a, b, gamut);
66
- }
67
- return oklch;
68
- });
69
- }
70
-
71
- console.log(
72
- "Testing with input type: %s%s",
73
- label,
74
- fixedCusp ? " (Fixed Cusp)" : ""
75
- );
76
- const culoriInputsOKLCH = inputPixelsOKLCH.map(([l, c, h]) => {
77
- return {
78
- mode: "oklch",
79
- l,
80
- c,
81
- h,
82
- };
83
- });
84
-
85
- let now, elapsedCulori, elapsedOurs;
86
- let tmp = [0, 0, 0];
87
-
88
- //// conversion
89
-
90
- tmp = [0, 0, 0];
91
- now = performance.now();
92
- for (let oklch of inputPixelsOKLCH) {
93
- convert(oklch, OKLCH, target, tmp);
94
- }
95
- elapsedOurs = performance.now() - now;
96
-
97
- now = performance.now();
98
- for (let oklchColor of culoriInputsOKLCH) {
99
- p3(oklchColor);
100
-
101
- // same perf ?
102
- // p3Converter(oklchColor);
103
- }
104
- elapsedCulori = performance.now() - now;
105
- print("Conversion OKLCH to P3");
106
-
107
- //// gamut
108
-
109
- tmp = [0, 0, 0];
110
- if (fixedCusp && cuspMap) {
111
- now = performance.now();
112
- for (let oklch of inputPixelsOKLCH) {
113
- const cusp = cuspMap[oklch[2]];
114
- gamutMapOKLCH(oklch, gamut, target, tmp, undefined, cusp);
115
- }
116
- elapsedOurs = performance.now() - now;
117
- } else {
118
- now = performance.now();
119
- for (let oklch of inputPixelsOKLCH) {
120
- gamutMapOKLCH(oklch, gamut, target, tmp);
121
- }
122
- elapsedOurs = performance.now() - now;
123
- }
124
-
125
- now = performance.now();
126
- for (let oklchColor of culoriInputsOKLCH) {
127
- toP3Gamut(oklchColor);
128
- }
129
- elapsedCulori = performance.now() - now;
130
- print("Gamut Mapping OKLCH to P3 Gamut");
131
-
132
- function print(label) {
133
- console.log("%s --", label);
134
- console.log("Culori: %s ms", elapsedCulori.toFixed(2));
135
- console.log("Ours: %s ms", elapsedOurs.toFixed(2));
136
- if (elapsedCulori > elapsedOurs)
137
- console.log(
138
- "Speedup: %sx faster",
139
- (elapsedCulori / elapsedOurs).toFixed(1)
140
- );
141
- else
142
- console.log(
143
- "Slowdown: %sx slower",
144
- (elapsedOurs / elapsedCulori).toFixed(1)
145
- );
146
- console.log();
147
- }
148
- }
@@ -1,51 +0,0 @@
1
- import {
2
- convert,
3
- OKLCH,
4
- sRGB,
5
- sRGBGamut,
6
- listColorSpaces,
7
- gamutMapOKLCH,
8
- } from "../src/index.js";
9
-
10
- const spaces = listColorSpaces().filter((f) => !/ok(hsv|hsl)/i.test(f.id));
11
-
12
- const vecs = Array(128 * 128)
13
- .fill()
14
- .map((_, i, lst) => {
15
- const t = i / (lst.length - 1);
16
- return (
17
- Array(3)
18
- .fill()
19
- // -0.5 .. 1.5
20
- .map(() => t + (t * 2 - 1) * 0.5)
21
- );
22
- });
23
-
24
- const tmp = [0, 0, 0];
25
-
26
- // console.time("bench");
27
-
28
- for (let vec of vecs) {
29
- for (let i = 0; i < spaces.length; i++) {
30
- for (let j = 0; j < spaces.length; j++) {
31
- const a = spaces[i];
32
- const b = spaces[j];
33
-
34
- // convert A to B
35
- convert(vec, a, b, tmp);
36
- // convert B to OKLCH
37
- convert(tmp, b, OKLCH, tmp);
38
- // gamut map OKLCH
39
- gamutMapOKLCH(tmp, sRGBGamut, sRGB, tmp);
40
- }
41
- }
42
- }
43
-
44
- // benchmark for EOK
45
- // for (let i = 0; i < 1000; i++) {
46
- // for (let vec of vecs) {
47
- // deltaEOK(vec, [0, 0.25, 1]);
48
- // }
49
- // }
50
-
51
- // console.timeEnd("bench");
@@ -1,12 +0,0 @@
1
- // To test @texel/color (~3.5 kb)
2
- import * as colors from "../src/index.js";
3
- const rgb = colors.convert([0.5, 0.15, 30], colors.OKLCH, colors.sRGB);
4
- console.log(rgb);
5
-
6
- // To test colorjs.io (~55.3 kb)
7
- // import Color from "colorjs.io";
8
- // console.log(new Color("oklch", [0.5, 0.15, 30]).to("srgb").coords);
9
-
10
- // To test Culori (~43.2 kb)
11
- // import { rgb } from "culori";
12
- // console.log(rgb({ mode: "oklch", l: 0.5, c: 0.15, h: 30 }));
@@ -1,212 +0,0 @@
1
- import canvasSketch from "canvas-sketch";
2
- import {
3
- findCuspOKLCH,
4
- gamutMapOKLCH,
5
- MapToAdaptiveCuspL,
6
- A98RGBGamut,
7
- convert,
8
- DisplayP3Gamut,
9
- OKLCH,
10
- Rec2020Gamut,
11
- sRGBGamut,
12
- degToRad,
13
- constrainAngle,
14
- floatToByte,
15
- isRGBInGamut,
16
- clampedRGB,
17
- } from "../src/index.js";
18
- import arrayAlmostEqual from "./almost-equal.js";
19
-
20
- const settings = {
21
- dimensions: [768, 768],
22
- animate: false,
23
- playbackRate: "throttle",
24
- fps: 2,
25
- attributes: {
26
- colorSpace: "display-p3",
27
- },
28
- };
29
-
30
- const sketch = ({ width, height }) => {
31
- return ({ context, width, height, frame, playhead }) => {
32
- const { colorSpace = "srgb" } = context.getContextAttributes();
33
- const gamut = colorSpace === "srgb" ? sRGBGamut : DisplayP3Gamut;
34
- const mapping = MapToAdaptiveCuspL;
35
-
36
- context.fillStyle = "gray";
37
- context.fillRect(0, 0, width, height);
38
- const H = 264.1;
39
- // const H = constrainAngle((frame * 45) / 2);
40
-
41
- // console.time("map");
42
- // console.profile("map");
43
- const tmp = [0, 0, 0];
44
- const pixels = new Uint8ClampedArray(width * height * 4).fill(0xff);
45
- for (let y = 0; y < height; y++) {
46
- for (let x = 0; x < width; x++) {
47
- const u = x / width;
48
- const v = y / height;
49
-
50
- const [L, C] = UVtoLC([u, v]);
51
-
52
- let oklch = [L, C, H];
53
- let rgb = convert(oklch, OKLCH, gamut.space, tmp);
54
- if (!isRGBInGamut(rgb, 0)) {
55
- rgb[0] = 0.25;
56
- rgb[1] = 0.25;
57
- rgb[2] = 0.25;
58
- // if we wanted to fill the whole space with mapped colors
59
- // rgb = gamutMapOKLCH(oklch, gamut, sRGB, tmp, mapping);
60
- }
61
-
62
- const idx = x + y * width;
63
- pixels[idx * 4 + 0] = floatToByte(rgb[0]);
64
- pixels[idx * 4 + 1] = floatToByte(rgb[1]);
65
- pixels[idx * 4 + 2] = floatToByte(rgb[2]);
66
- }
67
- }
68
- context.putImageData(
69
- new ImageData(pixels, width, height, { colorSpace }),
70
- 0,
71
- 0
72
- );
73
- // console.profileEnd("map");
74
- // console.timeEnd("map");
75
-
76
- const A = [1, 0];
77
- const B = [0, 0];
78
- const lineWidth = width * 0.003;
79
- context.lineWidth = lineWidth;
80
-
81
- const gamuts = [
82
- { defaultColor: "yellow", gamut: sRGBGamut },
83
- { defaultColor: "palegreen", gamut: DisplayP3Gamut },
84
- { defaultColor: "red", gamut: Rec2020Gamut },
85
- { defaultColor: "pink", gamut: A98RGBGamut },
86
- ];
87
-
88
- const hueAngle = degToRad(H);
89
- const a = Math.cos(hueAngle);
90
- const b = Math.sin(hueAngle);
91
-
92
- for (let { gamut: dispGamut, defaultColor } of gamuts) {
93
- const gamutCusp = findCuspOKLCH(a, b, dispGamut);
94
- const gamutTri = [A, gamutCusp, B];
95
- drawLCTriangle(
96
- context,
97
- gamutTri,
98
- gamut === dispGamut ? "white" : defaultColor
99
- );
100
- }
101
-
102
- context.strokeStyle = "white";
103
-
104
- const steps = 64;
105
- for (let i = 0; i < steps; i++) {
106
- // get some LC point that is very likely to be out of gamut
107
- const ox = 0.5;
108
- const oy = 0.5;
109
- const r = 1;
110
- const t = (i / steps) * degToRad(360) + degToRad(-180);
111
- const xy = [Math.cos(t) * r + ox, Math.sin(t) * r + oy];
112
- const [L, C] = UVtoLC(xy);
113
- const oklch = [L, C, H];
114
- const lc = oklch.slice(0, 2);
115
-
116
- const mapped = gamutMapOKLCH(oklch, gamut, OKLCH, tmp, mapping);
117
-
118
- const radius = width * 0.01;
119
- const didChange = !arrayAlmostEqual(mapped, oklch);
120
- if (didChange) {
121
- context.globalAlpha = 0.5;
122
- drawLCPoint(context, lc, radius / 2, "white");
123
- context.beginPath();
124
- context.lineTo(...LCtoXY(lc));
125
- context.lineTo(...LCtoXY(mapped));
126
- context.stroke();
127
- context.globalAlpha = 1;
128
- drawLCPoint(context, mapped.slice(0, 2), radius / 4, "white");
129
- } else {
130
- drawLCPoint(context, lc, radius);
131
- }
132
- }
133
-
134
- const fontSize = width * 0.03;
135
- const boxHeight = fontSize * gamuts.length;
136
- const pad = width * 0.05;
137
- const padleft = width * 0.1;
138
- context.fillStyle = "black";
139
-
140
- for (let i = 0; i < gamuts.length; i++) {
141
- const { gamut: dispGamut, defaultColor } = gamuts[i];
142
- const curColor = dispGamut === gamut ? "white" : defaultColor;
143
-
144
- context.font = `${fontSize}px monospace`;
145
- context.textAlign = "right";
146
- context.textBaseline = "top";
147
- context.fillStyle = curColor;
148
- const x = width - pad - padleft;
149
- const y = height - boxHeight + i * fontSize - pad;
150
- context.fillText(dispGamut.space.id, x, y);
151
- context.beginPath();
152
- context.lineTo(x + fontSize / 2, y + fontSize * 0.4);
153
- context.lineTo(x + padleft, y + fontSize * 0.4);
154
- context.strokeStyle = curColor;
155
- context.stroke();
156
- }
157
-
158
- context.fillStyle = "white";
159
- context.fillText(
160
- `Hue: ${H.toFixed(0)}º`,
161
- width - pad,
162
- height - pad - boxHeight - fontSize * 2
163
- );
164
- };
165
-
166
- function LCtoXY(okLC) {
167
- const x = (okLC[1] / 1) * width;
168
- const y = (1 - okLC[0]) * height;
169
- return [x, y];
170
- }
171
-
172
- function XYtoLC(xy) {
173
- return UVtoLC([xy[0] / width, xy[1] / height]);
174
- }
175
-
176
- function UVtoLC(xy) {
177
- const L = 1 - xy[1];
178
- const C = xy[0] * 1;
179
- return [L, C];
180
- }
181
-
182
- function drawLCTriangle(context, triangle, color = "white") {
183
- context.beginPath();
184
- triangle.forEach((oklch) => {
185
- const [x, y] = LCtoXY(oklch);
186
- context.lineTo(x, y);
187
- });
188
- context.closePath();
189
- context.strokeStyle = color;
190
- context.stroke();
191
- }
192
-
193
- function drawLCPoint(
194
- context,
195
- okLC,
196
- radius = width * 0.01,
197
- color = "white",
198
- fill = true
199
- ) {
200
- context.beginPath();
201
- context.arc(...LCtoXY(okLC), radius, 0, Math.PI * 2);
202
- if (fill) {
203
- context.fillStyle = color;
204
- context.fill();
205
- } else {
206
- context.strokeStyle = color;
207
- context.stroke();
208
- }
209
- }
210
- };
211
-
212
- canvasSketch(sketch, settings);