imagescript 1.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/LICENSE +4 -1
  2. package/README.md +64 -26
  3. package/THIRD_PARTY_NOTICES.md +74 -0
  4. package/dist/codecs/crc.d.ts +9 -0
  5. package/dist/codecs/crc.js +29 -0
  6. package/dist/codecs/exif.d.ts +11 -0
  7. package/dist/codecs/exif.js +27 -0
  8. package/dist/codecs/gif.d.ts +44 -0
  9. package/dist/codecs/gif.js +401 -0
  10. package/dist/codecs/jpeg.d.ts +35 -0
  11. package/dist/codecs/jpeg.js +980 -0
  12. package/dist/codecs/png.d.ts +44 -0
  13. package/dist/codecs/png.js +442 -0
  14. package/dist/codecs/quantize.d.ts +25 -0
  15. package/dist/codecs/quantize.js +302 -0
  16. package/dist/codecs/tiff.d.ts +6 -0
  17. package/dist/codecs/tiff.js +546 -0
  18. package/dist/codecs/zlib.d.ts +22 -0
  19. package/dist/codecs/zlib.js +607 -0
  20. package/dist/frame.d.ts +57 -0
  21. package/dist/frame.js +65 -0
  22. package/dist/gif.d.ts +44 -0
  23. package/dist/gif.js +93 -0
  24. package/dist/image-type.d.ts +20 -0
  25. package/dist/image-type.js +49 -0
  26. package/dist/image.d.ts +521 -0
  27. package/dist/image.js +1118 -0
  28. package/dist/index.d.ts +19 -0
  29. package/dist/index.js +13 -0
  30. package/dist/package.json +1 -0
  31. package/dist/render/font.d.ts +63 -0
  32. package/dist/render/font.js +857 -0
  33. package/dist/render/svg.d.ts +11 -0
  34. package/dist/render/svg.js +1717 -0
  35. package/dist/render/text.d.ts +61 -0
  36. package/dist/render/text.js +317 -0
  37. package/dist/text-layout.d.ts +46 -0
  38. package/dist/text-layout.js +35 -0
  39. package/dist/util/blur.d.ts +13 -0
  40. package/dist/util/blur.js +146 -0
  41. package/dist/util/mem.d.ts +10 -0
  42. package/dist/util/mem.js +23 -0
  43. package/dist/util/resample.d.ts +12 -0
  44. package/dist/util/resample.js +99 -0
  45. package/dist/util/utf8.d.ts +5 -0
  46. package/dist/util/utf8.js +55 -0
  47. package/dist/version.d.ts +1 -0
  48. package/dist/version.js +1 -0
  49. package/licenses/Apache-2.0.txt +202 -0
  50. package/licenses/README.ijg +260 -0
  51. package/package.json +31 -7
  52. package/ImageScript.d.ts +0 -235
  53. package/ImageScript.js +0 -1669
  54. package/bench/_.mjs +0 -166
  55. package/bench/all.mjs +0 -4
  56. package/bench/blur.mjs +0 -59
  57. package/bench/flip.mjs +0 -63
  58. package/bench/overlay.mjs +0 -35
  59. package/bench/package.json +0 -7
  60. package/bench/readme.md +0 -64
  61. package/bench/resize.mjs +0 -52
  62. package/codecs/LICENSE +0 -21
  63. package/codecs/lib.d.ts +0 -50
  64. package/codecs/lib.js +0 -6
  65. package/codecs/lib.mjs +0 -1
  66. package/codecs/node/bin/arm64-darwin.node +0 -0
  67. package/codecs/node/bin/arm64-linux.node +0 -0
  68. package/codecs/node/bin/x64-darwin.node +0 -0
  69. package/codecs/node/bin/x64-linux.node +0 -0
  70. package/codecs/node/bin/x64-win32.node +0 -0
  71. package/codecs/node/index.js +0 -3
  72. package/codecs/node/index.mjs +0 -6
  73. package/codecs/package.json +0 -14
  74. package/codecs/readme.md +0 -38
  75. package/codecs/tests/gif.js +0 -0
  76. package/codecs/tests/jpeg.js +0 -0
  77. package/codecs/tests/png.js +0 -0
  78. package/codecs/tests/results/gif/1024.nis.gif +0 -0
  79. package/codecs/tests/results/gif/1024.skyra.gif +0 -0
  80. package/codecs/tests/results/gif/256.nis.gif +0 -0
  81. package/codecs/tests/results/gif/256.skyra.gif +0 -0
  82. package/codecs/tests/results/jpeg/256.jpegjs.jpeg +0 -0
  83. package/codecs/tests/results/jpeg/256.nis.jpeg +0 -0
  84. package/codecs/tests/results/jpeg/256.sharp.jpeg +0 -0
  85. package/codecs/tests/results/jpeg/512.jpegjs.jpeg +0 -0
  86. package/codecs/tests/results/jpeg/512.nis.jpeg +0 -0
  87. package/codecs/tests/results/jpeg/512.sharp.jpeg +0 -0
  88. package/codecs/tests/results/jpeg/64.jpegjs.jpeg +0 -0
  89. package/codecs/tests/results/jpeg/64.nis.jpeg +0 -0
  90. package/codecs/tests/results/jpeg/64.sharp.jpeg +0 -0
  91. package/codecs/tests/results/png/256.jis.png +0 -0
  92. package/codecs/tests/results/png/256.nis.png +0 -0
  93. package/codecs/tests/results/png/256.sharp.png +0 -0
  94. package/codecs/tests/results/png/256.upng.png +0 -0
  95. package/codecs/tests/results/png/512.jis.png +0 -0
  96. package/codecs/tests/results/png/512.nis.png +0 -0
  97. package/codecs/tests/results/png/512.sharp.png +0 -0
  98. package/codecs/tests/results/png/512.upng.png +0 -0
  99. package/codecs/tests/results/png/64.jis.png +0 -0
  100. package/codecs/tests/results/png/64.nis.png +0 -0
  101. package/codecs/tests/results/png/64.sharp.png +0 -0
  102. package/codecs/tests/results/png/64.upng.png +0 -0
  103. package/codecs/tests/webp.js +0 -0
  104. package/codecs/wasm/bin/codecs.wasm +0 -0
  105. package/codecs/wasm/bin/simd.wasm +0 -0
  106. package/codecs/wasm/bundle/browser.js +0 -1
  107. package/codecs/wasm/index.js +0 -1
  108. package/codecs/wasm/index.mjs +0 -1
  109. package/png/README.md +0 -1
  110. package/png/node.js +0 -1137
  111. package/png/src/crc.mjs +0 -49
  112. package/png/src/mem.mjs +0 -24
  113. package/png/src/png.mjs +0 -254
  114. package/png/src/zlib.mjs +0 -618
  115. package/tsconfig.json +0 -21
  116. package/utils/mem.js +0 -26
  117. package/v2/codecs/magic.js +0 -25
  118. package/v2/framebuffer.d.ts +0 -67
  119. package/v2/framebuffer.js +0 -2352
  120. package/v2/framebuffer.mjs +0 -126
  121. package/v2/framebuffer.rs +0 -1264
  122. package/v2/index.d.ts +0 -84
  123. package/v2/index.js +0 -261
  124. package/v2/ops/blur.mjs +0 -303
  125. package/v2/ops/color.mjs +0 -277
  126. package/v2/ops/crop.mjs +0 -64
  127. package/v2/ops/fill.mjs +0 -33
  128. package/v2/ops/flip.mjs +0 -33
  129. package/v2/ops/index.mjs +0 -9
  130. package/v2/ops/iterator.mjs +0 -34
  131. package/v2/ops/overlay.mjs +0 -63
  132. package/v2/ops/resize.mjs +0 -166
  133. package/v2/ops/rotate.mjs +0 -117
  134. package/v2/util/mem.mjs +0 -24
  135. package/wasm/any/font.wasm +0 -0
  136. package/wasm/any/gif.wasm +0 -0
  137. package/wasm/any/jpeg.wasm +0 -0
  138. package/wasm/any/png.wasm +0 -0
  139. package/wasm/any/svg.wasm +0 -0
  140. package/wasm/any/tiff.wasm +0 -0
  141. package/wasm/node/font.js +0 -142
  142. package/wasm/node/gif.js +0 -76
  143. package/wasm/node/jpeg.js +0 -92
  144. package/wasm/node/png.js +0 -49
  145. package/wasm/node/svg.js +0 -56
  146. package/wasm/node/tiff.js +0 -47
@@ -0,0 +1,980 @@
1
+ /*!
2
+ * JPEG codec. This software is based in part on the work of the Independent JPEG Group: the IDCT, FDCT, upsampling,
3
+ * color conversion, progressive refinement and optimal Huffman tables are translated from libjpeg/libjpeg-turbo
4
+ * (IJG license, see licenses/README.ijg). The scan decoder follows the structure of pdf.js' jpg.js
5
+ * (Copyright 2014 Mozilla Foundation, Apache License 2.0). See THIRD_PARTY_NOTICES.md
6
+ */
7
+ import { orientation as exifOrientation } from "./exif.js";
8
+ const ZIGZAG = Int32Array.of(0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63);
9
+ function clamp8(v) {
10
+ return v < 0 ? 0 : v > 255 ? 255 : v;
11
+ }
12
+ export function decode(data, { transform } = {}) {
13
+ if (data[0] !== 0xff || data[1] !== 0xd8)
14
+ throw new Error('JPEG: invalid signature');
15
+ const quantizationTables = [];
16
+ const dcTables = [];
17
+ const acTables = [];
18
+ let frame = null;
19
+ let adobe = null;
20
+ let jfif = false;
21
+ let orientation = 1;
22
+ let restartInterval = 0;
23
+ let pos = 2;
24
+ const u16 = (offset) => data[offset] << 8 | data[offset + 1];
25
+ while (pos < data.length) {
26
+ if (data[pos] !== 0xff) {
27
+ pos++;
28
+ continue;
29
+ }
30
+ const marker = data[pos + 1];
31
+ if (marker === 0xff || marker === 0x00 || marker === undefined) {
32
+ pos++;
33
+ continue;
34
+ }
35
+ pos += 2;
36
+ if (marker === 0xd8 || (marker >= 0xd0 && marker <= 0xd7) || marker === 0x01)
37
+ continue;
38
+ if (marker === 0xd9)
39
+ break;
40
+ const length = u16(pos);
41
+ const start = pos + 2;
42
+ const end = pos + length;
43
+ if (end > data.length)
44
+ throw new Error('JPEG: unexpected end of data');
45
+ switch (marker) {
46
+ case 0xdb: {
47
+ let p = start;
48
+ while (p < end) {
49
+ const precision = data[p] >> 4;
50
+ const id = data[p++] & 15;
51
+ const table = new Int32Array(64);
52
+ for (let k = 0; k < 64; k++) {
53
+ table[ZIGZAG[k]] = precision ? u16(p) : data[p];
54
+ p += precision ? 2 : 1;
55
+ }
56
+ quantizationTables[id] = table;
57
+ }
58
+ break;
59
+ }
60
+ case 0xc4: {
61
+ let p = start;
62
+ while (p < end) {
63
+ const tableClass = data[p] >> 4;
64
+ const id = data[p++] & 15;
65
+ const counts = data.subarray(p, p + 16);
66
+ p += 16;
67
+ let total = 0;
68
+ for (let i = 0; i < 16; i++)
69
+ total += counts[i];
70
+ const table = buildHuffmanTable(counts, data.subarray(p, p + total));
71
+ p += total;
72
+ (tableClass === 0 ? dcTables : acTables)[id] = table;
73
+ }
74
+ break;
75
+ }
76
+ case 0xdd:
77
+ restartInterval = u16(start);
78
+ break;
79
+ case 0xc0:
80
+ case 0xc1:
81
+ case 0xc2: {
82
+ if (frame)
83
+ throw new Error('JPEG: multiple frames are not supported');
84
+ if (data[start] !== 8)
85
+ throw new Error(`JPEG: ${data[start]}-bit precision is not supported`);
86
+ frame = createFrame(marker === 0xc2, u16(start + 1), u16(start + 3), data.subarray(start + 5, end));
87
+ break;
88
+ }
89
+ case 0xc3:
90
+ case 0xc5:
91
+ case 0xc6:
92
+ case 0xc7:
93
+ case 0xc9:
94
+ case 0xca:
95
+ case 0xcb:
96
+ case 0xcd:
97
+ case 0xce:
98
+ case 0xcf:
99
+ throw new Error(marker === 0xc3 || marker === 0xc7 || marker === 0xcb || marker === 0xcf
100
+ ? 'JPEG: lossless JPEGs are not supported'
101
+ : marker >= 0xc9 ? 'JPEG: arithmetic coding is not supported' : 'JPEG: hierarchical JPEGs are not supported');
102
+ case 0xe0:
103
+ if (data[start] === 0x4a && data[start + 1] === 0x46 && data[start + 2] === 0x49 && data[start + 3] === 0x46 && data[start + 4] === 0)
104
+ jfif = true;
105
+ break;
106
+ case 0xe1:
107
+ if (orientation === 1)
108
+ orientation = exifOrientation(data.subarray(start, end));
109
+ break;
110
+ case 0xee:
111
+ if (length >= 14 && data[start] === 0x41 && data[start + 1] === 0x64 && data[start + 2] === 0x6f && data[start + 3] === 0x62 && data[start + 4] === 0x65)
112
+ adobe = { transform: data[start + 11] };
113
+ break;
114
+ case 0xda: {
115
+ if (!frame)
116
+ throw new Error('JPEG: scan before frame header');
117
+ if (frame.height === 0)
118
+ throw new Error('JPEG: DNL markers are not supported');
119
+ const count = data[start];
120
+ const components = [];
121
+ let p = start + 1;
122
+ for (let i = 0; i < count; i++) {
123
+ const component = frame.components.find(c => c.id === data[p]);
124
+ if (!component)
125
+ throw new Error('JPEG: scan references unknown component');
126
+ component.dcTable = dcTables[data[p + 1] >> 4];
127
+ component.acTable = acTables[data[p + 1] & 15];
128
+ if (!component.quantization) {
129
+ const table = quantizationTables[component.tq];
130
+ if (!table)
131
+ throw new Error('JPEG: missing quantization table');
132
+ component.quantization = table.slice();
133
+ }
134
+ components.push(component);
135
+ p += 2;
136
+ }
137
+ const spectralStart = data[p];
138
+ const spectralEnd = data[p + 1];
139
+ const successiveHigh = data[p + 2] >> 4;
140
+ const successiveLow = data[p + 2] & 15;
141
+ pos = decodeScan(data, end, frame, components, restartInterval, spectralStart, spectralEnd, successiveHigh, successiveLow);
142
+ continue;
143
+ }
144
+ }
145
+ pos = end;
146
+ }
147
+ if (!frame)
148
+ throw new Error('JPEG: missing frame header');
149
+ return { width: frame.width, height: frame.height, pixels: output(frame, adobe, jfif, transform), orientation };
150
+ }
151
+ function createFrame(progressive, height, width, componentData) {
152
+ const count = componentData[0];
153
+ const sampling = [];
154
+ for (let i = 0; i < count; i++) {
155
+ const p = 1 + i * 3;
156
+ sampling.push({ id: componentData[p], h: componentData[p + 1] >> 4 || 1, v: componentData[p + 1] & 15 || 1, tq: componentData[p + 2] });
157
+ }
158
+ if (!width || !sampling.length)
159
+ throw new Error('JPEG: invalid dimensions');
160
+ const hmax = Math.max(...sampling.map(c => c.h));
161
+ const vmax = Math.max(...sampling.map(c => c.v));
162
+ const mcusPerLine = Math.ceil(width / (8 * hmax));
163
+ const mcusPerColumn = Math.ceil(height / (8 * vmax));
164
+ const components = sampling.map((c) => {
165
+ const componentWidth = Math.ceil(width * c.h / hmax);
166
+ const componentHeight = Math.ceil(height * c.v / vmax);
167
+ const blocksPerLineForMcu = mcusPerLine * c.h;
168
+ const blocksPerColumnForMcu = mcusPerColumn * c.v;
169
+ return {
170
+ ...c,
171
+ width: componentWidth,
172
+ height: componentHeight,
173
+ blocksPerLine: Math.ceil(componentWidth / 8),
174
+ blocksPerColumn: Math.ceil(componentHeight / 8),
175
+ blocksPerLineForMcu,
176
+ blocksPerColumnForMcu,
177
+ coefficients: new Int16Array(blocksPerLineForMcu * blocksPerColumnForMcu * 64),
178
+ pred: 0
179
+ };
180
+ });
181
+ return { progressive, width, height, components, hmax, vmax, mcusPerLine, mcusPerColumn };
182
+ }
183
+ function buildHuffmanTable(counts, symbols) {
184
+ const lookup = new Uint16Array(512);
185
+ const maxCode = new Int32Array(18).fill(-1);
186
+ const valueOffset = new Int32Array(17);
187
+ const values = Uint8Array.from(symbols);
188
+ let code = 0, k = 0;
189
+ for (let length = 1; length <= 16; length++) {
190
+ valueOffset[length] = k - code;
191
+ for (let i = 0; i < counts[length - 1]; i++, k++, code++) {
192
+ if (length <= 9) {
193
+ const shift = 9 - length;
194
+ for (let j = 0; j < 1 << shift; j++)
195
+ lookup[code << shift | j] = length << 8 | values[k];
196
+ }
197
+ }
198
+ if (counts[length - 1])
199
+ maxCode[length] = code - 1;
200
+ code <<= 1;
201
+ }
202
+ maxCode[17] = 0x7fffffff;
203
+ return { lookup, maxCode, valueOffset, values };
204
+ }
205
+ function decodeScan(data, offset, frame, components, restartInterval, ss, se, ah, al) {
206
+ let pos = offset;
207
+ let buffer = 0, bits = 0;
208
+ const fill = () => {
209
+ while (bits <= 24) {
210
+ let byte = 0;
211
+ if (pos < data.length) {
212
+ byte = data[pos];
213
+ if (byte === 0xff) {
214
+ const next = data[pos + 1];
215
+ if (next === 0x00)
216
+ pos += 2;
217
+ else
218
+ byte = 0;
219
+ }
220
+ else
221
+ pos++;
222
+ }
223
+ buffer = buffer << 8 | byte;
224
+ bits += 8;
225
+ }
226
+ };
227
+ const getBits = (n) => {
228
+ if (bits < n)
229
+ fill();
230
+ bits -= n;
231
+ return buffer >>> bits & (1 << n) - 1;
232
+ };
233
+ const getBit = () => {
234
+ if (bits < 1)
235
+ fill();
236
+ return buffer >>> --bits & 1;
237
+ };
238
+ const huffman = (table) => {
239
+ if (!table)
240
+ throw new Error('JPEG: missing huffman table');
241
+ if (bits < 16)
242
+ fill();
243
+ const entry = table.lookup[buffer >>> bits - 9 & 511];
244
+ if (entry) {
245
+ bits -= entry >> 8;
246
+ return entry & 255;
247
+ }
248
+ let length = 10;
249
+ let code = buffer >>> bits - 10 & 1023;
250
+ while (code > table.maxCode[length]) {
251
+ if (++length > 16)
252
+ throw new Error('JPEG: invalid huffman code');
253
+ code = buffer >>> bits - length & (1 << length) - 1;
254
+ }
255
+ bits -= length;
256
+ return table.values[code + table.valueOffset[length]];
257
+ };
258
+ const extend = (value, size) => value < 1 << size - 1 ? value + (-1 << size) + 1 : value;
259
+ const receiveExtend = (size) => size ? extend(getBits(size), size) : 0;
260
+ let eobrun = 0;
261
+ const decodeBaseline = (component, offset) => {
262
+ const coefficients = component.coefficients;
263
+ const t = huffman(component.dcTable);
264
+ coefficients[offset] = component.pred += receiveExtend(t);
265
+ for (let k = 1; k < 64;) {
266
+ const rs = huffman(component.acTable);
267
+ const s = rs & 15, r = rs >> 4;
268
+ if (s === 0) {
269
+ if (r < 15)
270
+ break;
271
+ k += 16;
272
+ continue;
273
+ }
274
+ k += r;
275
+ if (k > 63)
276
+ break;
277
+ coefficients[offset + ZIGZAG[k++]] = receiveExtend(s);
278
+ }
279
+ };
280
+ const decodeDCFirst = (component, offset) => {
281
+ const t = huffman(component.dcTable);
282
+ component.coefficients[offset] = (component.pred += receiveExtend(t)) * (1 << al);
283
+ };
284
+ const decodeDCSuccessive = (component, offset) => {
285
+ if (getBit())
286
+ component.coefficients[offset] |= 1 << al;
287
+ };
288
+ const decodeACFirst = (component, offset) => {
289
+ if (eobrun > 0) {
290
+ eobrun--;
291
+ return;
292
+ }
293
+ const coefficients = component.coefficients;
294
+ for (let k = ss; k <= se;) {
295
+ const rs = huffman(component.acTable);
296
+ const s = rs & 15, r = rs >> 4;
297
+ if (s === 0) {
298
+ if (r < 15) {
299
+ eobrun = (1 << r) - 1;
300
+ if (r)
301
+ eobrun += getBits(r);
302
+ break;
303
+ }
304
+ k += 16;
305
+ continue;
306
+ }
307
+ k += r;
308
+ if (k > 63)
309
+ break;
310
+ coefficients[offset + ZIGZAG[k++]] = receiveExtend(s) * (1 << al);
311
+ }
312
+ };
313
+ const decodeACSuccessive = (component, offset) => {
314
+ const coefficients = component.coefficients;
315
+ const p1 = 1 << al;
316
+ const m1 = -1 << al;
317
+ let k = ss;
318
+ if (eobrun === 0) {
319
+ for (; k <= se; k++) {
320
+ const rs = huffman(component.acTable);
321
+ let r = rs >> 4;
322
+ let s = rs & 15;
323
+ if (s)
324
+ s = getBit() ? p1 : m1;
325
+ else if (r !== 15) {
326
+ eobrun = 1 << r;
327
+ if (r)
328
+ eobrun += getBits(r);
329
+ break;
330
+ }
331
+ do {
332
+ const z = offset + ZIGZAG[k];
333
+ const c = coefficients[z];
334
+ if (c !== 0) {
335
+ if (getBit() && (c & p1) === 0)
336
+ coefficients[z] = c >= 0 ? c + p1 : c + m1;
337
+ }
338
+ else if (--r < 0)
339
+ break;
340
+ k++;
341
+ } while (k <= se);
342
+ if (s && k <= se)
343
+ coefficients[offset + ZIGZAG[k]] = s;
344
+ }
345
+ }
346
+ if (eobrun > 0) {
347
+ for (; k <= se; k++) {
348
+ const z = offset + ZIGZAG[k];
349
+ const c = coefficients[z];
350
+ if (c !== 0 && getBit() && (c & p1) === 0)
351
+ coefficients[z] = c >= 0 ? c + p1 : c + m1;
352
+ }
353
+ eobrun--;
354
+ }
355
+ };
356
+ const decodeBlock = !frame.progressive ? decodeBaseline
357
+ : ss === 0 ? (ah === 0 ? decodeDCFirst : decodeDCSuccessive)
358
+ : (ah === 0 ? decodeACFirst : decodeACSuccessive);
359
+ const single = components.length === 1;
360
+ const total = single
361
+ ? components[0].blocksPerLine * components[0].blocksPerColumn
362
+ : frame.mcusPerLine * frame.mcusPerColumn;
363
+ const interval = restartInterval || total;
364
+ for (const component of components)
365
+ component.pred = 0;
366
+ for (let unit = 0; unit < total;) {
367
+ for (let n = 0; n < interval && unit < total; n++, unit++) {
368
+ if (single) {
369
+ const component = components[0];
370
+ const row = unit / component.blocksPerLine | 0;
371
+ const column = unit % component.blocksPerLine;
372
+ decodeBlock(component, (row * component.blocksPerLineForMcu + column) * 64);
373
+ }
374
+ else {
375
+ const mcuRow = unit / frame.mcusPerLine | 0;
376
+ const mcuColumn = unit % frame.mcusPerLine;
377
+ for (const component of components)
378
+ for (let v = 0; v < component.v; v++)
379
+ for (let h = 0; h < component.h; h++) {
380
+ const row = mcuRow * component.v + v;
381
+ const column = mcuColumn * component.h + h;
382
+ decodeBlock(component, (row * component.blocksPerLineForMcu + column) * 64);
383
+ }
384
+ }
385
+ }
386
+ if (unit >= total)
387
+ break;
388
+ bits = 0;
389
+ buffer = 0;
390
+ while (pos + 1 < data.length && !(data[pos] === 0xff && data[pos + 1] >= 0xd0 && data[pos + 1] <= 0xd7)) {
391
+ if (data[pos] === 0xff && data[pos + 1] !== 0x00 && data[pos + 1] !== 0xff)
392
+ return pos;
393
+ pos++;
394
+ }
395
+ if (pos + 1 >= data.length)
396
+ return pos;
397
+ pos += 2;
398
+ eobrun = 0;
399
+ for (const component of components)
400
+ component.pred = 0;
401
+ }
402
+ return pos;
403
+ }
404
+ const CONST_BITS = 13;
405
+ const PASS1_BITS = 2;
406
+ const FIX_0_298631336 = 2446;
407
+ const FIX_0_390180644 = 3196;
408
+ const FIX_0_541196100 = 4433;
409
+ const FIX_0_765366865 = 6270;
410
+ const FIX_0_899976223 = 7373;
411
+ const FIX_1_175875602 = 9633;
412
+ const FIX_1_501321110 = 12299;
413
+ const FIX_1_847759065 = 15137;
414
+ const FIX_1_961570560 = 16069;
415
+ const FIX_2_053119869 = 16819;
416
+ const FIX_2_562915447 = 20995;
417
+ const FIX_3_072711026 = 25172;
418
+ function idct(coefficients, offset, quantization, workspace, out, outOffset, stride) {
419
+ const P1 = CONST_BITS - PASS1_BITS;
420
+ const R1 = 1 << P1 - 1;
421
+ for (let column = 0; column < 8; column++) {
422
+ const c = offset + column;
423
+ if (coefficients[c + 8] === 0 && coefficients[c + 16] === 0 && coefficients[c + 24] === 0 && coefficients[c + 32] === 0
424
+ && coefficients[c + 40] === 0 && coefficients[c + 48] === 0 && coefficients[c + 56] === 0) {
425
+ const dc = coefficients[c] * quantization[column] << PASS1_BITS;
426
+ for (let row = 0; row < 64; row += 8)
427
+ workspace[row + column] = dc;
428
+ continue;
429
+ }
430
+ let z2 = coefficients[c + 16] * quantization[column + 16];
431
+ let z3 = coefficients[c + 48] * quantization[column + 48];
432
+ let z1 = (z2 + z3) * FIX_0_541196100;
433
+ let tmp2 = z1 - z3 * FIX_1_847759065;
434
+ let tmp3 = z1 + z2 * FIX_0_765366865;
435
+ z2 = coefficients[c] * quantization[column];
436
+ z3 = coefficients[c + 32] * quantization[column + 32];
437
+ let tmp0 = z2 + z3 << CONST_BITS;
438
+ let tmp1 = z2 - z3 << CONST_BITS;
439
+ const tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3;
440
+ const tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
441
+ tmp0 = coefficients[c + 56] * quantization[column + 56];
442
+ tmp1 = coefficients[c + 40] * quantization[column + 40];
443
+ tmp2 = coefficients[c + 24] * quantization[column + 24];
444
+ tmp3 = coefficients[c + 8] * quantization[column + 8];
445
+ z1 = tmp0 + tmp3;
446
+ z2 = tmp1 + tmp2;
447
+ z3 = tmp0 + tmp2;
448
+ let z4 = tmp1 + tmp3;
449
+ const z5 = (z3 + z4) * FIX_1_175875602;
450
+ tmp0 *= FIX_0_298631336;
451
+ tmp1 *= FIX_2_053119869;
452
+ tmp2 *= FIX_3_072711026;
453
+ tmp3 *= FIX_1_501321110;
454
+ z1 *= -FIX_0_899976223;
455
+ z2 *= -FIX_2_562915447;
456
+ z3 = z3 * -FIX_1_961570560 + z5;
457
+ z4 = z4 * -FIX_0_390180644 + z5;
458
+ tmp0 += z1 + z3;
459
+ tmp1 += z2 + z4;
460
+ tmp2 += z2 + z3;
461
+ tmp3 += z1 + z4;
462
+ workspace[column] = tmp10 + tmp3 + R1 >> P1;
463
+ workspace[column + 56] = tmp10 - tmp3 + R1 >> P1;
464
+ workspace[column + 8] = tmp11 + tmp2 + R1 >> P1;
465
+ workspace[column + 48] = tmp11 - tmp2 + R1 >> P1;
466
+ workspace[column + 16] = tmp12 + tmp1 + R1 >> P1;
467
+ workspace[column + 40] = tmp12 - tmp1 + R1 >> P1;
468
+ workspace[column + 24] = tmp13 + tmp0 + R1 >> P1;
469
+ workspace[column + 32] = tmp13 - tmp0 + R1 >> P1;
470
+ }
471
+ const P2 = CONST_BITS + PASS1_BITS + 3;
472
+ const R2 = 1 << P2 - 1;
473
+ for (let row = 0; row < 64; row += 8) {
474
+ const o = outOffset + (row >> 3) * stride;
475
+ let z2 = workspace[row + 2];
476
+ let z3 = workspace[row + 6];
477
+ let z1 = (z2 + z3) * FIX_0_541196100;
478
+ let tmp2 = z1 - z3 * FIX_1_847759065;
479
+ let tmp3 = z1 + z2 * FIX_0_765366865;
480
+ let tmp0 = workspace[row] + workspace[row + 4] << CONST_BITS;
481
+ let tmp1 = workspace[row] - workspace[row + 4] << CONST_BITS;
482
+ const tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3;
483
+ const tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
484
+ tmp0 = workspace[row + 7];
485
+ tmp1 = workspace[row + 5];
486
+ tmp2 = workspace[row + 3];
487
+ tmp3 = workspace[row + 1];
488
+ z1 = tmp0 + tmp3;
489
+ z2 = tmp1 + tmp2;
490
+ z3 = tmp0 + tmp2;
491
+ let z4 = tmp1 + tmp3;
492
+ const z5 = (z3 + z4) * FIX_1_175875602;
493
+ tmp0 *= FIX_0_298631336;
494
+ tmp1 *= FIX_2_053119869;
495
+ tmp2 *= FIX_3_072711026;
496
+ tmp3 *= FIX_1_501321110;
497
+ z1 *= -FIX_0_899976223;
498
+ z2 *= -FIX_2_562915447;
499
+ z3 = z3 * -FIX_1_961570560 + z5;
500
+ z4 = z4 * -FIX_0_390180644 + z5;
501
+ tmp0 += z1 + z3;
502
+ tmp1 += z2 + z4;
503
+ tmp2 += z2 + z3;
504
+ tmp3 += z1 + z4;
505
+ out[o] = clamp8((tmp10 + tmp3 + R2 >> P2) + 128);
506
+ out[o + 7] = clamp8((tmp10 - tmp3 + R2 >> P2) + 128);
507
+ out[o + 1] = clamp8((tmp11 + tmp2 + R2 >> P2) + 128);
508
+ out[o + 6] = clamp8((tmp11 - tmp2 + R2 >> P2) + 128);
509
+ out[o + 2] = clamp8((tmp12 + tmp1 + R2 >> P2) + 128);
510
+ out[o + 5] = clamp8((tmp12 - tmp1 + R2 >> P2) + 128);
511
+ out[o + 3] = clamp8((tmp13 + tmp0 + R2 >> P2) + 128);
512
+ out[o + 4] = clamp8((tmp13 - tmp0 + R2 >> P2) + 128);
513
+ }
514
+ }
515
+ function buildPlane(component) {
516
+ const stride = component.blocksPerLineForMcu * 8;
517
+ const plane = new Uint8Array(stride * component.blocksPerColumnForMcu * 8);
518
+ const workspace = new Int32Array(64);
519
+ const quantization = component.quantization || new Int32Array(64);
520
+ for (let row = 0; row < component.blocksPerColumn; row++)
521
+ for (let column = 0; column < component.blocksPerLine; column++)
522
+ idct(component.coefficients, (row * component.blocksPerLineForMcu + column) * 64, quantization, workspace, plane, row * 8 * stride + column * 8, stride);
523
+ return { plane, stride };
524
+ }
525
+ function upsample(component, frame) {
526
+ const { plane, stride } = buildPlane(component);
527
+ const hf = frame.hmax / component.h;
528
+ const vf = frame.vmax / component.v;
529
+ const w = component.width, h = component.height;
530
+ const W = Math.ceil(w * hf), H = Math.ceil(h * vf);
531
+ const out = new Uint8Array(W * H);
532
+ const fancy = w > 2;
533
+ if (hf === 1 && vf === 1) {
534
+ for (let y = 0; y < h; y++)
535
+ out.set(plane.subarray(y * stride, y * stride + w), y * W);
536
+ }
537
+ else if (hf === 2 && vf === 1 && fancy) {
538
+ for (let y = 0; y < h; y++) {
539
+ const i = y * stride, o = y * W;
540
+ out[o] = plane[i];
541
+ out[o + 1] = plane[i] * 3 + plane[i + 1] + 2 >> 2;
542
+ for (let x = 1; x < w - 1; x++) {
543
+ const v = plane[i + x] * 3;
544
+ out[o + 2 * x] = v + plane[i + x - 1] + 1 >> 2;
545
+ out[o + 2 * x + 1] = v + plane[i + x + 1] + 2 >> 2;
546
+ }
547
+ out[o + 2 * w - 2] = plane[i + w - 1] * 3 + plane[i + w - 2] + 1 >> 2;
548
+ out[o + 2 * w - 1] = plane[i + w - 1];
549
+ }
550
+ }
551
+ else if (hf === 1 && vf === 2 && fancy) {
552
+ for (let y = 0; y < h; y++) {
553
+ const i = y * stride;
554
+ const above = Math.max(0, y - 1) * stride;
555
+ const below = Math.min(h - 1, y + 1) * stride;
556
+ for (let x = 0; x < w; x++) {
557
+ const v = plane[i + x] * 3;
558
+ out[2 * y * W + x] = v + plane[above + x] + 1 >> 2;
559
+ out[(2 * y + 1) * W + x] = v + plane[below + x] + 2 >> 2;
560
+ }
561
+ }
562
+ }
563
+ else if (hf === 2 && vf === 2 && fancy) {
564
+ for (let y = 0; y < h; y++) {
565
+ const i = y * stride;
566
+ for (let pass = 0; pass < 2; pass++) {
567
+ const other = (pass === 0 ? Math.max(0, y - 1) : Math.min(h - 1, y + 1)) * stride;
568
+ const o = (2 * y + pass) * W;
569
+ let thisSum = plane[i] * 3 + plane[other];
570
+ let nextSum = plane[i + 1] * 3 + plane[other + 1];
571
+ out[o] = thisSum * 4 + 8 >> 4;
572
+ out[o + 1] = thisSum * 3 + nextSum + 7 >> 4;
573
+ let lastSum = thisSum;
574
+ thisSum = nextSum;
575
+ for (let x = 1; x < w - 1; x++) {
576
+ nextSum = plane[i + x + 1] * 3 + plane[other + x + 1];
577
+ out[o + 2 * x] = thisSum * 3 + lastSum + 8 >> 4;
578
+ out[o + 2 * x + 1] = thisSum * 3 + nextSum + 7 >> 4;
579
+ lastSum = thisSum;
580
+ thisSum = nextSum;
581
+ }
582
+ out[o + 2 * w - 2] = thisSum * 3 + lastSum + 8 >> 4;
583
+ out[o + 2 * w - 1] = thisSum * 4 + 7 >> 4;
584
+ }
585
+ }
586
+ }
587
+ else {
588
+ for (let y = 0; y < H; y++) {
589
+ const i = Math.min(h - 1, y / vf | 0) * stride;
590
+ for (let x = 0; x < W; x++)
591
+ out[y * W + x] = plane[i + Math.min(w - 1, x / hf | 0)];
592
+ }
593
+ }
594
+ return { samples: out, stride: W };
595
+ }
596
+ const crR = new Int32Array(256);
597
+ const cbB = new Int32Array(256);
598
+ const crG = new Int32Array(256);
599
+ const cbG = new Int32Array(256);
600
+ for (let i = 0; i < 256; i++) {
601
+ const x = i - 128;
602
+ crR[i] = 91881 * x + 32768 >> 16;
603
+ cbB[i] = 116130 * x + 32768 >> 16;
604
+ crG[i] = -46802 * x;
605
+ cbG[i] = -22554 * x + 32768;
606
+ }
607
+ function output(frame, adobe, jfif, override) {
608
+ const { width, height, components } = frame;
609
+ const planes = components.map(component => upsample(component, frame));
610
+ const pixels = new Uint8Array(width * height * 4);
611
+ const count = components.length;
612
+ let transform;
613
+ if (count === 1)
614
+ transform = 'grey';
615
+ else if (override && (count === 4) === (override === 'cmyk'))
616
+ transform = override;
617
+ else if (count === 3) {
618
+ if (adobe)
619
+ transform = adobe.transform === 0 ? 'rgb' : 'ycc';
620
+ else if (!jfif && components[0].id === 0x52 && components[1].id === 0x47 && components[2].id === 0x42)
621
+ transform = 'rgb';
622
+ else
623
+ transform = 'ycc';
624
+ }
625
+ else if (count === 4)
626
+ transform = adobe && adobe.transform === 2 ? 'ycck' : 'cmyk';
627
+ else
628
+ throw new Error(`JPEG: ${count} color components are not supported`);
629
+ const [p0, p1, p2, p3] = planes;
630
+ for (let y = 0, o = 0; y < height; y++) {
631
+ const r0 = y * p0.stride, r1 = p1 ? y * p1.stride : 0, r2 = p2 ? y * p2.stride : 0, r3 = p3 ? y * p3.stride : 0;
632
+ for (let x = 0; x < width; x++, o += 4) {
633
+ pixels[o + 3] = 0xff;
634
+ if (transform === 'grey') {
635
+ pixels[o] = pixels[o + 1] = pixels[o + 2] = p0.samples[r0 + x];
636
+ continue;
637
+ }
638
+ const a = p0.samples[r0 + x], b = p1.samples[r1 + x], c = p2.samples[r2 + x];
639
+ if (transform === 'rgb') {
640
+ pixels[o] = a;
641
+ pixels[o + 1] = b;
642
+ pixels[o + 2] = c;
643
+ continue;
644
+ }
645
+ if (transform === 'ycc') {
646
+ pixels[o] = clamp8(a + crR[c]);
647
+ pixels[o + 1] = clamp8(a + (cbG[b] + crG[c] >> 16));
648
+ pixels[o + 2] = clamp8(a + cbB[b]);
649
+ continue;
650
+ }
651
+ let cyan = a, magenta = b, yellow = c, k = p3.samples[r3 + x];
652
+ if (transform === 'ycck') {
653
+ cyan = 255 - clamp8(a + crR[c]);
654
+ magenta = 255 - clamp8(a + (cbG[b] + crG[c] >> 16));
655
+ yellow = 255 - clamp8(a + cbB[b]);
656
+ }
657
+ if (adobe) {
658
+ cyan = 255 - cyan;
659
+ magenta = 255 - magenta;
660
+ yellow = 255 - yellow;
661
+ k = 255 - k;
662
+ }
663
+ pixels[o] = cmykToRGB(cyan, k);
664
+ pixels[o + 1] = cmykToRGB(magenta, k);
665
+ pixels[o + 2] = cmykToRGB(yellow, k);
666
+ }
667
+ }
668
+ return pixels;
669
+ }
670
+ function cmykToRGB(c, k) {
671
+ const t = (255 - c) * k + 128;
672
+ return clamp8(255 - (c + ((t >> 8) + t >> 8)));
673
+ }
674
+ const LUMA_QUANTIZATION = [
675
+ 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55,
676
+ 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51, 87, 80, 62,
677
+ 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92,
678
+ 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99
679
+ ];
680
+ const CHROMA_QUANTIZATION = [
681
+ 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99,
682
+ 24, 26, 56, 99, 99, 99, 99, 99, 47, 66, 99, 99, 99, 99, 99, 99,
683
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
684
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99
685
+ ];
686
+ const AAN_SCALE = [1, 1.387039845, 1.306562965, 1.175875602, 1, 0.785694958, 0.5411961, 0.275899379];
687
+ export function encode(pixels, width, height, quality = 90) {
688
+ if (!width || !height || width > 0xffff || height > 0xffff)
689
+ throw new RangeError('JPEG: invalid dimensions');
690
+ quality = Math.min(100, Math.max(1, Math.round(quality)));
691
+ const scale = quality < 50 ? 5000 / quality : 200 - quality * 2;
692
+ const scaleTable = (base) => base.map(v => Math.min(255, Math.max(1, Math.floor((v * scale + 50) / 100))));
693
+ const tables = [scaleTable(LUMA_QUANTIZATION), scaleTable(CHROMA_QUANTIZATION)];
694
+ const divisors = tables.map(table => {
695
+ const d = new Float64Array(64);
696
+ for (let i = 0; i < 64; i++)
697
+ d[i] = 1 / (table[i] * AAN_SCALE[i >> 3] * AAN_SCALE[i & 7] * 8);
698
+ return d;
699
+ });
700
+ const subsample = quality <= 90;
701
+ const hmax = subsample ? 2 : 1;
702
+ const mcuSize = 8 * hmax;
703
+ const mcusPerLine = Math.ceil(width / mcuSize);
704
+ const mcusPerColumn = Math.ceil(height / mcuSize);
705
+ const W = mcusPerLine * mcuSize, H = mcusPerColumn * mcuSize;
706
+ const Y = new Float32Array(W * H), Cb = new Float32Array(W * H), Cr = new Float32Array(W * H);
707
+ for (let y = 0; y < height; y++) {
708
+ let i = y * width * 4, o = y * W;
709
+ for (let x = 0; x < width; x++, i += 4, o++) {
710
+ const r = pixels[i], g = pixels[i + 1], b = pixels[i + 2];
711
+ Y[o] = 0.299 * r + 0.587 * g + 0.114 * b - 128;
712
+ Cb[o] = -0.168735892 * r - 0.331264108 * g + 0.5 * b;
713
+ Cr[o] = 0.5 * r - 0.418687589 * g - 0.081312411 * b;
714
+ }
715
+ Y.fill(Y[o - 1], o, (y + 1) * W);
716
+ Cb.fill(Cb[o - 1], o, (y + 1) * W);
717
+ Cr.fill(Cr[o - 1], o, (y + 1) * W);
718
+ }
719
+ for (let y = height; y < H; y++) {
720
+ Y.copyWithin(y * W, (height - 1) * W, height * W);
721
+ Cb.copyWithin(y * W, (height - 1) * W, height * W);
722
+ Cr.copyWithin(y * W, (height - 1) * W, height * W);
723
+ }
724
+ const downsample = (plane) => {
725
+ const out = new Float32Array(W * H / 4);
726
+ for (let y = 0, o = 0; y < H; y += 2)
727
+ for (let x = 0; x < W; x += 2, o++) {
728
+ const i = y * W + x;
729
+ out[o] = (plane[i] + plane[i + 1] + plane[i + W] + plane[i + W + 1]) * 0.25;
730
+ }
731
+ return out;
732
+ };
733
+ const chromaStride = subsample ? W / 2 : W;
734
+ const components = [
735
+ { plane: Y, stride: W, h: hmax, v: hmax, table: 0 },
736
+ { plane: subsample ? downsample(Cb) : Cb, stride: chromaStride, h: 1, v: 1, table: 1 },
737
+ { plane: subsample ? downsample(Cr) : Cr, stride: chromaStride, h: 1, v: 1, table: 1 }
738
+ ];
739
+ const blocksPerMcu = hmax * hmax + 2;
740
+ const count = mcusPerLine * mcusPerColumn * blocksPerMcu;
741
+ const coefficients = new Int16Array(count * 64);
742
+ const owners = new Uint8Array(count);
743
+ const block = new Float64Array(64);
744
+ let n = 0;
745
+ for (let my = 0; my < mcusPerColumn; my++)
746
+ for (let mx = 0; mx < mcusPerLine; mx++)
747
+ for (let c = 0; c < 3; c++) {
748
+ const { plane, stride, h: ch, v: cv, table } = components[c];
749
+ const d = divisors[table];
750
+ for (let v = 0; v < cv; v++)
751
+ for (let h = 0; h < ch; h++, n++) {
752
+ const x0 = (mx * ch + h) * 8, y0 = (my * cv + v) * 8;
753
+ for (let y = 0; y < 8; y++) {
754
+ const row = (y0 + y) * stride + x0;
755
+ for (let x = 0; x < 8; x++)
756
+ block[y * 8 + x] = plane[row + x];
757
+ }
758
+ fdct(block);
759
+ const o = n * 64;
760
+ for (let i = 0; i < 64; i++)
761
+ coefficients[o + i] = (block[i] * d[i] + 16384.5 | 0) - 16384;
762
+ owners[n] = c;
763
+ }
764
+ }
765
+ const blocks = { coefficients, owners, count };
766
+ const frequencies = [new Int32Array(257), new Int32Array(257), new Int32Array(257), new Int32Array(257)];
767
+ encodeBlocks(blocks, (table, symbol) => frequencies[table][symbol]++, () => { });
768
+ const huffman = frequencies.map(buildOptimalTable);
769
+ const writer = new BitWriter(width * height);
770
+ writer.bytes(0xff, 0xd8);
771
+ writer.bytes(0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00);
772
+ writer.bytes(0xff, 0xdb, 0x00, 0x84);
773
+ for (let t = 0; t < 2; t++) {
774
+ writer.bytes(t);
775
+ for (let k = 0; k < 64; k++)
776
+ writer.bytes(tables[t][ZIGZAG[k]]);
777
+ }
778
+ writer.bytes(0xff, 0xc0, 0x00, 0x11, 0x08, height >> 8, height & 0xff, width >> 8, width & 0xff, 0x03);
779
+ writer.bytes(1, hmax << 4 | hmax, 0, 2, 0x11, 1, 3, 0x11, 1);
780
+ for (const [index, id] of [[0, 0x00], [1, 0x10], [2, 0x01], [3, 0x11]]) {
781
+ const { bits, values } = huffman[index];
782
+ const length = 19 + values.length;
783
+ writer.bytes(0xff, 0xc4, length >> 8, length & 0xff, id);
784
+ for (let i = 1; i <= 16; i++)
785
+ writer.bytes(bits[i]);
786
+ for (const value of values)
787
+ writer.bytes(value);
788
+ }
789
+ writer.bytes(0xff, 0xda, 0x00, 0x0c, 0x03, 1, 0x00, 2, 0x11, 3, 0x11, 0x00, 0x3f, 0x00);
790
+ encodeBlocks(blocks, (table, symbol) => writer.write(huffman[table].codes[symbol], huffman[table].sizes[symbol]), (value, size) => writer.write(value, size));
791
+ writer.flush();
792
+ writer.bytes(0xff, 0xd9);
793
+ return writer.finish();
794
+ }
795
+ function encodeBlocks({ coefficients, owners, count }, symbol, bits) {
796
+ const pred = [0, 0, 0];
797
+ for (let n = 0; n < count; n++) {
798
+ const component = owners[n];
799
+ const o = n * 64;
800
+ const dcTable = component === 0 ? 0 : 2;
801
+ const acTable = dcTable + 1;
802
+ const diff = coefficients[o] - pred[component];
803
+ pred[component] = coefficients[o];
804
+ const size = magnitude(diff);
805
+ symbol(dcTable, size);
806
+ if (size)
807
+ bits(diff < 0 ? diff - 1 & (1 << size) - 1 : diff, size);
808
+ let run = 0;
809
+ for (let k = 1; k < 64; k++) {
810
+ const value = coefficients[o + ZIGZAG[k]];
811
+ if (value === 0) {
812
+ run++;
813
+ continue;
814
+ }
815
+ while (run > 15) {
816
+ symbol(acTable, 0xf0);
817
+ run -= 16;
818
+ }
819
+ const s = magnitude(value);
820
+ symbol(acTable, run << 4 | s);
821
+ bits(value < 0 ? value - 1 & (1 << s) - 1 : value, s);
822
+ run = 0;
823
+ }
824
+ if (run)
825
+ symbol(acTable, 0x00);
826
+ }
827
+ }
828
+ function magnitude(value) {
829
+ value = Math.abs(value);
830
+ let size = 0;
831
+ while (value) {
832
+ size++;
833
+ value >>= 1;
834
+ }
835
+ return size;
836
+ }
837
+ function buildOptimalTable(frequencies) {
838
+ const freq = Int32Array.from(frequencies);
839
+ const codesize = new Int32Array(257);
840
+ const others = new Int32Array(257).fill(-1);
841
+ freq[256] = 1;
842
+ for (;;) {
843
+ let c1 = -1, c2 = -1, v1 = Infinity, v2 = Infinity;
844
+ for (let i = 0; i <= 256; i++)
845
+ if (freq[i] && freq[i] <= v1) {
846
+ v1 = freq[i];
847
+ c1 = i;
848
+ }
849
+ for (let i = 0; i <= 256; i++)
850
+ if (freq[i] && freq[i] <= v2 && i !== c1) {
851
+ v2 = freq[i];
852
+ c2 = i;
853
+ }
854
+ if (c2 < 0)
855
+ break;
856
+ freq[c1] += freq[c2];
857
+ freq[c2] = 0;
858
+ codesize[c1]++;
859
+ while (others[c1] >= 0) {
860
+ c1 = others[c1];
861
+ codesize[c1]++;
862
+ }
863
+ others[c1] = c2;
864
+ codesize[c2]++;
865
+ while (others[c2] >= 0) {
866
+ c2 = others[c2];
867
+ codesize[c2]++;
868
+ }
869
+ }
870
+ const bits = new Int32Array(33);
871
+ for (let i = 0; i <= 256; i++)
872
+ if (codesize[i])
873
+ bits[codesize[i]]++;
874
+ for (let i = 32; i > 16; i--) {
875
+ while (bits[i] > 0) {
876
+ let j = i - 2;
877
+ while (bits[j] === 0)
878
+ j--;
879
+ bits[i] -= 2;
880
+ bits[i - 1]++;
881
+ bits[j + 1] += 2;
882
+ bits[j]--;
883
+ }
884
+ }
885
+ let i = 16;
886
+ while (bits[i] === 0)
887
+ i--;
888
+ bits[i]--;
889
+ const values = [];
890
+ for (let length = 1; length <= 32; length++)
891
+ for (let symbol = 0; symbol < 256; symbol++)
892
+ if (codesize[symbol] === length)
893
+ values.push(symbol);
894
+ const codes = new Int32Array(256);
895
+ const sizes = new Int32Array(256);
896
+ let code = 0, k = 0;
897
+ for (let length = 1; length <= 16; length++) {
898
+ for (let n = 0; n < bits[length]; n++, k++) {
899
+ codes[values[k]] = code++;
900
+ sizes[values[k]] = length;
901
+ }
902
+ code <<= 1;
903
+ }
904
+ return { bits: bits.subarray(0, 17), values, codes, sizes };
905
+ }
906
+ function fdct(d) {
907
+ for (let p = 0; p < 64; p += 8)
908
+ fdctPass(d, p, 1);
909
+ for (let p = 0; p < 8; p++)
910
+ fdctPass(d, p, 8);
911
+ }
912
+ function fdctPass(d, o, s) {
913
+ const tmp0 = d[o] + d[o + 7 * s], tmp7 = d[o] - d[o + 7 * s];
914
+ const tmp1 = d[o + s] + d[o + 6 * s], tmp6 = d[o + s] - d[o + 6 * s];
915
+ const tmp2 = d[o + 2 * s] + d[o + 5 * s], tmp5 = d[o + 2 * s] - d[o + 5 * s];
916
+ const tmp3 = d[o + 3 * s] + d[o + 4 * s], tmp4 = d[o + 3 * s] - d[o + 4 * s];
917
+ let tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3;
918
+ let tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
919
+ d[o] = tmp10 + tmp11;
920
+ d[o + 4 * s] = tmp10 - tmp11;
921
+ const z1 = (tmp12 + tmp13) * 0.707106781;
922
+ d[o + 2 * s] = tmp13 + z1;
923
+ d[o + 6 * s] = tmp13 - z1;
924
+ tmp10 = tmp4 + tmp5;
925
+ tmp11 = tmp5 + tmp6;
926
+ tmp12 = tmp6 + tmp7;
927
+ const z5 = (tmp10 - tmp12) * 0.382683433;
928
+ const z2 = 0.5411961 * tmp10 + z5;
929
+ const z4 = 1.306562965 * tmp12 + z5;
930
+ const z3 = tmp11 * 0.707106781;
931
+ const z11 = tmp7 + z3, z13 = tmp7 - z3;
932
+ d[o + 5 * s] = z13 + z2;
933
+ d[o + 3 * s] = z13 - z2;
934
+ d[o + s] = z11 + z4;
935
+ d[o + 7 * s] = z11 - z4;
936
+ }
937
+ class BitWriter {
938
+ buffer;
939
+ length;
940
+ bits;
941
+ count;
942
+ constructor(capacity) {
943
+ this.buffer = new Uint8Array(Math.max(1024, capacity));
944
+ this.length = 0;
945
+ this.bits = 0;
946
+ this.count = 0;
947
+ }
948
+ reserve(size) {
949
+ if (this.length + size <= this.buffer.length)
950
+ return;
951
+ const buffer = new Uint8Array(Math.max(this.buffer.length * 2, this.length + size));
952
+ buffer.set(this.buffer.subarray(0, this.length));
953
+ this.buffer = buffer;
954
+ }
955
+ bytes(...values) {
956
+ this.reserve(values.length);
957
+ for (const value of values)
958
+ this.buffer[this.length++] = value;
959
+ }
960
+ write(value, size) {
961
+ this.bits = this.bits << size | value & (1 << size) - 1;
962
+ this.count += size;
963
+ while (this.count >= 8) {
964
+ this.count -= 8;
965
+ const byte = this.bits >>> this.count & 0xff;
966
+ this.reserve(2);
967
+ this.buffer[this.length++] = byte;
968
+ if (byte === 0xff)
969
+ this.buffer[this.length++] = 0x00;
970
+ }
971
+ this.bits &= (1 << this.count) - 1;
972
+ }
973
+ flush() {
974
+ if (this.count)
975
+ this.write((1 << 8 - this.count) - 1, 8 - this.count);
976
+ }
977
+ finish() {
978
+ return this.buffer.slice(0, this.length);
979
+ }
980
+ }