imagescript 3.2.1 → 3.4.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.
Files changed (101) hide show
  1. package/README.md +35 -10
  2. package/THIRD_PARTY_NOTICES.md +298 -2
  3. package/dist/animation.d.ts +25 -8
  4. package/dist/animation.js +44 -13
  5. package/dist/codecs/av1/cdef.js +197 -0
  6. package/dist/codecs/av1/data.js +141 -0
  7. package/dist/codecs/av1/decode.js +1769 -0
  8. package/dist/codecs/av1/filmgrain.js +219 -0
  9. package/dist/codecs/av1/frame.js +114 -0
  10. package/dist/codecs/av1/ipred.js +491 -0
  11. package/dist/codecs/av1/itx.js +950 -0
  12. package/dist/codecs/av1/loopfilter.js +149 -0
  13. package/dist/codecs/av1/msac.js +161 -0
  14. package/dist/codecs/av1/obu.js +516 -0
  15. package/dist/codecs/av1/postfilter.js +28 -0
  16. package/dist/codecs/av1/restoration.js +193 -0
  17. package/dist/codecs/av1/tables.js +160 -0
  18. package/dist/codecs/avif.js +581 -0
  19. package/dist/codecs/bmp.js +317 -0
  20. package/dist/codecs/crc.js +5 -0
  21. package/dist/codecs/gif.js +10 -35
  22. package/dist/codecs/hdr.d.ts +25 -0
  23. package/dist/codecs/hdr.js +201 -0
  24. package/dist/codecs/ico.js +48 -0
  25. package/dist/codecs/jpeg.js +11 -28
  26. package/dist/codecs/jxl/bits.js +126 -0
  27. package/dist/codecs/jxl/blend.js +117 -0
  28. package/dist/codecs/jxl/color.js +116 -0
  29. package/dist/codecs/jxl/dct.js +404 -0
  30. package/dist/codecs/jxl/entropy.js +556 -0
  31. package/dist/codecs/jxl/features.js +325 -0
  32. package/dist/codecs/jxl/filters.js +119 -0
  33. package/dist/codecs/jxl/frame-header.js +195 -0
  34. package/dist/codecs/jxl/frame.js +319 -0
  35. package/dist/codecs/jxl/headers.js +249 -0
  36. package/dist/codecs/jxl/idct.js +484 -0
  37. package/dist/codecs/jxl/modular.js +688 -0
  38. package/dist/codecs/jxl/quant.js +219 -0
  39. package/dist/codecs/jxl/upsample.js +52 -0
  40. package/dist/codecs/jxl/vardct.js +472 -0
  41. package/dist/codecs/jxl.js +89 -0
  42. package/dist/codecs/netpbm.js +239 -0
  43. package/dist/codecs/png.js +22 -10
  44. package/dist/codecs/qoi.js +150 -0
  45. package/dist/codecs/tga.js +3 -1
  46. package/dist/codecs/tiff.js +7 -2
  47. package/dist/codecs/vp8-enc-dsp.js +932 -0
  48. package/dist/codecs/vp8-enc-tables.js +78 -0
  49. package/dist/codecs/vp8-enc.js +1930 -0
  50. package/dist/codecs/vp8.js +3 -1
  51. package/dist/codecs/vp8l.js +126 -58
  52. package/dist/codecs/webp.js +47 -11
  53. package/dist/codecs/zlib.js +81 -69
  54. package/dist/frame.d.ts +1 -0
  55. package/dist/frame.js +4 -2
  56. package/dist/gif.d.ts +1 -0
  57. package/dist/gif.js +2 -2
  58. package/dist/image-type.d.ts +15 -1
  59. package/dist/image-type.js +45 -2
  60. package/dist/image.d.ts +89 -13
  61. package/dist/image.js +266 -113
  62. package/dist/index.d.ts +3 -2
  63. package/dist/index.js +4 -4
  64. package/dist/render/bidi.js +350 -0
  65. package/dist/render/buffer.js +362 -0
  66. package/dist/render/color.js +164 -0
  67. package/dist/render/colr.js +523 -0
  68. package/dist/render/font.js +173 -241
  69. package/dist/render/hinting.js +38 -22
  70. package/dist/render/layout.js +1553 -0
  71. package/dist/render/linebreak.js +210 -0
  72. package/dist/render/segment.js +106 -0
  73. package/dist/render/shape.js +984 -0
  74. package/dist/render/shapers/arabic.js +284 -0
  75. package/dist/render/shapers/hangul.js +142 -0
  76. package/dist/render/shapers/hebrew.js +49 -0
  77. package/dist/render/shapers/index.js +52 -0
  78. package/dist/render/shapers/indic.js +579 -0
  79. package/dist/render/shapers/khmer.js +104 -0
  80. package/dist/render/shapers/machine.js +129 -0
  81. package/dist/render/shapers/myanmar.js +121 -0
  82. package/dist/render/shapers/syllabic.js +100 -0
  83. package/dist/render/shapers/thai.js +125 -0
  84. package/dist/render/shapers/use.js +235 -0
  85. package/dist/render/svg.js +102 -23
  86. package/dist/render/text.js +373 -103
  87. package/dist/render/unicode-data.js +63 -0
  88. package/dist/render/unicode.js +50 -0
  89. package/dist/text-layout.d.ts +4 -1
  90. package/dist/text-layout.js +2 -2
  91. package/dist/util/alpha.js +43 -0
  92. package/dist/util/blur.js +20 -33
  93. package/dist/util/bytes.js +40 -0
  94. package/dist/util/limits.js +34 -0
  95. package/dist/util/options.js +7 -0
  96. package/dist/util/resample.js +126 -7
  97. package/dist/util/rotate.js +43 -0
  98. package/dist/version.js +1 -1
  99. package/licenses/HarfBuzz.txt +42 -0
  100. package/licenses/Unicode-3.0.txt +39 -0
  101. package/package.json +25 -2
@@ -0,0 +1,688 @@
1
+ import { unpackSigned } from "./bits.js";
2
+ import { EntropyDecoder } from "./entropy.js";
3
+ export const channel = (w, h, hshift = 0, vshift = 0) => ({ w, h, hshift, vshift, data: new Int32Array(w * h) });
4
+ function wpHeader(r) {
5
+ if (r.bool())
6
+ return { p1: 16, p2: 10, p3a: 7, p3b: 7, p3c: 7, p3d: 0, p3e: 0, w: [13, 12, 12, 12] };
7
+ return { p1: r.bits(5), p2: r.bits(5), p3a: r.bits(5), p3b: r.bits(5), p3c: r.bits(5), p3d: r.bits(5), p3e: r.bits(5), w: [r.bits(4), r.bits(4), r.bits(4), r.bits(4)] };
8
+ }
9
+ const DIV_LOOKUP = new Uint32Array(64).map((_, i) => Math.floor(2 ** 24 / (i + 1)));
10
+ const floorLog2 = (x) => 31 - Math.clz32(x);
11
+ function mulShift24(a, b) {
12
+ if (Math.abs(a) < 2 ** 28)
13
+ return Math.floor(a * b / 16777216);
14
+ return Number((BigInt(a) * BigInt(b)) >> 24n);
15
+ }
16
+ class WeightedPredictor {
17
+ header;
18
+ xsize;
19
+ predErrors;
20
+ error;
21
+ prediction = new Float64Array(4);
22
+ pred = 0;
23
+ maxError = 0;
24
+ constructor(header, xsize) {
25
+ this.header = header;
26
+ this.xsize = xsize;
27
+ this.predErrors = [0, 1, 2, 3].map(() => new Uint32Array((xsize + 2) * 2));
28
+ this.error = new Int32Array((xsize + 2) * 2);
29
+ }
30
+ predict(x, y, N, W, NE, NW, NN) {
31
+ const h = this.header, xsize = this.xsize;
32
+ const cur = y & 1 ? 0 : xsize + 2, prev = y & 1 ? xsize + 2 : 0;
33
+ const posN = prev + x, posNE = x < xsize - 1 ? posN + 1 : posN, posNW = x > 0 ? posN - 1 : posN;
34
+ const weights = [0, 0, 0, 0];
35
+ for (let i = 0; i < 4; i++) {
36
+ const e = this.predErrors[i];
37
+ const sum = (e[posN] + e[posNE] + e[posNW]) >>> 0;
38
+ const shift = Math.max((sum === 0xffffffff ? 32 : floorLog2(sum + 1)) - 5, 0);
39
+ weights[i] = 4 + ((h.w[i] * DIV_LOOKUP[Math.floor(sum / 2 ** shift)]) >>> shift);
40
+ }
41
+ N *= 8;
42
+ W *= 8;
43
+ NE *= 8;
44
+ NW *= 8;
45
+ NN *= 8;
46
+ const err = this.error;
47
+ const teW = x === 0 ? 0 : err[cur + x - 1], teN = err[posN], teNW = err[posNW], teNE = err[posNE];
48
+ let p = teW;
49
+ if (Math.abs(teN) > Math.abs(p))
50
+ p = teN;
51
+ if (Math.abs(teNW) > Math.abs(p))
52
+ p = teNW;
53
+ if (Math.abs(teNE) > Math.abs(p))
54
+ p = teNE;
55
+ this.maxError = p;
56
+ const sumWN = teN + teW, pr = this.prediction;
57
+ pr[0] = W + NE - N;
58
+ pr[1] = N - Math.floor((sumWN + teNE) * h.p1 / 32);
59
+ pr[2] = W - Math.floor((sumWN + teNW) * h.p2 / 32);
60
+ pr[3] = N - Math.floor((teNW * h.p3a + teN * h.p3b + teNE * h.p3c + (NN - N) * h.p3d + (NW - W) * h.p3e) / 32);
61
+ let weightSum = weights[0] + weights[1] + weights[2] + weights[3];
62
+ const logWeight = floorLog2(weightSum) - 4;
63
+ weightSum = 0;
64
+ for (let i = 0; i < 4; i++)
65
+ weightSum += weights[i] >>= logWeight;
66
+ let sum = (weightSum >> 1) - 1;
67
+ for (let i = 0; i < 4; i++)
68
+ sum += pr[i] * weights[i];
69
+ let pred = mulShift24(sum, DIV_LOOKUP[weightSum - 1]);
70
+ if (((teN ^ teW) | (teN ^ teNW)) <= 0)
71
+ pred = Math.min(Math.max(pred, Math.min(W, NE, N)), Math.max(W, NE, N));
72
+ this.pred = pred;
73
+ return Math.floor((pred + 3) / 8);
74
+ }
75
+ update(value, x, y) {
76
+ const xsize = this.xsize, cur = y & 1 ? 0 : xsize + 2, prev = y & 1 ? xsize + 2 : 0;
77
+ const val = value * 8;
78
+ this.error[cur + x] = this.pred - val;
79
+ for (let i = 0; i < 4; i++) {
80
+ const e = Math.floor((Math.abs(this.prediction[i] - val) + 3) / 8) >>> 0;
81
+ const errors = this.predErrors[i];
82
+ errors[cur + x] = e;
83
+ errors[prev + x + 1] = (errors[prev + x + 1] + e) >>> 0;
84
+ }
85
+ }
86
+ }
87
+ export const clampedGradient = (n, w, l) => {
88
+ const m = n < w ? n : w, M = n < w ? w : n;
89
+ return l < m ? M : l > M ? m : (n + w - l) | 0;
90
+ };
91
+ function predict(p, left, top, toptop, topleft, topright, leftleft, toprightright, wp) {
92
+ switch (p) {
93
+ case 0: return 0;
94
+ case 1: return left;
95
+ case 2: return top;
96
+ case 3: return Math.trunc((left + top) / 2);
97
+ case 4: return Math.abs(top - topleft) < Math.abs(left - topleft) ? left : top;
98
+ case 5: return clampedGradient(left, top, topleft);
99
+ case 6: return wp;
100
+ case 7: return topright;
101
+ case 8: return topleft;
102
+ case 9: return leftleft;
103
+ case 10: return Math.trunc((left + topleft) / 2);
104
+ case 11: return Math.trunc((topleft + top) / 2);
105
+ case 12: return Math.trunc((top + topright) / 2);
106
+ default: return Math.trunc((6 * top - 2 * toptop + 7 * left + leftleft + toprightright + 3 * topright + 8) / 16);
107
+ }
108
+ }
109
+ export function readTree(r, limit) {
110
+ const dec = new EntropyDecoder(r, 6);
111
+ if (dec.singleToken(dec.clusters[1]) > 0)
112
+ throw new Error('JPEG XL: infinite MA tree');
113
+ dec.begin(r);
114
+ const property = [], split = [], left = [], right = [];
115
+ const ctx = [], predictor = [], offset = [], multiplier = [];
116
+ let leaves = 0;
117
+ for (let toDecode = 1; toDecode > 0;) {
118
+ if (property.length > Math.min(limit, 1 << 22))
119
+ throw new Error('JPEG XL: MA tree too large');
120
+ toDecode--;
121
+ const prop = dec.read(r, 1) - 1;
122
+ if (prop > 255)
123
+ throw new Error('JPEG XL: invalid MA tree property');
124
+ if (prop < 0) {
125
+ const pred = dec.read(r, 2);
126
+ if (pred > 13)
127
+ throw new Error('JPEG XL: invalid predictor');
128
+ const off = unpackSigned(dec.read(r, 3)), mulLog = dec.read(r, 4);
129
+ if (mulLog > 30)
130
+ throw new Error('JPEG XL: invalid multiplier');
131
+ const mulBits = dec.read(r, 5);
132
+ if (mulBits >= 2 ** (31 - mulLog) - 1)
133
+ throw new Error('JPEG XL: invalid multiplier');
134
+ property.push(-1);
135
+ split.push(0);
136
+ left.push(0);
137
+ right.push(0);
138
+ ctx.push(leaves++);
139
+ predictor.push(pred);
140
+ offset.push(off);
141
+ multiplier.push((mulBits + 1) * 2 ** mulLog);
142
+ }
143
+ else {
144
+ const n = property.length;
145
+ property.push(prop);
146
+ split.push(unpackSigned(dec.read(r, 0)));
147
+ left.push(n + toDecode + 1);
148
+ right.push(n + toDecode + 2);
149
+ ctx.push(0);
150
+ predictor.push(0);
151
+ offset.push(0);
152
+ multiplier.push(1);
153
+ toDecode += 2;
154
+ }
155
+ }
156
+ dec.finish();
157
+ const tree = {
158
+ property: Int32Array.from(property), split: Int32Array.from(split), left: Int32Array.from(left), right: Int32Array.from(right),
159
+ ctx: Int32Array.from(ctx), predictor: Uint8Array.from(predictor), offset: Int32Array.from(offset), multiplier: Int32Array.from(multiplier)
160
+ };
161
+ validateTree(tree);
162
+ return { tree, code: new EntropyDecoder(r, leaves) };
163
+ }
164
+ function validateTree(t) {
165
+ const n = t.property.length, depth = new Int32Array(n);
166
+ const stack = [[0, new Map()]];
167
+ while (stack.length) {
168
+ const [i, ranges] = stack.pop();
169
+ if (depth[i] > 2048)
170
+ throw new Error('JPEG XL: MA tree too deep');
171
+ const p = t.property[i];
172
+ if (p < 0)
173
+ continue;
174
+ const [lo, hi] = ranges.get(p) ?? [-(2 ** 31), 2 ** 31 - 1];
175
+ const v = t.split[i];
176
+ if (lo > v || hi <= v)
177
+ throw new Error('JPEG XL: invalid MA tree');
178
+ depth[t.left[i]] = depth[t.right[i]] = depth[i] + 1;
179
+ stack.push([t.left[i], new Map(ranges).set(p, [v + 1, hi])], [t.right[i], new Map(ranges).set(p, [lo, v])]);
180
+ }
181
+ }
182
+ function specialize(t, chan, stream) {
183
+ const map = new Map(), order = [];
184
+ const resolve = (i) => {
185
+ while (t.property[i] === 0 || t.property[i] === 1)
186
+ i = (t.property[i] === 0 ? chan : stream) > t.split[i] ? t.left[i] : t.right[i];
187
+ return i;
188
+ };
189
+ const visit = (i) => {
190
+ i = resolve(i);
191
+ let id = map.get(i);
192
+ if (id === undefined) {
193
+ id = order.length;
194
+ map.set(i, id);
195
+ order.push(i);
196
+ }
197
+ return id;
198
+ };
199
+ visit(0);
200
+ const left = [], right = [];
201
+ for (let k = 0; k < order.length; k++) {
202
+ const i = order[k];
203
+ if (t.property[i] < 0) {
204
+ left.push(0);
205
+ right.push(0);
206
+ }
207
+ else {
208
+ left.push(visit(t.left[i]));
209
+ right.push(visit(t.right[i]));
210
+ }
211
+ }
212
+ const pick = (a) => a.constructor === Uint8Array ? Uint8Array.from(order, i => a[i]) : Int32Array.from(order, i => a[i]);
213
+ return {
214
+ property: pick(t.property), split: pick(t.split), left: Int32Array.from(left), right: Int32Array.from(right),
215
+ ctx: pick(t.ctx), predictor: pick(t.predictor), offset: pick(t.offset), multiplier: pick(t.multiplier)
216
+ };
217
+ }
218
+ export const TR_RCT = 0, TR_PALETTE = 1, TR_SQUEEZE = 2;
219
+ function transform(r) {
220
+ const id = r.u32(0, 1, 2, 3);
221
+ if (id === 3)
222
+ throw new Error('JPEG XL: invalid transform');
223
+ const t = { id, beginC: 0, rctType: 6, numC: 3, nbColors: 256, nbDeltas: 0, predictor: 0, squeezes: [] };
224
+ const beginDist = [[0, 3], [8, 6], [72, 10], [1096, 13]];
225
+ if (id === TR_RCT || id === TR_PALETTE)
226
+ t.beginC = r.u32(...beginDist);
227
+ if (id === TR_RCT) {
228
+ t.rctType = r.u32(6, [0, 2], [2, 4], [10, 6]);
229
+ if (t.rctType >= 42)
230
+ throw new Error('JPEG XL: invalid RCT type');
231
+ }
232
+ if (id === TR_PALETTE) {
233
+ t.numC = r.u32(1, 3, 4, [1, 13]);
234
+ t.nbColors = r.u32([0, 8], [256, 10], [1280, 12], [5376, 16]);
235
+ t.nbDeltas = r.u32(0, [1, 8], [257, 10], [1281, 16]);
236
+ t.predictor = r.bits(4);
237
+ if (t.predictor > 13)
238
+ throw new Error('JPEG XL: invalid predictor');
239
+ }
240
+ if (id === TR_SQUEEZE) {
241
+ const n = r.u32(0, [1, 4], [9, 6], [41, 8]);
242
+ for (let i = 0; i < n; i++)
243
+ t.squeezes.push({ horizontal: r.bool(), inPlace: r.bool(), beginC: r.u32(...beginDist), numC: r.u32(1, 2, 3, [4, 4]) });
244
+ }
245
+ return t;
246
+ }
247
+ export class ModularImage {
248
+ channels;
249
+ bitDepth;
250
+ nbMeta = 0;
251
+ transforms = [];
252
+ wp = null;
253
+ constructor(channels, bitDepth) {
254
+ this.channels = channels;
255
+ this.bitDepth = bitDepth;
256
+ }
257
+ checkEqual(c1, c2) {
258
+ if (c1 > this.channels.length || c2 >= this.channels.length || c2 < c1)
259
+ throw new Error('JPEG XL: invalid channel range');
260
+ if (c1 < this.nbMeta && c2 >= this.nbMeta)
261
+ throw new Error('JPEG XL: transform of meta and non-meta channels');
262
+ const a = this.channels[c1];
263
+ for (let c = c1 + 1; c <= c2; c++) {
264
+ const b = this.channels[c];
265
+ if (a.w !== b.w || a.h !== b.h || a.hshift !== b.hshift || a.vshift !== b.vshift)
266
+ throw new Error('JPEG XL: transform of different channels');
267
+ }
268
+ }
269
+ meta(t) {
270
+ const ch = this.channels;
271
+ if (t.id === TR_RCT)
272
+ this.checkEqual(t.beginC, t.beginC + 2);
273
+ else if (t.id === TR_PALETTE) {
274
+ const end = t.beginC + t.numC - 1;
275
+ this.checkEqual(t.beginC, end);
276
+ if (t.beginC >= this.nbMeta)
277
+ this.nbMeta++;
278
+ else {
279
+ if (end >= this.nbMeta)
280
+ throw new Error('JPEG XL: invalid palette');
281
+ this.nbMeta += 2 - t.numC;
282
+ }
283
+ ch.splice(t.beginC + 1, end - t.beginC);
284
+ ch.unshift(channel(t.nbColors + t.nbDeltas, t.numC, -1, -1));
285
+ }
286
+ else {
287
+ if (!t.squeezes.length)
288
+ t.squeezes = this.defaultSqueezes();
289
+ for (const s of t.squeezes) {
290
+ const begin = s.beginC, end = s.beginC + s.numC - 1;
291
+ if (begin >= ch.length || end >= ch.length)
292
+ throw new Error('JPEG XL: invalid squeeze');
293
+ if (begin < this.nbMeta) {
294
+ if (end >= this.nbMeta || !s.inPlace)
295
+ throw new Error('JPEG XL: invalid squeeze');
296
+ this.nbMeta += s.numC;
297
+ }
298
+ const offset = s.inPlace ? end + 1 : ch.length;
299
+ for (let c = begin; c <= end; c++) {
300
+ const x = ch[c];
301
+ if (x.hshift > 30 || x.vshift > 30)
302
+ throw new Error('JPEG XL: too many squeezes');
303
+ let w = x.w, h = x.h;
304
+ if (w === 0 || h === 0)
305
+ throw new Error('JPEG XL: squeeze of an empty channel');
306
+ if (s.horizontal) {
307
+ x.w = (w + 1) >> 1;
308
+ if (x.hshift >= 0)
309
+ x.hshift++;
310
+ w -= x.w;
311
+ }
312
+ else {
313
+ x.h = (h + 1) >> 1;
314
+ if (x.vshift >= 0)
315
+ x.vshift++;
316
+ h -= x.h;
317
+ }
318
+ x.data = new Int32Array(x.w * x.h);
319
+ ch.splice(offset + c - begin, 0, channel(w, h, x.hshift, x.vshift));
320
+ }
321
+ }
322
+ }
323
+ }
324
+ defaultSqueezes() {
325
+ const out = [], ch = this.channels, m = this.nbMeta, n = ch.length - m;
326
+ let w = ch[m].w, h = ch[m].h;
327
+ if (n > 2 && ch[m + 1].w === w && ch[m + 1].h === h) {
328
+ out.push({ horizontal: true, inPlace: false, beginC: m + 1, numC: 2 }, { horizontal: false, inPlace: false, beginC: m + 1, numC: 2 });
329
+ }
330
+ const sq = (horizontal) => out.push({ horizontal, inPlace: true, beginC: m, numC: n });
331
+ if (w <= h && h > 8) {
332
+ sq(false);
333
+ h = (h + 1) >> 1;
334
+ }
335
+ while (w > 8 || h > 8) {
336
+ if (w > 8) {
337
+ sq(true);
338
+ w = (w + 1) >> 1;
339
+ }
340
+ if (h > 8) {
341
+ sq(false);
342
+ h = (h + 1) >> 1;
343
+ }
344
+ }
345
+ return out;
346
+ }
347
+ undo() {
348
+ while (this.transforms.length) {
349
+ const t = this.transforms.pop();
350
+ if (t.id === TR_RCT)
351
+ this.inverseRCT(t);
352
+ else if (t.id === TR_PALETTE)
353
+ this.inversePalette(t);
354
+ else
355
+ for (let i = t.squeezes.length - 1; i >= 0; i--)
356
+ this.inverseSqueeze(t.squeezes[i]);
357
+ }
358
+ }
359
+ inverseRCT(t) {
360
+ this.checkEqual(t.beginC, t.beginC + 2);
361
+ const m = t.beginC, perm = Math.floor(t.rctType / 7), type = t.rctType % 7;
362
+ const ch = this.channels, c0 = ch[m], c1 = ch[m + 1], c2 = ch[m + 2];
363
+ const o0 = m + perm % 3, o1 = m + (perm + 1 + Math.floor(perm / 3)) % 3, o2 = m + (perm + 2 - Math.floor(perm / 3)) % 3;
364
+ if (type !== 0) {
365
+ const a = c0.data, b = c1.data, c = c2.data, n = c0.w * c0.h;
366
+ if (type === 6) {
367
+ for (let i = 0; i < n; i++) {
368
+ const Y = a[i], Co = b[i], Cg = c[i];
369
+ const tmp = (Y - (Cg >> 1)) | 0, G = (Cg + tmp) | 0, B = (tmp - (Co >> 1)) | 0;
370
+ a[i] = (B + Co) | 0;
371
+ b[i] = G;
372
+ c[i] = B;
373
+ }
374
+ }
375
+ else {
376
+ const second = type >> 1, third = type & 1;
377
+ for (let i = 0; i < n; i++) {
378
+ const first = a[i];
379
+ if (third)
380
+ c[i] = (c[i] + first) | 0;
381
+ if (second === 1)
382
+ b[i] = (b[i] + first) | 0;
383
+ else if (second === 2)
384
+ b[i] = (b[i] + (((first + c[i]) | 0) >> 1)) | 0;
385
+ }
386
+ }
387
+ }
388
+ ch[o0] = c0;
389
+ ch[o1] = c1;
390
+ ch[o2] = c2;
391
+ }
392
+ inversePalette(t) {
393
+ if (this.nbMeta < 1)
394
+ throw new Error('JPEG XL: palette transform without a palette');
395
+ const ch = this.channels, palette = ch[0], nb = palette.h, c0 = t.beginC + 1;
396
+ if (c0 >= ch.length || nb < 1)
397
+ throw new Error('JPEG XL: invalid palette');
398
+ const { w, h } = ch[c0], bitDepth = Math.min(this.bitDepth, 24);
399
+ for (let i = 1; i < nb; i++)
400
+ ch.splice(c0 + 1, 0, channel(w, h, ch[c0].hshift, ch[c0].vshift));
401
+ const indices = ch[c0].data;
402
+ if (nb > 1 || t.nbDeltas || t.predictor)
403
+ ch[c0].data = new Int32Array(w * h);
404
+ for (let c = 0; c < nb; c++) {
405
+ const out = ch[c0 + c].data;
406
+ if (!t.nbDeltas && !t.predictor) {
407
+ for (let i = 0; i < w * h; i++) {
408
+ const index = nb === 1 ? Math.min(Math.max(indices[i], 0), palette.w - 1) : indices[i];
409
+ out[i] = paletteValue(palette, index, c, bitDepth);
410
+ }
411
+ continue;
412
+ }
413
+ const wp = t.predictor === 6 ? new WeightedPredictor(this.wp, w) : null;
414
+ for (let y = 0, i = 0; y < h; y++) {
415
+ for (let x = 0; x < w; x++, i++) {
416
+ const index = indices[i];
417
+ let v = paletteValue(palette, index, c, bitDepth);
418
+ if (index < t.nbDeltas) {
419
+ const left = x ? out[i - 1] : y ? out[i - w] : 0, top = y ? out[i - w] : left;
420
+ const topleft = x && y ? out[i - w - 1] : left, topright = x + 1 < w && y ? out[i - w + 1] : top;
421
+ const leftleft = x > 1 ? out[i - 2] : left, toptop = y > 1 ? out[i - 2 * w] : top;
422
+ const toprightright = x + 2 < w && y ? out[i - w + 2] : topright;
423
+ const wpPred = wp ? wp.predict(x, y, top, left, topright, topleft, toptop) : 0;
424
+ v += predict(t.predictor, left, top, toptop, topleft, topright, leftleft, toprightright, wpPred);
425
+ }
426
+ out[i] = v;
427
+ wp?.update(out[i], x, y);
428
+ }
429
+ }
430
+ }
431
+ if (c0 >= this.nbMeta)
432
+ this.nbMeta--;
433
+ else
434
+ this.nbMeta -= 2 - nb;
435
+ ch.shift();
436
+ }
437
+ inverseSqueeze(s) {
438
+ const ch = this.channels, begin = s.beginC, end = s.beginC + s.numC - 1;
439
+ if (begin >= ch.length || end >= ch.length)
440
+ throw new Error('JPEG XL: invalid squeeze');
441
+ const offset = s.inPlace ? end + 1 : ch.length + begin - end - 1;
442
+ if (begin < this.nbMeta)
443
+ this.nbMeta -= s.numC;
444
+ for (let c = begin; c <= end; c++) {
445
+ const rc = offset + c - begin;
446
+ if (rc >= ch.length || ch[c].w < ch[rc].w || ch[c].h < ch[rc].h)
447
+ throw new Error('JPEG XL: invalid squeeze');
448
+ ch[c] = s.horizontal ? unsqueezeH(ch[c], ch[rc]) : unsqueezeV(ch[c], ch[rc]);
449
+ }
450
+ ch.splice(offset, end - begin + 1);
451
+ }
452
+ }
453
+ const DELTA_PALETTE = [
454
+ 0, 0, 0, 4, 4, 4, 11, 0, 0, 0, 0, -13, 0, -12, 0, -10, -10, -10, -18, -18, -18, -27, -27, -27, -18, -18, 0, 0, 0, -32,
455
+ -32, 0, 0, -37, -37, -37, 0, -32, -32, 24, 24, 45, 50, 50, 50, -45, -24, -24, -24, -45, -45, 0, -24, -24, -34, -34, 0,
456
+ -24, 0, -24, -45, -45, -24, 64, 64, 64, -32, 0, -32, 0, -32, 0, -32, 0, 32, -24, -45, -24, 45, 24, 45, 24, -24, -45,
457
+ -45, -24, 24, 80, 80, 80, 64, 0, 0, 0, 0, -64, 0, -64, -64, -24, -24, 45, 96, 96, 96, 64, 64, 0, 45, -24, -24, 34, -34,
458
+ 0, 112, 112, 112, 24, -45, -45, 45, 45, -24, 0, -32, 32, 24, -24, 45, 0, 96, 96, 45, -24, 24, 24, -45, -24, -24, -45,
459
+ 24, 0, -64, 0, 96, 0, 0, 128, 128, 128, 64, 0, 64, 144, 144, 144, 96, 96, 0, -36, -36, 36, 45, -24, -45, 45, -45, -24,
460
+ 0, 0, -96, 0, 128, 128, 0, 96, 0, 45, 24, -45, -128, 0, 0, 24, -45, 24, -45, 24, -45, 64, 0, -64, 64, -64, -64, 96, 0,
461
+ 96, 45, -45, 24, 24, 45, -45, 64, 64, -64, 128, 128, 0, 0, 0, -128, -24, 45, -45
462
+ ];
463
+ function paletteValue(palette, index, c, bitDepth) {
464
+ const size = palette.w;
465
+ if (index < 0) {
466
+ if (c >= 3)
467
+ return 0;
468
+ index = -(index + 1) % 143;
469
+ let v = DELTA_PALETTE[((index + 1) >> 1) * 3 + c] * (index & 1 ? 1 : -1);
470
+ if (bitDepth > 8)
471
+ v *= 2 ** (bitDepth - 8);
472
+ return v | 0;
473
+ }
474
+ if (index >= size && index < size + 64) {
475
+ if (c >= 3)
476
+ return 0;
477
+ const v = ((index - size) >> (2 * c)) % 4;
478
+ return Math.floor(v * (2 ** bitDepth - 1) / 4) + (1 << Math.max(0, bitDepth - 3));
479
+ }
480
+ if (index >= size + 64) {
481
+ if (c >= 3)
482
+ return 0;
483
+ const v = Math.floor((index - size - 64) / [1, 5, 25][c]) % 5;
484
+ return Math.floor(v * (2 ** bitDepth - 1) / 4);
485
+ }
486
+ return palette.data[c * size + index];
487
+ }
488
+ function tendency(B, a, n) {
489
+ let diff = 0;
490
+ if (B >= a && a >= n) {
491
+ diff = Math.trunc((4 * B - 3 * n - a + 6) / 12);
492
+ if (diff - (diff & 1) > 2 * (B - a))
493
+ diff = 2 * (B - a) + 1;
494
+ if (diff + (diff & 1) > 2 * (a - n))
495
+ diff = 2 * (a - n);
496
+ }
497
+ else if (B <= a && a <= n) {
498
+ diff = Math.trunc((4 * B - 3 * n - a - 6) / 12);
499
+ if (diff + (diff & 1) < 2 * (B - a))
500
+ diff = 2 * (B - a) - 1;
501
+ if (diff - (diff & 1) < 2 * (a - n))
502
+ diff = 2 * (a - n);
503
+ }
504
+ return diff;
505
+ }
506
+ function unsqueezeH(avg, res) {
507
+ if (avg.w !== ((avg.w + res.w + 1) >> 1) || avg.h !== res.h)
508
+ throw new Error('JPEG XL: invalid squeeze');
509
+ if (res.w === 0)
510
+ return { ...avg, hshift: avg.hshift - 1 };
511
+ const out = channel(avg.w + res.w, avg.h, avg.hshift - 1, avg.vshift), o = out.data, a = avg.data, r = res.data;
512
+ for (let y = 0; y < avg.h; y++) {
513
+ const ra = y * avg.w, rr = y * res.w, ro = y * out.w;
514
+ for (let x = 0; x < res.w; x++) {
515
+ const av = a[ra + x], next = x + 1 < avg.w ? a[ra + x + 1] : av, left = x ? o[ro + 2 * x - 1] : av;
516
+ const diff = r[rr + x] + tendency(left, av, next), A = av + Math.trunc(diff / 2);
517
+ o[ro + 2 * x] = A;
518
+ o[ro + 2 * x + 1] = A - diff;
519
+ }
520
+ if (out.w & 1)
521
+ o[ro + out.w - 1] = a[ra + avg.w - 1];
522
+ }
523
+ return out;
524
+ }
525
+ function unsqueezeV(avg, res) {
526
+ if (avg.h !== ((avg.h + res.h + 1) >> 1) || avg.w !== res.w)
527
+ throw new Error('JPEG XL: invalid squeeze');
528
+ if (res.h === 0)
529
+ return { ...avg, vshift: avg.vshift - 1 };
530
+ const w = avg.w, out = channel(w, avg.h + res.h, avg.hshift, avg.vshift - 1), o = out.data, a = avg.data, r = res.data;
531
+ for (let y = 0; y < res.h; y++) {
532
+ const ra = y * w, rn = (y + 1 < avg.h ? y + 1 : y) * w, ro = 2 * y * w, rt = y ? (2 * y - 1) * w : -1;
533
+ for (let x = 0; x < w; x++) {
534
+ const av = a[ra + x], next = a[rn + x], top = rt >= 0 ? o[rt + x] : av;
535
+ const diff = r[y * w + x] + tendency(top, av, next), v = av + Math.trunc(diff / 2);
536
+ o[ro + x] = v;
537
+ o[ro + w + x] = v - diff;
538
+ }
539
+ }
540
+ if (out.h & 1)
541
+ o.set(a.subarray((avg.h - 1) * w, avg.h * w), (out.h - 1) * w);
542
+ return out;
543
+ }
544
+ export function decodeModular(r, image, stream, global, maxChanSize, undo) {
545
+ if (!image.channels.length)
546
+ return;
547
+ const useGlobal = r.bool();
548
+ image.wp = wpHeader(r);
549
+ const n = r.u32(0, 1, [2, 4], [18, 8]);
550
+ if (n > 512)
551
+ throw new Error('JPEG XL: too many transforms');
552
+ for (let i = 0; i < n; i++) {
553
+ const t = transform(r);
554
+ image.transforms.push(t);
555
+ image.meta(t);
556
+ }
557
+ const ch = image.channels;
558
+ let count = 0, distMultiplier = 0, pixels = 0;
559
+ for (let i = 0; i < ch.length; i++) {
560
+ if (!ch[i].w || !ch[i].h)
561
+ continue;
562
+ if (i >= image.nbMeta && (ch[i].w > maxChanSize || ch[i].h > maxChanSize)) {
563
+ count = i;
564
+ break;
565
+ }
566
+ distMultiplier = Math.max(distMultiplier, ch[i].w);
567
+ pixels += ch[i].w * ch[i].h;
568
+ count = i + 1;
569
+ }
570
+ if (count === 0 || !distMultiplier) {
571
+ if (undo)
572
+ image.undo();
573
+ return;
574
+ }
575
+ let config;
576
+ if (useGlobal) {
577
+ if (!global)
578
+ throw new Error('JPEG XL: no global MA tree');
579
+ config = global;
580
+ }
581
+ else
582
+ config = readTree(r, Math.min(1024 + pixels, 1 << 20));
583
+ const dec = config.code.fork();
584
+ dec.begin(r);
585
+ for (let i = 0; i < count; i++) {
586
+ if (!ch[i].w || !ch[i].h)
587
+ continue;
588
+ decodeChannel(r, dec, config.tree, image, i, stream, distMultiplier);
589
+ }
590
+ dec.finish();
591
+ if (undo)
592
+ image.undo();
593
+ }
594
+ function decodeChannel(r, dec, globalTree, image, chan, stream, distMultiplier) {
595
+ const c = image.channels[chan], { w, h } = c, data = c.data;
596
+ const t = specialize(globalTree, chan, stream);
597
+ const clusters = dec.clusters;
598
+ let maxProp = -1, usesWP = false;
599
+ const uses = new Uint8Array(16);
600
+ for (let i = 0; i < t.property.length; i++) {
601
+ if (t.property[i] > maxProp)
602
+ maxProp = t.property[i];
603
+ if (t.property[i] >= 0 && t.property[i] < 16)
604
+ uses[t.property[i]] = 1;
605
+ if (t.property[i] === 15 || (t.property[i] < 0 && t.predictor[i] === 6))
606
+ usesWP = true;
607
+ }
608
+ const neighborProps = uses.subarray(4).some(u => u === 1), gradient = uses[8] === 1 || uses[9] === 1;
609
+ if (t.property[0] < 0) {
610
+ const pred = t.predictor[0], offset = t.offset[0], mult = t.multiplier[0], cluster = clusters[t.ctx[0]];
611
+ if (pred === 0) {
612
+ const single = dec.singleToken(cluster);
613
+ if (single >= 0) {
614
+ data.fill((Math.imul(unpackSigned(single), mult) + offset) | 0);
615
+ return;
616
+ }
617
+ for (let i = 0; i < w * h; i++)
618
+ data[i] = (Math.imul(unpackSigned(dec.readClustered(r, cluster, distMultiplier)), mult) + offset) | 0;
619
+ return;
620
+ }
621
+ if (pred === 5 && mult === 1 && offset === 0) {
622
+ for (let y = 0, i = 0; y < h; y++) {
623
+ for (let x = 0; x < w; x++, i++) {
624
+ const left = x ? data[i - 1] : y ? data[i - w] : 0, top = y ? data[i - w] : left, topleft = x && y ? data[i - w - 1] : left;
625
+ data[i] = (unpackSigned(dec.readClustered(r, cluster, distMultiplier)) + clampedGradient(top, left, topleft)) | 0;
626
+ }
627
+ }
628
+ return;
629
+ }
630
+ }
631
+ const refs = [];
632
+ const numRefs = maxProp >= 16 ? ((maxProp - 16) >> 2) + 1 : 0;
633
+ for (let j = chan - 1; j >= 0 && refs.length < numRefs; j--) {
634
+ const o = image.channels[j];
635
+ if (o.w === w && o.h === h && o.hshift === c.hshift && o.vshift === c.vshift)
636
+ refs.push(o);
637
+ }
638
+ const props = new Int32Array(16 + 4 * numRefs);
639
+ props[0] = chan;
640
+ props[1] = stream;
641
+ const wp = usesWP ? new WeightedPredictor(image.wp, w) : null;
642
+ const { property, split, left: lc, right: rc, ctx, predictor, offset, multiplier } = t;
643
+ for (let y = 0, i = 0; y < h; y++) {
644
+ props[2] = y;
645
+ props[9] = 0;
646
+ for (let x = 0; x < w; x++, i++) {
647
+ const left = x ? data[i - 1] : y ? data[i - w] : 0, top = y ? data[i - w] : left;
648
+ const topleft = x && y ? data[i - w - 1] : left, topright = x + 1 < w && y ? data[i - w + 1] : top;
649
+ const leftleft = x > 1 ? data[i - 2] : left, toptop = y > 1 ? data[i - 2 * w] : top;
650
+ const toprightright = x + 2 < w && y ? data[i - w + 2] : topright;
651
+ props[3] = x;
652
+ if (neighborProps) {
653
+ props[4] = Math.abs(top);
654
+ props[5] = Math.abs(left);
655
+ props[6] = top;
656
+ props[7] = left;
657
+ if (gradient) {
658
+ props[8] = left - props[9];
659
+ props[9] = left + top - topleft;
660
+ }
661
+ props[10] = left - topleft;
662
+ props[11] = topleft - top;
663
+ props[12] = top - topright;
664
+ props[13] = top - toptop;
665
+ props[14] = left - leftleft;
666
+ }
667
+ const wpPred = wp ? wp.predict(x, y, top, left, topright, topleft, toptop) : 0;
668
+ if (wp)
669
+ props[15] = wp.maxError;
670
+ for (let k = 0; k < refs.length; k++) {
671
+ const rd = refs[k].data, v = rd[i];
672
+ const vleft = x ? rd[i - 1] : 0, vtop = y ? rd[i - w] : vleft, vtopleft = x && y ? rd[i - w - 1] : vleft;
673
+ const g = clampedGradient(vleft, vtop, vtopleft);
674
+ props[16 + 4 * k] = Math.abs(v);
675
+ props[17 + 4 * k] = v;
676
+ props[18 + 4 * k] = Math.abs(v - g);
677
+ props[19 + 4 * k] = v - g;
678
+ }
679
+ let node = 0;
680
+ while (property[node] >= 0)
681
+ node = props[property[node]] > split[node] ? lc[node] : rc[node];
682
+ const v = unpackSigned(dec.readClustered(r, clusters[ctx[node]], distMultiplier));
683
+ const guess = offset[node] + predict(predictor[node], left, top, toptop, topleft, topright, leftleft, toprightright, wpPred);
684
+ data[i] = (Math.imul(v, multiplier[node]) + guess) | 0;
685
+ wp?.update(data[i], x, y);
686
+ }
687
+ }
688
+ }