imagescript 3.0.0 → 3.2.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 (52) hide show
  1. package/README.md +29 -9
  2. package/THIRD_PARTY_NOTICES.md +82 -0
  3. package/dist/animation.d.ts +140 -0
  4. package/dist/animation.js +197 -0
  5. package/dist/codecs/exif.js +62 -0
  6. package/dist/codecs/gif.js +32 -19
  7. package/dist/codecs/jpeg.js +13 -1
  8. package/dist/codecs/png.js +348 -62
  9. package/dist/codecs/quantize.js +2 -2
  10. package/dist/codecs/tga.js +246 -0
  11. package/dist/codecs/vp8-tables.js +17 -0
  12. package/dist/codecs/vp8.js +711 -0
  13. package/dist/codecs/vp8l.js +906 -0
  14. package/dist/codecs/webp.js +293 -0
  15. package/dist/frame.d.ts +40 -12
  16. package/dist/frame.js +20 -9
  17. package/dist/gif.d.ts +16 -26
  18. package/dist/gif.js +10 -81
  19. package/dist/image-type.d.ts +6 -1
  20. package/dist/image-type.js +17 -1
  21. package/dist/image.d.ts +262 -30
  22. package/dist/image.js +144 -45
  23. package/dist/index.d.ts +20 -6
  24. package/dist/index.js +13 -4
  25. package/dist/metadata.d.ts +35 -0
  26. package/dist/metadata.js +43 -0
  27. package/dist/render/font.js +99 -2
  28. package/dist/render/hinting.js +1236 -0
  29. package/dist/render/svg.js +39 -21
  30. package/dist/render/text.js +32 -7
  31. package/dist/util/optimize.js +264 -0
  32. package/dist/util/options.js +3 -0
  33. package/dist/util/resample.js +44 -26
  34. package/dist/util/utf8.js +4 -51
  35. package/dist/version.js +1 -1
  36. package/licenses/FTL.txt +169 -0
  37. package/package.json +10 -3
  38. package/dist/codecs/crc.d.ts +0 -9
  39. package/dist/codecs/exif.d.ts +0 -11
  40. package/dist/codecs/gif.d.ts +0 -44
  41. package/dist/codecs/jpeg.d.ts +0 -35
  42. package/dist/codecs/png.d.ts +0 -44
  43. package/dist/codecs/quantize.d.ts +0 -25
  44. package/dist/codecs/tiff.d.ts +0 -6
  45. package/dist/codecs/zlib.d.ts +0 -22
  46. package/dist/render/font.d.ts +0 -63
  47. package/dist/render/svg.d.ts +0 -11
  48. package/dist/render/text.d.ts +0 -61
  49. package/dist/util/blur.d.ts +0 -13
  50. package/dist/util/resample.d.ts +0 -12
  51. package/dist/util/utf8.d.ts +0 -5
  52. package/dist/version.d.ts +0 -1
@@ -0,0 +1,906 @@
1
+ /*!
2
+ * WebP lossless (VP8L) bitstream, as specified in RFC 9649 ("WebP Image Format") section 3 onwards, including its
3
+ * table of short distance codes. Copyright (c) 2024 IETF Trust and the persons identified as the document authors,
4
+ * Revised BSD License (see THIRD_PARTY_NOTICES.md)
5
+ */
6
+ const SIGNATURE = 0x2f;
7
+ const CODE_LENGTH_ORDER = [17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
8
+ const DISTANCE_MAP = Int8Array.of(0, 1, 1, 0, 1, 1, -1, 1, 0, 2, 2, 0, 1, 2, -1, 2, 2, 1, -2, 1, 2, 2, -2, 2, 0, 3, 3, 0, 1, 3, -1, 3, 3, 1, -3, 1, 2, 3, -2, 3, 3, 2, -3, 2, 0, 4, 4, 0, 1, 4, -1, 4, 4, 1, -4, 1, 3, 3, -3, 3, 2, 4, -2, 4, 4, 2, -4, 2, 0, 5, 3, 4, -3, 4, 4, 3, -4, 3, 5, 0, 1, 5, -1, 5, 5, 1, -5, 1, 2, 5, -2, 5, 5, 2, -5, 2, 4, 4, -4, 4, 3, 5, -3, 5, 5, 3, -5, 3, 0, 6, 6, 0, 1, 6, -1, 6, 6, 1, -6, 1, 2, 6, -2, 6, 6, 2, -6, 2, 4, 5, -4, 5, 5, 4, -5, 4, 3, 6, -3, 6, 6, 3, -6, 3, 0, 7, 7, 0, 1, 7, -1, 7, 5, 5, -5, 5, 7, 1, -7, 1, 4, 6, -4, 6, 6, 4, -6, 4, 2, 7, -2, 7, 7, 2, -7, 2, 3, 7, -3, 7, 7, 3, -7, 3, 5, 6, -5, 6, 6, 5, -6, 5, 8, 0, 4, 7, -4, 7, 7, 4, -7, 4, 8, 1, 8, 2, 6, 6, -6, 6, 8, 3, 5, 7, -5, 7, 7, 5, -7, 5, 8, 4, 6, 7, -6, 7, 7, 6, -7, 6, 8, 5, 7, 7, -7, 7, 8, 6, 8, 7);
9
+ const NUM_LITERAL_CODES = 256;
10
+ const NUM_LENGTH_CODES = 24;
11
+ const NUM_DISTANCE_CODES = 40;
12
+ export class BitReader {
13
+ data;
14
+ pos = 0;
15
+ buffer = 0;
16
+ count = 0;
17
+ constructor(data, offset = 0) {
18
+ this.data = data;
19
+ this.pos = offset;
20
+ }
21
+ fill() {
22
+ while (this.count <= 24) {
23
+ const byte = this.pos < this.data.length ? this.data[this.pos] : 0;
24
+ if (this.pos > this.data.length + 8)
25
+ throw new Error('VP8L: unexpected end of data');
26
+ this.pos++;
27
+ this.buffer |= byte << this.count;
28
+ this.count += 8;
29
+ }
30
+ }
31
+ read(bits) {
32
+ if (bits === 0)
33
+ return 0;
34
+ if (this.count < bits)
35
+ this.fill();
36
+ const value = this.buffer & (1 << bits) - 1;
37
+ this.buffer >>>= bits;
38
+ this.count -= bits;
39
+ return value;
40
+ }
41
+ peek() {
42
+ if (this.count < 24)
43
+ this.fill();
44
+ return this.buffer & 0xffffff;
45
+ }
46
+ skip(bits) {
47
+ this.buffer >>>= bits;
48
+ this.count -= bits;
49
+ }
50
+ get overrun() {
51
+ return this.pos - (this.count >> 3) > this.data.length;
52
+ }
53
+ }
54
+ const ROOT_BITS = 8;
55
+ const SUBTABLE = 0x40000000;
56
+ export function buildCode(lengths, size) {
57
+ let nonZero = 0, last = 0, maxLength = 0;
58
+ const counts = new Int32Array(16);
59
+ for (let s = 0; s < size; s++) {
60
+ const l = lengths[s];
61
+ if (l) {
62
+ nonZero++;
63
+ last = s;
64
+ counts[l]++;
65
+ if (l > maxLength)
66
+ maxLength = l;
67
+ }
68
+ }
69
+ if (nonZero === 0)
70
+ throw new Error('VP8L: empty prefix code');
71
+ if (nonZero === 1)
72
+ return { table: new Int32Array(0), single: last };
73
+ const next = new Int32Array(16);
74
+ let code = 0, left = 1;
75
+ for (let l = 1; l <= 15; l++) {
76
+ code = code + counts[l - 1] << 1;
77
+ next[l] = code;
78
+ left = (left << 1) - counts[l];
79
+ if (left < 0)
80
+ throw new Error('VP8L: invalid prefix code (oversubscribed)');
81
+ }
82
+ if (left !== 0)
83
+ throw new Error('VP8L: invalid prefix code (incomplete)');
84
+ const reverse = (c, len) => {
85
+ let r = 0;
86
+ for (let i = 0; i < len; i++, c >>= 1)
87
+ r = r << 1 | c & 1;
88
+ return r;
89
+ };
90
+ const rootSize = 1 << ROOT_BITS;
91
+ const subBits = new Int32Array(rootSize);
92
+ const symbolCodes = new Int32Array(size);
93
+ for (let s = 0; s < size; s++) {
94
+ const l = lengths[s];
95
+ if (!l)
96
+ continue;
97
+ const c = reverse(next[l]++, l);
98
+ symbolCodes[s] = c;
99
+ if (l > ROOT_BITS) {
100
+ const root = c & rootSize - 1;
101
+ subBits[root] = Math.max(subBits[root], l - ROOT_BITS);
102
+ }
103
+ }
104
+ let total = rootSize;
105
+ const subOffset = new Int32Array(rootSize);
106
+ for (let r = 0; r < rootSize; r++)
107
+ if (subBits[r]) {
108
+ subOffset[r] = total;
109
+ total += 1 << subBits[r];
110
+ }
111
+ const table = new Int32Array(total);
112
+ for (let r = 0; r < rootSize; r++)
113
+ if (subBits[r])
114
+ table[r] = SUBTABLE | subOffset[r] << 4 | subBits[r];
115
+ for (let s = 0; s < size; s++) {
116
+ const l = lengths[s];
117
+ if (!l)
118
+ continue;
119
+ const c = symbolCodes[s];
120
+ if (l <= ROOT_BITS) {
121
+ for (let i = c; i < rootSize; i += 1 << l)
122
+ table[i] = s << 4 | l;
123
+ }
124
+ else {
125
+ const root = c & rootSize - 1;
126
+ const bits = subBits[root];
127
+ const rest = c >> ROOT_BITS, restLength = l - ROOT_BITS;
128
+ for (let i = rest; i < 1 << bits; i += 1 << restLength)
129
+ table[subOffset[root] + i] = s << 4 | l;
130
+ }
131
+ }
132
+ return { table, single: -1 };
133
+ }
134
+ export function readSymbol(reader, code) {
135
+ if (code.single >= 0)
136
+ return code.single;
137
+ const bits = reader.peek();
138
+ let entry = code.table[bits & (1 << ROOT_BITS) - 1];
139
+ if (entry & SUBTABLE) {
140
+ const offset = (entry & ~SUBTABLE) >> 4, sub = entry & 15;
141
+ entry = code.table[offset + (bits >> ROOT_BITS & (1 << sub) - 1)];
142
+ }
143
+ reader.skip(entry & 15);
144
+ return entry >> 4;
145
+ }
146
+ function readCode(reader, alphabetSize) {
147
+ const lengths = new Uint8Array(alphabetSize);
148
+ if (reader.read(1)) {
149
+ const count = reader.read(1) + 1;
150
+ const first = reader.read(reader.read(1) ? 8 : 1);
151
+ if (first >= alphabetSize)
152
+ throw new Error('VP8L: invalid simple prefix code');
153
+ if (count === 1)
154
+ return { table: new Int32Array(0), single: first };
155
+ const second = reader.read(8);
156
+ if (second >= alphabetSize)
157
+ throw new Error('VP8L: invalid simple prefix code');
158
+ lengths[first] = 1;
159
+ lengths[second] = 1;
160
+ if (first === second)
161
+ return { table: new Int32Array(0), single: first };
162
+ return buildCode(lengths, alphabetSize);
163
+ }
164
+ const codeLengthLengths = new Uint8Array(19);
165
+ const count = reader.read(4) + 4;
166
+ if (count > 19)
167
+ throw new Error('VP8L: invalid code length code');
168
+ for (let i = 0; i < count; i++)
169
+ codeLengthLengths[CODE_LENGTH_ORDER[i]] = reader.read(3);
170
+ const lengthCode = buildCode(codeLengthLengths, 19);
171
+ let maxSymbol = alphabetSize;
172
+ if (reader.read(1)) {
173
+ const bits = 2 + 2 * reader.read(3);
174
+ maxSymbol = 2 + reader.read(bits);
175
+ if (maxSymbol > alphabetSize)
176
+ throw new Error('VP8L: invalid max symbol');
177
+ }
178
+ let previous = 8;
179
+ for (let symbol = 0; symbol < alphabetSize;) {
180
+ if (maxSymbol-- === 0)
181
+ break;
182
+ const length = readSymbol(reader, lengthCode);
183
+ if (length < 16) {
184
+ lengths[symbol++] = length;
185
+ if (length)
186
+ previous = length;
187
+ continue;
188
+ }
189
+ const repeat = length === 16 ? 3 + reader.read(2) : length === 17 ? 3 + reader.read(3) : 11 + reader.read(7);
190
+ const value = length === 16 ? previous : 0;
191
+ if (symbol + repeat > alphabetSize)
192
+ throw new Error('VP8L: invalid code lengths');
193
+ lengths.fill(value, symbol, symbol + repeat);
194
+ symbol += repeat;
195
+ }
196
+ return buildCode(lengths, alphabetSize);
197
+ }
198
+ const subSize = (size, bits) => (size + (1 << bits) - 1) >> bits;
199
+ function prefixValue(reader, symbol) {
200
+ if (symbol < 4)
201
+ return symbol + 1;
202
+ const extra = (symbol - 2) >> 1;
203
+ const offset = (2 + (symbol & 1)) << extra;
204
+ return offset + reader.read(extra) + 1;
205
+ }
206
+ function decodeImage(reader, width, height, isMain) {
207
+ let cacheBits = 0;
208
+ if (reader.read(1)) {
209
+ cacheBits = reader.read(4);
210
+ if (cacheBits < 1 || cacheBits > 11)
211
+ throw new Error('VP8L: invalid color cache size');
212
+ }
213
+ let prefixBits = 0, prefixWidth = 1, groupImage = null, groupCount = 1;
214
+ if (isMain && reader.read(1)) {
215
+ prefixBits = reader.read(3) + 2;
216
+ prefixWidth = subSize(width, prefixBits);
217
+ groupImage = decodeImage(reader, prefixWidth, subSize(height, prefixBits), false);
218
+ for (let i = 0; i < groupImage.length; i++) {
219
+ const group = groupImage[i] >> 8 & 0xffff;
220
+ groupImage[i] = group;
221
+ if (group >= groupCount)
222
+ groupCount = group + 1;
223
+ }
224
+ }
225
+ const cacheSize = cacheBits ? 1 << cacheBits : 0;
226
+ const greenSize = NUM_LITERAL_CODES + NUM_LENGTH_CODES + cacheSize;
227
+ const groups = [];
228
+ for (let g = 0; g < groupCount; g++)
229
+ groups.push([
230
+ readCode(reader, greenSize),
231
+ readCode(reader, NUM_LITERAL_CODES),
232
+ readCode(reader, NUM_LITERAL_CODES),
233
+ readCode(reader, NUM_LITERAL_CODES),
234
+ readCode(reader, NUM_DISTANCE_CODES)
235
+ ]);
236
+ const total = width * height;
237
+ const pixels = new Uint32Array(total);
238
+ const cache = cacheSize ? new Uint32Array(cacheSize) : null;
239
+ const cacheShift = 32 - cacheBits;
240
+ let cached = 0;
241
+ const prefixMask = (1 << prefixBits) - 1;
242
+ let group = groups[0];
243
+ for (let i = 0, x = 0, y = 0; i < total;) {
244
+ if (groupImage && (x & prefixMask) === 0)
245
+ group = groups[groupImage[(y >> prefixBits) * prefixWidth + (x >> prefixBits)]];
246
+ const green = readSymbol(reader, group[0]);
247
+ if (green < NUM_LITERAL_CODES) {
248
+ const red = readSymbol(reader, group[1]);
249
+ const blue = readSymbol(reader, group[2]);
250
+ const alpha = readSymbol(reader, group[3]);
251
+ pixels[i++] = (alpha << 24 | red << 16 | green << 8 | blue) >>> 0;
252
+ if (++x === width) {
253
+ x = 0;
254
+ y++;
255
+ }
256
+ }
257
+ else if (green < NUM_LITERAL_CODES + NUM_LENGTH_CODES) {
258
+ const length = prefixValue(reader, green - NUM_LITERAL_CODES);
259
+ const distanceSymbol = readSymbol(reader, group[4]);
260
+ let distance = prefixValue(reader, distanceSymbol);
261
+ if (distance > 120)
262
+ distance -= 120;
263
+ else {
264
+ const dx = DISTANCE_MAP[2 * distance - 2], dy = DISTANCE_MAP[2 * distance - 1];
265
+ distance = Math.max(1, dy * width + dx);
266
+ }
267
+ if (distance > i || i + length > total)
268
+ throw new Error('VP8L: invalid backward reference');
269
+ for (let k = 0; k < length; k++, i++)
270
+ pixels[i] = pixels[i - distance];
271
+ x += length;
272
+ while (x >= width) {
273
+ x -= width;
274
+ y++;
275
+ }
276
+ if (groupImage)
277
+ group = groups[groupImage[(y >> prefixBits) * prefixWidth + (x >> prefixBits)]];
278
+ }
279
+ else {
280
+ if (!cache)
281
+ throw new Error('VP8L: invalid color cache symbol');
282
+ for (; cached < i; cached++)
283
+ cache[Math.imul(pixels[cached], 0x1e35a7bd) >>> cacheShift] = pixels[cached];
284
+ pixels[i++] = cache[green - NUM_LITERAL_CODES - NUM_LENGTH_CODES];
285
+ if (++x === width) {
286
+ x = 0;
287
+ y++;
288
+ }
289
+ }
290
+ if (reader.overrun)
291
+ throw new Error('VP8L: unexpected end of data');
292
+ }
293
+ return pixels;
294
+ }
295
+ const average2 = (a, b) => (((a ^ b) & 0xfefefefe) >>> 1) + (a & b) >>> 0;
296
+ const clamp255 = (v) => v < 0 ? 0 : v > 255 ? 255 : v;
297
+ function clampAddSubtractFull(a, b, c) {
298
+ return (clamp255((a & 255) + (b & 255) - (c & 255))
299
+ | clamp255((a >>> 8 & 255) + (b >>> 8 & 255) - (c >>> 8 & 255)) << 8
300
+ | clamp255((a >>> 16 & 255) + (b >>> 16 & 255) - (c >>> 16 & 255)) << 16
301
+ | clamp255((a >>> 24) + (b >>> 24) - (c >>> 24)) << 24) >>> 0;
302
+ }
303
+ function clampAddSubtractHalf(a, b) {
304
+ const a0 = a & 255, a1 = a >>> 8 & 255, a2 = a >>> 16 & 255, a3 = a >>> 24;
305
+ return (clamp255(a0 + ((a0 - (b & 255)) / 2 | 0))
306
+ | clamp255(a1 + ((a1 - (b >>> 8 & 255)) / 2 | 0)) << 8
307
+ | clamp255(a2 + ((a2 - (b >>> 16 & 255)) / 2 | 0)) << 16
308
+ | clamp255(a3 + ((a3 - (b >>> 24)) / 2 | 0)) << 24) >>> 0;
309
+ }
310
+ function select(l, t, tl) {
311
+ const tl0 = tl & 255, tl1 = tl >>> 8 & 255, tl2 = tl >>> 16 & 255, tl3 = tl >>> 24;
312
+ const pl = Math.abs((t & 255) - tl0) + Math.abs((t >>> 8 & 255) - tl1) + Math.abs((t >>> 16 & 255) - tl2) + Math.abs((t >>> 24) - tl3);
313
+ const pt = Math.abs((l & 255) - tl0) + Math.abs((l >>> 8 & 255) - tl1) + Math.abs((l >>> 16 & 255) - tl2) + Math.abs((l >>> 24) - tl3);
314
+ return pl < pt ? l : t;
315
+ }
316
+ const addPixels = (a, b) => ((a & 0xff00ff00) + (b & 0xff00ff00) & 0xff00ff00 | (a & 0x00ff00ff) + (b & 0x00ff00ff) & 0x00ff00ff) >>> 0;
317
+ function predict(mode, p, i, width) {
318
+ const l = p[i - 1], t = p[i - width], tl = p[i - width - 1], tr = p[i - width + 1];
319
+ switch (mode) {
320
+ case 0: return 0xff000000;
321
+ case 1: return l;
322
+ case 2: return t;
323
+ case 3: return tr;
324
+ case 4: return tl;
325
+ case 5: return average2(average2(l, tr), t);
326
+ case 6: return average2(l, tl);
327
+ case 7: return average2(l, t);
328
+ case 8: return average2(tl, t);
329
+ case 9: return average2(t, tr);
330
+ case 10: return average2(average2(l, tl), average2(t, tr));
331
+ case 11: return select(l, t, tl);
332
+ case 12: return clampAddSubtractFull(l, t, tl);
333
+ case 13: return clampAddSubtractHalf(average2(l, t), tl);
334
+ default: return 0xff000000;
335
+ }
336
+ }
337
+ function inversePredictor(p, width, height, bits, modes) {
338
+ const blocksPerRow = subSize(width, bits);
339
+ p[0] = addPixels(p[0], 0xff000000);
340
+ for (let x = 1; x < width; x++)
341
+ p[x] = addPixels(p[x], p[x - 1]);
342
+ for (let y = 1; y < height; y++) {
343
+ const row = y * width;
344
+ p[row] = addPixels(p[row], p[row - width]);
345
+ const modeRow = (y >> bits) * blocksPerRow;
346
+ for (let x = 1; x < width; x++) {
347
+ const mode = modes[modeRow + (x >> bits)] >> 8 & 15;
348
+ const i = row + x;
349
+ p[i] = addPixels(p[i], predict(mode, p, i, width));
350
+ }
351
+ }
352
+ }
353
+ const colorDelta = (t, c) => ((t << 24 >> 24) * (c << 24 >> 24)) >> 5;
354
+ function inverseColorTransform(p, width, height, bits, elements) {
355
+ const blocksPerRow = subSize(width, bits);
356
+ for (let y = 0; y < height; y++) {
357
+ const elementRow = (y >> bits) * blocksPerRow;
358
+ for (let x = 0; x < width; x++) {
359
+ const e = elements[elementRow + (x >> bits)];
360
+ const greenToRed = e & 255, greenToBlue = e >> 8 & 255, redToBlue = e >> 16 & 255;
361
+ const i = y * width + x, argb = p[i];
362
+ const green = argb >> 8 & 255;
363
+ const red = (argb >> 16) + colorDelta(greenToRed, green) & 255;
364
+ const blue = (argb & 255) + colorDelta(greenToBlue, green) + colorDelta(redToBlue, red) & 255;
365
+ p[i] = (argb & 0xff00ff00 | red << 16 | blue) >>> 0;
366
+ }
367
+ }
368
+ }
369
+ function addGreen(p) {
370
+ for (let i = 0; i < p.length; i++) {
371
+ const argb = p[i], green = argb >> 8 & 255;
372
+ const redBlue = (argb & 0x00ff00ff) + (green << 16 | green) & 0x00ff00ff;
373
+ p[i] = (argb & 0xff00ff00 | redBlue) >>> 0;
374
+ }
375
+ }
376
+ function inverseColorIndexing(packed, packedWidth, width, height, bits, palette) {
377
+ const out = new Uint32Array(width * height);
378
+ const lookup = (index) => index < palette.length ? palette[index] : 0;
379
+ if (bits === 0) {
380
+ for (let i = 0; i < out.length; i++)
381
+ out[i] = lookup(packed[i] >> 8 & 255);
382
+ return out;
383
+ }
384
+ const perByte = 1 << bits, bitsPerIndex = 8 >> bits, mask = (1 << bitsPerIndex) - 1;
385
+ for (let y = 0; y < height; y++)
386
+ for (let x = 0; x < width; x++) {
387
+ const green = packed[y * packedWidth + (x >> bits)] >> 8 & 255;
388
+ out[y * width + x] = lookup(green >> (x & perByte - 1) * bitsPerIndex & mask);
389
+ }
390
+ return out;
391
+ }
392
+ export function readHeader(data) {
393
+ if (data.length < 5 || data[0] !== SIGNATURE)
394
+ throw new Error('VP8L: invalid signature');
395
+ const reader = new BitReader(data, 1);
396
+ const width = reader.read(14) + 1;
397
+ const height = reader.read(14) + 1;
398
+ const hasAlpha = reader.read(1) === 1;
399
+ if (reader.read(3) !== 0)
400
+ throw new Error('VP8L: unsupported version');
401
+ return { width, height, hasAlpha };
402
+ }
403
+ export function decode(data) {
404
+ const { width, height, hasAlpha } = readHeader(data);
405
+ const reader = new BitReader(data, 5);
406
+ return { width, height, hasAlpha, argb: decodeStream(reader, width, height) };
407
+ }
408
+ export function decodeStream(reader, width, height) {
409
+ const transforms = [];
410
+ let codedWidth = width;
411
+ const seen = new Set();
412
+ while (reader.read(1)) {
413
+ const type = reader.read(2);
414
+ if (seen.has(type))
415
+ throw new Error('VP8L: transform used twice');
416
+ seen.add(type);
417
+ if (type === 0 || type === 1) {
418
+ const bits = reader.read(3) + 2;
419
+ const data = decodeImage(reader, subSize(codedWidth, bits), subSize(height, bits), false);
420
+ transforms.push({ type, width: codedWidth, bits, data });
421
+ }
422
+ else if (type === 2) {
423
+ transforms.push({ type, width: codedWidth, bits: 0, data: new Uint32Array(0) });
424
+ }
425
+ else {
426
+ const size = reader.read(8) + 1;
427
+ const palette = decodeImage(reader, size, 1, false);
428
+ for (let i = 1; i < size; i++)
429
+ palette[i] = addPixels(palette[i], palette[i - 1]);
430
+ const bits = size <= 2 ? 3 : size <= 4 ? 2 : size <= 16 ? 1 : 0;
431
+ transforms.push({ type, width: codedWidth, bits, data: palette });
432
+ codedWidth = subSize(codedWidth, bits);
433
+ }
434
+ }
435
+ let pixels = decodeImage(reader, codedWidth, height, true);
436
+ for (let t = transforms.length - 1; t >= 0; t--) {
437
+ const { type, width: w, bits, data } = transforms[t];
438
+ if (type === 0)
439
+ inversePredictor(pixels, w, height, bits, data);
440
+ else if (type === 1)
441
+ inverseColorTransform(pixels, w, height, bits, data);
442
+ else if (type === 2)
443
+ addGreen(pixels);
444
+ else
445
+ pixels = inverseColorIndexing(pixels, subSize(w, bits), w, height, bits, data);
446
+ }
447
+ return pixels;
448
+ }
449
+ class BitWriter {
450
+ bytes = new Uint8Array(1 << 16);
451
+ length = 0;
452
+ buffer = 0;
453
+ count = 0;
454
+ write(value, bits) {
455
+ while (bits > 0) {
456
+ const take = Math.min(bits, 24 - this.count);
457
+ this.buffer |= (value & (1 << take) - 1) << this.count;
458
+ this.count += take;
459
+ value >>>= take;
460
+ bits -= take;
461
+ while (this.count >= 8) {
462
+ if (this.length === this.bytes.length) {
463
+ const grown = new Uint8Array(this.bytes.length * 2);
464
+ grown.set(this.bytes);
465
+ this.bytes = grown;
466
+ }
467
+ this.bytes[this.length++] = this.buffer & 255;
468
+ this.buffer >>>= 8;
469
+ this.count -= 8;
470
+ }
471
+ }
472
+ }
473
+ finish() {
474
+ if (this.count > 0)
475
+ this.write(0, 8 - this.count);
476
+ return this.bytes.slice(0, this.length);
477
+ }
478
+ }
479
+ function codeLengths(counts, maxLength) {
480
+ const size = counts.length;
481
+ const lengths = new Uint8Array(size);
482
+ let freq = Array.from(counts);
483
+ for (;;) {
484
+ const used = [];
485
+ for (let s = 0; s < size; s++)
486
+ if (freq[s] > 0)
487
+ used.push(s);
488
+ if (used.length === 0)
489
+ return lengths;
490
+ if (used.length === 1) {
491
+ lengths[used[0]] = 1;
492
+ return lengths;
493
+ }
494
+ used.sort((a, b) => freq[a] - freq[b] || a - b);
495
+ const n = used.length;
496
+ const weight = new Float64Array(2 * n), parent = new Int32Array(2 * n);
497
+ for (let i = 0; i < n; i++)
498
+ weight[i] = freq[used[i]];
499
+ let leaf = 0, node = n, next = n;
500
+ const pick = () => {
501
+ if (leaf < n && (node >= next || weight[leaf] <= weight[node]))
502
+ return leaf++;
503
+ return node++;
504
+ };
505
+ for (; next < 2 * n - 1; next++) {
506
+ const a = pick(), b = pick();
507
+ weight[next] = weight[a] + weight[b];
508
+ parent[a] = parent[b] = next;
509
+ }
510
+ let longest = 0;
511
+ const depth = new Int32Array(2 * n);
512
+ for (let i = 2 * n - 3; i >= 0; i--)
513
+ depth[i] = depth[parent[i]] + 1;
514
+ for (let i = 0; i < n; i++) {
515
+ lengths[used[i]] = depth[i];
516
+ if (depth[i] > longest)
517
+ longest = depth[i];
518
+ }
519
+ if (longest <= maxLength)
520
+ return lengths;
521
+ lengths.fill(0);
522
+ freq = freq.map(f => f > 0 ? (f >> 1) + 1 : 0);
523
+ }
524
+ }
525
+ function canonicalCodes(lengths) {
526
+ const counts = new Int32Array(16), next = new Int32Array(16);
527
+ for (const l of lengths)
528
+ if (l)
529
+ counts[l]++;
530
+ let code = 0;
531
+ for (let l = 1; l <= 15; l++) {
532
+ code = code + counts[l - 1] << 1;
533
+ next[l] = code;
534
+ }
535
+ const codes = new Uint16Array(lengths.length);
536
+ for (let s = 0; s < lengths.length; s++) {
537
+ const l = lengths[s];
538
+ if (!l)
539
+ continue;
540
+ let c = next[l]++, r = 0;
541
+ for (let i = 0; i < l; i++, c >>= 1)
542
+ r = r << 1 | c & 1;
543
+ codes[s] = r;
544
+ }
545
+ return codes;
546
+ }
547
+ function buildEncodeCode(counts, maxLength = 15) {
548
+ const lengths = codeLengths(counts, maxLength);
549
+ return { lengths, codes: canonicalCodes(lengths) };
550
+ }
551
+ const writeSymbol = (w, code, symbol) => {
552
+ const l = code.lengths[symbol];
553
+ if (l && code.codes.length)
554
+ w.write(code.codes[symbol], l);
555
+ };
556
+ function writeCode(w, counts) {
557
+ const used = [];
558
+ for (let s = 0; s < counts.length; s++)
559
+ if (counts[s] > 0)
560
+ used.push(s);
561
+ if (used.length <= 2 && used.every(s => s < 256)) {
562
+ const symbols = used.length ? used : [0];
563
+ w.write(1, 1);
564
+ w.write(symbols.length - 1, 1);
565
+ if (symbols[0] <= 1) {
566
+ w.write(0, 1);
567
+ w.write(symbols[0], 1);
568
+ }
569
+ else {
570
+ w.write(1, 1);
571
+ w.write(symbols[0], 8);
572
+ }
573
+ if (symbols.length === 2)
574
+ w.write(symbols[1], 8);
575
+ const lengths = new Uint8Array(counts.length);
576
+ if (symbols.length === 1)
577
+ return { lengths, codes: new Uint16Array(0) };
578
+ lengths[symbols[0]] = lengths[symbols[1]] = 1;
579
+ return { lengths, codes: canonicalCodes(lengths) };
580
+ }
581
+ const code = buildEncodeCode(counts);
582
+ const tokens = [], extras = [];
583
+ const lengths = code.lengths;
584
+ let previous = 8;
585
+ for (let i = 0; i < lengths.length;) {
586
+ const l = lengths[i];
587
+ let run = 1;
588
+ while (i + run < lengths.length && lengths[i + run] === l)
589
+ run++;
590
+ if (l === 0) {
591
+ let left = run;
592
+ while (left > 0) {
593
+ if (left < 3) {
594
+ tokens.push(0);
595
+ extras.push(0);
596
+ left--;
597
+ }
598
+ else if (left <= 10) {
599
+ tokens.push(17);
600
+ extras.push(left - 3);
601
+ left = 0;
602
+ }
603
+ else {
604
+ const n = Math.min(left, 138);
605
+ tokens.push(18);
606
+ extras.push(n - 11);
607
+ left -= n;
608
+ }
609
+ }
610
+ }
611
+ else {
612
+ let left = run;
613
+ if (l !== previous) {
614
+ tokens.push(l);
615
+ extras.push(0);
616
+ left--;
617
+ previous = l;
618
+ }
619
+ while (left > 0) {
620
+ if (left < 3) {
621
+ tokens.push(l);
622
+ extras.push(0);
623
+ left--;
624
+ }
625
+ else {
626
+ const n = Math.min(left, 6);
627
+ tokens.push(16);
628
+ extras.push(n - 3);
629
+ left -= n;
630
+ }
631
+ }
632
+ }
633
+ i += run;
634
+ }
635
+ const lengthCounts = new Int32Array(19);
636
+ for (const t of tokens)
637
+ lengthCounts[t]++;
638
+ const lengthCode = buildEncodeCode(lengthCounts, 7);
639
+ let lengthCodeLengths = lengthCode.lengths;
640
+ if (lengthCodeLengths.filter(l => l).length === 1) {
641
+ lengthCodeLengths = lengthCodeLengths.slice();
642
+ const only = lengthCodeLengths.findIndex(l => l !== 0);
643
+ lengthCodeLengths[only] = 1;
644
+ lengthCodeLengths[only === 0 ? 1 : 0] = 1;
645
+ }
646
+ const lengthCodes = canonicalCodes(lengthCodeLengths);
647
+ let count = 19;
648
+ while (count > 4 && lengthCodeLengths[CODE_LENGTH_ORDER[count - 1]] === 0)
649
+ count--;
650
+ w.write(0, 1);
651
+ w.write(count - 4, 4);
652
+ for (let i = 0; i < count; i++)
653
+ w.write(lengthCodeLengths[CODE_LENGTH_ORDER[i]], 3);
654
+ w.write(0, 1);
655
+ for (let i = 0; i < tokens.length; i++) {
656
+ const t = tokens[i];
657
+ w.write(lengthCodes[t], lengthCodeLengths[t]);
658
+ if (t === 16)
659
+ w.write(extras[i], 2);
660
+ else if (t === 17)
661
+ w.write(extras[i], 3);
662
+ else if (t === 18)
663
+ w.write(extras[i], 7);
664
+ }
665
+ return code;
666
+ }
667
+ function prefixEncode(value) {
668
+ const d = value - 1;
669
+ if (d < 4)
670
+ return { symbol: d, bits: 0, extra: 0 };
671
+ const h = 31 - Math.clz32(d);
672
+ const second = d >> h - 1 & 1;
673
+ return { symbol: 2 * h + second, bits: h - 1, extra: d & (1 << h - 1) - 1 };
674
+ }
675
+ function tokenize(pixels, width, cacheBits) {
676
+ const n = pixels.length;
677
+ const ops = new Uint8Array(n), values = new Uint32Array(n), distances = new Uint32Array(n);
678
+ let count = 0;
679
+ const shortCodes = new Map();
680
+ for (let c = 119; c >= 0; c--) {
681
+ const d = DISTANCE_MAP[2 * c + 1] * width + DISTANCE_MAP[2 * c];
682
+ if (d >= 1)
683
+ shortCodes.set(d, c + 1);
684
+ }
685
+ const distanceCode = (d) => shortCodes.get(d) ?? d + 120;
686
+ const HASH_BITS = 15, CHAIN = 24, MAX_LENGTH = 4096, MIN_LENGTH = 3, WINDOW = (1 << 20) - 120;
687
+ const head = new Int32Array(1 << HASH_BITS).fill(-1);
688
+ const chain = new Int32Array(n);
689
+ const hash = (i) => Math.imul(Math.imul(pixels[i], 0x1e35a7bd) ^ pixels[i + 1], 0x9e3779b1) >>> 32 - HASH_BITS;
690
+ const insert = (i) => {
691
+ if (i + 1 >= n)
692
+ return;
693
+ const h = hash(i);
694
+ chain[i] = head[h];
695
+ head[h] = i;
696
+ };
697
+ const matchLength = (i, j) => {
698
+ const max = Math.min(MAX_LENGTH, n - i);
699
+ let l = 0;
700
+ while (l < max && pixels[i + l] === pixels[j + l])
701
+ l++;
702
+ return l;
703
+ };
704
+ const cache = cacheBits ? new Uint32Array(1 << cacheBits) : null;
705
+ const cacheShift = 32 - cacheBits;
706
+ let cached = 0;
707
+ for (let i = 0; i < n;) {
708
+ let bestLength = 0, bestDistance = 0;
709
+ if (i >= 1) {
710
+ const l = matchLength(i, i - 1);
711
+ if (l > bestLength) {
712
+ bestLength = l;
713
+ bestDistance = 1;
714
+ }
715
+ }
716
+ if (width <= i) {
717
+ const l = matchLength(i, i - width);
718
+ if (l > bestLength) {
719
+ bestLength = l;
720
+ bestDistance = width;
721
+ }
722
+ }
723
+ if (i + 1 < n && bestLength < MAX_LENGTH) {
724
+ for (let j = head[hash(i)], k = 0; j >= 0 && k < CHAIN && i - j <= WINDOW; j = chain[j], k++) {
725
+ if (i - j >= bestDistance && (i + bestLength >= n || pixels[j + bestLength] !== pixels[i + bestLength]))
726
+ continue;
727
+ const l = matchLength(i, j);
728
+ if (l > bestLength || l === bestLength && l > 0 && i - j < bestDistance) {
729
+ bestLength = l;
730
+ bestDistance = i - j;
731
+ }
732
+ if (l === MAX_LENGTH)
733
+ break;
734
+ }
735
+ }
736
+ if (bestLength >= MIN_LENGTH) {
737
+ ops[count] = 1;
738
+ values[count] = bestLength;
739
+ distances[count++] = distanceCode(bestDistance);
740
+ for (let k = 0; k < bestLength; k++)
741
+ insert(i + k);
742
+ i += bestLength;
743
+ continue;
744
+ }
745
+ const argb = pixels[i];
746
+ if (cache) {
747
+ for (; cached < i; cached++)
748
+ cache[Math.imul(pixels[cached], 0x1e35a7bd) >>> cacheShift] = pixels[cached];
749
+ const key = Math.imul(argb, 0x1e35a7bd) >>> cacheShift;
750
+ if (cache[key] === argb) {
751
+ ops[count] = 2;
752
+ values[count++] = key;
753
+ insert(i++);
754
+ continue;
755
+ }
756
+ }
757
+ ops[count] = 0;
758
+ values[count++] = argb;
759
+ insert(i++);
760
+ }
761
+ return { ops, values, distances, count };
762
+ }
763
+ function writeImage(w, pixels, width, isMain) {
764
+ const cacheBits = isMain && pixels.length > 4096 ? 10 : 0;
765
+ const stream = tokenize(pixels, width, cacheBits);
766
+ if (cacheBits) {
767
+ w.write(1, 1);
768
+ w.write(cacheBits, 4);
769
+ }
770
+ else
771
+ w.write(0, 1);
772
+ if (isMain)
773
+ w.write(0, 1);
774
+ const greenSize = NUM_LITERAL_CODES + NUM_LENGTH_CODES + (cacheBits ? 1 << cacheBits : 0);
775
+ const counts = [new Int32Array(greenSize), new Int32Array(256), new Int32Array(256), new Int32Array(256), new Int32Array(NUM_DISTANCE_CODES)];
776
+ const { ops, values, distances, count } = stream;
777
+ for (let k = 0; k < count; k++) {
778
+ const v = values[k];
779
+ if (ops[k] === 0) {
780
+ counts[0][v >> 8 & 255]++;
781
+ counts[1][v >> 16 & 255]++;
782
+ counts[2][v & 255]++;
783
+ counts[3][v >>> 24]++;
784
+ }
785
+ else if (ops[k] === 1) {
786
+ counts[0][NUM_LITERAL_CODES + prefixEncode(v).symbol]++;
787
+ counts[4][prefixEncode(distances[k]).symbol]++;
788
+ }
789
+ else
790
+ counts[0][NUM_LITERAL_CODES + NUM_LENGTH_CODES + v]++;
791
+ }
792
+ const codes = counts.map(c => writeCode(w, c));
793
+ for (let k = 0; k < count; k++) {
794
+ const v = values[k];
795
+ if (ops[k] === 0) {
796
+ writeSymbol(w, codes[0], v >> 8 & 255);
797
+ writeSymbol(w, codes[1], v >> 16 & 255);
798
+ writeSymbol(w, codes[2], v & 255);
799
+ writeSymbol(w, codes[3], v >>> 24);
800
+ }
801
+ else if (ops[k] === 1) {
802
+ const length = prefixEncode(v), distance = prefixEncode(distances[k]);
803
+ writeSymbol(w, codes[0], NUM_LITERAL_CODES + length.symbol);
804
+ w.write(length.extra, length.bits);
805
+ writeSymbol(w, codes[4], distance.symbol);
806
+ w.write(distance.extra, distance.bits);
807
+ }
808
+ else
809
+ writeSymbol(w, codes[0], NUM_LITERAL_CODES + NUM_LENGTH_CODES + v);
810
+ }
811
+ }
812
+ const subPixels = (a, b) => ((a | 0x00ff00ff) - (b & 0xff00ff00) & 0xff00ff00 | (a | 0xff00ff00) - (b & 0x00ff00ff) & 0x00ff00ff) >>> 0;
813
+ function residualCost(r) {
814
+ const c0 = r & 255, c1 = r >>> 8 & 255, c2 = r >>> 16 & 255, c3 = r >>> 24;
815
+ return (c0 < 128 ? c0 : 256 - c0) + (c1 < 128 ? c1 : 256 - c1) + (c2 < 128 ? c2 : 256 - c2) + (c3 < 128 ? c3 : 256 - c3);
816
+ }
817
+ function applyPredictor(p, width, height, bits) {
818
+ const blocksX = subSize(width, bits), blocksY = subSize(height, bits);
819
+ const modes = new Uint32Array(blocksX * blocksY);
820
+ const size = 1 << bits;
821
+ const src = p.slice();
822
+ const predictAt = (mode, i, x, y) => y === 0 ? (x === 0 ? 0xff000000 : src[i - 1]) : x === 0 ? src[i - width] : predict(mode, src, i, width);
823
+ for (let by = 0; by < blocksY; by++)
824
+ for (let bx = 0; bx < blocksX; bx++) {
825
+ let best = 0, bestCost = Infinity;
826
+ const x0 = bx * size, y0 = by * size, x1 = Math.min(width, x0 + size), y1 = Math.min(height, y0 + size);
827
+ for (let mode = 0; mode < 14; mode++) {
828
+ let cost = 0;
829
+ for (let y = y0; y < y1 && cost < bestCost; y++)
830
+ for (let x = x0; x < x1; x++) {
831
+ const i = y * width + x;
832
+ cost += residualCost(subPixels(src[i], predictAt(mode, i, x, y)));
833
+ }
834
+ if (cost < bestCost) {
835
+ bestCost = cost;
836
+ best = mode;
837
+ }
838
+ }
839
+ modes[by * blocksX + bx] = (0xff000000 | best << 8) >>> 0;
840
+ for (let y = y0; y < y1; y++)
841
+ for (let x = x0; x < x1; x++) {
842
+ const i = y * width + x;
843
+ p[i] = subPixels(src[i], predictAt(best, i, x, y));
844
+ }
845
+ }
846
+ return modes;
847
+ }
848
+ export function encode(argb, width, height) {
849
+ if (width < 1 || height < 1 || width > 16384 || height > 16384)
850
+ throw new RangeError('VP8L: dimensions must be 1-16384');
851
+ const w = new BitWriter();
852
+ let hasAlpha = false;
853
+ for (let i = 0; i < argb.length; i++)
854
+ if (argb[i] >>> 24 !== 255) {
855
+ hasAlpha = true;
856
+ break;
857
+ }
858
+ w.write(SIGNATURE, 8);
859
+ w.write(width - 1, 14);
860
+ w.write(height - 1, 14);
861
+ w.write(hasAlpha ? 1 : 0, 1);
862
+ w.write(0, 3);
863
+ const colors = new Map();
864
+ for (let i = 0; i < argb.length && colors.size <= 256; i++)
865
+ if (!colors.has(argb[i]))
866
+ colors.set(argb[i], colors.size);
867
+ let pixels, codedWidth = width;
868
+ if (colors.size <= 256) {
869
+ const palette = Uint32Array.from([...colors.keys()].sort((a, b) => a - b));
870
+ palette.forEach((c, i) => colors.set(c, i));
871
+ const bits = palette.length <= 2 ? 3 : palette.length <= 4 ? 2 : palette.length <= 16 ? 1 : 0;
872
+ w.write(1, 1);
873
+ w.write(3, 2);
874
+ w.write(palette.length - 1, 8);
875
+ const deltas = palette.slice();
876
+ for (let i = palette.length - 1; i > 0; i--)
877
+ deltas[i] = subPixels(palette[i], palette[i - 1]);
878
+ writeImage(w, deltas, palette.length, false);
879
+ codedWidth = subSize(width, bits);
880
+ pixels = new Uint32Array(codedWidth * height);
881
+ const perPixel = 8 >> bits;
882
+ for (let y = 0; y < height; y++)
883
+ for (let x = 0; x < width; x++) {
884
+ const o = y * codedWidth + (x >> bits);
885
+ pixels[o] = (pixels[o] | 0xff000000 | colors.get(argb[y * width + x]) << 8 + (x & (1 << bits) - 1) * perPixel) >>> 0;
886
+ }
887
+ }
888
+ else {
889
+ pixels = argb.slice();
890
+ w.write(1, 1);
891
+ w.write(2, 2);
892
+ for (let i = 0; i < pixels.length; i++) {
893
+ const p = pixels[i], green = p >> 8 & 255;
894
+ pixels[i] = (p & 0xff00ff00 | (p | 0x0000ff00) - (green << 16 | green) & 0x00ff00ff) >>> 0;
895
+ }
896
+ const bits = 4;
897
+ w.write(1, 1);
898
+ w.write(0, 2);
899
+ w.write(bits - 2, 3);
900
+ const modes = applyPredictor(pixels, width, height, bits);
901
+ writeImage(w, modes, subSize(width, bits), false);
902
+ }
903
+ w.write(0, 1);
904
+ writeImage(w, pixels, codedWidth, true);
905
+ return w.finish();
906
+ }