@pixerate/editor 0.113.0 → 0.114.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/THIRD_PARTY_NOTICES.md +5 -0
- package/dist/animation-editor/index.cjs +60 -54
- package/dist/animation-editor/index.cjs.map +1 -1
- package/dist/animation-editor/index.d.cts +3 -3
- package/dist/animation-editor/index.d.ts +3 -3
- package/dist/animation-editor/index.js +14 -8
- package/dist/animation-editor/index.js.map +1 -1
- package/dist/builtin-CY97ppKM.d.cts +354 -0
- package/dist/builtin-CY97ppKM.d.ts +354 -0
- package/dist/canvas/index.cjs +3 -3
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.js +9 -9
- package/dist/chunk-26PGXHY7.cjs +753 -0
- package/dist/chunk-26PGXHY7.cjs.map +1 -0
- package/dist/chunk-2OJACTPR.cjs +929 -0
- package/dist/chunk-2OJACTPR.cjs.map +1 -0
- package/dist/chunk-3UFGNHSP.js +929 -0
- package/dist/chunk-3UFGNHSP.js.map +1 -0
- package/dist/{chunk-YGIMK4NX.cjs → chunk-5OVJHMZQ.cjs} +757 -825
- package/dist/chunk-5OVJHMZQ.cjs.map +1 -0
- package/dist/chunk-643B2HKC.cjs +534 -0
- package/dist/chunk-643B2HKC.cjs.map +1 -0
- package/dist/chunk-6FCKOS2B.js +534 -0
- package/dist/chunk-6FCKOS2B.js.map +1 -0
- package/dist/chunk-BRLYOPLT.js +2574 -0
- package/dist/chunk-BRLYOPLT.js.map +1 -0
- package/dist/{chunk-LQEEDHOP.js → chunk-C6C5PDKU.js} +759 -761
- package/dist/chunk-C6C5PDKU.js.map +1 -0
- package/dist/{chunk-4KLT7ZYS.cjs → chunk-DFQEX3AM.cjs} +29 -29
- package/dist/{chunk-4KLT7ZYS.cjs.map → chunk-DFQEX3AM.cjs.map} +1 -1
- package/dist/{chunk-CZOXTEMN.js → chunk-EUVSQ25B.js} +336 -404
- package/dist/chunk-EUVSQ25B.js.map +1 -0
- package/dist/chunk-G5NMLUVF.cjs +2574 -0
- package/dist/chunk-G5NMLUVF.cjs.map +1 -0
- package/dist/{chunk-LPYOIVLA.js → chunk-LKCYANMW.js} +3 -3
- package/dist/{chunk-W3GXSUSQ.cjs → chunk-LPLCWZTM.cjs} +5087 -1283
- package/dist/chunk-LPLCWZTM.cjs.map +1 -0
- package/dist/chunk-MMVHBKMM.js +753 -0
- package/dist/chunk-MMVHBKMM.js.map +1 -0
- package/dist/{chunk-POGBUYWE.cjs → chunk-ST7E3NEE.cjs} +857 -859
- package/dist/chunk-ST7E3NEE.cjs.map +1 -0
- package/dist/{chunk-7VOHOPLW.cjs → chunk-TCKIXI46.cjs} +59 -59
- package/dist/{chunk-7VOHOPLW.cjs.map → chunk-TCKIXI46.cjs.map} +1 -1
- package/dist/{chunk-XIOMMGPD.cjs → chunk-TFOPHZ3U.cjs} +1 -1
- package/dist/chunk-TFOPHZ3U.cjs.map +1 -0
- package/dist/{chunk-Z4BC6M74.js → chunk-UBIXVXPR.js} +2 -2
- package/dist/{chunk-BLWCAQCW.js → chunk-VXR7XWMF.js} +4574 -770
- package/dist/chunk-VXR7XWMF.js.map +1 -0
- package/dist/{chunk-HNS7UHCM.js → chunk-XK5LZKBD.js} +1 -1
- package/dist/chunk-XK5LZKBD.js.map +1 -0
- package/dist/cms/index.cjs +146 -0
- package/dist/cms/index.cjs.map +1 -0
- package/dist/cms/index.d.cts +51 -0
- package/dist/cms/index.d.ts +51 -0
- package/dist/cms/index.js +146 -0
- package/dist/cms/index.js.map +1 -0
- package/dist/{export-DIkoK8-r.d.ts → export-_1dUKwpF.d.ts} +217 -191
- package/dist/{export-CEXEzqBf.d.cts → export-b6NjCZ5-.d.cts} +217 -191
- package/dist/image-editor/index.cjs +1779 -1280
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +77 -93
- package/dist/image-editor/index.d.ts +77 -93
- package/dist/image-editor/index.js +740 -241
- package/dist/image-editor/index.js.map +1 -1
- package/dist/opentype-BZEOSOX3.d.ts +188 -0
- package/dist/opentype-Dpe75vL5.d.cts +188 -0
- package/dist/paint/index.cjs +4 -4
- package/dist/paint/index.d.cts +2 -2
- package/dist/paint/index.d.ts +2 -2
- package/dist/paint/index.js +3 -3
- package/dist/paint-editor/index.cjs +146 -145
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +9 -8
- package/dist/paint-editor/index.d.ts +9 -8
- package/dist/paint-editor/index.js +6 -5
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/raster/index.cjs +53 -3
- package/dist/raster/index.cjs.map +1 -1
- package/dist/raster/index.d.cts +62 -91
- package/dist/raster/index.d.ts +62 -91
- package/dist/raster/index.js +59 -9
- package/dist/{render-BEriFuqK.d.cts → render-B1K_OoLy.d.cts} +87 -2
- package/dist/{render-tjzKaik-.d.ts → render-CrmHOgpo.d.ts} +87 -2
- package/dist/{retouch-CEIKR22W.d.cts → retouch-Bxkuc0KH.d.cts} +1 -1
- package/dist/{retouch-BXmeSCaB.d.ts → retouch-Do6tBs2H.d.ts} +1 -1
- package/dist/{surface-C8RRg_Bh.d.cts → surface-Cn30xIV6.d.cts} +6 -1
- package/dist/{surface-C8RRg_Bh.d.ts → surface-Cn30xIV6.d.ts} +6 -1
- package/dist/{svg-D_cfkhlB.d.cts → svg-BnRj0q8i.d.cts} +16 -4
- package/dist/{svg-wIaKLrU0.d.ts → svg-CmmBFfGT.d.ts} +16 -4
- package/dist/{types-DtL_qLFz.d.ts → types-1SUQKDAw.d.ts} +273 -4
- package/dist/{types-DyQq-WPY.d.cts → types-yINF17Ta.d.cts} +273 -4
- package/dist/vector/index.cjs +12170 -7343
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +1579 -16
- package/dist/vector/index.d.ts +1579 -16
- package/dist/vector/index.js +6303 -1476
- package/dist/vector/index.js.map +1 -1
- package/dist/warp-B39VsOtd.d.cts +348 -0
- package/dist/warp-DMN30aY6.d.ts +348 -0
- package/dist/zlib-dxQdgHyN.d.cts +95 -0
- package/dist/zlib-dxQdgHyN.d.ts +95 -0
- package/package.json +6 -1
- package/skills/pixerate-image-editor/SKILL.md +12 -36
- package/skills/pixerate-image-editor/references/color-management.md +37 -0
- package/skills/pixerate-image-editor/references/photoshop-files.md +39 -0
- package/skills/pixerate-vector/SKILL.md +54 -2
- package/dist/chunk-BLWCAQCW.js.map +0 -1
- package/dist/chunk-CZOXTEMN.js.map +0 -1
- package/dist/chunk-HNS7UHCM.js.map +0 -1
- package/dist/chunk-JLYX4V57.cjs +0 -2001
- package/dist/chunk-JLYX4V57.cjs.map +0 -1
- package/dist/chunk-KB2YBCVS.js +0 -2001
- package/dist/chunk-KB2YBCVS.js.map +0 -1
- package/dist/chunk-LQEEDHOP.js.map +0 -1
- package/dist/chunk-POGBUYWE.cjs.map +0 -1
- package/dist/chunk-W3GXSUSQ.cjs.map +0 -1
- package/dist/chunk-XIOMMGPD.cjs.map +0 -1
- package/dist/chunk-YGIMK4NX.cjs.map +0 -1
- package/dist/homography-CZ-tFjay.d.cts +0 -50
- package/dist/homography-Ctfjoc1b.d.ts +0 -50
- package/dist/warp-CWHZzE3x.d.ts +0 -118
- package/dist/warp-D5jz_tN7.d.cts +0 -118
- /package/dist/{chunk-LPYOIVLA.js.map → chunk-LKCYANMW.js.map} +0 -0
- /package/dist/{chunk-Z4BC6M74.js.map → chunk-UBIXVXPR.js.map} +0 -0
|
@@ -0,0 +1,2574 @@
|
|
|
1
|
+
import {
|
|
2
|
+
deflate,
|
|
3
|
+
inflate
|
|
4
|
+
} from "./chunk-6FCKOS2B.js";
|
|
5
|
+
|
|
6
|
+
// src/cms/math.ts
|
|
7
|
+
var D50 = [0.9642, 1, 0.8249];
|
|
8
|
+
var D65_XY = [0.3127, 0.329];
|
|
9
|
+
var IDENTITY = [
|
|
10
|
+
[1, 0, 0],
|
|
11
|
+
[0, 1, 0],
|
|
12
|
+
[0, 0, 1]
|
|
13
|
+
];
|
|
14
|
+
function mul(a, b) {
|
|
15
|
+
const o = [
|
|
16
|
+
[0, 0, 0],
|
|
17
|
+
[0, 0, 0],
|
|
18
|
+
[0, 0, 0]
|
|
19
|
+
];
|
|
20
|
+
for (let i = 0; i < 3; i++) for (let j = 0; j < 3; j++) o[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j] + a[i][2] * b[2][j];
|
|
21
|
+
return o;
|
|
22
|
+
}
|
|
23
|
+
function apply(m, v) {
|
|
24
|
+
return [m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2], m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2], m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2]];
|
|
25
|
+
}
|
|
26
|
+
function det(m) {
|
|
27
|
+
return m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) - m[0][1] * (m[1][0] * m[2][2] - m[1][2] * m[2][0]) + m[0][2] * (m[1][0] * m[2][1] - m[1][1] * m[2][0]);
|
|
28
|
+
}
|
|
29
|
+
function invert(m) {
|
|
30
|
+
const d = det(m);
|
|
31
|
+
if (!Number.isFinite(d) || Math.abs(d) < 1e-12) return null;
|
|
32
|
+
const k = 1 / d;
|
|
33
|
+
return [
|
|
34
|
+
[(m[1][1] * m[2][2] - m[1][2] * m[2][1]) * k, (m[0][2] * m[2][1] - m[0][1] * m[2][2]) * k, (m[0][1] * m[1][2] - m[0][2] * m[1][1]) * k],
|
|
35
|
+
[(m[1][2] * m[2][0] - m[1][0] * m[2][2]) * k, (m[0][0] * m[2][2] - m[0][2] * m[2][0]) * k, (m[0][2] * m[1][0] - m[0][0] * m[1][2]) * k],
|
|
36
|
+
[(m[1][0] * m[2][1] - m[1][1] * m[2][0]) * k, (m[0][1] * m[2][0] - m[0][0] * m[2][1]) * k, (m[0][0] * m[1][1] - m[0][1] * m[1][0]) * k]
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
function xyToXyz(xy) {
|
|
40
|
+
const [x, y] = xy;
|
|
41
|
+
return [x / y, 1, (1 - x - y) / y];
|
|
42
|
+
}
|
|
43
|
+
var BRADFORD = [
|
|
44
|
+
[0.8951, 0.2664, -0.1614],
|
|
45
|
+
[-0.7502, 1.7135, 0.0367],
|
|
46
|
+
[0.0389, -0.0685, 1.0296]
|
|
47
|
+
];
|
|
48
|
+
function bradford(src, dst) {
|
|
49
|
+
const inv = invert(BRADFORD);
|
|
50
|
+
const s = apply(BRADFORD, src);
|
|
51
|
+
const d = apply(BRADFORD, dst);
|
|
52
|
+
const scale = [
|
|
53
|
+
[d[0] / s[0], 0, 0],
|
|
54
|
+
[0, d[1] / s[1], 0],
|
|
55
|
+
[0, 0, d[2] / s[2]]
|
|
56
|
+
];
|
|
57
|
+
return mul(inv, mul(scale, BRADFORD));
|
|
58
|
+
}
|
|
59
|
+
function rgbToXyzMatrix(r, g, b, white) {
|
|
60
|
+
const xr = xyToXyz(r);
|
|
61
|
+
const xg = xyToXyz(g);
|
|
62
|
+
const xb = xyToXyz(b);
|
|
63
|
+
const m = [
|
|
64
|
+
[xr[0], xg[0], xb[0]],
|
|
65
|
+
[xr[1], xg[1], xb[1]],
|
|
66
|
+
[xr[2], xg[2], xb[2]]
|
|
67
|
+
];
|
|
68
|
+
const inv = invert(m);
|
|
69
|
+
if (!inv) return null;
|
|
70
|
+
const s = apply(inv, white);
|
|
71
|
+
return [
|
|
72
|
+
[m[0][0] * s[0], m[0][1] * s[1], m[0][2] * s[2]],
|
|
73
|
+
[m[1][0] * s[0], m[1][1] * s[1], m[1][2] * s[2]],
|
|
74
|
+
[m[2][0] * s[0], m[2][1] * s[1], m[2][2] * s[2]]
|
|
75
|
+
];
|
|
76
|
+
}
|
|
77
|
+
var EPS = 216 / 24389;
|
|
78
|
+
var KAPPA = 24389 / 27;
|
|
79
|
+
var labF = (t) => t > EPS ? Math.cbrt(t) : (KAPPA * t + 16) / 116;
|
|
80
|
+
var labFinv = (f) => {
|
|
81
|
+
const f3 = f * f * f;
|
|
82
|
+
return f3 > EPS ? f3 : (116 * f - 16) / KAPPA;
|
|
83
|
+
};
|
|
84
|
+
function xyzToLab(xyz, white = D50) {
|
|
85
|
+
const fx = labF(xyz[0] / white[0]);
|
|
86
|
+
const fy = labF(xyz[1] / white[1]);
|
|
87
|
+
const fz = labF(xyz[2] / white[2]);
|
|
88
|
+
return [116 * fy - 16, 500 * (fx - fy), 200 * (fy - fz)];
|
|
89
|
+
}
|
|
90
|
+
function labToXyz(lab, white = D50) {
|
|
91
|
+
const fy = (lab[0] + 16) / 116;
|
|
92
|
+
const fx = fy + lab[1] / 500;
|
|
93
|
+
const fz = fy - lab[2] / 200;
|
|
94
|
+
return [labFinv(fx) * white[0], labFinv(fy) * white[1], labFinv(fz) * white[2]];
|
|
95
|
+
}
|
|
96
|
+
function deltaE76(a, b) {
|
|
97
|
+
return Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
|
|
98
|
+
}
|
|
99
|
+
var s15f16Round = (v) => Math.round(v * 65536) / 65536;
|
|
100
|
+
var d50Quantized = () => [s15f16Round(D50[0]), s15f16Round(D50[1]), s15f16Round(D50[2])];
|
|
101
|
+
|
|
102
|
+
// src/cms/curve.ts
|
|
103
|
+
var IDENTITY_CURVE = { type: "identity" };
|
|
104
|
+
function srgbTrc() {
|
|
105
|
+
return { type: "parametric", kind: 3, p: [2.4, 1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 0, 0] };
|
|
106
|
+
}
|
|
107
|
+
var gammaCurve = (g) => ({ type: "gamma", gamma: g });
|
|
108
|
+
function paramCount(kind) {
|
|
109
|
+
return kind === 0 ? 1 : kind === 1 ? 3 : kind === 2 ? 4 : kind === 3 ? 5 : 7;
|
|
110
|
+
}
|
|
111
|
+
function isIdentityCurve(c) {
|
|
112
|
+
switch (c.type) {
|
|
113
|
+
case "identity":
|
|
114
|
+
return true;
|
|
115
|
+
case "gamma":
|
|
116
|
+
return Math.abs(c.gamma - 1) < 1e-9;
|
|
117
|
+
case "parametric":
|
|
118
|
+
return c.kind === 0 && Math.abs(c.p[0] - 1) < 1e-9;
|
|
119
|
+
case "table": {
|
|
120
|
+
const n = c.table.length;
|
|
121
|
+
if (n < 2) return true;
|
|
122
|
+
for (let i = 0; i < n; i++) if (Math.abs(c.table[i] - i / (n - 1)) >= 1 / 65535) return false;
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
var mirror = (x, f) => x < 0 ? -f(-x) : f(x);
|
|
128
|
+
var pw = (base, g) => base <= 0 ? 0 : Math.pow(base, g);
|
|
129
|
+
function para(kind, p, x) {
|
|
130
|
+
const [g, a, b, c, d, e, f] = p;
|
|
131
|
+
switch (kind) {
|
|
132
|
+
case 0:
|
|
133
|
+
return mirror(x, (v) => Math.pow(v, g));
|
|
134
|
+
case 1:
|
|
135
|
+
return a !== 0 && x >= -b / a ? pw(a * x + b, g) : 0;
|
|
136
|
+
case 2:
|
|
137
|
+
return a !== 0 && x >= -b / a ? pw(a * x + b, g) + c : c;
|
|
138
|
+
case 3:
|
|
139
|
+
if (x < 0 && b === 0 && d === 0) return -para(kind, p, -x);
|
|
140
|
+
return x >= d ? pw(a * x + b, g) : c * x;
|
|
141
|
+
default:
|
|
142
|
+
return x >= d ? pw(a * x + b, g) + e : c * x + f;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function paraInverse(kind, p, y) {
|
|
146
|
+
const [g, a, b, c, d, e, f] = p;
|
|
147
|
+
if (Math.abs(g) < 1e-12) return null;
|
|
148
|
+
const root = (v) => v <= 0 ? 0 : Math.pow(v, 1 / g);
|
|
149
|
+
switch (kind) {
|
|
150
|
+
case 0:
|
|
151
|
+
return mirror(y, (v) => Math.pow(v, 1 / g));
|
|
152
|
+
case 1:
|
|
153
|
+
if (a === 0) return null;
|
|
154
|
+
return y <= 0 ? -b / a : (root(y) - b) / a;
|
|
155
|
+
case 2:
|
|
156
|
+
if (a === 0) return null;
|
|
157
|
+
return y <= c ? -b / a : (root(y - c) - b) / a;
|
|
158
|
+
case 3: {
|
|
159
|
+
if (a === 0) return null;
|
|
160
|
+
if (y < 0 && b === 0 && d === 0) {
|
|
161
|
+
const v = paraInverse(kind, p, -y);
|
|
162
|
+
return v === null ? null : -v;
|
|
163
|
+
}
|
|
164
|
+
const yd = pw(a * d + b, g);
|
|
165
|
+
if (y >= yd) return (root(y) - b) / a;
|
|
166
|
+
return c !== 0 ? y / c : 0;
|
|
167
|
+
}
|
|
168
|
+
default: {
|
|
169
|
+
if (a === 0) return null;
|
|
170
|
+
const yd = pw(a * d + b, g) + e;
|
|
171
|
+
if (y >= yd) return (root(y - e) - b) / a;
|
|
172
|
+
return c !== 0 ? (y - f) / c : 0;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function numericInverse(f, y) {
|
|
177
|
+
const increasing = f(1) >= f(0);
|
|
178
|
+
let lo = 0;
|
|
179
|
+
let hi = 1;
|
|
180
|
+
for (let k = 0; k < 60; k++) {
|
|
181
|
+
const mid = 0.5 * (lo + hi);
|
|
182
|
+
if (f(mid) < y === increasing) lo = mid;
|
|
183
|
+
else hi = mid;
|
|
184
|
+
}
|
|
185
|
+
return 0.5 * (lo + hi);
|
|
186
|
+
}
|
|
187
|
+
function table(t, x) {
|
|
188
|
+
const n = t.length;
|
|
189
|
+
if (n === 0) return x;
|
|
190
|
+
if (n === 1) return t[0];
|
|
191
|
+
const pos = Math.min(1, Math.max(0, x)) * (n - 1);
|
|
192
|
+
const i = Math.min(n - 2, Math.floor(pos));
|
|
193
|
+
const f = pos - i;
|
|
194
|
+
return t[i] + (t[i + 1] - t[i]) * f;
|
|
195
|
+
}
|
|
196
|
+
function edgeIndex(t, start) {
|
|
197
|
+
const n = t.length;
|
|
198
|
+
if (start) {
|
|
199
|
+
const v2 = t[0];
|
|
200
|
+
let i2 = n - 1;
|
|
201
|
+
for (let k = 0; k < n; k++) {
|
|
202
|
+
if (Math.abs(t[k] - v2) > 1e-7) {
|
|
203
|
+
i2 = k - 1;
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return i2 / (n - 1);
|
|
208
|
+
}
|
|
209
|
+
const v = t[n - 1];
|
|
210
|
+
let i = 0;
|
|
211
|
+
for (let k = n - 1; k >= 0; k--) {
|
|
212
|
+
if (Math.abs(t[k] - v) > 1e-7) {
|
|
213
|
+
i = k + 1;
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return i / (n - 1);
|
|
218
|
+
}
|
|
219
|
+
function tableInverse(t, y) {
|
|
220
|
+
const n = t.length;
|
|
221
|
+
if (n < 2) return y;
|
|
222
|
+
const increasing = t[n - 1] >= t[0];
|
|
223
|
+
const loV = increasing ? t[0] : t[n - 1];
|
|
224
|
+
const hiV = increasing ? t[n - 1] : t[0];
|
|
225
|
+
if (y <= loV) return edgeIndex(t, increasing);
|
|
226
|
+
if (y >= hiV) return edgeIndex(t, !increasing);
|
|
227
|
+
let lo = 0;
|
|
228
|
+
let hi = n - 1;
|
|
229
|
+
while (hi - lo > 1) {
|
|
230
|
+
const mid = lo + hi >> 1;
|
|
231
|
+
if (t[mid] < y === increasing) lo = mid;
|
|
232
|
+
else hi = mid;
|
|
233
|
+
}
|
|
234
|
+
const a = t[lo];
|
|
235
|
+
const b = t[hi];
|
|
236
|
+
const f = Math.abs(b - a) > 1e-12 ? (y - a) / (b - a) : 0.5;
|
|
237
|
+
return (lo + f) / (n - 1);
|
|
238
|
+
}
|
|
239
|
+
function evalCurve(c, x) {
|
|
240
|
+
switch (c.type) {
|
|
241
|
+
case "identity":
|
|
242
|
+
return x;
|
|
243
|
+
case "gamma":
|
|
244
|
+
return mirror(x, (v) => Math.pow(v, c.gamma));
|
|
245
|
+
case "parametric":
|
|
246
|
+
return para(c.kind, c.p, x);
|
|
247
|
+
case "table":
|
|
248
|
+
return table(c.table, x);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function evalCurveInverse(c, y) {
|
|
252
|
+
switch (c.type) {
|
|
253
|
+
case "identity":
|
|
254
|
+
return y;
|
|
255
|
+
case "gamma":
|
|
256
|
+
return Math.abs(c.gamma) < 1e-12 ? y : mirror(y, (v) => Math.pow(v, 1 / c.gamma));
|
|
257
|
+
case "parametric":
|
|
258
|
+
return paraInverse(c.kind, c.p, y) ?? numericInverse((x) => para(c.kind, c.p, x), y);
|
|
259
|
+
case "table":
|
|
260
|
+
return tableInverse(c.table, y);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function sampleCurve(c, n) {
|
|
264
|
+
const out = new Float32Array(n);
|
|
265
|
+
for (let i = 0; i < n; i++) out[i] = evalCurve(c, i / (n - 1));
|
|
266
|
+
return out;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// src/cms/clut.ts
|
|
270
|
+
var MAX_CHANNELS = 15;
|
|
271
|
+
var Clut = class _Clut {
|
|
272
|
+
constructor(grid, outputs, data) {
|
|
273
|
+
this.grid = grid;
|
|
274
|
+
this.outputs = outputs;
|
|
275
|
+
this.data = data;
|
|
276
|
+
const inputs = grid.length;
|
|
277
|
+
if (inputs < 1 || inputs > MAX_CHANNELS || outputs < 1 || outputs > MAX_CHANNELS) throw new RangeError("bad CLUT dimensions");
|
|
278
|
+
this.inputs = inputs;
|
|
279
|
+
this.strides = new Array(inputs).fill(0);
|
|
280
|
+
let s = outputs;
|
|
281
|
+
for (let i = inputs - 1; i >= 0; i--) {
|
|
282
|
+
this.strides[i] = s;
|
|
283
|
+
s *= Math.max(1, grid[i]);
|
|
284
|
+
}
|
|
285
|
+
if (data.length !== s) throw new RangeError(`CLUT data length ${data.length}, expected ${s}`);
|
|
286
|
+
}
|
|
287
|
+
grid;
|
|
288
|
+
outputs;
|
|
289
|
+
data;
|
|
290
|
+
inputs;
|
|
291
|
+
strides;
|
|
292
|
+
get nodes() {
|
|
293
|
+
return this.data.length / this.outputs;
|
|
294
|
+
}
|
|
295
|
+
/** A table sampled from `f` at every node. */
|
|
296
|
+
static sample(grid, outputs, f) {
|
|
297
|
+
const inputs = grid.length;
|
|
298
|
+
const total = grid.reduce((a, b) => a * b, 1);
|
|
299
|
+
const data = new Float32Array(total * outputs);
|
|
300
|
+
const idx = new Array(inputs).fill(0);
|
|
301
|
+
const inp = new Float32Array(inputs);
|
|
302
|
+
for (let node = 0; node < total; node++) {
|
|
303
|
+
let rem = node;
|
|
304
|
+
for (let d = inputs - 1; d >= 0; d--) {
|
|
305
|
+
idx[d] = rem % grid[d];
|
|
306
|
+
rem = Math.floor(rem / grid[d]);
|
|
307
|
+
}
|
|
308
|
+
for (let d = 0; d < inputs; d++) inp[d] = grid[d] > 1 ? idx[d] / (grid[d] - 1) : 0;
|
|
309
|
+
f(inp, data.subarray(node * outputs, (node + 1) * outputs));
|
|
310
|
+
}
|
|
311
|
+
return new _Clut(grid, outputs, data);
|
|
312
|
+
}
|
|
313
|
+
/** Base offset, fraction and stride along input `d` at `v`. */
|
|
314
|
+
locate(d, v, out, k) {
|
|
315
|
+
const n = this.grid[d];
|
|
316
|
+
if (n < 2) {
|
|
317
|
+
out[k] = 0;
|
|
318
|
+
out[k + 1] = 0;
|
|
319
|
+
out[k + 2] = 0;
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
const p = Math.min(1, Math.max(0, v)) * (n - 1);
|
|
323
|
+
const i = Math.min(n - 2, Math.floor(p));
|
|
324
|
+
out[k] = i * this.strides[d];
|
|
325
|
+
out[k + 1] = p - i;
|
|
326
|
+
out[k + 2] = this.strides[d];
|
|
327
|
+
}
|
|
328
|
+
loc = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
329
|
+
lo = new Float32Array(MAX_CHANNELS);
|
|
330
|
+
hi = new Float32Array(MAX_CHANNELS);
|
|
331
|
+
eval(input, out) {
|
|
332
|
+
const L = this.loc;
|
|
333
|
+
switch (this.inputs) {
|
|
334
|
+
case 1: {
|
|
335
|
+
this.locate(0, input[0], L, 0);
|
|
336
|
+
const [b, r, s] = L;
|
|
337
|
+
for (let k = 0; k < this.outputs; k++) {
|
|
338
|
+
const a = this.data[b + k];
|
|
339
|
+
const c = s > 0 ? this.data[b + s + k] : a;
|
|
340
|
+
out[k] = a + (c - a) * r;
|
|
341
|
+
}
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
case 3:
|
|
345
|
+
this.locate(0, input[0], L, 0);
|
|
346
|
+
this.locate(1, input[1], L, 3);
|
|
347
|
+
this.locate(2, input[2], L, 6);
|
|
348
|
+
this.tetra(L[0] + L[3] + L[6], L[2], L[5], L[8], L[1], L[4], L[7], out);
|
|
349
|
+
return;
|
|
350
|
+
case 4: {
|
|
351
|
+
this.locate(0, input[0], L, 0);
|
|
352
|
+
this.locate(1, input[1], L, 3);
|
|
353
|
+
this.locate(2, input[2], L, 6);
|
|
354
|
+
this.locate(3, input[3], L, 9);
|
|
355
|
+
const base = L[0] + L[3] + L[6] + L[9];
|
|
356
|
+
const r0 = L[1];
|
|
357
|
+
const s0 = L[2];
|
|
358
|
+
this.tetra(base, L[5], L[8], L[11], L[4], L[7], L[10], this.lo);
|
|
359
|
+
if (r0 > 0 && s0 > 0) {
|
|
360
|
+
this.tetra(base + s0, L[5], L[8], L[11], L[4], L[7], L[10], this.hi);
|
|
361
|
+
for (let k = 0; k < this.outputs; k++) out[k] = this.lo[k] + (this.hi[k] - this.lo[k]) * r0;
|
|
362
|
+
} else for (let k = 0; k < this.outputs; k++) out[k] = this.lo[k];
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
default:
|
|
366
|
+
this.evalMultilinear(input, out);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/** Tetrahedral interpolation in one cube at `base` with strides `sx, sy, sz` and fractions `rx, ry, rz`. */
|
|
370
|
+
tetra(base, sx, sy, sz, rx, ry, rz, out) {
|
|
371
|
+
const d = this.data;
|
|
372
|
+
let p1, p2, p3, w1, w2, w3;
|
|
373
|
+
const sxy = sx + sy;
|
|
374
|
+
const sall = sxy + sz;
|
|
375
|
+
if (rx >= ry) {
|
|
376
|
+
if (ry >= rz) {
|
|
377
|
+
p1 = sx;
|
|
378
|
+
p2 = sxy;
|
|
379
|
+
p3 = sall;
|
|
380
|
+
w1 = rx;
|
|
381
|
+
w2 = ry;
|
|
382
|
+
w3 = rz;
|
|
383
|
+
} else if (rx >= rz) {
|
|
384
|
+
p1 = sx;
|
|
385
|
+
p2 = sx + sz;
|
|
386
|
+
p3 = sall;
|
|
387
|
+
w1 = rx;
|
|
388
|
+
w2 = rz;
|
|
389
|
+
w3 = ry;
|
|
390
|
+
} else {
|
|
391
|
+
p1 = sz;
|
|
392
|
+
p2 = sx + sz;
|
|
393
|
+
p3 = sall;
|
|
394
|
+
w1 = rz;
|
|
395
|
+
w2 = rx;
|
|
396
|
+
w3 = ry;
|
|
397
|
+
}
|
|
398
|
+
} else if (rx >= rz) {
|
|
399
|
+
p1 = sy;
|
|
400
|
+
p2 = sxy;
|
|
401
|
+
p3 = sall;
|
|
402
|
+
w1 = ry;
|
|
403
|
+
w2 = rx;
|
|
404
|
+
w3 = rz;
|
|
405
|
+
} else if (ry >= rz) {
|
|
406
|
+
p1 = sy;
|
|
407
|
+
p2 = sy + sz;
|
|
408
|
+
p3 = sall;
|
|
409
|
+
w1 = ry;
|
|
410
|
+
w2 = rz;
|
|
411
|
+
w3 = rx;
|
|
412
|
+
} else {
|
|
413
|
+
p1 = sz;
|
|
414
|
+
p2 = sy + sz;
|
|
415
|
+
p3 = sall;
|
|
416
|
+
w1 = rz;
|
|
417
|
+
w2 = ry;
|
|
418
|
+
w3 = rx;
|
|
419
|
+
}
|
|
420
|
+
for (let k = 0; k < this.outputs; k++) {
|
|
421
|
+
const v0 = d[base + k];
|
|
422
|
+
const v1 = d[base + p1 + k];
|
|
423
|
+
const v2 = d[base + p2 + k];
|
|
424
|
+
const v3 = d[base + p3 + k];
|
|
425
|
+
out[k] = v0 + (v1 - v0) * w1 + (v2 - v1) * w2 + (v3 - v2) * w3;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
evalMultilinear(input, out) {
|
|
429
|
+
const n = this.inputs;
|
|
430
|
+
let base = 0;
|
|
431
|
+
const r = new Array(n);
|
|
432
|
+
const s = new Array(n);
|
|
433
|
+
const L = [0, 0, 0];
|
|
434
|
+
for (let d = 0; d < n; d++) {
|
|
435
|
+
this.locate(d, input[d], L, 0);
|
|
436
|
+
base += L[0];
|
|
437
|
+
r[d] = L[1];
|
|
438
|
+
s[d] = L[2];
|
|
439
|
+
}
|
|
440
|
+
const o = this.outputs;
|
|
441
|
+
for (let k = 0; k < o; k++) out[k] = 0;
|
|
442
|
+
for (let corner = 0; corner < 1 << n; corner++) {
|
|
443
|
+
let w = 1;
|
|
444
|
+
let off = base;
|
|
445
|
+
for (let d = 0; d < n; d++) {
|
|
446
|
+
if (corner & 1 << d) {
|
|
447
|
+
w *= r[d];
|
|
448
|
+
off += s[d];
|
|
449
|
+
} else w *= 1 - r[d];
|
|
450
|
+
}
|
|
451
|
+
if (w === 0) continue;
|
|
452
|
+
for (let k = 0; k < o; k++) out[k] += w * this.data[off + k];
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// src/cms/pipeline.ts
|
|
458
|
+
var MAX_PIPE = 16;
|
|
459
|
+
var XYZ_SCALE = 65535 / 32768;
|
|
460
|
+
var V2_L = 100 * 65535 / 65280;
|
|
461
|
+
var V2_AB = 255 * 65535 / 65280;
|
|
462
|
+
var clamp01 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
463
|
+
function matrix3(m) {
|
|
464
|
+
return { kind: "matrix", rows: 3, cols: 3, m: [...m[0], ...m[1], ...m[2]], offset: [0, 0, 0] };
|
|
465
|
+
}
|
|
466
|
+
function stageOutputs(s, n) {
|
|
467
|
+
return s.kind === "matrix" ? s.rows : s.kind === "clut" ? s.clut.outputs : n;
|
|
468
|
+
}
|
|
469
|
+
var EPS2 = 216 / 24389;
|
|
470
|
+
var KAPPA2 = 24389 / 27;
|
|
471
|
+
function runStage(s, v, n, tmp, tmpIn, tmpOut) {
|
|
472
|
+
switch (s.kind) {
|
|
473
|
+
case "curves":
|
|
474
|
+
for (let k = 0; k < s.curves.length; k++) v[k] = evalCurve(s.curves[k], v[k]);
|
|
475
|
+
return n;
|
|
476
|
+
case "inv-curves":
|
|
477
|
+
for (let k = 0; k < s.curves.length; k++) v[k] = evalCurveInverse(s.curves[k], v[k]);
|
|
478
|
+
return n;
|
|
479
|
+
case "matrix": {
|
|
480
|
+
for (let r = 0; r < s.rows; r++) {
|
|
481
|
+
let acc = s.offset[r];
|
|
482
|
+
for (let c = 0; c < s.cols; c++) acc += s.m[r * s.cols + c] * v[c];
|
|
483
|
+
tmp[r] = acc;
|
|
484
|
+
}
|
|
485
|
+
for (let r = 0; r < s.rows; r++) v[r] = tmp[r];
|
|
486
|
+
return s.rows;
|
|
487
|
+
}
|
|
488
|
+
case "clut": {
|
|
489
|
+
const c = s.clut;
|
|
490
|
+
for (let k = 0; k < c.inputs; k++) tmpIn[k] = v[k];
|
|
491
|
+
c.eval(tmpIn, tmpOut);
|
|
492
|
+
for (let k = 0; k < c.outputs; k++) v[k] = tmpOut[k];
|
|
493
|
+
return c.outputs;
|
|
494
|
+
}
|
|
495
|
+
case "lab-to-xyz": {
|
|
496
|
+
const fy = (v[0] + 16) / 116;
|
|
497
|
+
const fx = fy + v[1] / 500;
|
|
498
|
+
const fz = fy - v[2] / 200;
|
|
499
|
+
const finv = (f) => {
|
|
500
|
+
const f3 = f * f * f;
|
|
501
|
+
return f3 > EPS2 ? f3 : (116 * f - 16) / KAPPA2;
|
|
502
|
+
};
|
|
503
|
+
v[0] = finv(fx) * D50[0];
|
|
504
|
+
v[1] = finv(fy) * D50[1];
|
|
505
|
+
v[2] = finv(fz) * D50[2];
|
|
506
|
+
return 3;
|
|
507
|
+
}
|
|
508
|
+
case "xyz-to-lab": {
|
|
509
|
+
const f = (t) => t > EPS2 ? Math.cbrt(t) : (KAPPA2 * t + 16) / 116;
|
|
510
|
+
const fx = f(Math.max(0, v[0]) / D50[0]);
|
|
511
|
+
const fy = f(Math.max(0, v[1]) / D50[1]);
|
|
512
|
+
const fz = f(Math.max(0, v[2]) / D50[2]);
|
|
513
|
+
v[0] = 116 * fy - 16;
|
|
514
|
+
v[1] = 500 * (fx - fy);
|
|
515
|
+
v[2] = 200 * (fy - fz);
|
|
516
|
+
return 3;
|
|
517
|
+
}
|
|
518
|
+
case "decode-lab-v4":
|
|
519
|
+
v[0] *= 100;
|
|
520
|
+
v[1] = v[1] * 255 - 128;
|
|
521
|
+
v[2] = v[2] * 255 - 128;
|
|
522
|
+
return 3;
|
|
523
|
+
case "encode-lab-v4":
|
|
524
|
+
v[0] = clamp01(v[0] / 100);
|
|
525
|
+
v[1] = clamp01((v[1] + 128) / 255);
|
|
526
|
+
v[2] = clamp01((v[2] + 128) / 255);
|
|
527
|
+
return 3;
|
|
528
|
+
case "decode-lab-v2":
|
|
529
|
+
v[0] *= V2_L;
|
|
530
|
+
v[1] = v[1] * V2_AB - 128;
|
|
531
|
+
v[2] = v[2] * V2_AB - 128;
|
|
532
|
+
return 3;
|
|
533
|
+
case "encode-lab-v2":
|
|
534
|
+
v[0] = clamp01(v[0] / V2_L);
|
|
535
|
+
v[1] = clamp01((v[1] + 128) / V2_AB);
|
|
536
|
+
v[2] = clamp01((v[2] + 128) / V2_AB);
|
|
537
|
+
return 3;
|
|
538
|
+
case "decode-xyz":
|
|
539
|
+
for (let k = 0; k < 3; k++) v[k] *= XYZ_SCALE;
|
|
540
|
+
return 3;
|
|
541
|
+
case "encode-xyz":
|
|
542
|
+
for (let k = 0; k < 3; k++) v[k] = clamp01(v[k] / XYZ_SCALE);
|
|
543
|
+
return 3;
|
|
544
|
+
case "clamp01":
|
|
545
|
+
for (let k = 0; k < n; k++) v[k] = clamp01(v[k]);
|
|
546
|
+
return n;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
var Pipeline = class {
|
|
550
|
+
constructor(inputs, stages) {
|
|
551
|
+
this.inputs = inputs;
|
|
552
|
+
this.stages = stages;
|
|
553
|
+
this.outputs = stages.reduce((n, s) => stageOutputs(s, n), inputs);
|
|
554
|
+
}
|
|
555
|
+
inputs;
|
|
556
|
+
stages;
|
|
557
|
+
outputs;
|
|
558
|
+
v = new Float64Array(MAX_PIPE);
|
|
559
|
+
tmp = new Float64Array(MAX_PIPE);
|
|
560
|
+
tmpIn = new Float32Array(MAX_PIPE);
|
|
561
|
+
tmpOut = new Float32Array(MAX_PIPE);
|
|
562
|
+
extend(stages) {
|
|
563
|
+
for (const s of stages) {
|
|
564
|
+
this.outputs = stageOutputs(s, this.outputs);
|
|
565
|
+
this.stages.push(s);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
/** Evaluates one colour: `input` has `inputs` values, `out` receives `outputs`. */
|
|
569
|
+
eval(input, out) {
|
|
570
|
+
const v = this.v;
|
|
571
|
+
for (let k = 0; k < this.inputs; k++) v[k] = input[k];
|
|
572
|
+
let n = this.inputs;
|
|
573
|
+
for (const s of this.stages) n = runStage(s, v, n, this.tmp, this.tmpIn, this.tmpOut);
|
|
574
|
+
for (let k = 0; k < this.outputs; k++) out[k] = v[k];
|
|
575
|
+
}
|
|
576
|
+
/** Removes no-op stages, cancels adjacent inverse pairs and merges consecutive 3×3 matrices. */
|
|
577
|
+
optimize() {
|
|
578
|
+
const out = [];
|
|
579
|
+
for (const s of this.stages) {
|
|
580
|
+
const noop = (s.kind === "curves" || s.kind === "inv-curves") && s.curves.every(isIdentityCurve) || s.kind === "matrix" && s.rows === 3 && s.cols === 3 && s.offset.every((o) => Math.abs(o) < 1e-12) && s.m.every((v, i) => Math.abs(v - (i % 4 === 0 ? 1 : 0)) < 1e-12);
|
|
581
|
+
if (noop) continue;
|
|
582
|
+
const last = out[out.length - 1];
|
|
583
|
+
const pair = last ? `${last.kind}>${s.kind}` : "";
|
|
584
|
+
const cancels = [
|
|
585
|
+
"lab-to-xyz>xyz-to-lab",
|
|
586
|
+
"xyz-to-lab>lab-to-xyz",
|
|
587
|
+
"encode-lab-v4>decode-lab-v4",
|
|
588
|
+
"encode-lab-v2>decode-lab-v2",
|
|
589
|
+
"encode-xyz>decode-xyz",
|
|
590
|
+
"decode-lab-v4>encode-lab-v4",
|
|
591
|
+
"decode-lab-v2>encode-lab-v2",
|
|
592
|
+
"decode-xyz>encode-xyz"
|
|
593
|
+
].includes(pair);
|
|
594
|
+
if (cancels) {
|
|
595
|
+
out.pop();
|
|
596
|
+
continue;
|
|
597
|
+
}
|
|
598
|
+
if (last && last.kind === "matrix" && s.kind === "matrix" && last.rows === 3 && last.cols === 3 && s.rows === 3 && s.cols === 3) {
|
|
599
|
+
const a = last.m;
|
|
600
|
+
const b = s.m;
|
|
601
|
+
const m = new Array(9).fill(0);
|
|
602
|
+
const off = [0, 0, 0];
|
|
603
|
+
for (let r = 0; r < 3; r++) {
|
|
604
|
+
for (let c = 0; c < 3; c++) for (let k = 0; k < 3; k++) m[r * 3 + c] += b[r * 3 + k] * a[k * 3 + c];
|
|
605
|
+
off[r] = b[r * 3] * last.offset[0] + b[r * 3 + 1] * last.offset[1] + b[r * 3 + 2] * last.offset[2] + s.offset[r];
|
|
606
|
+
}
|
|
607
|
+
out.pop();
|
|
608
|
+
out.push({ kind: "matrix", rows: 3, cols: 3, m, offset: off });
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
out.push(s);
|
|
612
|
+
}
|
|
613
|
+
this.stages = out;
|
|
614
|
+
this.outputs = out.reduce((n, s) => stageOutputs(s, n), this.inputs);
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
// src/cms/profile.ts
|
|
619
|
+
var INTENTS = ["perceptual", "relative", "saturation", "absolute"];
|
|
620
|
+
var INTENT_CODES = { perceptual: 0, relative: 1, saturation: 2, absolute: 3 };
|
|
621
|
+
var intentCode = (i) => INTENT_CODES[i];
|
|
622
|
+
var intentFromCode = (n) => INTENTS[n] ?? null;
|
|
623
|
+
var CmsError = class extends Error {
|
|
624
|
+
constructor(message) {
|
|
625
|
+
super(message);
|
|
626
|
+
this.name = "CmsError";
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
var CLASS_SIGS = { scnr: "input", mntr: "display", prtr: "output", link: "link", spac: "colorspace", abst: "abstract", nmcl: "named" };
|
|
630
|
+
var classSig = (c) => Object.entries(CLASS_SIGS).find(([, v]) => v === c)?.[0] ?? "mntr";
|
|
631
|
+
var SPACE_SIGS = {
|
|
632
|
+
"XYZ ": "xyz",
|
|
633
|
+
"Lab ": "lab",
|
|
634
|
+
"Luv ": "luv",
|
|
635
|
+
YCbr: "ycbcr",
|
|
636
|
+
"Yxy ": "yxy",
|
|
637
|
+
"RGB ": "rgb",
|
|
638
|
+
GRAY: "gray",
|
|
639
|
+
"HSV ": "hsv",
|
|
640
|
+
"HLS ": "hls",
|
|
641
|
+
CMYK: "cmyk",
|
|
642
|
+
"CMY ": "cmy"
|
|
643
|
+
};
|
|
644
|
+
function spaceFromSig(s) {
|
|
645
|
+
if (SPACE_SIGS[s]) return SPACE_SIGS[s];
|
|
646
|
+
const m = /^([2-9A-F])CLR$/.exec(s);
|
|
647
|
+
if (m) return `${parseInt(m[1], 16)}clr`;
|
|
648
|
+
return "unknown";
|
|
649
|
+
}
|
|
650
|
+
function spaceSig(c) {
|
|
651
|
+
const hit = Object.entries(SPACE_SIGS).find(([, v]) => v === c);
|
|
652
|
+
if (hit) return hit[0];
|
|
653
|
+
const m = /^(\d+)clr$/.exec(c);
|
|
654
|
+
if (m) return `${Number(m[1]).toString(16).toUpperCase()}CLR`;
|
|
655
|
+
return "RGB ";
|
|
656
|
+
}
|
|
657
|
+
function spaceChannels(c) {
|
|
658
|
+
if (c === "gray") return 1;
|
|
659
|
+
if (c === "cmyk") return 4;
|
|
660
|
+
if (c === "unknown") return 0;
|
|
661
|
+
const m = /^(\d+)clr$/.exec(c);
|
|
662
|
+
return m ? Number(m[1]) : 3;
|
|
663
|
+
}
|
|
664
|
+
function profileChannels(p) {
|
|
665
|
+
return spaceChannels(p.colorSpace);
|
|
666
|
+
}
|
|
667
|
+
var SAME_COLORS_MAX_DELTA_E = 0.5;
|
|
668
|
+
function mediaWhite(p) {
|
|
669
|
+
if (p.version[0] < 4 && p.profileClass === "display") return [D50[0], D50[1], D50[2]];
|
|
670
|
+
return p.whitePoint;
|
|
671
|
+
}
|
|
672
|
+
function isMatrixShaper(p) {
|
|
673
|
+
return p.a2b.every((l) => !l) && (p.matrix !== null && p.trc !== null || p.grayTrc !== null);
|
|
674
|
+
}
|
|
675
|
+
var lutIndex = (intent) => intent === "perceptual" ? 0 : intent === "saturation" ? 2 : 1;
|
|
676
|
+
function lutFor(luts, intent) {
|
|
677
|
+
return luts[lutIndex(intent)] ?? luts[0];
|
|
678
|
+
}
|
|
679
|
+
function supportsIntent(p, intent, input) {
|
|
680
|
+
const luts = input ? p.a2b : p.b2a;
|
|
681
|
+
return Boolean(luts[lutIndex(intent)]) || luts.every((l) => !l);
|
|
682
|
+
}
|
|
683
|
+
function decodePcs(pcs, kind) {
|
|
684
|
+
if (pcs === "xyz") return { kind: "decode-xyz" };
|
|
685
|
+
return kind === "lut16" ? { kind: "decode-lab-v2" } : { kind: "decode-lab-v4" };
|
|
686
|
+
}
|
|
687
|
+
function encodePcs(pcs, kind) {
|
|
688
|
+
if (pcs === "xyz") return { kind: "encode-xyz" };
|
|
689
|
+
return kind === "lut16" ? { kind: "encode-lab-v2" } : { kind: "encode-lab-v4" };
|
|
690
|
+
}
|
|
691
|
+
function deviceToPcs(p, intent) {
|
|
692
|
+
const l = lutFor(p.a2b, intent);
|
|
693
|
+
if (l) return { stages: [...l.stages, decodePcs(p.pcs, l.kind)], pcs: p.pcs };
|
|
694
|
+
switch (p.colorSpace) {
|
|
695
|
+
case "rgb":
|
|
696
|
+
if (!p.matrix || !p.trc) throw new CmsError("RGB profile without matrix/TRC or AToB tags");
|
|
697
|
+
return { stages: [{ kind: "curves", curves: [...p.trc] }, matrix3(p.matrix)], pcs: "xyz" };
|
|
698
|
+
case "gray": {
|
|
699
|
+
if (!p.grayTrc) throw new CmsError("gray profile without kTRC");
|
|
700
|
+
if (p.pcs === "lab") return { stages: [{ kind: "curves", curves: [p.grayTrc] }, { kind: "matrix", rows: 3, cols: 1, m: [100, 0, 0], offset: [0, 0, 0] }], pcs: "lab" };
|
|
701
|
+
return { stages: [{ kind: "curves", curves: [p.grayTrc] }, { kind: "matrix", rows: 3, cols: 1, m: [D50[0], D50[1], D50[2]], offset: [0, 0, 0] }], pcs: "xyz" };
|
|
702
|
+
}
|
|
703
|
+
case "lab":
|
|
704
|
+
return { stages: [{ kind: "decode-lab-v4" }], pcs: "lab" };
|
|
705
|
+
case "xyz":
|
|
706
|
+
return { stages: [{ kind: "decode-xyz" }], pcs: "xyz" };
|
|
707
|
+
default:
|
|
708
|
+
throw new CmsError(`${p.colorSpace} profile without AToB tags`);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
function pcsToDevice(p, intent) {
|
|
712
|
+
const l = lutFor(p.b2a, intent);
|
|
713
|
+
if (l) return { stages: [encodePcs(p.pcs, l.kind), ...l.stages], pcs: p.pcs };
|
|
714
|
+
switch (p.colorSpace) {
|
|
715
|
+
case "rgb": {
|
|
716
|
+
if (!p.matrix || !p.trc) throw new CmsError("RGB profile without matrix/TRC or BToA tags");
|
|
717
|
+
const inv = invert(p.matrix);
|
|
718
|
+
if (!inv) throw new CmsError("singular colorant matrix");
|
|
719
|
+
return { stages: [matrix3(inv), { kind: "inv-curves", curves: [...p.trc] }], pcs: "xyz" };
|
|
720
|
+
}
|
|
721
|
+
case "gray": {
|
|
722
|
+
if (!p.grayTrc) throw new CmsError("gray profile without kTRC");
|
|
723
|
+
if (p.pcs === "lab") return { stages: [{ kind: "matrix", rows: 1, cols: 3, m: [0.01, 0, 0], offset: [0] }, { kind: "inv-curves", curves: [p.grayTrc] }], pcs: "lab" };
|
|
724
|
+
return { stages: [{ kind: "matrix", rows: 1, cols: 3, m: [0, 1, 0], offset: [0] }, { kind: "inv-curves", curves: [p.grayTrc] }], pcs: "xyz" };
|
|
725
|
+
}
|
|
726
|
+
case "lab":
|
|
727
|
+
return { stages: [{ kind: "encode-lab-v4" }], pcs: "lab" };
|
|
728
|
+
case "xyz":
|
|
729
|
+
return { stages: [{ kind: "encode-xyz" }], pcs: "xyz" };
|
|
730
|
+
default:
|
|
731
|
+
throw new CmsError(`${p.colorSpace} profile without BToA tags`);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
function canBeDestination(p) {
|
|
735
|
+
try {
|
|
736
|
+
for (const i of ["perceptual", "relative", "saturation"]) pcsToDevice(p, i);
|
|
737
|
+
return true;
|
|
738
|
+
} catch {
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
function grayAsRgb(p) {
|
|
743
|
+
if (!p.grayTrc || p.pcs !== "xyz") return null;
|
|
744
|
+
const w = D50;
|
|
745
|
+
const c = p.grayTrc;
|
|
746
|
+
return {
|
|
747
|
+
...p,
|
|
748
|
+
colorSpace: "rgb",
|
|
749
|
+
description: `${p.description} (as RGB)`,
|
|
750
|
+
matrix: [
|
|
751
|
+
[w[0] / 3, w[0] / 3, w[0] / 3],
|
|
752
|
+
[w[1] / 3, w[1] / 3, w[1] / 3],
|
|
753
|
+
[w[2] / 3, w[2] / 3, w[2] / 3]
|
|
754
|
+
],
|
|
755
|
+
trc: [c, c, c],
|
|
756
|
+
grayTrc: null,
|
|
757
|
+
a2b: [null, null, null],
|
|
758
|
+
b2a: [null, null, null],
|
|
759
|
+
bytes: null
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
var Reader = class {
|
|
763
|
+
constructor(b) {
|
|
764
|
+
this.b = b;
|
|
765
|
+
}
|
|
766
|
+
b;
|
|
767
|
+
check(off, len) {
|
|
768
|
+
if (off < 0 || len < 0 || off + len > this.b.length) throw new CmsError("ICC profile is truncated");
|
|
769
|
+
}
|
|
770
|
+
slice(off, len) {
|
|
771
|
+
this.check(off, len);
|
|
772
|
+
return this.b.subarray(off, off + len);
|
|
773
|
+
}
|
|
774
|
+
u8(off) {
|
|
775
|
+
this.check(off, 1);
|
|
776
|
+
return this.b[off];
|
|
777
|
+
}
|
|
778
|
+
u16(off) {
|
|
779
|
+
this.check(off, 2);
|
|
780
|
+
return this.b[off] << 8 | this.b[off + 1];
|
|
781
|
+
}
|
|
782
|
+
u32(off) {
|
|
783
|
+
this.check(off, 4);
|
|
784
|
+
return (this.b[off] << 24 | this.b[off + 1] << 16 | this.b[off + 2] << 8 | this.b[off + 3]) >>> 0;
|
|
785
|
+
}
|
|
786
|
+
s15f16(off) {
|
|
787
|
+
return (this.u32(off) | 0) / 65536;
|
|
788
|
+
}
|
|
789
|
+
xyz(off) {
|
|
790
|
+
return [this.s15f16(off), this.s15f16(off + 4), this.s15f16(off + 8)];
|
|
791
|
+
}
|
|
792
|
+
ascii(off, len) {
|
|
793
|
+
return String.fromCharCode(...this.slice(off, len));
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
var MAX_CLUT_VALUES = 16 * 1024 * 1024;
|
|
797
|
+
function parseProfile(bytes) {
|
|
798
|
+
if (bytes.length < 132) throw new CmsError("ICC profile is truncated");
|
|
799
|
+
const head = new Reader(bytes);
|
|
800
|
+
if (head.ascii(36, 4) !== "acsp") throw new CmsError("not an ICC profile (missing 'acsp' signature)");
|
|
801
|
+
const declared = head.u32(0);
|
|
802
|
+
const end = declared >= 132 && declared <= bytes.length ? declared : bytes.length;
|
|
803
|
+
const r = new Reader(bytes.subarray(0, end));
|
|
804
|
+
const version = [r.u8(8), r.u8(9)];
|
|
805
|
+
const profileClass = CLASS_SIGS[r.ascii(12, 4)] ?? "unknown";
|
|
806
|
+
const colorSpace = spaceFromSig(r.ascii(16, 4));
|
|
807
|
+
const pcsSig = r.ascii(20, 4);
|
|
808
|
+
const pcs = pcsSig === "Lab " ? "lab" : "xyz";
|
|
809
|
+
const renderingIntent = intentFromCode(r.u32(64) & 65535) ?? "perceptual";
|
|
810
|
+
const count = r.u32(128);
|
|
811
|
+
if (count > 1024) throw new CmsError(`invalid ICC profile: ${count} tags`);
|
|
812
|
+
const tags = /* @__PURE__ */ new Map();
|
|
813
|
+
for (let i = 0; i < count; i++) {
|
|
814
|
+
const o = 132 + i * 12;
|
|
815
|
+
const sig = r.ascii(o, 4);
|
|
816
|
+
if (!tags.has(sig)) tags.set(sig, [r.u32(o + 4), r.u32(o + 8)]);
|
|
817
|
+
}
|
|
818
|
+
const tagData = (sig) => {
|
|
819
|
+
const t = tags.get(sig);
|
|
820
|
+
return t ? r.slice(t[0], t[1]) : null;
|
|
821
|
+
};
|
|
822
|
+
const xyzTag = (sig) => {
|
|
823
|
+
const d = tagData(sig);
|
|
824
|
+
if (!d) return null;
|
|
825
|
+
const dr = new Reader(d);
|
|
826
|
+
if (d.length < 20 || dr.ascii(0, 4) !== "XYZ ") throw new CmsError(`invalid ICC profile: tag ${sig} is not XYZType`);
|
|
827
|
+
return dr.xyz(8);
|
|
828
|
+
};
|
|
829
|
+
const curveTag = (sig) => {
|
|
830
|
+
const d = tagData(sig);
|
|
831
|
+
return d ? parseCurve(d)[0] : null;
|
|
832
|
+
};
|
|
833
|
+
const whitePoint = xyzTag("wtpt") ?? [D50[0], D50[1], D50[2]];
|
|
834
|
+
let blackPoint2 = null;
|
|
835
|
+
try {
|
|
836
|
+
blackPoint2 = xyzTag("bkpt");
|
|
837
|
+
} catch {
|
|
838
|
+
blackPoint2 = null;
|
|
839
|
+
}
|
|
840
|
+
let chad = null;
|
|
841
|
+
const cd = tagData("chad");
|
|
842
|
+
if (cd && cd.length >= 44 && new Reader(cd).ascii(0, 4) === "sf32") {
|
|
843
|
+
const rd = new Reader(cd);
|
|
844
|
+
chad = [
|
|
845
|
+
[rd.s15f16(8), rd.s15f16(12), rd.s15f16(16)],
|
|
846
|
+
[rd.s15f16(20), rd.s15f16(24), rd.s15f16(28)],
|
|
847
|
+
[rd.s15f16(32), rd.s15f16(36), rd.s15f16(40)]
|
|
848
|
+
];
|
|
849
|
+
}
|
|
850
|
+
const rr = xyzTag("rXYZ");
|
|
851
|
+
const gg = xyzTag("gXYZ");
|
|
852
|
+
const bb = xyzTag("bXYZ");
|
|
853
|
+
const matrix = rr && gg && bb ? [
|
|
854
|
+
[rr[0], gg[0], bb[0]],
|
|
855
|
+
[rr[1], gg[1], bb[1]],
|
|
856
|
+
[rr[2], gg[2], bb[2]]
|
|
857
|
+
] : null;
|
|
858
|
+
const tr = curveTag("rTRC");
|
|
859
|
+
const tg = curveTag("gTRC");
|
|
860
|
+
const tb = curveTag("bTRC");
|
|
861
|
+
const trc = tr && tg && tb ? [tr, tg, tb] : null;
|
|
862
|
+
const grayTrc = curveTag("kTRC");
|
|
863
|
+
const lut = (sig, a2b2) => {
|
|
864
|
+
const d = tagData(sig);
|
|
865
|
+
return d ? parseLut(d, a2b2) : null;
|
|
866
|
+
};
|
|
867
|
+
const a2b = [lut("A2B0", true), lut("A2B1", true), lut("A2B2", true)];
|
|
868
|
+
const b2a = [lut("B2A0", false), lut("B2A1", false), lut("B2A2", false)];
|
|
869
|
+
const desc = tagData("desc");
|
|
870
|
+
const cprt = tagData("cprt");
|
|
871
|
+
const p = {
|
|
872
|
+
version,
|
|
873
|
+
profileClass,
|
|
874
|
+
colorSpace,
|
|
875
|
+
pcs,
|
|
876
|
+
renderingIntent,
|
|
877
|
+
description: desc ? parseText(desc) : "",
|
|
878
|
+
copyright: cprt ? parseText(cprt) : "",
|
|
879
|
+
whitePoint,
|
|
880
|
+
blackPoint: blackPoint2,
|
|
881
|
+
chad,
|
|
882
|
+
matrix,
|
|
883
|
+
trc,
|
|
884
|
+
grayTrc,
|
|
885
|
+
a2b,
|
|
886
|
+
b2a,
|
|
887
|
+
bytes: bytes.slice()
|
|
888
|
+
};
|
|
889
|
+
const dev = spaceChannels(colorSpace);
|
|
890
|
+
const conn = profileClass === "link" ? spaceChannels(spaceFromSig(pcsSig)) : 3;
|
|
891
|
+
for (const l of a2b) if (l && (dev !== 0 && l.inputs !== dev || conn !== 0 && l.outputs !== conn)) throw new CmsError(`invalid ICC profile: AToB tag has ${l.inputs}\u2192${l.outputs} channels for a ${colorSpace} profile`);
|
|
892
|
+
for (const l of b2a) if (l && (conn !== 0 && l.inputs !== conn || dev !== 0 && l.outputs !== dev)) throw new CmsError(`invalid ICC profile: BToA tag has ${l.inputs}\u2192${l.outputs} channels for a ${colorSpace} profile`);
|
|
893
|
+
return p;
|
|
894
|
+
}
|
|
895
|
+
function parseCurve(d) {
|
|
896
|
+
const r = new Reader(d);
|
|
897
|
+
const type = r.ascii(0, 4);
|
|
898
|
+
if (type === "curv") {
|
|
899
|
+
const n = r.u32(8);
|
|
900
|
+
const len = 12 + n * 2;
|
|
901
|
+
if (n === 0) return [IDENTITY_CURVE, len];
|
|
902
|
+
if (n === 1) return [gammaCurve(r.u16(12) / 256), len];
|
|
903
|
+
if (n > 1 << 20) throw new CmsError("invalid ICC profile: curve too long");
|
|
904
|
+
r.check(12, n * 2);
|
|
905
|
+
const t = new Float32Array(n);
|
|
906
|
+
for (let i = 0; i < n; i++) t[i] = r.u16(12 + i * 2) / 65535;
|
|
907
|
+
return [{ type: "table", table: t }, len];
|
|
908
|
+
}
|
|
909
|
+
if (type === "para") {
|
|
910
|
+
const kind = r.u16(8);
|
|
911
|
+
if (kind > 4) throw new CmsError(`unsupported ICC feature: parametric curve type ${kind}`);
|
|
912
|
+
const n = paramCount(kind);
|
|
913
|
+
const p = [0, 0, 0, 0, 0, 0, 0];
|
|
914
|
+
for (let i = 0; i < n; i++) p[i] = r.s15f16(12 + i * 4);
|
|
915
|
+
return [kind === 0 ? gammaCurve(p[0]) : { type: "parametric", kind, p }, 12 + n * 4];
|
|
916
|
+
}
|
|
917
|
+
throw new CmsError(`unsupported ICC feature: curve type "${type}"`);
|
|
918
|
+
}
|
|
919
|
+
function parseCurvesAt(d, off, n) {
|
|
920
|
+
const out = [];
|
|
921
|
+
let o = off;
|
|
922
|
+
for (let k = 0; k < n; k++) {
|
|
923
|
+
if (o >= d.length) throw new CmsError("ICC profile is truncated");
|
|
924
|
+
const [c, len] = parseCurve(d.subarray(o));
|
|
925
|
+
out.push(c);
|
|
926
|
+
o += len + 3 & ~3;
|
|
927
|
+
}
|
|
928
|
+
return out;
|
|
929
|
+
}
|
|
930
|
+
function parseLut(d, a2b) {
|
|
931
|
+
const r = new Reader(d);
|
|
932
|
+
const ty = r.ascii(0, 4);
|
|
933
|
+
if (ty === "mft1" || ty === "mft2") {
|
|
934
|
+
const wide = ty === "mft2";
|
|
935
|
+
const i = r.u8(8);
|
|
936
|
+
const o = r.u8(9);
|
|
937
|
+
const g = r.u8(10);
|
|
938
|
+
if (i < 1 || i > 15 || o < 1 || o > 15 || g === 0) throw new CmsError("invalid ICC profile: bad lut dimensions");
|
|
939
|
+
const m = [
|
|
940
|
+
[r.s15f16(12), r.s15f16(16), r.s15f16(20)],
|
|
941
|
+
[r.s15f16(24), r.s15f16(28), r.s15f16(32)],
|
|
942
|
+
[r.s15f16(36), r.s15f16(40), r.s15f16(44)]
|
|
943
|
+
];
|
|
944
|
+
const nIn = wide ? r.u16(48) : 256;
|
|
945
|
+
const nOut = wide ? r.u16(50) : 256;
|
|
946
|
+
let off = wide ? 52 : 48;
|
|
947
|
+
if (wide && (nIn < 2 || nIn > 4096 || nOut < 2 || nOut > 4096)) throw new CmsError("invalid ICC profile: bad lut16 table size");
|
|
948
|
+
const bps = wide ? 2 : 1;
|
|
949
|
+
const values = (at, count) => {
|
|
950
|
+
r.check(at, count * bps);
|
|
951
|
+
const out = new Float32Array(count);
|
|
952
|
+
if (wide) for (let k = 0; k < count; k++) out[k] = r.u16(at + 2 * k) / 65535;
|
|
953
|
+
else for (let k = 0; k < count; k++) out[k] = d[at + k] / 255;
|
|
954
|
+
return out;
|
|
955
|
+
};
|
|
956
|
+
const tables = (count, len) => {
|
|
957
|
+
const v = [];
|
|
958
|
+
for (let k = 0; k < count; k++) {
|
|
959
|
+
v.push({ type: "table", table: values(off, len) });
|
|
960
|
+
off += len * bps;
|
|
961
|
+
}
|
|
962
|
+
return v;
|
|
963
|
+
};
|
|
964
|
+
const inCurves = tables(i, nIn);
|
|
965
|
+
const nodes = Math.pow(g, i);
|
|
966
|
+
if (nodes * o > MAX_CLUT_VALUES) throw new CmsError("invalid ICC profile: CLUT too large");
|
|
967
|
+
const data = values(off, nodes * o);
|
|
968
|
+
off += nodes * o * bps;
|
|
969
|
+
const outCurves = tables(o, nOut);
|
|
970
|
+
const stages = [];
|
|
971
|
+
const ident = m.every((row, a) => row.every((v, b) => Math.abs(v - (a === b ? 1 : 0)) < 1e-6));
|
|
972
|
+
if (!a2b && i === 3 && !ident) stages.push(matrix3(m));
|
|
973
|
+
stages.push({ kind: "curves", curves: inCurves });
|
|
974
|
+
stages.push({ kind: "clut", clut: new Clut(new Array(i).fill(g), o, data) });
|
|
975
|
+
stages.push({ kind: "curves", curves: outCurves });
|
|
976
|
+
return { kind: wide ? "lut16" : "lut8", inputs: i, outputs: o, stages };
|
|
977
|
+
}
|
|
978
|
+
if (ty === "mAB " || ty === "mBA ") {
|
|
979
|
+
const i = r.u8(8);
|
|
980
|
+
const o = r.u8(9);
|
|
981
|
+
if (i < 1 || i > 15 || o < 1 || o > 15) throw new CmsError("invalid ICC profile: bad lutAB dimensions");
|
|
982
|
+
const ob = r.u32(12);
|
|
983
|
+
const om = r.u32(16);
|
|
984
|
+
const omc = r.u32(20);
|
|
985
|
+
const oc = r.u32(24);
|
|
986
|
+
const oa = r.u32(28);
|
|
987
|
+
const matrix = (at) => {
|
|
988
|
+
const m = [];
|
|
989
|
+
for (let k = 0; k < 9; k++) m.push(r.s15f16(at + k * 4));
|
|
990
|
+
return { kind: "matrix", rows: 3, cols: 3, m, offset: [r.s15f16(at + 36), r.s15f16(at + 40), r.s15f16(at + 44)] };
|
|
991
|
+
};
|
|
992
|
+
const clut = (at, cin, cout) => {
|
|
993
|
+
const grid = [];
|
|
994
|
+
for (let k = 0; k < cin; k++) {
|
|
995
|
+
const gp = r.u8(at + k);
|
|
996
|
+
if (gp < 1) throw new CmsError("invalid ICC profile: zero grid points");
|
|
997
|
+
grid.push(gp);
|
|
998
|
+
}
|
|
999
|
+
const prec = r.u8(at + 16);
|
|
1000
|
+
const nodes = grid.reduce((a, b) => a * b, 1);
|
|
1001
|
+
if (nodes * cout > MAX_CLUT_VALUES) throw new CmsError("invalid ICC profile: CLUT too large");
|
|
1002
|
+
const total = nodes * cout;
|
|
1003
|
+
const data = new Float32Array(total);
|
|
1004
|
+
if (prec === 1) {
|
|
1005
|
+
r.check(at + 20, total);
|
|
1006
|
+
for (let k = 0; k < total; k++) data[k] = d[at + 20 + k] / 255;
|
|
1007
|
+
} else if (prec === 2) {
|
|
1008
|
+
r.check(at + 20, total * 2);
|
|
1009
|
+
for (let k = 0; k < total; k++) data[k] = r.u16(at + 20 + 2 * k) / 65535;
|
|
1010
|
+
} else throw new CmsError(`invalid ICC profile: CLUT precision ${prec}`);
|
|
1011
|
+
return { kind: "clut", clut: new Clut(grid, cout, data) };
|
|
1012
|
+
};
|
|
1013
|
+
const stages = [];
|
|
1014
|
+
if (ty === "mAB ") {
|
|
1015
|
+
if (oc !== 0) {
|
|
1016
|
+
if (oa === 0) throw new CmsError("invalid ICC profile: lutAtoB with CLUT but no A curves");
|
|
1017
|
+
stages.push({ kind: "curves", curves: parseCurvesAt(d, oa, i) });
|
|
1018
|
+
stages.push(clut(oc, i, o));
|
|
1019
|
+
} else if (i !== o) throw new CmsError("invalid ICC profile: lutAtoB without CLUT changes channel count");
|
|
1020
|
+
if (om !== 0) {
|
|
1021
|
+
stages.push({ kind: "curves", curves: parseCurvesAt(d, omc, o) });
|
|
1022
|
+
stages.push(matrix(om));
|
|
1023
|
+
}
|
|
1024
|
+
if (ob === 0) throw new CmsError("invalid ICC profile: lutAtoB without B curves");
|
|
1025
|
+
stages.push({ kind: "curves", curves: parseCurvesAt(d, ob, o) });
|
|
1026
|
+
} else {
|
|
1027
|
+
if (ob === 0) throw new CmsError("invalid ICC profile: lutBtoA without B curves");
|
|
1028
|
+
stages.push({ kind: "curves", curves: parseCurvesAt(d, ob, i) });
|
|
1029
|
+
if (om !== 0) {
|
|
1030
|
+
stages.push(matrix(om));
|
|
1031
|
+
stages.push({ kind: "curves", curves: parseCurvesAt(d, omc, i) });
|
|
1032
|
+
}
|
|
1033
|
+
if (oc !== 0) {
|
|
1034
|
+
if (oa === 0) throw new CmsError("invalid ICC profile: lutBtoA with CLUT but no A curves");
|
|
1035
|
+
stages.push(clut(oc, i, o));
|
|
1036
|
+
stages.push({ kind: "curves", curves: parseCurvesAt(d, oa, o) });
|
|
1037
|
+
} else if (i !== o) throw new CmsError("invalid ICC profile: lutBtoA without CLUT changes channel count");
|
|
1038
|
+
}
|
|
1039
|
+
return { kind: "ab", inputs: i, outputs: o, stages };
|
|
1040
|
+
}
|
|
1041
|
+
throw new CmsError(`unsupported ICC feature: LUT type "${ty}"`);
|
|
1042
|
+
}
|
|
1043
|
+
function parseText(d) {
|
|
1044
|
+
try {
|
|
1045
|
+
const r = new Reader(d);
|
|
1046
|
+
const ty = r.ascii(0, 4);
|
|
1047
|
+
const ascii = (s) => {
|
|
1048
|
+
const z = s.indexOf(0);
|
|
1049
|
+
return String.fromCharCode(...z >= 0 ? s.subarray(0, z) : s).trim();
|
|
1050
|
+
};
|
|
1051
|
+
if (ty === "desc") return ascii(r.slice(12, Math.min(r.u32(8), d.length - 12)));
|
|
1052
|
+
if (ty === "text") return ascii(d.subarray(8));
|
|
1053
|
+
if (ty === "mluc") {
|
|
1054
|
+
const n = r.u32(8);
|
|
1055
|
+
const size = r.u32(12);
|
|
1056
|
+
let best = null;
|
|
1057
|
+
for (let k = 0; k < Math.min(n, 256); k++) {
|
|
1058
|
+
const o = 16 + k * size;
|
|
1059
|
+
const lang = r.ascii(o, 2);
|
|
1060
|
+
const len = r.u32(o + 4);
|
|
1061
|
+
const off = r.u32(o + 8);
|
|
1062
|
+
if (!best || lang === "en") {
|
|
1063
|
+
best = [off, len];
|
|
1064
|
+
if (lang === "en") break;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
if (!best) return "";
|
|
1068
|
+
const s = r.slice(best[0], best[1]);
|
|
1069
|
+
let out = "";
|
|
1070
|
+
for (let k = 0; k + 1 < s.length; k += 2) out += String.fromCharCode(s[k] << 8 | s[k + 1]);
|
|
1071
|
+
return out.replace(/\0+$/, "").trim();
|
|
1072
|
+
}
|
|
1073
|
+
} catch {
|
|
1074
|
+
return "";
|
|
1075
|
+
}
|
|
1076
|
+
return "";
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// src/cms/write.ts
|
|
1080
|
+
var Bytes = class {
|
|
1081
|
+
buf = new Uint8Array(1024);
|
|
1082
|
+
length = 0;
|
|
1083
|
+
grow(n) {
|
|
1084
|
+
if (this.length + n <= this.buf.length) return;
|
|
1085
|
+
let cap = this.buf.length * 2;
|
|
1086
|
+
while (cap < this.length + n) cap *= 2;
|
|
1087
|
+
const next = new Uint8Array(cap);
|
|
1088
|
+
next.set(this.buf.subarray(0, this.length));
|
|
1089
|
+
this.buf = next;
|
|
1090
|
+
}
|
|
1091
|
+
u8(...v) {
|
|
1092
|
+
this.grow(v.length);
|
|
1093
|
+
for (const x of v) this.buf[this.length++] = x & 255;
|
|
1094
|
+
}
|
|
1095
|
+
u16(v) {
|
|
1096
|
+
this.u8(v >> 8, v);
|
|
1097
|
+
}
|
|
1098
|
+
u32(v) {
|
|
1099
|
+
this.u8(v >>> 24, v >>> 16, v >>> 8, v);
|
|
1100
|
+
}
|
|
1101
|
+
ascii(s) {
|
|
1102
|
+
for (let i = 0; i < s.length; i++) this.u8(s.charCodeAt(i));
|
|
1103
|
+
}
|
|
1104
|
+
s15(v) {
|
|
1105
|
+
this.u32(Math.round(v * 65536) | 0);
|
|
1106
|
+
}
|
|
1107
|
+
bytes(b) {
|
|
1108
|
+
this.grow(b.length);
|
|
1109
|
+
this.buf.set(b, this.length);
|
|
1110
|
+
this.length += b.length;
|
|
1111
|
+
}
|
|
1112
|
+
pad4() {
|
|
1113
|
+
while (this.length % 4) this.u8(0);
|
|
1114
|
+
}
|
|
1115
|
+
set32(at, v) {
|
|
1116
|
+
this.buf[at] = v >>> 24;
|
|
1117
|
+
this.buf[at + 1] = v >>> 16 & 255;
|
|
1118
|
+
this.buf[at + 2] = v >>> 8 & 255;
|
|
1119
|
+
this.buf[at + 3] = v & 255;
|
|
1120
|
+
}
|
|
1121
|
+
result() {
|
|
1122
|
+
return this.buf.slice(0, this.length);
|
|
1123
|
+
}
|
|
1124
|
+
};
|
|
1125
|
+
var u16q = (v) => Math.round(Math.min(1, Math.max(0, v)) * 65535);
|
|
1126
|
+
function xyzType(v) {
|
|
1127
|
+
const o = new Bytes();
|
|
1128
|
+
o.ascii("XYZ ");
|
|
1129
|
+
o.u32(0);
|
|
1130
|
+
for (const c of v) o.s15(c);
|
|
1131
|
+
return o.result();
|
|
1132
|
+
}
|
|
1133
|
+
function mluc(text) {
|
|
1134
|
+
const o = new Bytes();
|
|
1135
|
+
o.ascii("mluc");
|
|
1136
|
+
o.u32(0);
|
|
1137
|
+
o.u32(1);
|
|
1138
|
+
o.u32(12);
|
|
1139
|
+
o.ascii("enUS");
|
|
1140
|
+
o.u32(text.length * 2);
|
|
1141
|
+
o.u32(28);
|
|
1142
|
+
for (let i = 0; i < text.length; i++) o.u16(text.charCodeAt(i));
|
|
1143
|
+
return o.result();
|
|
1144
|
+
}
|
|
1145
|
+
function curveElem(c) {
|
|
1146
|
+
const o = new Bytes();
|
|
1147
|
+
switch (c.type) {
|
|
1148
|
+
case "identity":
|
|
1149
|
+
o.ascii("curv");
|
|
1150
|
+
o.u32(0);
|
|
1151
|
+
o.u32(0);
|
|
1152
|
+
break;
|
|
1153
|
+
case "gamma":
|
|
1154
|
+
o.ascii("para");
|
|
1155
|
+
o.u32(0);
|
|
1156
|
+
o.u32(0);
|
|
1157
|
+
o.s15(c.gamma);
|
|
1158
|
+
break;
|
|
1159
|
+
case "parametric":
|
|
1160
|
+
o.ascii("para");
|
|
1161
|
+
o.u32(0);
|
|
1162
|
+
o.u16(c.kind);
|
|
1163
|
+
o.u16(0);
|
|
1164
|
+
for (let i = 0; i < paramCount(c.kind); i++) o.s15(c.p[i]);
|
|
1165
|
+
break;
|
|
1166
|
+
case "table":
|
|
1167
|
+
o.ascii("curv");
|
|
1168
|
+
o.u32(0);
|
|
1169
|
+
o.u32(c.table.length);
|
|
1170
|
+
for (const v of c.table) o.u16(u16q(v));
|
|
1171
|
+
break;
|
|
1172
|
+
}
|
|
1173
|
+
return o.result();
|
|
1174
|
+
}
|
|
1175
|
+
function curveTable(c, n) {
|
|
1176
|
+
return c.type === "table" && c.table.length === n ? c.table : sampleCurve(c, n);
|
|
1177
|
+
}
|
|
1178
|
+
function tableLen(cs) {
|
|
1179
|
+
let n = 2;
|
|
1180
|
+
for (const c of cs) n = Math.max(n, c.type === "table" ? c.table.length : c.type === "identity" ? 2 : 1024);
|
|
1181
|
+
return Math.min(4096, Math.max(2, n));
|
|
1182
|
+
}
|
|
1183
|
+
function lutParts(stages) {
|
|
1184
|
+
let i = 0;
|
|
1185
|
+
let mat = null;
|
|
1186
|
+
const first = stages[0];
|
|
1187
|
+
if (first?.kind === "matrix" && first.rows === 3 && first.cols === 3) {
|
|
1188
|
+
mat = first.m;
|
|
1189
|
+
i = 1;
|
|
1190
|
+
}
|
|
1191
|
+
const [a, c, b] = stages.slice(i);
|
|
1192
|
+
if (stages.length - i !== 3 || a?.kind !== "curves" || c?.kind !== "clut" || b?.kind !== "curves") return null;
|
|
1193
|
+
return { mat, a: a.curves, clut: c.clut, b: b.curves };
|
|
1194
|
+
}
|
|
1195
|
+
function encodeMft(stages, wide) {
|
|
1196
|
+
const parts = lutParts(stages);
|
|
1197
|
+
if (!parts) return null;
|
|
1198
|
+
const { mat, a, clut, b } = parts;
|
|
1199
|
+
const g = clut.grid[0];
|
|
1200
|
+
if (!clut.grid.every((x) => x === g)) return null;
|
|
1201
|
+
const o = new Bytes();
|
|
1202
|
+
o.ascii(wide ? "mft2" : "mft1");
|
|
1203
|
+
o.u32(0);
|
|
1204
|
+
o.u8(clut.inputs, clut.outputs, g, 0);
|
|
1205
|
+
for (const v of mat ?? [1, 0, 0, 0, 1, 0, 0, 0, 1]) o.s15(v);
|
|
1206
|
+
const nIn = wide ? tableLen(a) : 256;
|
|
1207
|
+
const nOut = wide ? tableLen(b) : 256;
|
|
1208
|
+
if (wide) {
|
|
1209
|
+
o.u16(nIn);
|
|
1210
|
+
o.u16(nOut);
|
|
1211
|
+
}
|
|
1212
|
+
const put = (v) => wide ? o.u16(u16q(v)) : o.u8(Math.round(Math.min(1, Math.max(0, v)) * 255));
|
|
1213
|
+
for (const c of a) for (const v of curveTable(c, nIn)) put(v);
|
|
1214
|
+
for (const v of clut.data) put(v);
|
|
1215
|
+
for (const c of b) for (const v of curveTable(c, nOut)) put(v);
|
|
1216
|
+
return o.result();
|
|
1217
|
+
}
|
|
1218
|
+
function encodeAb(stages, a2b, inputs, outputs) {
|
|
1219
|
+
let aCurves = null;
|
|
1220
|
+
let clut = null;
|
|
1221
|
+
let mCurves = null;
|
|
1222
|
+
let matrix = null;
|
|
1223
|
+
let bCurves = null;
|
|
1224
|
+
const v = stages;
|
|
1225
|
+
if (a2b) {
|
|
1226
|
+
let k = 0;
|
|
1227
|
+
if (v[0]?.kind === "curves" && v[1]?.kind === "clut") {
|
|
1228
|
+
aCurves = v[0].curves;
|
|
1229
|
+
clut = v[1].clut;
|
|
1230
|
+
k = 2;
|
|
1231
|
+
}
|
|
1232
|
+
const rest = v.slice(k);
|
|
1233
|
+
if (rest.length === 3 && rest[0].kind === "curves" && rest[1].kind === "matrix" && rest[2].kind === "curves") {
|
|
1234
|
+
mCurves = rest[0].curves;
|
|
1235
|
+
matrix = { m: rest[1].m, offset: rest[1].offset };
|
|
1236
|
+
bCurves = rest[2].curves;
|
|
1237
|
+
} else if (rest.length === 1 && rest[0].kind === "curves") bCurves = rest[0].curves;
|
|
1238
|
+
else return null;
|
|
1239
|
+
} else {
|
|
1240
|
+
let k = 0;
|
|
1241
|
+
if (v[0]?.kind === "curves") {
|
|
1242
|
+
bCurves = v[0].curves;
|
|
1243
|
+
k = 1;
|
|
1244
|
+
}
|
|
1245
|
+
const m0 = v[k];
|
|
1246
|
+
const m1 = v[k + 1];
|
|
1247
|
+
if (m0?.kind === "matrix" && m1?.kind === "curves") {
|
|
1248
|
+
matrix = { m: m0.m, offset: m0.offset };
|
|
1249
|
+
mCurves = m1.curves;
|
|
1250
|
+
k += 2;
|
|
1251
|
+
}
|
|
1252
|
+
const c0 = v[k];
|
|
1253
|
+
const c1 = v[k + 1];
|
|
1254
|
+
if (c0?.kind === "clut" && c1?.kind === "curves") {
|
|
1255
|
+
clut = c0.clut;
|
|
1256
|
+
aCurves = c1.curves;
|
|
1257
|
+
k += 2;
|
|
1258
|
+
}
|
|
1259
|
+
if (k !== v.length) return null;
|
|
1260
|
+
}
|
|
1261
|
+
if (!bCurves) return null;
|
|
1262
|
+
const o = new Bytes();
|
|
1263
|
+
o.ascii(a2b ? "mAB " : "mBA ");
|
|
1264
|
+
o.u32(0);
|
|
1265
|
+
o.u8(inputs, outputs, 0, 0);
|
|
1266
|
+
for (let k = 0; k < 5; k++) o.u32(0);
|
|
1267
|
+
const offs = [0, 0, 0, 0, 0];
|
|
1268
|
+
const curves = (cs) => {
|
|
1269
|
+
for (const c of cs) {
|
|
1270
|
+
o.bytes(curveElem(c));
|
|
1271
|
+
o.pad4();
|
|
1272
|
+
}
|
|
1273
|
+
};
|
|
1274
|
+
offs[0] = o.length;
|
|
1275
|
+
curves(bCurves);
|
|
1276
|
+
if (matrix) {
|
|
1277
|
+
offs[1] = o.length;
|
|
1278
|
+
for (const x of matrix.m) o.s15(x);
|
|
1279
|
+
for (const x of matrix.offset) o.s15(x);
|
|
1280
|
+
offs[2] = o.length;
|
|
1281
|
+
if (!mCurves) return null;
|
|
1282
|
+
curves(mCurves);
|
|
1283
|
+
}
|
|
1284
|
+
if (clut) {
|
|
1285
|
+
offs[3] = o.length;
|
|
1286
|
+
const g = new Array(16).fill(0);
|
|
1287
|
+
clut.grid.forEach((n, k) => g[k] = n);
|
|
1288
|
+
o.u8(...g);
|
|
1289
|
+
o.u8(2, 0, 0, 0);
|
|
1290
|
+
for (const x of clut.data) o.u16(u16q(x));
|
|
1291
|
+
o.pad4();
|
|
1292
|
+
offs[4] = o.length;
|
|
1293
|
+
if (!aCurves) return null;
|
|
1294
|
+
curves(aCurves);
|
|
1295
|
+
}
|
|
1296
|
+
offs.forEach((x, k) => o.set32(12 + k * 4, x));
|
|
1297
|
+
return o.result();
|
|
1298
|
+
}
|
|
1299
|
+
var sameBytes = (a, b) => a.length === b.length && a.every((v, i) => v === b[i]);
|
|
1300
|
+
function encodeProfile(p) {
|
|
1301
|
+
const tags = [];
|
|
1302
|
+
tags.push(["desc", mluc(p.description)]);
|
|
1303
|
+
tags.push(["cprt", mluc(p.copyright)]);
|
|
1304
|
+
tags.push(["wtpt", xyzType(p.whitePoint)]);
|
|
1305
|
+
if (p.blackPoint) tags.push(["bkpt", xyzType(p.blackPoint)]);
|
|
1306
|
+
if (p.chad) {
|
|
1307
|
+
const o2 = new Bytes();
|
|
1308
|
+
o2.ascii("sf32");
|
|
1309
|
+
o2.u32(0);
|
|
1310
|
+
for (const row of p.chad) for (const v of row) o2.s15(v);
|
|
1311
|
+
tags.push(["chad", o2.result()]);
|
|
1312
|
+
}
|
|
1313
|
+
if (p.matrix) {
|
|
1314
|
+
const m = p.matrix;
|
|
1315
|
+
tags.push(["rXYZ", xyzType([m[0][0], m[1][0], m[2][0]])]);
|
|
1316
|
+
tags.push(["gXYZ", xyzType([m[0][1], m[1][1], m[2][1]])]);
|
|
1317
|
+
tags.push(["bXYZ", xyzType([m[0][2], m[1][2], m[2][2]])]);
|
|
1318
|
+
}
|
|
1319
|
+
if (p.trc) {
|
|
1320
|
+
tags.push(["rTRC", curveElem(p.trc[0])]);
|
|
1321
|
+
tags.push(["gTRC", curveElem(p.trc[1])]);
|
|
1322
|
+
tags.push(["bTRC", curveElem(p.trc[2])]);
|
|
1323
|
+
}
|
|
1324
|
+
if (p.grayTrc) tags.push(["kTRC", curveElem(p.grayTrc)]);
|
|
1325
|
+
for (const [luts, names, a2b] of [
|
|
1326
|
+
[p.a2b, ["A2B0", "A2B1", "A2B2"], true],
|
|
1327
|
+
[p.b2a, ["B2A0", "B2A1", "B2A2"], false]
|
|
1328
|
+
]) {
|
|
1329
|
+
luts.forEach((l, k) => {
|
|
1330
|
+
if (!l) return;
|
|
1331
|
+
const data2 = l.kind === "lut16" ? encodeMft(l.stages, true) : l.kind === "lut8" ? encodeMft(l.stages, false) : encodeAb(l.stages, a2b, l.inputs, l.outputs);
|
|
1332
|
+
if (data2) tags.push([names[k], data2]);
|
|
1333
|
+
});
|
|
1334
|
+
}
|
|
1335
|
+
const tableLength = 4 + tags.length * 12;
|
|
1336
|
+
const data = new Bytes();
|
|
1337
|
+
const entries = [];
|
|
1338
|
+
const placed = [];
|
|
1339
|
+
for (const [sig, d] of tags) {
|
|
1340
|
+
const same = placed.find(([b]) => sameBytes(b, d));
|
|
1341
|
+
if (same) {
|
|
1342
|
+
entries.push([sig, same[1], same[2]]);
|
|
1343
|
+
continue;
|
|
1344
|
+
}
|
|
1345
|
+
const off = 128 + tableLength + data.length;
|
|
1346
|
+
data.bytes(d);
|
|
1347
|
+
data.pad4();
|
|
1348
|
+
entries.push([sig, off, d.length]);
|
|
1349
|
+
placed.push([d, off, d.length]);
|
|
1350
|
+
}
|
|
1351
|
+
const size = 128 + tableLength + data.length;
|
|
1352
|
+
const o = new Bytes();
|
|
1353
|
+
o.u32(size);
|
|
1354
|
+
o.u32(0);
|
|
1355
|
+
o.u8(4, 48, 0, 0);
|
|
1356
|
+
o.ascii(classSig(p.profileClass));
|
|
1357
|
+
o.ascii(spaceSig(p.colorSpace));
|
|
1358
|
+
o.ascii(p.pcs === "lab" ? "Lab " : "XYZ ");
|
|
1359
|
+
for (const v of [2026, 1, 1, 0, 0, 0]) o.u16(v);
|
|
1360
|
+
o.ascii("acsp");
|
|
1361
|
+
for (let k = 0; k < 6; k++) o.u32(0);
|
|
1362
|
+
o.u32(intentCode(p.renderingIntent));
|
|
1363
|
+
for (const v of D50) o.s15(v);
|
|
1364
|
+
o.u32(0);
|
|
1365
|
+
while (o.length < 128) o.u8(0);
|
|
1366
|
+
o.u32(entries.length);
|
|
1367
|
+
for (const [sig, off, len] of entries) {
|
|
1368
|
+
o.ascii(sig);
|
|
1369
|
+
o.u32(off);
|
|
1370
|
+
o.u32(len);
|
|
1371
|
+
}
|
|
1372
|
+
o.bytes(data.result());
|
|
1373
|
+
return o.result();
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
// src/cms/transform.ts
|
|
1377
|
+
var DEFAULT_TRANSFORM_OPTIONS = { intent: "relative", bpc: true, preciseFloat: true };
|
|
1378
|
+
var GRID_3D = 33;
|
|
1379
|
+
var GRID_4D = 17;
|
|
1380
|
+
var TABLE_1D = 4096;
|
|
1381
|
+
var OUT_TABLE = 4096;
|
|
1382
|
+
var PERCEPTUAL_BLACK_Y = 34731e-7;
|
|
1383
|
+
function shaper(cs, m) {
|
|
1384
|
+
const curves8 = cs.map((c) => {
|
|
1385
|
+
const t = new Float32Array(256);
|
|
1386
|
+
for (let i = 0; i < 256; i++) t[i] = evalCurve(c, i / 255);
|
|
1387
|
+
return t;
|
|
1388
|
+
});
|
|
1389
|
+
const curves = cs.map((c) => {
|
|
1390
|
+
const t = new Float32Array(TABLE_1D + 1);
|
|
1391
|
+
for (let i = 0; i <= TABLE_1D; i++) t[i] = evalCurve(c, i / TABLE_1D);
|
|
1392
|
+
return t;
|
|
1393
|
+
});
|
|
1394
|
+
return { kind: "shaper", curves8, curves, matrix: m ? { rows: m.rows, cols: m.cols, m: Float32Array.from(m.m), offset: Float32Array.from(m.offset) } : null };
|
|
1395
|
+
}
|
|
1396
|
+
function table2(t, x) {
|
|
1397
|
+
const n = t.length - 1;
|
|
1398
|
+
const p = x * n;
|
|
1399
|
+
const i = Math.min(n - 1, Math.max(0, Math.floor(p)));
|
|
1400
|
+
const f = p - i;
|
|
1401
|
+
return t[i] + (t[i + 1] - t[i]) * f;
|
|
1402
|
+
}
|
|
1403
|
+
function deviceWhite(cs) {
|
|
1404
|
+
switch (cs) {
|
|
1405
|
+
case "rgb":
|
|
1406
|
+
return [1, 1, 1];
|
|
1407
|
+
case "gray":
|
|
1408
|
+
return [1];
|
|
1409
|
+
case "cmyk":
|
|
1410
|
+
return [0, 0, 0, 0];
|
|
1411
|
+
case "cmy":
|
|
1412
|
+
return [0, 0, 0];
|
|
1413
|
+
case "lab":
|
|
1414
|
+
return [1, 0.5, 0.5];
|
|
1415
|
+
default:
|
|
1416
|
+
return null;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
var Transform = class _Transform {
|
|
1420
|
+
constructor(pipeline, options) {
|
|
1421
|
+
this.pipeline = pipeline;
|
|
1422
|
+
this.options = options;
|
|
1423
|
+
pipeline.optimize();
|
|
1424
|
+
this.inputs = pipeline.inputs;
|
|
1425
|
+
this.outputs = pipeline.outputs;
|
|
1426
|
+
const head = pipeline.stages.slice();
|
|
1427
|
+
const last = head[head.length - 1];
|
|
1428
|
+
if (last?.kind === "inv-curves" && last.curves.length === this.outputs) {
|
|
1429
|
+
this.post = last.curves.map((c) => {
|
|
1430
|
+
const t = new Float32Array(OUT_TABLE);
|
|
1431
|
+
for (let i = 0; i < OUT_TABLE; i++) {
|
|
1432
|
+
const u = i / (OUT_TABLE - 1);
|
|
1433
|
+
t[i] = Math.min(1, Math.max(0, evalCurveInverse(c, u * u * u * u)));
|
|
1434
|
+
}
|
|
1435
|
+
return t;
|
|
1436
|
+
});
|
|
1437
|
+
head.pop();
|
|
1438
|
+
if (head[head.length - 1]?.kind === "clamp01") head.pop();
|
|
1439
|
+
}
|
|
1440
|
+
const headPipe = new Pipeline(this.inputs, head);
|
|
1441
|
+
const mid = headPipe.outputs;
|
|
1442
|
+
const hasClut = head.some((s) => s.kind === "clut");
|
|
1443
|
+
const [h0, h1] = head;
|
|
1444
|
+
if (head.length === 0) this.core = shaper([], null);
|
|
1445
|
+
else if (head.length === 1 && h0.kind === "curves" && h0.curves.length === this.inputs) this.core = shaper(h0.curves, null);
|
|
1446
|
+
else if (head.length === 1 && h0.kind === "matrix") this.core = shaper([], h0);
|
|
1447
|
+
else if (head.length === 2 && h0.kind === "curves" && h0.curves.length === this.inputs && h1.kind === "matrix") this.core = shaper(h0.curves, h1);
|
|
1448
|
+
else if (!hasClut) this.core = { kind: "exact" };
|
|
1449
|
+
else {
|
|
1450
|
+
const sample = (grid) => Clut.sample(grid, mid, (i, o) => headPipe.eval(i, o));
|
|
1451
|
+
switch (this.inputs) {
|
|
1452
|
+
case 1:
|
|
1453
|
+
this.core = { kind: "grid", clut: sample([TABLE_1D]) };
|
|
1454
|
+
break;
|
|
1455
|
+
case 2:
|
|
1456
|
+
this.core = { kind: "grid", clut: sample([GRID_3D, GRID_3D]) };
|
|
1457
|
+
break;
|
|
1458
|
+
case 3:
|
|
1459
|
+
this.core = { kind: "grid", clut: sample([GRID_3D, GRID_3D, GRID_3D]) };
|
|
1460
|
+
break;
|
|
1461
|
+
case 4:
|
|
1462
|
+
this.core = { kind: "grid", clut: sample([GRID_4D, GRID_4D, GRID_4D, GRID_4D]) };
|
|
1463
|
+
break;
|
|
1464
|
+
default:
|
|
1465
|
+
this.core = { kind: "exact" };
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
pipeline;
|
|
1470
|
+
options;
|
|
1471
|
+
inputs;
|
|
1472
|
+
outputs;
|
|
1473
|
+
core;
|
|
1474
|
+
post = null;
|
|
1475
|
+
mid = new Float32Array(16);
|
|
1476
|
+
tmp = new Float32Array(16);
|
|
1477
|
+
inp = new Float32Array(16);
|
|
1478
|
+
/** From `src` to `dst` with `intent` (and black point compensation). */
|
|
1479
|
+
static create(src, dst, options = {}) {
|
|
1480
|
+
const opts = { ...DEFAULT_TRANSFORM_OPTIONS, ...options };
|
|
1481
|
+
const t = new _Transform(new Pipeline(profileChannels(src), linkStages(src, dst, opts.intent, opts.bpc)), opts);
|
|
1482
|
+
if (opts.intent !== "absolute") t.fixWhite(src.colorSpace, dst.colorSpace);
|
|
1483
|
+
return t;
|
|
1484
|
+
}
|
|
1485
|
+
/**
|
|
1486
|
+
* Soft proof `src → proof → dst` (View › Proof Setup): `intent` / `bpc` apply to the
|
|
1487
|
+
* document → proof step; proof → display is relative colorimetric with BPC, or absolute
|
|
1488
|
+
* colorimetric without it when `simulatePaper` (paper colour and black ink simulated).
|
|
1489
|
+
*/
|
|
1490
|
+
static proof(src, proof, dst, intent, bpc, simulatePaper) {
|
|
1491
|
+
const stages = linkStages(src, proof, intent, bpc);
|
|
1492
|
+
stages.push({ kind: "clamp01" });
|
|
1493
|
+
stages.push(...simulatePaper ? linkStages(proof, dst, "absolute", false) : linkStages(proof, dst, "relative", true));
|
|
1494
|
+
return new _Transform(new Pipeline(profileChannels(src), stages), { intent, bpc, preciseFloat: true });
|
|
1495
|
+
}
|
|
1496
|
+
/** The device-link node at the source's device white gets exactly the destination's white. */
|
|
1497
|
+
fixWhite(src, dst) {
|
|
1498
|
+
const sw = deviceWhite(src);
|
|
1499
|
+
let dw = deviceWhite(dst);
|
|
1500
|
+
if (!sw || !dw) return;
|
|
1501
|
+
if (this.post) {
|
|
1502
|
+
if (dst !== "rgb" && dst !== "gray") return;
|
|
1503
|
+
dw = dw.map(() => 1);
|
|
1504
|
+
}
|
|
1505
|
+
if (this.core.kind !== "grid") return;
|
|
1506
|
+
const c = this.core.clut;
|
|
1507
|
+
if (c.inputs !== sw.length || c.outputs !== dw.length) return;
|
|
1508
|
+
let off = 0;
|
|
1509
|
+
let stride = c.outputs;
|
|
1510
|
+
for (let d = c.inputs - 1; d >= 0; d--) {
|
|
1511
|
+
const n = c.grid[d];
|
|
1512
|
+
const pos = sw[d] * (n - 1);
|
|
1513
|
+
if (Math.abs(pos - Math.round(pos)) > 1e-6) return;
|
|
1514
|
+
off += Math.round(pos) * stride;
|
|
1515
|
+
stride *= n;
|
|
1516
|
+
}
|
|
1517
|
+
for (let k = 0; k < c.outputs; k++) c.data[off + k] = dw[k];
|
|
1518
|
+
}
|
|
1519
|
+
/** Exact evaluation of one colour. */
|
|
1520
|
+
eval(input, out) {
|
|
1521
|
+
this.pipeline.eval(input, out);
|
|
1522
|
+
}
|
|
1523
|
+
/** Table evaluation of one colour (what integer buffers use). */
|
|
1524
|
+
evalFast(input, out) {
|
|
1525
|
+
const mid = this.mid;
|
|
1526
|
+
const core = this.core;
|
|
1527
|
+
if (core.kind === "exact") {
|
|
1528
|
+
this.pipeline.eval(input, out);
|
|
1529
|
+
return;
|
|
1530
|
+
}
|
|
1531
|
+
if (core.kind === "grid") {
|
|
1532
|
+
for (let d = 0; d < this.inputs; d++) this.inp[d] = Math.min(1, Math.max(0, input[d]));
|
|
1533
|
+
core.clut.eval(this.inp, mid);
|
|
1534
|
+
} else {
|
|
1535
|
+
const v = this.tmp;
|
|
1536
|
+
for (let k = 0; k < this.inputs; k++) v[k] = core.curves.length ? table2(core.curves[k], input[k]) : input[k];
|
|
1537
|
+
applyMatrix(core.matrix, v, mid, this.inputs);
|
|
1538
|
+
}
|
|
1539
|
+
this.finish(mid, out);
|
|
1540
|
+
}
|
|
1541
|
+
finish(mid, out) {
|
|
1542
|
+
const post = this.post;
|
|
1543
|
+
if (!post) {
|
|
1544
|
+
for (let k = 0; k < this.outputs; k++) out[k] = mid[k];
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
for (let k = 0; k < this.outputs; k++) {
|
|
1548
|
+
const t = post[k];
|
|
1549
|
+
const x = mid[k];
|
|
1550
|
+
const u = Math.sqrt(Math.sqrt(x < 0 ? 0 : x > 1 ? 1 : x)) * (OUT_TABLE - 1);
|
|
1551
|
+
const i = Math.min(OUT_TABLE - 2, Math.floor(u));
|
|
1552
|
+
out[k] = t[i] + (t[i + 1] - t[i]) * (u - i);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1556
|
+
* Converts 8-bit interleaved pixels: `src` has `srcStride` samples per pixel (colour first),
|
|
1557
|
+
* `dst` `dstStride`. With `copyExtra`, trailing channels (alpha) are copied.
|
|
1558
|
+
*/
|
|
1559
|
+
convert8(src, srcStride, dst, dstStride, copyExtra = true) {
|
|
1560
|
+
this.check(srcStride, dstStride);
|
|
1561
|
+
const extra = copyExtra ? Math.min(srcStride - this.inputs, dstStride - this.outputs) : 0;
|
|
1562
|
+
const n = Math.min(Math.floor(src.length / srcStride), Math.floor(dst.length / dstStride));
|
|
1563
|
+
const out = new Float32Array(16);
|
|
1564
|
+
const mid = this.mid;
|
|
1565
|
+
const core = this.core;
|
|
1566
|
+
const q = (v) => v <= 0 ? 0 : v >= 1 ? 255 : v * 255 + 0.5 | 0;
|
|
1567
|
+
for (let p = 0; p < n; p++) {
|
|
1568
|
+
const s = p * srcStride;
|
|
1569
|
+
const d = p * dstStride;
|
|
1570
|
+
if (core.kind === "shaper") {
|
|
1571
|
+
const v = this.tmp;
|
|
1572
|
+
for (let k = 0; k < this.inputs; k++) v[k] = core.curves8.length ? core.curves8[k][src[s + k]] : src[s + k] / 255;
|
|
1573
|
+
applyMatrix(core.matrix, v, mid, this.inputs);
|
|
1574
|
+
this.finish(mid, out);
|
|
1575
|
+
} else {
|
|
1576
|
+
for (let k = 0; k < this.inputs; k++) this.inp[k] = src[s + k] / 255;
|
|
1577
|
+
this.evalFast(this.inp, out);
|
|
1578
|
+
}
|
|
1579
|
+
for (let k = 0; k < this.outputs; k++) dst[d + k] = q(out[k]);
|
|
1580
|
+
for (let e = 0; e < extra; e++) dst[d + this.outputs + e] = src[s + this.inputs + e];
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
/** Converts 16-bit interleaved pixels (see `convert8`). */
|
|
1584
|
+
convert16(src, srcStride, dst, dstStride, copyExtra = true) {
|
|
1585
|
+
this.check(srcStride, dstStride);
|
|
1586
|
+
const extra = copyExtra ? Math.min(srcStride - this.inputs, dstStride - this.outputs) : 0;
|
|
1587
|
+
const n = Math.min(Math.floor(src.length / srcStride), Math.floor(dst.length / dstStride));
|
|
1588
|
+
const out = new Float32Array(16);
|
|
1589
|
+
for (let p = 0; p < n; p++) {
|
|
1590
|
+
const s = p * srcStride;
|
|
1591
|
+
const d = p * dstStride;
|
|
1592
|
+
for (let k = 0; k < this.inputs; k++) this.inp[k] = src[s + k] / 65535;
|
|
1593
|
+
this.evalFast(this.inp, out);
|
|
1594
|
+
for (let k = 0; k < this.outputs; k++) dst[d + k] = Math.round(Math.min(1, Math.max(0, out[k])) * 65535);
|
|
1595
|
+
for (let e = 0; e < extra; e++) dst[d + this.outputs + e] = src[s + this.inputs + e];
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
/** Converts float interleaved pixels (exact unless `preciseFloat` is off). */
|
|
1599
|
+
convertFloat(src, srcStride, dst, dstStride, copyExtra = true) {
|
|
1600
|
+
this.check(srcStride, dstStride);
|
|
1601
|
+
const extra = copyExtra ? Math.min(srcStride - this.inputs, dstStride - this.outputs) : 0;
|
|
1602
|
+
const n = Math.min(Math.floor(src.length / srcStride), Math.floor(dst.length / dstStride));
|
|
1603
|
+
const precise = this.options.preciseFloat || this.core.kind === "exact";
|
|
1604
|
+
const out = new Float32Array(16);
|
|
1605
|
+
for (let p = 0; p < n; p++) {
|
|
1606
|
+
const s = p * srcStride;
|
|
1607
|
+
const d = p * dstStride;
|
|
1608
|
+
for (let k = 0; k < this.inputs; k++) this.inp[k] = src[s + k];
|
|
1609
|
+
if (precise) this.pipeline.eval(this.inp, out);
|
|
1610
|
+
else this.evalFast(this.inp, out);
|
|
1611
|
+
for (let k = 0; k < this.outputs; k++) dst[d + k] = out[k];
|
|
1612
|
+
for (let e = 0; e < extra; e++) dst[d + this.outputs + e] = src[s + this.inputs + e];
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
check(ss, ds) {
|
|
1616
|
+
if (ss < this.inputs || ds < this.outputs) throw new RangeError(`pixel stride smaller than the channel count (${this.inputs} \u2192 ${this.outputs})`);
|
|
1617
|
+
}
|
|
1618
|
+
};
|
|
1619
|
+
function applyMatrix(m, v, out, n) {
|
|
1620
|
+
if (!m) {
|
|
1621
|
+
for (let k = 0; k < n; k++) out[k] = v[k];
|
|
1622
|
+
return;
|
|
1623
|
+
}
|
|
1624
|
+
for (let r = 0; r < m.rows; r++) {
|
|
1625
|
+
let s = m.offset[r];
|
|
1626
|
+
for (let c = 0; c < m.cols; c++) s += m.m[r * m.cols + c] * v[c];
|
|
1627
|
+
out[r] = s;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
function linkStages(src, dst, intent, bpc) {
|
|
1631
|
+
for (const p of [src, dst]) {
|
|
1632
|
+
const labToLab = p.colorSpace === "lab" && p.a2b.every((l) => !l || l.outputs === 3) && p.b2a.every((l) => !l || l.inputs === 3);
|
|
1633
|
+
if ((p.profileClass === "link" || p.profileClass === "named" || p.profileClass === "abstract") && !labToLab) {
|
|
1634
|
+
throw new CmsError(`${p.profileClass} profiles cannot be used as a source or destination`);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
const s = deviceToPcs(src, intent);
|
|
1638
|
+
const d = pcsToDevice(dst, intent);
|
|
1639
|
+
const stages = s.stages.slice();
|
|
1640
|
+
const adjust = connection(src, dst, intent, bpc);
|
|
1641
|
+
if (adjust) {
|
|
1642
|
+
if (s.pcs === "lab") stages.push({ kind: "lab-to-xyz" });
|
|
1643
|
+
stages.push({ kind: "matrix", rows: 3, cols: 3, m: [adjust.scale[0], 0, 0, 0, adjust.scale[1], 0, 0, 0, adjust.scale[2]], offset: adjust.offset });
|
|
1644
|
+
if (d.pcs === "lab") stages.push({ kind: "xyz-to-lab" });
|
|
1645
|
+
} else if (s.pcs === "lab" && d.pcs === "xyz") stages.push({ kind: "lab-to-xyz" });
|
|
1646
|
+
else if (s.pcs === "xyz" && d.pcs === "lab") stages.push({ kind: "xyz-to-lab" });
|
|
1647
|
+
stages.push(...d.stages);
|
|
1648
|
+
return stages;
|
|
1649
|
+
}
|
|
1650
|
+
function connection(src, dst, intent, bpc) {
|
|
1651
|
+
if (intent === "absolute") {
|
|
1652
|
+
const ws = mediaWhite(src);
|
|
1653
|
+
const wd = mediaWhite(dst);
|
|
1654
|
+
const usable = (w) => w.every((v) => Number.isFinite(v) && v > 0);
|
|
1655
|
+
if (!usable(ws) || !usable(wd)) throw new CmsError("absolute colorimetric needs a positive media white point in both profiles");
|
|
1656
|
+
const s = [ws[0] / wd[0], ws[1] / wd[1], ws[2] / wd[2]];
|
|
1657
|
+
if (s.every((v) => Math.abs(v - 1) < 1e-6)) return null;
|
|
1658
|
+
return { scale: s, offset: [0, 0, 0] };
|
|
1659
|
+
}
|
|
1660
|
+
if (!bpc) return null;
|
|
1661
|
+
const bs = blackPoint(src, intent);
|
|
1662
|
+
const bd = blackPoint(dst, intent);
|
|
1663
|
+
if (Math.abs(bs - bd) < 1e-6) return null;
|
|
1664
|
+
const scale = [1, 1, 1];
|
|
1665
|
+
const offset = [0, 0, 0];
|
|
1666
|
+
for (let i = 0; i < 3; i++) {
|
|
1667
|
+
const s = bs * D50[i];
|
|
1668
|
+
const d = bd * D50[i];
|
|
1669
|
+
const a = (D50[i] - d) / (D50[i] - s);
|
|
1670
|
+
scale[i] = a;
|
|
1671
|
+
offset[i] = D50[i] * (1 - a);
|
|
1672
|
+
}
|
|
1673
|
+
return { scale, offset };
|
|
1674
|
+
}
|
|
1675
|
+
function blackPoint(p, intent) {
|
|
1676
|
+
if (p.colorSpace === "lab" || p.colorSpace === "xyz") return 0;
|
|
1677
|
+
const lutBased = p.a2b.some(Boolean);
|
|
1678
|
+
if (lutBased && p.version[0] >= 4 && (intent === "perceptual" || intent === "saturation")) return PERCEPTUAL_BLACK_Y;
|
|
1679
|
+
const n = profileChannels(p);
|
|
1680
|
+
const pcsY = (st, pcs, dev) => {
|
|
1681
|
+
const out = new Float32Array(16);
|
|
1682
|
+
new Pipeline(n, st.slice()).eval(dev, out);
|
|
1683
|
+
const y = pcs === "xyz" ? out[1] : labToXyz([out[0], 0, 0])[1];
|
|
1684
|
+
return Math.min(0.5, Math.max(0, y));
|
|
1685
|
+
};
|
|
1686
|
+
const to = deviceToPcs(p, intent);
|
|
1687
|
+
const additive = p.colorSpace === "rgb" || p.colorSpace === "gray";
|
|
1688
|
+
if (additive || !lutBased) return pcsY(to.stages, to.pcs, new Array(n).fill(0));
|
|
1689
|
+
const colorimetric = intent === "saturation" ? "saturation" : "relative";
|
|
1690
|
+
try {
|
|
1691
|
+
const from = pcsToDevice(p, colorimetric);
|
|
1692
|
+
const st = from.pcs === "xyz" ? [{ kind: "lab-to-xyz" }, ...from.stages] : from.stages.slice();
|
|
1693
|
+
const dev = new Float32Array(16);
|
|
1694
|
+
new Pipeline(3, st).eval([0, 0, 0], dev);
|
|
1695
|
+
return pcsY(to.stages, to.pcs, dev.subarray(0, n));
|
|
1696
|
+
} catch {
|
|
1697
|
+
return pcsY(to.stages, to.pcs, new Array(n).fill(1));
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
var ids = /* @__PURE__ */ new WeakMap();
|
|
1701
|
+
var nextId = 1;
|
|
1702
|
+
var idOf = (p) => {
|
|
1703
|
+
let id = ids.get(p);
|
|
1704
|
+
if (!id) {
|
|
1705
|
+
id = nextId++;
|
|
1706
|
+
ids.set(p, id);
|
|
1707
|
+
}
|
|
1708
|
+
return id;
|
|
1709
|
+
};
|
|
1710
|
+
var cache = /* @__PURE__ */ new Map();
|
|
1711
|
+
function cachedTransform(src, dst, options = {}) {
|
|
1712
|
+
const opts = { ...DEFAULT_TRANSFORM_OPTIONS, ...options };
|
|
1713
|
+
const key = `${idOf(src)}:${idOf(dst)}:${opts.intent}:${opts.bpc}:${opts.preciseFloat}`;
|
|
1714
|
+
let t = cache.get(key);
|
|
1715
|
+
if (!t) {
|
|
1716
|
+
if (cache.size > 64) cache.clear();
|
|
1717
|
+
t = Transform.create(src, dst, opts);
|
|
1718
|
+
cache.set(key, t);
|
|
1719
|
+
}
|
|
1720
|
+
return t;
|
|
1721
|
+
}
|
|
1722
|
+
function sameColors(a, b, lab) {
|
|
1723
|
+
if (a === b) return true;
|
|
1724
|
+
if (a.bytes && b.bytes && a.bytes.length === b.bytes.length && a.bytes.every((v, i) => v === b.bytes[i])) return true;
|
|
1725
|
+
const n = profileChannels(a);
|
|
1726
|
+
if (a.colorSpace !== b.colorSpace || n === 0 || n > 4) return false;
|
|
1727
|
+
let ta;
|
|
1728
|
+
let tb;
|
|
1729
|
+
try {
|
|
1730
|
+
ta = Transform.create(a, lab, { intent: "relative", bpc: false });
|
|
1731
|
+
tb = Transform.create(b, lab, { intent: "relative", bpc: false });
|
|
1732
|
+
} catch {
|
|
1733
|
+
return false;
|
|
1734
|
+
}
|
|
1735
|
+
const steps = n <= 3 ? 17 : 9;
|
|
1736
|
+
const decode = (o) => [o[0] * 100, o[1] * 255 - 128, o[2] * 255 - 128];
|
|
1737
|
+
const input = new Float32Array(4);
|
|
1738
|
+
const la = new Float32Array(16);
|
|
1739
|
+
const lb = new Float32Array(16);
|
|
1740
|
+
const total = Math.pow(steps, n);
|
|
1741
|
+
for (let i = 0; i < total; i++) {
|
|
1742
|
+
let k = i;
|
|
1743
|
+
for (let c = 0; c < n; c++) {
|
|
1744
|
+
input[c] = k % steps / (steps - 1);
|
|
1745
|
+
k = Math.floor(k / steps);
|
|
1746
|
+
}
|
|
1747
|
+
ta.eval(input, la);
|
|
1748
|
+
tb.eval(input, lb);
|
|
1749
|
+
if (deltaE76(decode(la), decode(lb)) > 0.5) return false;
|
|
1750
|
+
}
|
|
1751
|
+
return true;
|
|
1752
|
+
}
|
|
1753
|
+
var GamutCheck = class {
|
|
1754
|
+
constructor(src, proof, lab, threshold = 4) {
|
|
1755
|
+
this.threshold = threshold;
|
|
1756
|
+
const n = profileChannels(src);
|
|
1757
|
+
this.direct = new Pipeline(n, linkStages(src, lab, "relative", false));
|
|
1758
|
+
const via = linkStages(src, proof, "relative", false);
|
|
1759
|
+
via.push({ kind: "clamp01" });
|
|
1760
|
+
via.push(...linkStages(proof, lab, "relative", false));
|
|
1761
|
+
this.via = new Pipeline(n, via);
|
|
1762
|
+
const a = new Float32Array(16);
|
|
1763
|
+
const b = new Float32Array(16);
|
|
1764
|
+
const de = (p, o) => {
|
|
1765
|
+
this.direct.eval(p, a);
|
|
1766
|
+
this.via.eval(p, b);
|
|
1767
|
+
o[0] = Math.hypot((a[0] - b[0]) * 100, (a[1] - b[1]) * 255, (a[2] - b[2]) * 255);
|
|
1768
|
+
};
|
|
1769
|
+
this.grid = n === 1 ? Clut.sample([256], 1, de) : n === 3 ? Clut.sample([33, 33, 33], 1, de) : n === 4 ? Clut.sample([17, 17, 17, 17], 1, de) : null;
|
|
1770
|
+
}
|
|
1771
|
+
threshold;
|
|
1772
|
+
grid;
|
|
1773
|
+
direct;
|
|
1774
|
+
via;
|
|
1775
|
+
/** ΔE*ab of a colour (normalised device values). */
|
|
1776
|
+
deltaE(px) {
|
|
1777
|
+
const o = new Float32Array(16);
|
|
1778
|
+
if (this.grid) this.grid.eval(px, o);
|
|
1779
|
+
else {
|
|
1780
|
+
const a = new Float32Array(16);
|
|
1781
|
+
const b = new Float32Array(16);
|
|
1782
|
+
this.direct.eval(px, a);
|
|
1783
|
+
this.via.eval(px, b);
|
|
1784
|
+
o[0] = Math.hypot((a[0] - b[0]) * 100, (a[1] - b[1]) * 255, (a[2] - b[2]) * 255);
|
|
1785
|
+
}
|
|
1786
|
+
return o[0];
|
|
1787
|
+
}
|
|
1788
|
+
outOfGamut(px) {
|
|
1789
|
+
return this.deltaE(px) > this.threshold;
|
|
1790
|
+
}
|
|
1791
|
+
};
|
|
1792
|
+
var Lut3d = class {
|
|
1793
|
+
constructor(t, size = 33) {
|
|
1794
|
+
this.size = size;
|
|
1795
|
+
if (t.inputs !== 3 || t.outputs !== 3 && t.outputs !== 1) throw new RangeError("display LUTs need a 3 \u2192 3 or 3 \u2192 1 transform");
|
|
1796
|
+
const n = size;
|
|
1797
|
+
this.data = new Float32Array(n * n * n * 3);
|
|
1798
|
+
const out = new Float32Array(16);
|
|
1799
|
+
const s = n - 1;
|
|
1800
|
+
for (let b = 0; b < n; b++) {
|
|
1801
|
+
for (let g = 0; g < n; g++) {
|
|
1802
|
+
for (let r = 0; r < n; r++) {
|
|
1803
|
+
t.eval([r / s, g / s, b / s], out);
|
|
1804
|
+
const i = (r + g * n + b * n * n) * 3;
|
|
1805
|
+
this.data[i] = out[0];
|
|
1806
|
+
this.data[i + 1] = t.outputs === 1 ? out[0] : out[1];
|
|
1807
|
+
this.data[i + 2] = t.outputs === 1 ? out[0] : out[2];
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
size;
|
|
1813
|
+
data;
|
|
1814
|
+
/** Trilinear sample of 0..1 RGB into `out`. */
|
|
1815
|
+
sample(r, g, b, out) {
|
|
1816
|
+
const n = this.size;
|
|
1817
|
+
const loc = (v) => {
|
|
1818
|
+
const p = Math.min(1, Math.max(0, v)) * (n - 1);
|
|
1819
|
+
const i = Math.min(n - 2, Math.floor(p));
|
|
1820
|
+
return [i, p - i];
|
|
1821
|
+
};
|
|
1822
|
+
const [r0, fr] = loc(r);
|
|
1823
|
+
const [g0, fg] = loc(g);
|
|
1824
|
+
const [b0, fb] = loc(b);
|
|
1825
|
+
const d = this.data;
|
|
1826
|
+
const at = (rr, gg, bb, k) => d[(rr + gg * n + bb * n * n) * 3 + k];
|
|
1827
|
+
for (let k = 0; k < 3; k++) {
|
|
1828
|
+
const x00 = at(r0, g0, b0, k) + (at(r0 + 1, g0, b0, k) - at(r0, g0, b0, k)) * fr;
|
|
1829
|
+
const x10 = at(r0, g0 + 1, b0, k) + (at(r0 + 1, g0 + 1, b0, k) - at(r0, g0 + 1, b0, k)) * fr;
|
|
1830
|
+
const x01 = at(r0, g0, b0 + 1, k) + (at(r0 + 1, g0, b0 + 1, k) - at(r0, g0, b0 + 1, k)) * fr;
|
|
1831
|
+
const x11 = at(r0, g0 + 1, b0 + 1, k) + (at(r0 + 1, g0 + 1, b0 + 1, k) - at(r0, g0 + 1, b0 + 1, k)) * fr;
|
|
1832
|
+
const y0 = x00 + (x10 - x00) * fg;
|
|
1833
|
+
const y1 = x01 + (x11 - x01) * fg;
|
|
1834
|
+
out[k] = y0 + (y1 - y0) * fb;
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
/** Applies the table to 8-bit RGBA in place (alpha kept), through a per-code 8-bit cache of the lattice. */
|
|
1838
|
+
apply8(data) {
|
|
1839
|
+
const o = [0, 0, 0];
|
|
1840
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
1841
|
+
this.sample(data[i] / 255, data[i + 1] / 255, data[i + 2] / 255, o);
|
|
1842
|
+
data[i] = Math.round(Math.min(1, Math.max(0, o[0])) * 255);
|
|
1843
|
+
data[i + 1] = Math.round(Math.min(1, Math.max(0, o[1])) * 255);
|
|
1844
|
+
data[i + 2] = Math.round(Math.min(1, Math.max(0, o[2])) * 255);
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
};
|
|
1848
|
+
|
|
1849
|
+
// src/cms/synth.ts
|
|
1850
|
+
var COATED_CMYK_DESCRIPTION = "Pixerate Coated CMYK (synthetic, 300% TAC, medium GCR)";
|
|
1851
|
+
var DEFAULT_CMYK_PARAMS = {
|
|
1852
|
+
description: COATED_CMYK_DESCRIPTION,
|
|
1853
|
+
tac: 3,
|
|
1854
|
+
gcrStart: 0.2,
|
|
1855
|
+
kMax: 0.95,
|
|
1856
|
+
gcrGamma: 1.1,
|
|
1857
|
+
tvi: [0.14, 0.14, 0.14, 0.17],
|
|
1858
|
+
gridA2b: 11,
|
|
1859
|
+
gridB2a: 17
|
|
1860
|
+
};
|
|
1861
|
+
var YN = 2;
|
|
1862
|
+
var CmykModel = class {
|
|
1863
|
+
constructor(tvi) {
|
|
1864
|
+
this.tvi = tvi;
|
|
1865
|
+
const lab = (l, a, b) => labToXyz([l, a, b]);
|
|
1866
|
+
const w = lab(95, 0, -2);
|
|
1867
|
+
const base = [
|
|
1868
|
+
w,
|
|
1869
|
+
// paper
|
|
1870
|
+
lab(55, -37, -50),
|
|
1871
|
+
// C
|
|
1872
|
+
lab(48, 74, -3),
|
|
1873
|
+
// M
|
|
1874
|
+
lab(24, 22, -46),
|
|
1875
|
+
// C+M
|
|
1876
|
+
lab(89, -5, 93),
|
|
1877
|
+
// Y
|
|
1878
|
+
lab(50, -65, 27),
|
|
1879
|
+
// C+Y
|
|
1880
|
+
lab(47, 68, 48),
|
|
1881
|
+
// M+Y
|
|
1882
|
+
lab(23, 0, 0)
|
|
1883
|
+
// C+M+Y
|
|
1884
|
+
];
|
|
1885
|
+
const k = lab(16, 0, 0);
|
|
1886
|
+
const floor = 6e-3;
|
|
1887
|
+
for (let m = 0; m < 16; m++) {
|
|
1888
|
+
const p = base[m & 7];
|
|
1889
|
+
const prim = m & 8 ? [0, 1, 2].map((i) => {
|
|
1890
|
+
const s = floor * w[i];
|
|
1891
|
+
return s + (p[i] - s) * (k[i] - s) / (w[i] - s);
|
|
1892
|
+
}) : p;
|
|
1893
|
+
this.primN.push(prim.map((v) => Math.pow(Math.max(0, v), 1 / YN)));
|
|
1894
|
+
}
|
|
1895
|
+
this.paper = w;
|
|
1896
|
+
}
|
|
1897
|
+
tvi;
|
|
1898
|
+
primN = [];
|
|
1899
|
+
paper;
|
|
1900
|
+
xyz(cmyk) {
|
|
1901
|
+
const a = [0, 1, 2, 3].map((i) => {
|
|
1902
|
+
const v = Math.min(1, Math.max(0, cmyk[i]));
|
|
1903
|
+
return Math.min(1, Math.max(0, v + 4 * this.tvi[i] * v * (1 - v)));
|
|
1904
|
+
});
|
|
1905
|
+
const s = [0, 0, 0];
|
|
1906
|
+
for (let m = 0; m < 16; m++) {
|
|
1907
|
+
let w = 1;
|
|
1908
|
+
for (let i = 0; i < 4; i++) w *= m & 1 << i ? a[i] : 1 - a[i];
|
|
1909
|
+
if (w === 0) continue;
|
|
1910
|
+
const p = this.primN[m];
|
|
1911
|
+
s[0] += w * p[0];
|
|
1912
|
+
s[1] += w * p[1];
|
|
1913
|
+
s[2] += w * p[2];
|
|
1914
|
+
}
|
|
1915
|
+
return [Math.pow(s[0], YN), Math.pow(s[1], YN), Math.pow(s[2], YN)];
|
|
1916
|
+
}
|
|
1917
|
+
lab(cmyk) {
|
|
1918
|
+
const x = this.xyz(cmyk);
|
|
1919
|
+
return xyzToLab([x[0] * D50[0] / this.paper[0], x[1] * D50[1] / this.paper[1], x[2] * D50[2] / this.paper[2]]);
|
|
1920
|
+
}
|
|
1921
|
+
};
|
|
1922
|
+
function solveCmy(model, target, k, limit, seeds) {
|
|
1923
|
+
const project = (x) => {
|
|
1924
|
+
const v = [x[0], x[1], x[2]];
|
|
1925
|
+
for (let it = 0; it < 4; it++) {
|
|
1926
|
+
for (let i = 0; i < 3; i++) v[i] = Math.min(1, Math.max(0, v[i]));
|
|
1927
|
+
const s = v[0] + v[1] + v[2];
|
|
1928
|
+
if (s <= limit + 1e-12) break;
|
|
1929
|
+
const active = Math.max(1, v.filter((x2) => x2 > 0).length);
|
|
1930
|
+
const d = (s - limit) / active;
|
|
1931
|
+
for (let i = 0; i < 3; i++) if (v[i] > 0) v[i] -= d;
|
|
1932
|
+
}
|
|
1933
|
+
return v;
|
|
1934
|
+
};
|
|
1935
|
+
const err = (x) => {
|
|
1936
|
+
const l = model.lab([x[0], x[1], x[2], k]);
|
|
1937
|
+
return [l[0] - target[0], l[1] - target[1], l[2] - target[2]];
|
|
1938
|
+
};
|
|
1939
|
+
const norm2 = (e) => e[0] * e[0] + e[1] * e[1] + e[2] * e[2];
|
|
1940
|
+
let best = [[0, 0, 0], Infinity];
|
|
1941
|
+
for (const seed of seeds) {
|
|
1942
|
+
let x = project(seed);
|
|
1943
|
+
let e = err(x);
|
|
1944
|
+
let f = norm2(e);
|
|
1945
|
+
let lambda = 1e-3;
|
|
1946
|
+
for (let iter = 0; iter < 60; iter++) {
|
|
1947
|
+
const h = 1e-5;
|
|
1948
|
+
const j = [
|
|
1949
|
+
[0, 0, 0],
|
|
1950
|
+
[0, 0, 0],
|
|
1951
|
+
[0, 0, 0]
|
|
1952
|
+
];
|
|
1953
|
+
for (let c = 0; c < 3; c++) {
|
|
1954
|
+
const xp = [x[0], x[1], x[2]];
|
|
1955
|
+
xp[c] += h;
|
|
1956
|
+
const ep = err(xp);
|
|
1957
|
+
for (let r = 0; r < 3; r++) j[r][c] = (ep[r] - e[r]) / h;
|
|
1958
|
+
}
|
|
1959
|
+
const a = [
|
|
1960
|
+
[0, 0, 0],
|
|
1961
|
+
[0, 0, 0],
|
|
1962
|
+
[0, 0, 0]
|
|
1963
|
+
];
|
|
1964
|
+
const g = [0, 0, 0];
|
|
1965
|
+
for (let r = 0; r < 3; r++) {
|
|
1966
|
+
for (let c = 0; c < 3; c++) a[r][c] = j[0][r] * j[0][c] + j[1][r] * j[1][c] + j[2][r] * j[2][c];
|
|
1967
|
+
g[r] = -(j[0][r] * e[0] + j[1][r] * e[1] + j[2][r] * e[2]);
|
|
1968
|
+
}
|
|
1969
|
+
let improved = false;
|
|
1970
|
+
for (let t = 0; t < 8; t++) {
|
|
1971
|
+
const al = [
|
|
1972
|
+
[a[0][0], a[0][1], a[0][2]],
|
|
1973
|
+
[a[1][0], a[1][1], a[1][2]],
|
|
1974
|
+
[a[2][0], a[2][1], a[2][2]]
|
|
1975
|
+
];
|
|
1976
|
+
for (let d = 0; d < 3; d++) al[d][d] += lambda * (1 + a[d][d]);
|
|
1977
|
+
const inv = invert(al);
|
|
1978
|
+
if (!inv) {
|
|
1979
|
+
lambda *= 10;
|
|
1980
|
+
continue;
|
|
1981
|
+
}
|
|
1982
|
+
const step = apply(inv, g);
|
|
1983
|
+
const xn = project([x[0] + step[0], x[1] + step[1], x[2] + step[2]]);
|
|
1984
|
+
const en = err(xn);
|
|
1985
|
+
const fnew = norm2(en);
|
|
1986
|
+
if (fnew < f) {
|
|
1987
|
+
const moved = Math.max(Math.abs(xn[0] - x[0]), Math.abs(xn[1] - x[1]), Math.abs(xn[2] - x[2]));
|
|
1988
|
+
x = xn;
|
|
1989
|
+
e = en;
|
|
1990
|
+
f = fnew;
|
|
1991
|
+
lambda = Math.max(1e-9, lambda * 0.3);
|
|
1992
|
+
improved = moved > 1e-9;
|
|
1993
|
+
break;
|
|
1994
|
+
}
|
|
1995
|
+
lambda *= 10;
|
|
1996
|
+
}
|
|
1997
|
+
if (!improved || f < 1e-10) break;
|
|
1998
|
+
}
|
|
1999
|
+
if (f < best[1]) best = [x, f];
|
|
2000
|
+
if (best[1] < 1e-6) break;
|
|
2001
|
+
}
|
|
2002
|
+
return [best[0], Math.sqrt(best[1])];
|
|
2003
|
+
}
|
|
2004
|
+
var SEEDS = [
|
|
2005
|
+
[0.3, 0.3, 0.3],
|
|
2006
|
+
[0.05, 0.05, 0.05],
|
|
2007
|
+
[0.8, 0.8, 0.8],
|
|
2008
|
+
[0.9, 0.2, 0.2],
|
|
2009
|
+
[0.2, 0.9, 0.9]
|
|
2010
|
+
];
|
|
2011
|
+
function separate(model, p, target, warm) {
|
|
2012
|
+
const seeds = warm ? [[warm[0], warm[1], warm[2]], ...SEEDS] : SEEDS;
|
|
2013
|
+
const [cmy0] = solveCmy(model, target, 0, 3, seeds);
|
|
2014
|
+
const g = Math.min(cmy0[0], cmy0[1], cmy0[2]);
|
|
2015
|
+
const k = g <= p.gcrStart ? 0 : p.kMax * Math.pow((g - p.gcrStart) / (1 - p.gcrStart), p.gcrGamma);
|
|
2016
|
+
const seeds2 = [[Math.max(0, cmy0[0] - k), Math.max(0, cmy0[1] - k), Math.max(0, cmy0[2] - k)], cmy0, ...seeds];
|
|
2017
|
+
const [cmy, de] = solveCmy(model, target, k, Math.max(0, p.tac - k), seeds2);
|
|
2018
|
+
return [[cmy[0], cmy[1], cmy[2], k], de];
|
|
2019
|
+
}
|
|
2020
|
+
var Gbd = class {
|
|
2021
|
+
lBins = 41;
|
|
2022
|
+
hBins = 72;
|
|
2023
|
+
cmax;
|
|
2024
|
+
blackL;
|
|
2025
|
+
constructor(model, p) {
|
|
2026
|
+
const { lBins, hBins } = this;
|
|
2027
|
+
const cmax = new Float64Array(lBins * hBins);
|
|
2028
|
+
const n = 13;
|
|
2029
|
+
let blackL = 100;
|
|
2030
|
+
for (let ci = 0; ci < n; ci++) {
|
|
2031
|
+
for (let mi = 0; mi < n; mi++) {
|
|
2032
|
+
for (let yi = 0; yi < n; yi++) {
|
|
2033
|
+
for (let ki = 0; ki < 7; ki++) {
|
|
2034
|
+
const v = [ci / (n - 1), mi / (n - 1), yi / (n - 1), ki / 6];
|
|
2035
|
+
if (v[0] + v[1] + v[2] + v[3] > p.tac + 1e-9) continue;
|
|
2036
|
+
const lab = model.lab(v);
|
|
2037
|
+
blackL = Math.min(blackL, lab[0]);
|
|
2038
|
+
const c = Math.hypot(lab[1], lab[2]);
|
|
2039
|
+
const h = (Math.atan2(lab[2], lab[1]) + 2 * Math.PI) % (2 * Math.PI);
|
|
2040
|
+
const li = Math.min(lBins - 1, Math.max(0, Math.round(lab[0] / 100 * (lBins - 1))));
|
|
2041
|
+
const hi = Math.round(h / (2 * Math.PI) * hBins) % hBins;
|
|
2042
|
+
cmax[li * hBins + hi] = Math.max(cmax[li * hBins + hi], c);
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
const get = (v, l, h) => v[Math.min(lBins - 1, Math.max(0, l)) * hBins + (h % hBins + hBins) % hBins];
|
|
2048
|
+
const filled = new Float64Array(cmax.length);
|
|
2049
|
+
for (let l = 0; l < lBins; l++) {
|
|
2050
|
+
for (let h = 0; h < hBins; h++) {
|
|
2051
|
+
let m = 0;
|
|
2052
|
+
for (let dl = -1; dl <= 1; dl++) for (let dh = -1; dh <= 1; dh++) m = Math.max(m, get(cmax, l + dl, h + dh));
|
|
2053
|
+
filled[l * hBins + h] = m;
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
this.cmax = new Float64Array(cmax.length);
|
|
2057
|
+
for (let l = 0; l < lBins; l++) {
|
|
2058
|
+
for (let h = 0; h < hBins; h++) {
|
|
2059
|
+
let s = 0;
|
|
2060
|
+
for (let dl = -1; dl <= 1; dl++) for (let dh = -1; dh <= 1; dh++) s += get(filled, l + dl, h + dh);
|
|
2061
|
+
this.cmax[l * hBins + h] = s / 9;
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
this.blackL = blackL;
|
|
2065
|
+
}
|
|
2066
|
+
maxChroma(l, h) {
|
|
2067
|
+
const { lBins, hBins } = this;
|
|
2068
|
+
const lp = Math.min(1, Math.max(0, l / 100)) * (lBins - 1);
|
|
2069
|
+
const hp = (h % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI) / (2 * Math.PI) * hBins;
|
|
2070
|
+
const l0 = Math.floor(lp);
|
|
2071
|
+
const h0 = Math.floor(hp);
|
|
2072
|
+
const fl = lp - l0;
|
|
2073
|
+
const fh = hp - h0;
|
|
2074
|
+
const l1 = Math.min(lBins - 1, l0 + 1);
|
|
2075
|
+
const h0m = h0 % hBins;
|
|
2076
|
+
const h1m = (h0 + 1) % hBins;
|
|
2077
|
+
const v = (ll, hh) => this.cmax[ll * hBins + hh];
|
|
2078
|
+
const a = v(l0, h0m) + (v(l0, h1m) - v(l0, h0m)) * fh;
|
|
2079
|
+
const b = v(l1, h0m) + (v(l1, h1m) - v(l1, h0m)) * fh;
|
|
2080
|
+
return a + (b - a) * fl;
|
|
2081
|
+
}
|
|
2082
|
+
/** Lightness mapped onto the printable range, chroma softly compressed above 80 % of the boundary. */
|
|
2083
|
+
compress(lab) {
|
|
2084
|
+
const l = this.blackL + Math.min(100, Math.max(0, lab[0])) * (100 - this.blackL) / 100;
|
|
2085
|
+
const c = Math.hypot(lab[1], lab[2]);
|
|
2086
|
+
if (c < 1e-9) return [l, 0, 0];
|
|
2087
|
+
const h = Math.atan2(lab[2], lab[1]);
|
|
2088
|
+
const cm = Math.max(1e-3, this.maxChroma(l, h));
|
|
2089
|
+
const knee = 0.8 * cm;
|
|
2090
|
+
const c2 = c <= knee ? c : knee + (cm - knee) * Math.tanh((c - knee) / (cm - knee));
|
|
2091
|
+
return [l, c2 * Math.cos(h), c2 * Math.sin(h)];
|
|
2092
|
+
}
|
|
2093
|
+
};
|
|
2094
|
+
var decodeV2 = (v) => [v[0] * 100 * 65535 / 65280, v[1] * 65535 / 256 - 128, v[2] * 65535 / 256 - 128];
|
|
2095
|
+
var encodeV2 = (lab) => [
|
|
2096
|
+
Math.min(1, Math.max(0, lab[0] / (100 * 65535 / 65280))),
|
|
2097
|
+
Math.min(1, Math.max(0, (lab[1] + 128) * 256 / 65535)),
|
|
2098
|
+
Math.min(1, Math.max(0, (lab[2] + 128) * 256 / 65535))
|
|
2099
|
+
];
|
|
2100
|
+
function b2aTable(model, p, gbd) {
|
|
2101
|
+
const n = p.gridB2a;
|
|
2102
|
+
const data = new Float32Array(n * n * n * 4);
|
|
2103
|
+
for (let li = 0; li < n; li++) {
|
|
2104
|
+
for (let ai = 0; ai < n; ai++) {
|
|
2105
|
+
let warm = null;
|
|
2106
|
+
for (let bi = 0; bi < n; bi++) {
|
|
2107
|
+
const lab = decodeV2([li / (n - 1), ai / (n - 1), bi / (n - 1)]);
|
|
2108
|
+
const target = gbd ? gbd.compress(lab) : lab;
|
|
2109
|
+
const [cmyk] = separate(model, p, target, warm);
|
|
2110
|
+
warm = cmyk;
|
|
2111
|
+
data.set(cmyk, ((li * n + ai) * n + bi) * 4);
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
return new Clut([n, n, n], 4, data);
|
|
2116
|
+
}
|
|
2117
|
+
var ramp = () => ({ type: "table", table: Float32Array.from([0, 1]) });
|
|
2118
|
+
function lut16(clut) {
|
|
2119
|
+
return {
|
|
2120
|
+
kind: "lut16",
|
|
2121
|
+
inputs: clut.inputs,
|
|
2122
|
+
outputs: clut.outputs,
|
|
2123
|
+
stages: [
|
|
2124
|
+
{ kind: "curves", curves: Array.from({ length: clut.inputs }, ramp) },
|
|
2125
|
+
{ kind: "clut", clut },
|
|
2126
|
+
{ kind: "curves", curves: Array.from({ length: clut.outputs }, ramp) }
|
|
2127
|
+
]
|
|
2128
|
+
};
|
|
2129
|
+
}
|
|
2130
|
+
function cmykProfile(p = DEFAULT_CMYK_PARAMS, copyright = "") {
|
|
2131
|
+
const model = new CmykModel(p.tvi);
|
|
2132
|
+
const g = p.gridA2b;
|
|
2133
|
+
const a2bClut = Clut.sample([g, g, g, g], 3, (inp, out) => out.set(encodeV2(model.lab(inp))));
|
|
2134
|
+
const gbd = new Gbd(model, p);
|
|
2135
|
+
const a2b = lut16(a2bClut);
|
|
2136
|
+
const perceptual = lut16(b2aTable(model, p, gbd));
|
|
2137
|
+
const colorimetric = lut16(b2aTable(model, p, null));
|
|
2138
|
+
return {
|
|
2139
|
+
version: [4, 48],
|
|
2140
|
+
profileClass: "output",
|
|
2141
|
+
colorSpace: "cmyk",
|
|
2142
|
+
pcs: "lab",
|
|
2143
|
+
renderingIntent: "perceptual",
|
|
2144
|
+
description: p.description,
|
|
2145
|
+
copyright,
|
|
2146
|
+
whitePoint: model.paper.map(s15f16Round),
|
|
2147
|
+
blackPoint: null,
|
|
2148
|
+
chad: null,
|
|
2149
|
+
matrix: null,
|
|
2150
|
+
trc: null,
|
|
2151
|
+
grayTrc: null,
|
|
2152
|
+
a2b: [a2b, a2b, a2b],
|
|
2153
|
+
b2a: [perceptual, colorimetric, perceptual],
|
|
2154
|
+
bytes: null
|
|
2155
|
+
};
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
// src/cms/builtin.ts
|
|
2159
|
+
var BUILTIN_COPYRIGHT = "No copyright: CC0-1.0 public domain dedication. Generated by Pixerate.";
|
|
2160
|
+
var BUILTIN_PROFILES = [
|
|
2161
|
+
{ id: "srgb", description: "sRGB IEC61966-2.1", colorSpace: "rgb" },
|
|
2162
|
+
{ id: "display-p3", description: "Display P3", colorSpace: "rgb" },
|
|
2163
|
+
{ id: "adobe-rgb-compat", description: "Adobe RGB (1998)-compatible (Pixerate)", colorSpace: "rgb" },
|
|
2164
|
+
{ id: "prophoto-compat", description: "ProPhoto RGB-compatible (ROMM primaries, Pixerate)", colorSpace: "rgb" },
|
|
2165
|
+
{ id: "linear-srgb", description: "Linear sRGB (Pixerate)", colorSpace: "rgb" },
|
|
2166
|
+
{ id: "rec2020", description: "Rec. ITU-R BT.2020 (Pixerate)", colorSpace: "rgb" },
|
|
2167
|
+
{ id: "gray-gamma-2.2", description: "Gray Gamma 2.2 (Pixerate)", colorSpace: "gray" },
|
|
2168
|
+
{ id: "sgray", description: "sGray (sRGB tone curve, Pixerate)", colorSpace: "gray" },
|
|
2169
|
+
{ id: "dot-gain-20-compat", description: "Dot Gain 20%-compatible (gamma 1.732, Pixerate)", colorSpace: "gray" },
|
|
2170
|
+
{ id: "lab-d50", description: "Lab D50 identity (Pixerate)", colorSpace: "lab" },
|
|
2171
|
+
{ id: "coated-cmyk", description: "Pixerate Coated CMYK (synthetic, 300% TAC, medium GCR)", colorSpace: "cmyk" }
|
|
2172
|
+
];
|
|
2173
|
+
function builtinProfileId(s) {
|
|
2174
|
+
const n = s.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
2175
|
+
const aliases = {
|
|
2176
|
+
srgb: "srgb",
|
|
2177
|
+
srgbiec6196621: "srgb",
|
|
2178
|
+
p3: "display-p3",
|
|
2179
|
+
displayp3: "display-p3",
|
|
2180
|
+
adobergb: "adobe-rgb-compat",
|
|
2181
|
+
adobergb1998: "adobe-rgb-compat",
|
|
2182
|
+
adobergbcompat: "adobe-rgb-compat",
|
|
2183
|
+
prophoto: "prophoto-compat",
|
|
2184
|
+
prophotorgb: "prophoto-compat",
|
|
2185
|
+
prophotocompat: "prophoto-compat",
|
|
2186
|
+
linearsrgb: "linear-srgb",
|
|
2187
|
+
rec2020: "rec2020",
|
|
2188
|
+
bt2020: "rec2020",
|
|
2189
|
+
gray: "gray-gamma-2.2",
|
|
2190
|
+
graygamma22: "gray-gamma-2.2",
|
|
2191
|
+
sgray: "sgray",
|
|
2192
|
+
dotgain20: "dot-gain-20-compat",
|
|
2193
|
+
dotgain20compat: "dot-gain-20-compat",
|
|
2194
|
+
lab: "lab-d50",
|
|
2195
|
+
labd50: "lab-d50",
|
|
2196
|
+
cmyk: "coated-cmyk",
|
|
2197
|
+
coatedcmyk: "coated-cmyk"
|
|
2198
|
+
};
|
|
2199
|
+
return aliases[n] ?? BUILTIN_PROFILES.find((b) => b.description.toLowerCase() === s.toLowerCase())?.id ?? null;
|
|
2200
|
+
}
|
|
2201
|
+
function colorants(r, g, b, white) {
|
|
2202
|
+
const m = rgbToXyzMatrix(r, g, b, white);
|
|
2203
|
+
const d50 = d50Quantized();
|
|
2204
|
+
const nearD50 = Math.abs(white[0] - D50[0]) < 1e-3 && Math.abs(white[2] - D50[2]) < 1e-3;
|
|
2205
|
+
const adapt = nearD50 ? null : bradford(white, D50);
|
|
2206
|
+
let m50;
|
|
2207
|
+
if (adapt) m50 = mul(adapt, m);
|
|
2208
|
+
else {
|
|
2209
|
+
const s = apply(m, [1, 1, 1]);
|
|
2210
|
+
m50 = m.map((row, i) => row.map((v) => v * D50[i] / s[i]));
|
|
2211
|
+
}
|
|
2212
|
+
const q = m50.map((row) => row.map(s15f16Round));
|
|
2213
|
+
for (let i = 0; i < 3; i++) {
|
|
2214
|
+
const row = q[i];
|
|
2215
|
+
const err = d50[i] - (row[0] + row[1] + row[2]);
|
|
2216
|
+
let k = 0;
|
|
2217
|
+
for (let j = 1; j < 3; j++) if (Math.abs(row[j]) > Math.abs(row[k])) k = j;
|
|
2218
|
+
row[k] = s15f16Round(row[k] + err);
|
|
2219
|
+
}
|
|
2220
|
+
return { m: q, chad: adapt ? adapt.map((row) => row.map(s15f16Round)) : null };
|
|
2221
|
+
}
|
|
2222
|
+
var withBytes = (p) => ({ ...p, bytes: encodeProfile(p) });
|
|
2223
|
+
function rgbProfile(desc, prim, white, trc) {
|
|
2224
|
+
const { m, chad } = colorants(prim[0], prim[1], prim[2], white);
|
|
2225
|
+
return withBytes({
|
|
2226
|
+
version: [4, 48],
|
|
2227
|
+
profileClass: "display",
|
|
2228
|
+
colorSpace: "rgb",
|
|
2229
|
+
pcs: "xyz",
|
|
2230
|
+
renderingIntent: "perceptual",
|
|
2231
|
+
description: desc,
|
|
2232
|
+
copyright: BUILTIN_COPYRIGHT,
|
|
2233
|
+
whitePoint: d50Quantized(),
|
|
2234
|
+
blackPoint: null,
|
|
2235
|
+
chad,
|
|
2236
|
+
matrix: m,
|
|
2237
|
+
trc: [trc, trc, trc],
|
|
2238
|
+
grayTrc: null,
|
|
2239
|
+
a2b: [null, null, null],
|
|
2240
|
+
b2a: [null, null, null],
|
|
2241
|
+
bytes: null
|
|
2242
|
+
});
|
|
2243
|
+
}
|
|
2244
|
+
function grayProfile(desc, trc) {
|
|
2245
|
+
return withBytes({
|
|
2246
|
+
version: [4, 48],
|
|
2247
|
+
profileClass: "display",
|
|
2248
|
+
colorSpace: "gray",
|
|
2249
|
+
pcs: "xyz",
|
|
2250
|
+
renderingIntent: "perceptual",
|
|
2251
|
+
description: desc,
|
|
2252
|
+
copyright: BUILTIN_COPYRIGHT,
|
|
2253
|
+
whitePoint: d50Quantized(),
|
|
2254
|
+
blackPoint: null,
|
|
2255
|
+
chad: null,
|
|
2256
|
+
matrix: null,
|
|
2257
|
+
trc: null,
|
|
2258
|
+
grayTrc: trc,
|
|
2259
|
+
a2b: [null, null, null],
|
|
2260
|
+
b2a: [null, null, null],
|
|
2261
|
+
bytes: null
|
|
2262
|
+
});
|
|
2263
|
+
}
|
|
2264
|
+
function labProfile(desc) {
|
|
2265
|
+
const ident = () => ({ kind: "ab", inputs: 3, outputs: 3, stages: [{ kind: "curves", curves: [IDENTITY_CURVE, IDENTITY_CURVE, IDENTITY_CURVE] }] });
|
|
2266
|
+
return withBytes({
|
|
2267
|
+
version: [4, 48],
|
|
2268
|
+
profileClass: "colorspace",
|
|
2269
|
+
colorSpace: "lab",
|
|
2270
|
+
pcs: "lab",
|
|
2271
|
+
renderingIntent: "perceptual",
|
|
2272
|
+
description: desc,
|
|
2273
|
+
copyright: BUILTIN_COPYRIGHT,
|
|
2274
|
+
whitePoint: d50Quantized(),
|
|
2275
|
+
blackPoint: null,
|
|
2276
|
+
chad: null,
|
|
2277
|
+
matrix: null,
|
|
2278
|
+
trc: null,
|
|
2279
|
+
grayTrc: null,
|
|
2280
|
+
a2b: [ident(), null, null],
|
|
2281
|
+
b2a: [ident(), null, null],
|
|
2282
|
+
bytes: null
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
function build(id) {
|
|
2286
|
+
const desc = BUILTIN_PROFILES.find((b) => b.id === id).description;
|
|
2287
|
+
const d65 = xyToXyz(D65_XY);
|
|
2288
|
+
const srgbP = [
|
|
2289
|
+
[0.64, 0.33],
|
|
2290
|
+
[0.3, 0.6],
|
|
2291
|
+
[0.15, 0.06]
|
|
2292
|
+
];
|
|
2293
|
+
switch (id) {
|
|
2294
|
+
case "srgb":
|
|
2295
|
+
return rgbProfile(desc, srgbP, d65, srgbTrc());
|
|
2296
|
+
case "display-p3":
|
|
2297
|
+
return rgbProfile(desc, [[0.68, 0.32], [0.265, 0.69], [0.15, 0.06]], d65, srgbTrc());
|
|
2298
|
+
case "adobe-rgb-compat":
|
|
2299
|
+
return rgbProfile(desc, [[0.64, 0.33], [0.21, 0.71], [0.15, 0.06]], d65, gammaCurve(563 / 256));
|
|
2300
|
+
case "prophoto-compat":
|
|
2301
|
+
return rgbProfile(desc, [[0.7347, 0.2653], [0.1596, 0.8404], [0.0366, 1e-4]], [D50[0], D50[1], D50[2]], gammaCurve(1.8));
|
|
2302
|
+
case "linear-srgb":
|
|
2303
|
+
return rgbProfile(desc, srgbP, d65, gammaCurve(1));
|
|
2304
|
+
case "rec2020": {
|
|
2305
|
+
const a = 1.099;
|
|
2306
|
+
return rgbProfile(desc, [[0.708, 0.292], [0.17, 0.797], [0.131, 0.046]], d65, { type: "parametric", kind: 3, p: [1 / 0.45, 1 / a, (a - 1) / a, 1 / 4.5, 0.081, 0, 0] });
|
|
2307
|
+
}
|
|
2308
|
+
case "gray-gamma-2.2":
|
|
2309
|
+
return grayProfile(desc, gammaCurve(2.2));
|
|
2310
|
+
case "sgray":
|
|
2311
|
+
return grayProfile(desc, srgbTrc());
|
|
2312
|
+
case "dot-gain-20-compat":
|
|
2313
|
+
return grayProfile(desc, gammaCurve(1.732));
|
|
2314
|
+
case "lab-d50":
|
|
2315
|
+
return labProfile(desc);
|
|
2316
|
+
case "coated-cmyk": {
|
|
2317
|
+
const p = cmykProfile(void 0, BUILTIN_COPYRIGHT);
|
|
2318
|
+
const bytes = encodeProfile(p);
|
|
2319
|
+
return { ...parseProfile(bytes), bytes };
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
var built = /* @__PURE__ */ new Map();
|
|
2324
|
+
function builtinProfile(id) {
|
|
2325
|
+
let p = built.get(id);
|
|
2326
|
+
if (!p) {
|
|
2327
|
+
p = build(id);
|
|
2328
|
+
built.set(id, p);
|
|
2329
|
+
}
|
|
2330
|
+
return p;
|
|
2331
|
+
}
|
|
2332
|
+
var srgbProfile = () => builtinProfile("srgb");
|
|
2333
|
+
function defaultProfileFor(cs) {
|
|
2334
|
+
switch (cs) {
|
|
2335
|
+
case "rgb":
|
|
2336
|
+
return builtinProfile("srgb");
|
|
2337
|
+
case "gray":
|
|
2338
|
+
return builtinProfile("dot-gain-20-compat");
|
|
2339
|
+
case "cmyk":
|
|
2340
|
+
return builtinProfile("coated-cmyk");
|
|
2341
|
+
case "lab":
|
|
2342
|
+
return builtinProfile("lab-d50");
|
|
2343
|
+
default:
|
|
2344
|
+
return null;
|
|
2345
|
+
}
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
// src/cms/embed.ts
|
|
2349
|
+
var crcTable = null;
|
|
2350
|
+
function crc32(bytes) {
|
|
2351
|
+
if (!crcTable) {
|
|
2352
|
+
crcTable = new Uint32Array(256);
|
|
2353
|
+
for (let n = 0; n < 256; n++) {
|
|
2354
|
+
let c2 = n;
|
|
2355
|
+
for (let k = 0; k < 8; k++) c2 = c2 & 1 ? 3988292384 ^ c2 >>> 1 : c2 >>> 1;
|
|
2356
|
+
crcTable[n] = c2 >>> 0;
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
let c = 4294967295;
|
|
2360
|
+
for (let i = 0; i < bytes.length; i++) c = crcTable[(c ^ bytes[i]) & 255] ^ c >>> 8;
|
|
2361
|
+
return (c ^ 4294967295) >>> 0;
|
|
2362
|
+
}
|
|
2363
|
+
var u32 = (b, o) => (b[o] << 24 | b[o + 1] << 16 | b[o + 2] << 8 | b[o + 3]) >>> 0;
|
|
2364
|
+
var PNG_SIG = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
2365
|
+
var isPngBytes = (b) => b.length >= 8 && PNG_SIG.every((v, i) => b[i] === v);
|
|
2366
|
+
function readPngIcc(png) {
|
|
2367
|
+
if (!isPngBytes(png)) return null;
|
|
2368
|
+
let o = 8;
|
|
2369
|
+
while (o + 12 <= png.length) {
|
|
2370
|
+
const len = u32(png, o);
|
|
2371
|
+
const type = String.fromCharCode(png[o + 4], png[o + 5], png[o + 6], png[o + 7]);
|
|
2372
|
+
if (type === "iCCP") {
|
|
2373
|
+
const d = png.subarray(o + 8, o + 8 + len);
|
|
2374
|
+
const z = d.indexOf(0);
|
|
2375
|
+
if (z < 1 || z > 79 || d[z + 1] !== 0) return null;
|
|
2376
|
+
try {
|
|
2377
|
+
return inflate(d.subarray(z + 2), { maxOutput: 64 * 1024 * 1024 });
|
|
2378
|
+
} catch {
|
|
2379
|
+
return null;
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
if (type === "IDAT" || type === "IEND") return null;
|
|
2383
|
+
o += 12 + len;
|
|
2384
|
+
}
|
|
2385
|
+
return null;
|
|
2386
|
+
}
|
|
2387
|
+
function writePngIcc(png, icc, name = "ICC profile") {
|
|
2388
|
+
if (!isPngBytes(png)) throw new Error("Not a PNG file.");
|
|
2389
|
+
const chunks = [png.subarray(0, 8)];
|
|
2390
|
+
let o = 8;
|
|
2391
|
+
let inserted = false;
|
|
2392
|
+
const label = Array.from(name.slice(0, 79), (c) => c.charCodeAt(0) & 127);
|
|
2393
|
+
const body = new Uint8Array([...label, 0, 0, ...deflate(icc)]);
|
|
2394
|
+
const chunk = new Uint8Array(12 + body.length);
|
|
2395
|
+
chunk.set([body.length >>> 24, body.length >>> 16 & 255, body.length >>> 8 & 255, body.length & 255]);
|
|
2396
|
+
chunk.set([105, 67, 67, 80], 4);
|
|
2397
|
+
chunk.set(body, 8);
|
|
2398
|
+
const crc = crc32(chunk.subarray(4, 8 + body.length));
|
|
2399
|
+
chunk.set([crc >>> 24, crc >>> 16 & 255, crc >>> 8 & 255, crc & 255], 8 + body.length);
|
|
2400
|
+
while (o + 12 <= png.length) {
|
|
2401
|
+
const len = u32(png, o);
|
|
2402
|
+
const type = String.fromCharCode(png[o + 4], png[o + 5], png[o + 6], png[o + 7]);
|
|
2403
|
+
const whole = png.subarray(o, o + 12 + len);
|
|
2404
|
+
if (type !== "iCCP" && type !== "sRGB" && type !== "gAMA") chunks.push(whole);
|
|
2405
|
+
if (type === "IHDR" && !inserted) {
|
|
2406
|
+
chunks.push(chunk);
|
|
2407
|
+
inserted = true;
|
|
2408
|
+
}
|
|
2409
|
+
o += 12 + len;
|
|
2410
|
+
}
|
|
2411
|
+
const out = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));
|
|
2412
|
+
let at = 0;
|
|
2413
|
+
for (const c of chunks) {
|
|
2414
|
+
out.set(c, at);
|
|
2415
|
+
at += c.length;
|
|
2416
|
+
}
|
|
2417
|
+
return out;
|
|
2418
|
+
}
|
|
2419
|
+
var ICC_TAG = "ICC_PROFILE\0";
|
|
2420
|
+
function readJpegIcc(jpeg) {
|
|
2421
|
+
if (jpeg.length < 4 || jpeg[0] !== 255 || jpeg[1] !== 216) return null;
|
|
2422
|
+
const parts = /* @__PURE__ */ new Map();
|
|
2423
|
+
let count = 0;
|
|
2424
|
+
let o = 2;
|
|
2425
|
+
while (o + 4 <= jpeg.length) {
|
|
2426
|
+
if (jpeg[o] !== 255) break;
|
|
2427
|
+
const marker = jpeg[o + 1];
|
|
2428
|
+
if (marker === 216 || marker >= 208 && marker <= 215 || marker === 1) {
|
|
2429
|
+
o += 2;
|
|
2430
|
+
continue;
|
|
2431
|
+
}
|
|
2432
|
+
if (marker === 218 || marker === 217) break;
|
|
2433
|
+
const len = jpeg[o + 2] << 8 | jpeg[o + 3];
|
|
2434
|
+
if (marker === 226 && len >= 16) {
|
|
2435
|
+
const d = jpeg.subarray(o + 4, o + 2 + len);
|
|
2436
|
+
if (String.fromCharCode(...d.subarray(0, 12)) === ICC_TAG) {
|
|
2437
|
+
parts.set(d[12], d.subarray(14));
|
|
2438
|
+
count = d[13];
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
o += 2 + len;
|
|
2442
|
+
}
|
|
2443
|
+
if (!parts.size || !count) return null;
|
|
2444
|
+
const ordered = [];
|
|
2445
|
+
for (let i = 1; i <= count; i++) {
|
|
2446
|
+
const p = parts.get(i);
|
|
2447
|
+
if (!p) return null;
|
|
2448
|
+
ordered.push(p);
|
|
2449
|
+
}
|
|
2450
|
+
const out = new Uint8Array(ordered.reduce((n, p) => n + p.length, 0));
|
|
2451
|
+
let at = 0;
|
|
2452
|
+
for (const p of ordered) {
|
|
2453
|
+
out.set(p, at);
|
|
2454
|
+
at += p.length;
|
|
2455
|
+
}
|
|
2456
|
+
return out;
|
|
2457
|
+
}
|
|
2458
|
+
function writeJpegIcc(jpeg, icc) {
|
|
2459
|
+
if (jpeg.length < 4 || jpeg[0] !== 255 || jpeg[1] !== 216) throw new Error("Not a JPEG file.");
|
|
2460
|
+
const MAX = 65519;
|
|
2461
|
+
const count = Math.max(1, Math.ceil(icc.length / MAX));
|
|
2462
|
+
if (count > 255) throw new Error("The ICC profile is too large for a JPEG.");
|
|
2463
|
+
const segments = [];
|
|
2464
|
+
for (let i = 0; i < count; i++) {
|
|
2465
|
+
const part = icc.subarray(i * MAX, Math.min(icc.length, (i + 1) * MAX));
|
|
2466
|
+
const len = 2 + 14 + part.length;
|
|
2467
|
+
const s = new Uint8Array(2 + len);
|
|
2468
|
+
s.set([255, 226, len >> 8, len & 255]);
|
|
2469
|
+
for (let k = 0; k < 12; k++) s[4 + k] = ICC_TAG.charCodeAt(k);
|
|
2470
|
+
s[16] = i + 1;
|
|
2471
|
+
s[17] = count;
|
|
2472
|
+
s.set(part, 18);
|
|
2473
|
+
segments.push(s);
|
|
2474
|
+
}
|
|
2475
|
+
const keep = [jpeg.subarray(0, 2)];
|
|
2476
|
+
let o = 2;
|
|
2477
|
+
let inserted = false;
|
|
2478
|
+
while (o + 4 <= jpeg.length && jpeg[o] === 255) {
|
|
2479
|
+
const marker = jpeg[o + 1];
|
|
2480
|
+
if (marker === 218 || marker === 217) break;
|
|
2481
|
+
const len = jpeg[o + 2] << 8 | jpeg[o + 3];
|
|
2482
|
+
const seg = jpeg.subarray(o, o + 2 + len);
|
|
2483
|
+
const isIcc = marker === 226 && String.fromCharCode(...seg.subarray(4, 16)) === ICC_TAG;
|
|
2484
|
+
if (!inserted && marker !== 224 && marker !== 225) {
|
|
2485
|
+
keep.push(...segments);
|
|
2486
|
+
inserted = true;
|
|
2487
|
+
}
|
|
2488
|
+
if (!isIcc) keep.push(seg);
|
|
2489
|
+
o += 2 + len;
|
|
2490
|
+
}
|
|
2491
|
+
if (!inserted) keep.push(...segments);
|
|
2492
|
+
keep.push(jpeg.subarray(o));
|
|
2493
|
+
const out = new Uint8Array(keep.reduce((n, p) => n + p.length, 0));
|
|
2494
|
+
let at = 0;
|
|
2495
|
+
for (const p of keep) {
|
|
2496
|
+
out.set(p, at);
|
|
2497
|
+
at += p.length;
|
|
2498
|
+
}
|
|
2499
|
+
return out;
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2502
|
+
export {
|
|
2503
|
+
D50,
|
|
2504
|
+
D65_XY,
|
|
2505
|
+
IDENTITY,
|
|
2506
|
+
mul,
|
|
2507
|
+
apply,
|
|
2508
|
+
det,
|
|
2509
|
+
invert,
|
|
2510
|
+
xyToXyz,
|
|
2511
|
+
bradford,
|
|
2512
|
+
rgbToXyzMatrix,
|
|
2513
|
+
xyzToLab,
|
|
2514
|
+
labToXyz,
|
|
2515
|
+
deltaE76,
|
|
2516
|
+
s15f16Round,
|
|
2517
|
+
d50Quantized,
|
|
2518
|
+
IDENTITY_CURVE,
|
|
2519
|
+
srgbTrc,
|
|
2520
|
+
gammaCurve,
|
|
2521
|
+
paramCount,
|
|
2522
|
+
isIdentityCurve,
|
|
2523
|
+
evalCurve,
|
|
2524
|
+
evalCurveInverse,
|
|
2525
|
+
sampleCurve,
|
|
2526
|
+
MAX_CHANNELS,
|
|
2527
|
+
Clut,
|
|
2528
|
+
MAX_PIPE,
|
|
2529
|
+
matrix3,
|
|
2530
|
+
stageOutputs,
|
|
2531
|
+
Pipeline,
|
|
2532
|
+
INTENTS,
|
|
2533
|
+
intentCode,
|
|
2534
|
+
intentFromCode,
|
|
2535
|
+
CmsError,
|
|
2536
|
+
classSig,
|
|
2537
|
+
spaceSig,
|
|
2538
|
+
spaceChannels,
|
|
2539
|
+
profileChannels,
|
|
2540
|
+
SAME_COLORS_MAX_DELTA_E,
|
|
2541
|
+
mediaWhite,
|
|
2542
|
+
isMatrixShaper,
|
|
2543
|
+
supportsIntent,
|
|
2544
|
+
deviceToPcs,
|
|
2545
|
+
pcsToDevice,
|
|
2546
|
+
canBeDestination,
|
|
2547
|
+
grayAsRgb,
|
|
2548
|
+
parseProfile,
|
|
2549
|
+
encodeProfile,
|
|
2550
|
+
DEFAULT_TRANSFORM_OPTIONS,
|
|
2551
|
+
Transform,
|
|
2552
|
+
linkStages,
|
|
2553
|
+
blackPoint,
|
|
2554
|
+
cachedTransform,
|
|
2555
|
+
sameColors,
|
|
2556
|
+
GamutCheck,
|
|
2557
|
+
Lut3d,
|
|
2558
|
+
COATED_CMYK_DESCRIPTION,
|
|
2559
|
+
DEFAULT_CMYK_PARAMS,
|
|
2560
|
+
CmykModel,
|
|
2561
|
+
separate,
|
|
2562
|
+
cmykProfile,
|
|
2563
|
+
BUILTIN_COPYRIGHT,
|
|
2564
|
+
BUILTIN_PROFILES,
|
|
2565
|
+
builtinProfileId,
|
|
2566
|
+
builtinProfile,
|
|
2567
|
+
srgbProfile,
|
|
2568
|
+
defaultProfileFor,
|
|
2569
|
+
readPngIcc,
|
|
2570
|
+
writePngIcc,
|
|
2571
|
+
readJpegIcc,
|
|
2572
|
+
writeJpegIcc
|
|
2573
|
+
};
|
|
2574
|
+
//# sourceMappingURL=chunk-BRLYOPLT.js.map
|