@twin.org/image 0.0.1-next.1

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.
@@ -0,0 +1,1900 @@
1
+ 'use strict';
2
+
3
+ var core = require('@twin.org/core');
4
+
5
+ // Copyright 2024 IOTA Stiftung.
6
+ // SPDX-License-Identifier: Apache-2.0.
7
+ /* eslint-disable no-bitwise */
8
+ /**
9
+ * Class to represent a color.
10
+ */
11
+ class Color {
12
+ /**
13
+ * Runtime name for the class.
14
+ * @internal
15
+ */
16
+ static _CLASS_NAME = "Color";
17
+ /**
18
+ * @internal
19
+ */
20
+ _alpha;
21
+ /**
22
+ * @internal
23
+ */
24
+ _red;
25
+ /**
26
+ * @internal
27
+ */
28
+ _green;
29
+ /**
30
+ * @internal
31
+ */
32
+ _blue;
33
+ /**
34
+ * Create a new instance of color.
35
+ * @param alpha The alpha element of the color.
36
+ * @param red The red element of the color.
37
+ * @param green The green element of the color.
38
+ * @param blue The blue element of the color.
39
+ */
40
+ constructor(alpha, red, green, blue) {
41
+ core.Guards.number(Color._CLASS_NAME, "alpha", alpha);
42
+ core.Guards.number(Color._CLASS_NAME, "red", red);
43
+ core.Guards.number(Color._CLASS_NAME, "green", green);
44
+ core.Guards.number(Color._CLASS_NAME, "blue", blue);
45
+ if (alpha < 0 || alpha > 255) {
46
+ throw new core.GeneralError(Color._CLASS_NAME, "range", {
47
+ prop: "alpha",
48
+ value: alpha
49
+ });
50
+ }
51
+ if (red < 0 || red > 255) {
52
+ throw new core.GeneralError(Color._CLASS_NAME, "range", {
53
+ prop: "red",
54
+ value: red
55
+ });
56
+ }
57
+ if (green < 0 || green > 255) {
58
+ throw new core.GeneralError(Color._CLASS_NAME, "range", {
59
+ prop: "green",
60
+ value: green
61
+ });
62
+ }
63
+ if (blue < 0 || blue > 255) {
64
+ throw new core.GeneralError(Color._CLASS_NAME, "range", {
65
+ prop: "blue",
66
+ value: blue
67
+ });
68
+ }
69
+ this._alpha = alpha;
70
+ this._red = red;
71
+ this._green = green;
72
+ this._blue = blue;
73
+ }
74
+ /**
75
+ * Construct a color from a hex string.
76
+ * @param hex The hex string to parse.
77
+ * @returns The color.
78
+ * @throws Error if the format is incorrect.
79
+ */
80
+ static fromHex(hex) {
81
+ core.Guards.stringValue(Color._CLASS_NAME, "hex", hex);
82
+ let alpha;
83
+ let red;
84
+ let green;
85
+ let blue;
86
+ if (/^#[\dA-Fa-f]{3}$/.test(hex)) {
87
+ // #RGB
88
+ alpha = "0xFF";
89
+ red = hex.slice(1, 2).repeat(2);
90
+ green = hex.slice(2, 3).repeat(2);
91
+ blue = hex.slice(3, 4).repeat(2);
92
+ }
93
+ else if (/^#[\dA-Fa-f]{4}$/.test(hex)) {
94
+ // #ARGB
95
+ alpha = hex.slice(1, 2).repeat(2);
96
+ red = hex.slice(2, 3).repeat(2);
97
+ green = hex.slice(3, 4).repeat(2);
98
+ blue = hex.slice(4, 5).repeat(2);
99
+ }
100
+ else if (/^#[\dA-Fa-f]{6}$/.test(hex)) {
101
+ // #RRGGBB
102
+ alpha = "0xFF";
103
+ red = hex.slice(1, 3);
104
+ green = hex.slice(3, 5);
105
+ blue = hex.slice(5, 7);
106
+ }
107
+ else if (/^#[\dA-Fa-f]{8}$/.test(hex)) {
108
+ // #AARRGGBB
109
+ alpha = hex.slice(1, 3);
110
+ red = hex.slice(3, 5);
111
+ green = hex.slice(5, 7);
112
+ blue = hex.slice(7, 9);
113
+ }
114
+ else {
115
+ throw new core.GeneralError(Color._CLASS_NAME, "hex");
116
+ }
117
+ return new Color(Number.parseInt(alpha, 16), Number.parseInt(red, 16), Number.parseInt(green, 16), Number.parseInt(blue, 16));
118
+ }
119
+ /**
120
+ * Coerce an unknown type to a color.
121
+ * @param value The value to try and convert.
122
+ * @returns The color if one can be created.
123
+ */
124
+ static coerce(value) {
125
+ if (core.Is.object(value) &&
126
+ core.Is.number(value._alpha) &&
127
+ core.Is.number(value._red) &&
128
+ core.Is.number(value._green) &&
129
+ core.Is.number(value._blue)) {
130
+ return new Color(value._alpha, value._red, value._green, value._blue);
131
+ }
132
+ else if (core.Is.stringValue(value) && value.startsWith("#")) {
133
+ try {
134
+ return Color.fromHex(value);
135
+ }
136
+ catch { }
137
+ }
138
+ }
139
+ /**
140
+ * Get the alpha element.
141
+ * @returns The alpha element.
142
+ */
143
+ alpha() {
144
+ return this._alpha;
145
+ }
146
+ /**
147
+ * Get the red element.
148
+ * @returns The red element.
149
+ */
150
+ red() {
151
+ return this._red;
152
+ }
153
+ /**
154
+ * Get the green element.
155
+ * @returns The green element.
156
+ */
157
+ green() {
158
+ return this._green;
159
+ }
160
+ /**
161
+ * Get the blue element.
162
+ * @returns The blue element.
163
+ */
164
+ blue() {
165
+ return this._blue;
166
+ }
167
+ /**
168
+ * Get color as argb.
169
+ * @returns The color as argb.
170
+ */
171
+ argb() {
172
+ return ((this._alpha << 24) | (this._red << 16) | (this._green << 8) | this._blue) >>> 0;
173
+ }
174
+ /**
175
+ * Get color as rgba.
176
+ * @returns The color as rgba.
177
+ */
178
+ rgba() {
179
+ return ((this._red << 24) | (this._green << 16) | (this._blue << 8) | this._alpha) >>> 0;
180
+ }
181
+ /**
182
+ * Get color as rgb text.
183
+ * @returns The color as rgb.
184
+ */
185
+ rgbText() {
186
+ return `rgb(${this._red},${this._green},${this._blue})`;
187
+ }
188
+ /**
189
+ * Get color as rgba text.
190
+ * @returns The color as rgba.
191
+ */
192
+ rgbaText() {
193
+ return `rgba(${this._red},${this._green},${this._blue},${Math.round((this._alpha / 255) * 100) / 100})`;
194
+ }
195
+ /**
196
+ * Get color as hex no alpha.
197
+ * @returns The color as hex with no alpha component.
198
+ */
199
+ hex() {
200
+ const red = `00${this._red.toString(16)}`.slice(-2);
201
+ const green = `00${this._green.toString(16)}`.slice(-2);
202
+ const blue = `00${this._blue.toString(16)}`.slice(-2);
203
+ return `#${red}${green}${blue}`.toUpperCase();
204
+ }
205
+ /**
206
+ * Get color as hex with alpha.
207
+ * @returns The color as hex with with alpha component.
208
+ */
209
+ hexWithAlpha() {
210
+ const alpha = `00${this._alpha.toString(16)}`.slice(-2);
211
+ const red = `00${this._red.toString(16)}`.slice(-2);
212
+ const green = `00${this._green.toString(16)}`.slice(-2);
213
+ const blue = `00${this._blue.toString(16)}`.slice(-2);
214
+ return `#${alpha}${red}${green}${blue}`.toUpperCase();
215
+ }
216
+ }
217
+
218
+ // Copyright 2024 IOTA Stiftung.
219
+ // SPDX-License-Identifier: Apache-2.0.
220
+ /* eslint-disable no-bitwise */
221
+ /* eslint-disable no-mixed-operators */
222
+ /* eslint-disable array-bracket-newline */
223
+ /**
224
+ * JPEG Encoder.
225
+ * Based on JPEG encoder ported to JavaScript and optimized by Andreas Ritter.
226
+ */
227
+ class JpegEncoder {
228
+ /**
229
+ * Runtime name for the class.
230
+ * @internal
231
+ */
232
+ static _CLASS_NAME = "JpegEncoder";
233
+ /**
234
+ * @internal
235
+ */
236
+ static _STD_DC_LUMINANCE_NR_CODES = [
237
+ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
238
+ ];
239
+ /**
240
+ * @internal
241
+ */
242
+ static _STD_DC_LUMINANCE_VALUES = [
243
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
244
+ ];
245
+ /**
246
+ * @internal
247
+ */
248
+ static _STD_AC_LUMINANCE_NR_CODES = [
249
+ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d
250
+ ];
251
+ /**
252
+ * @internal
253
+ */
254
+ static _STD_AC_LUMINANCE_VALUES = [
255
+ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
256
+ 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
257
+ 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
258
+ 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
259
+ 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
260
+ 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
261
+ 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
262
+ 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
263
+ 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
264
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
265
+ 0xf9, 0xfa
266
+ ];
267
+ /**
268
+ * @internal
269
+ */
270
+ static _STD_DC_CHROMINANCE_NR_CODES = [
271
+ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
272
+ ];
273
+ /**
274
+ * @internal
275
+ */
276
+ static _STD_DC_CHROMINANCE_VALUES = [
277
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
278
+ ];
279
+ /**
280
+ * @internal
281
+ */
282
+ static _STD_AC_CHROMINANCE_NR_CODES = [
283
+ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77
284
+ ];
285
+ /**
286
+ * @internal
287
+ */
288
+ static _STD_AC_CHROMINANCE_VALUES = [
289
+ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
290
+ 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
291
+ 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
292
+ 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
293
+ 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
294
+ 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
295
+ 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
296
+ 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
297
+ 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
298
+ 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
299
+ 0xf9, 0xfa
300
+ ];
301
+ /** @internal */
302
+ static _SIG_ZAG = [
303
+ 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11,
304
+ 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34,
305
+ 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63
306
+ ];
307
+ /**
308
+ * @internal
309
+ */
310
+ _yTable;
311
+ /**
312
+ * @internal
313
+ */
314
+ _uvTable;
315
+ /**
316
+ * @internal
317
+ */
318
+ _fdTblY;
319
+ /**
320
+ * @internal
321
+ */
322
+ _fdTblUV;
323
+ /**
324
+ * @internal
325
+ */
326
+ _ydcHashTable;
327
+ /**
328
+ * @internal
329
+ */
330
+ _uVdcHashTable;
331
+ /**
332
+ * @internal
333
+ */
334
+ _yacHashTable;
335
+ /**
336
+ * @internal
337
+ */
338
+ _uVacHashTable;
339
+ /**
340
+ * @internal
341
+ */
342
+ _bitCode;
343
+ /**
344
+ * @internal
345
+ */
346
+ _category;
347
+ /**
348
+ * @internal
349
+ */
350
+ _outputFDctQuant;
351
+ /**
352
+ * @internal
353
+ */
354
+ _du;
355
+ /**
356
+ * @internal
357
+ */
358
+ _byteOut;
359
+ /**
360
+ * @internal
361
+ */
362
+ _byteNew;
363
+ /**
364
+ * @internal
365
+ */
366
+ _bytePos;
367
+ /**
368
+ * @internal
369
+ */
370
+ _ydu;
371
+ /**
372
+ * @internal
373
+ */
374
+ _udu;
375
+ /**
376
+ * @internal
377
+ */
378
+ _vdu;
379
+ /**
380
+ * @internal
381
+ */
382
+ _rgbYuvTable;
383
+ /**
384
+ * Create a new instance of JpegEncoder.
385
+ */
386
+ constructor() {
387
+ this._yTable = Array.from({ length: 64 });
388
+ this._uvTable = Array.from({ length: 64 });
389
+ this._fdTblY = Array.from({ length: 64 });
390
+ this._fdTblUV = Array.from({ length: 64 });
391
+ this._bitCode = Array.from({ length: 65535 });
392
+ this._category = Array.from({ length: 65535 });
393
+ this._outputFDctQuant = Array.from({ length: 64 });
394
+ this._du = Array.from({ length: 64 });
395
+ this._byteOut = [];
396
+ this._byteNew = 0;
397
+ this._bytePos = 7;
398
+ this._ydu = Array.from({ length: 64 });
399
+ this._udu = Array.from({ length: 64 });
400
+ this._vdu = Array.from({ length: 64 });
401
+ this._rgbYuvTable = Array.from({ length: 2048 });
402
+ this.initHuffmanTbl();
403
+ this.initCategoryNumber();
404
+ this.initRGBYUVTable();
405
+ }
406
+ /**
407
+ * Encode the image with the given quality.
408
+ * @param width The width of the image to encode.
409
+ * @param height The height of the image to encode.
410
+ * @param imageData The data for the image.
411
+ * @param quality The quality to encode the image at.
412
+ * @returns The data for the encoded image.
413
+ */
414
+ encode(width, height, imageData, quality) {
415
+ this.setQuality(quality);
416
+ // Initialize bit writer
417
+ this._byteOut = [];
418
+ this._byteNew = 0;
419
+ this._bytePos = 7;
420
+ // Add JPEG headers
421
+ this.writeWord(0xffd8); // SOI
422
+ this.writeAPP0();
423
+ this.writeDQT();
424
+ this.writeSOF0(width, height);
425
+ this.writeDHT();
426
+ this.writeSOS();
427
+ // Encode 8x8 macro blocks
428
+ let DCY = 0;
429
+ let DCU = 0;
430
+ let DCV = 0;
431
+ this._byteNew = 0;
432
+ this._bytePos = 7;
433
+ const quadWidth = width * 4;
434
+ let x;
435
+ let y = 0;
436
+ let r;
437
+ let g;
438
+ let b;
439
+ let start;
440
+ let p;
441
+ let col;
442
+ let row;
443
+ let pos;
444
+ while (y < height) {
445
+ x = 0;
446
+ while (x < quadWidth) {
447
+ start = quadWidth * y + x;
448
+ p = start;
449
+ col = -1;
450
+ row = 0;
451
+ for (pos = 0; pos < 64; pos++) {
452
+ row = pos >> 3; // /8
453
+ col = (pos & 7) * 4; // %8
454
+ p = start + row * quadWidth + col;
455
+ if (y + row >= height) {
456
+ // padding bottom
457
+ p -= quadWidth * (y + 1 + row - height);
458
+ }
459
+ if (x + col >= quadWidth) {
460
+ // padding right
461
+ p -= x + col - quadWidth + 4;
462
+ }
463
+ r = imageData[p++];
464
+ g = imageData[p++];
465
+ b = imageData[p++];
466
+ // use lookup table (slightly faster)
467
+ this._ydu[pos] =
468
+ ((this._rgbYuvTable[r] +
469
+ this._rgbYuvTable[Math.trunc(g + 256)] +
470
+ this._rgbYuvTable[Math.trunc(b + 512)]) >>
471
+ 16) -
472
+ 128;
473
+ this._udu[pos] =
474
+ ((this._rgbYuvTable[Math.trunc(r + 768)] +
475
+ this._rgbYuvTable[Math.trunc(g + 1024)] +
476
+ this._rgbYuvTable[Math.trunc(b + 1280)]) >>
477
+ 16) -
478
+ 128;
479
+ this._vdu[pos] =
480
+ ((this._rgbYuvTable[Math.trunc(r + 1280)] +
481
+ this._rgbYuvTable[Math.trunc(g + 1536)] +
482
+ this._rgbYuvTable[Math.trunc(b + 1792)]) >>
483
+ 16) -
484
+ 128;
485
+ }
486
+ if (this._ydcHashTable && this._yacHashTable) {
487
+ DCY = this.processDU(this._ydu, this._fdTblY, DCY, this._ydcHashTable, this._yacHashTable);
488
+ }
489
+ if (this._uVdcHashTable && this._uVacHashTable) {
490
+ DCU = this.processDU(this._udu, this._fdTblUV, DCU, this._uVdcHashTable, this._uVacHashTable);
491
+ }
492
+ if (this._uVdcHashTable && this._uVacHashTable) {
493
+ DCV = this.processDU(this._vdu, this._fdTblUV, DCV, this._uVdcHashTable, this._uVacHashTable);
494
+ }
495
+ x += 32;
496
+ }
497
+ y += 8;
498
+ }
499
+ // Do the bit alignment of the EOI marker
500
+ if (this._bytePos >= 0) {
501
+ const fillBits = [];
502
+ fillBits[1] = this._bytePos + 1;
503
+ fillBits[0] = (1 << (this._bytePos + 1)) - 1;
504
+ this.writeBits(fillBits);
505
+ }
506
+ this.writeWord(0xffd9); // EOI
507
+ return new Uint8Array(this._byteOut);
508
+ }
509
+ /**
510
+ * @internal
511
+ */
512
+ setQuality(quality) {
513
+ if (quality <= 0 || quality > 100) {
514
+ throw new core.GeneralError(JpegEncoder._CLASS_NAME, "invalidQuality", { value: quality });
515
+ }
516
+ let sf = 0;
517
+ if (quality < 50) {
518
+ sf = Math.floor(5000 / quality);
519
+ }
520
+ else {
521
+ sf = Math.floor(200 - quality * 2);
522
+ }
523
+ this.initQuantTables(sf);
524
+ }
525
+ /**
526
+ * @internal
527
+ */
528
+ initQuantTables(sf) {
529
+ const YQT = [
530
+ 16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69,
531
+ 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104,
532
+ 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100, 103, 99
533
+ ];
534
+ for (let i = 0; i < 64; i++) {
535
+ let t = Math.floor((YQT[i] * sf + 50) / 100);
536
+ if (t < 1) {
537
+ t = 1;
538
+ }
539
+ else if (t > 255) {
540
+ t = 255;
541
+ }
542
+ this._yTable[JpegEncoder._SIG_ZAG[i]] = t;
543
+ }
544
+ const UVQT = [
545
+ 17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99, 99,
546
+ 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
547
+ 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99
548
+ ];
549
+ for (let j = 0; j < 64; j++) {
550
+ let u = Math.floor((UVQT[j] * sf + 50) / 100);
551
+ if (u < 1) {
552
+ u = 1;
553
+ }
554
+ else if (u > 255) {
555
+ u = 255;
556
+ }
557
+ this._uvTable[JpegEncoder._SIG_ZAG[j]] = u;
558
+ }
559
+ const aAsf = [1, 1.387039845, 1.306562965, 1.175875602, 1, 0.785694958, 0.5411961, 0.275899379];
560
+ let k = 0;
561
+ for (let row = 0; row < 8; row++) {
562
+ for (let col = 0; col < 8; col++) {
563
+ this._fdTblY[k] = 1 / (this._yTable[JpegEncoder._SIG_ZAG[k]] * aAsf[row] * aAsf[col] * 8);
564
+ this._fdTblUV[k] = 1 / (this._uvTable[JpegEncoder._SIG_ZAG[k]] * aAsf[row] * aAsf[col] * 8);
565
+ k++;
566
+ }
567
+ }
568
+ }
569
+ /**
570
+ * @internal
571
+ */
572
+ computeHuffmanTbl(nrCodes, stdTable) {
573
+ let codeValue = 0;
574
+ let posInTable = 0;
575
+ const HT = [];
576
+ for (let k = 1; k <= 16; k++) {
577
+ for (let j = 1; j <= nrCodes[k]; j++) {
578
+ HT[stdTable[posInTable]] = [];
579
+ HT[stdTable[posInTable]][0] = codeValue;
580
+ HT[stdTable[posInTable]][1] = k;
581
+ posInTable++;
582
+ codeValue++;
583
+ }
584
+ codeValue *= 2;
585
+ }
586
+ return HT;
587
+ }
588
+ /**
589
+ * @internal
590
+ */
591
+ initHuffmanTbl() {
592
+ this._ydcHashTable = this.computeHuffmanTbl(JpegEncoder._STD_DC_LUMINANCE_NR_CODES, JpegEncoder._STD_DC_LUMINANCE_VALUES);
593
+ this._uVdcHashTable = this.computeHuffmanTbl(JpegEncoder._STD_DC_CHROMINANCE_NR_CODES, JpegEncoder._STD_DC_CHROMINANCE_VALUES);
594
+ this._yacHashTable = this.computeHuffmanTbl(JpegEncoder._STD_AC_LUMINANCE_NR_CODES, JpegEncoder._STD_AC_LUMINANCE_VALUES);
595
+ this._uVacHashTable = this.computeHuffmanTbl(JpegEncoder._STD_AC_CHROMINANCE_NR_CODES, JpegEncoder._STD_AC_CHROMINANCE_VALUES);
596
+ }
597
+ /**
598
+ * @internal
599
+ */
600
+ initCategoryNumber() {
601
+ let nrLower = 1;
602
+ let nrUpper = 2;
603
+ for (let cat = 1; cat <= 15; cat++) {
604
+ // Positive numbers
605
+ for (let nr = nrLower; nr < nrUpper; nr++) {
606
+ this._category[32767 + nr] = cat;
607
+ this._bitCode[32767 + nr] = [];
608
+ this._bitCode[32767 + nr][1] = cat;
609
+ this._bitCode[32767 + nr][0] = nr;
610
+ }
611
+ // Negative numbers
612
+ for (let nrNeg = -(nrUpper - 1); nrNeg <= -nrLower; nrNeg++) {
613
+ this._category[32767 + nrNeg] = cat;
614
+ this._bitCode[32767 + nrNeg] = [];
615
+ this._bitCode[32767 + nrNeg][1] = cat;
616
+ this._bitCode[32767 + nrNeg][0] = nrUpper - 1 + nrNeg;
617
+ }
618
+ nrLower <<= 1;
619
+ nrUpper <<= 1;
620
+ }
621
+ }
622
+ /**
623
+ * @internal
624
+ */
625
+ initRGBYUVTable() {
626
+ for (let i = 0; i < 256; i++) {
627
+ this._rgbYuvTable[i] = 19595 * i;
628
+ this._rgbYuvTable[Math.trunc(i + 256)] = 38470 * i;
629
+ this._rgbYuvTable[Math.trunc(i + 512)] = 7471 * i + 0x8000;
630
+ this._rgbYuvTable[Math.trunc(i + 768)] = -11059 * i;
631
+ this._rgbYuvTable[Math.trunc(i + 1024)] = -21709 * i;
632
+ this._rgbYuvTable[Math.trunc(i + 1280)] = 32768 * i + 0x807fff;
633
+ this._rgbYuvTable[Math.trunc(i + 1536)] = -27439 * i;
634
+ this._rgbYuvTable[Math.trunc(i + 1792)] = -5329 * i;
635
+ }
636
+ }
637
+ /**
638
+ * @internal
639
+ */
640
+ writeBits(bs) {
641
+ const value = bs[0];
642
+ let posVal = bs[1] - 1;
643
+ while (posVal >= 0) {
644
+ if (value & (1 << posVal)) {
645
+ this._byteNew |= 1 << this._bytePos;
646
+ }
647
+ posVal--;
648
+ this._bytePos--;
649
+ if (this._bytePos < 0) {
650
+ if (this._byteNew === 0xff) {
651
+ this.writeByte(0xff);
652
+ this.writeByte(0);
653
+ }
654
+ else {
655
+ this.writeByte(this._byteNew);
656
+ }
657
+ this._bytePos = 7;
658
+ this._byteNew = 0;
659
+ }
660
+ }
661
+ }
662
+ /**
663
+ * @internal
664
+ */
665
+ writeByte(value) {
666
+ this._byteOut.push(value);
667
+ }
668
+ /**
669
+ * @internal
670
+ */
671
+ writeWord(value) {
672
+ this.writeByte((value >> 8) & 0xff);
673
+ this.writeByte(value & 0xff);
674
+ }
675
+ /**
676
+ * @internal
677
+ */
678
+ fDCTQuant(data, fdTbl) {
679
+ let d0;
680
+ let d1;
681
+ let d2;
682
+ let d3;
683
+ let d4;
684
+ let d5;
685
+ let d6;
686
+ let d7;
687
+ /* Pass 1: process rows. */
688
+ let dataOff = 0;
689
+ let i;
690
+ const I8 = 8;
691
+ const I64 = 64;
692
+ for (i = 0; i < I8; ++i) {
693
+ d0 = data[dataOff];
694
+ d1 = data[dataOff + 1];
695
+ d2 = data[dataOff + 2];
696
+ d3 = data[dataOff + 3];
697
+ d4 = data[dataOff + 4];
698
+ d5 = data[dataOff + 5];
699
+ d6 = data[dataOff + 6];
700
+ d7 = data[dataOff + 7];
701
+ const tmp0 = d0 + d7;
702
+ const tmp7 = d0 - d7;
703
+ const tmp1 = d1 + d6;
704
+ const tmp6 = d1 - d6;
705
+ const tmp2 = d2 + d5;
706
+ const tmp5 = d2 - d5;
707
+ const tmp3 = d3 + d4;
708
+ const tmp4 = d3 - d4;
709
+ /* Even part */
710
+ let tmp10 = tmp0 + tmp3; /* phase 2 */
711
+ const tmp13 = tmp0 - tmp3;
712
+ let tmp11 = tmp1 + tmp2;
713
+ let tmp12 = tmp1 - tmp2;
714
+ data[dataOff] = tmp10 + tmp11; /* phase 3 */
715
+ data[dataOff + 4] = tmp10 - tmp11;
716
+ const z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */
717
+ data[dataOff + 2] = tmp13 + z1; /* phase 5 */
718
+ data[dataOff + 6] = tmp13 - z1;
719
+ /* Odd part */
720
+ tmp10 = tmp4 + tmp5; /* phase 2 */
721
+ tmp11 = tmp5 + tmp6;
722
+ tmp12 = tmp6 + tmp7;
723
+ /* The rotator is modified from fig 4-8 to avoid extra negations. */
724
+ const z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */
725
+ const z2 = 0.5411961 * tmp10 + z5; /* c2-c6 */
726
+ const z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */
727
+ const z3 = tmp11 * 0.707106781; /* c4 */
728
+ const z11 = tmp7 + z3; /* phase 5 */
729
+ const z13 = tmp7 - z3;
730
+ data[dataOff + 5] = z13 + z2; /* phase 6 */
731
+ data[dataOff + 3] = z13 - z2;
732
+ data[dataOff + 1] = z11 + z4;
733
+ data[dataOff + 7] = z11 - z4;
734
+ dataOff += 8; /* advance pointer to next row */
735
+ }
736
+ /* Pass 2: process columns. */
737
+ dataOff = 0;
738
+ for (i = 0; i < I8; ++i) {
739
+ d0 = data[dataOff];
740
+ d1 = data[dataOff + 8];
741
+ d2 = data[dataOff + 16];
742
+ d3 = data[dataOff + 24];
743
+ d4 = data[dataOff + 32];
744
+ d5 = data[dataOff + 40];
745
+ d6 = data[dataOff + 48];
746
+ d7 = data[dataOff + 56];
747
+ const tmp0p2 = d0 + d7;
748
+ const tmp7p2 = d0 - d7;
749
+ const tmp1p2 = d1 + d6;
750
+ const tmp6p2 = d1 - d6;
751
+ const tmp2p2 = d2 + d5;
752
+ const tmp5p2 = d2 - d5;
753
+ const tmp3p2 = d3 + d4;
754
+ const tmp4p2 = d3 - d4;
755
+ /* Even part */
756
+ let tmp10p2 = tmp0p2 + tmp3p2; /* phase 2 */
757
+ const tmp13p2 = tmp0p2 - tmp3p2;
758
+ let tmp11p2 = tmp1p2 + tmp2p2;
759
+ let tmp12p2 = tmp1p2 - tmp2p2;
760
+ data[dataOff] = tmp10p2 + tmp11p2; /* phase 3 */
761
+ data[dataOff + 32] = tmp10p2 - tmp11p2;
762
+ const z1p2 = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */
763
+ data[dataOff + 16] = tmp13p2 + z1p2; /* phase 5 */
764
+ data[dataOff + 48] = tmp13p2 - z1p2;
765
+ /* Odd part */
766
+ tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */
767
+ tmp11p2 = tmp5p2 + tmp6p2;
768
+ tmp12p2 = tmp6p2 + tmp7p2;
769
+ /* The rotator is modified from fig 4-8 to avoid extra negations. */
770
+ const z5p2 = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */
771
+ const z2p2 = 0.5411961 * tmp10p2 + z5p2; /* c2-c6 */
772
+ const z4p2 = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */
773
+ const z3p2 = tmp11p2 * 0.707106781; /* c4 */
774
+ const z11p2 = tmp7p2 + z3p2; /* phase 5 */
775
+ const z13p2 = tmp7p2 - z3p2;
776
+ data[dataOff + 40] = z13p2 + z2p2; /* phase 6 */
777
+ data[dataOff + 24] = z13p2 - z2p2;
778
+ data[dataOff + 8] = z11p2 + z4p2;
779
+ data[dataOff + 56] = z11p2 - z4p2;
780
+ dataOff++; /* advance pointer to next column */
781
+ }
782
+ // Quantize/descale the coefficients
783
+ let fDCTQuant;
784
+ for (i = 0; i < I64; ++i) {
785
+ // Apply the quantization and scaling factor & Round to nearest integer
786
+ fDCTQuant = data[i] * fdTbl[i];
787
+ this._outputFDctQuant[i] =
788
+ fDCTQuant > 0 ? Math.trunc(fDCTQuant + 0.5) : Math.trunc(fDCTQuant - 0.5);
789
+ }
790
+ return this._outputFDctQuant;
791
+ }
792
+ /**
793
+ * @internal
794
+ */
795
+ writeAPP0() {
796
+ this.writeWord(0xffe0); // marker
797
+ this.writeWord(16); // length
798
+ this.writeByte(0x4a); // J
799
+ this.writeByte(0x46); // F
800
+ this.writeByte(0x49); // I
801
+ this.writeByte(0x46); // F
802
+ // cspell:disable-next-line
803
+ this.writeByte(0); // = "JFIF",'\0'
804
+ this.writeByte(1); // version hi
805
+ this.writeByte(1); // version lo
806
+ this.writeByte(0); // xy units
807
+ this.writeWord(1); // x density
808
+ this.writeWord(1); // y density
809
+ this.writeByte(0); // thumb n width
810
+ this.writeByte(0); // thumb n height
811
+ }
812
+ /**
813
+ * @internal
814
+ */
815
+ writeSOF0(width, height) {
816
+ this.writeWord(0xffc0); // marker
817
+ this.writeWord(17); // length, true color YUV JPG
818
+ this.writeByte(8); // precision
819
+ this.writeWord(height);
820
+ this.writeWord(width);
821
+ this.writeByte(3); // nr of components
822
+ this.writeByte(1); // IdY
823
+ this.writeByte(0x11); // HVY
824
+ this.writeByte(0); // QTY
825
+ this.writeByte(2); // IdU
826
+ this.writeByte(0x11); // HVU
827
+ this.writeByte(1); // QTU
828
+ this.writeByte(3); // IdV
829
+ this.writeByte(0x11); // HVV
830
+ this.writeByte(1); // QTV
831
+ }
832
+ /**
833
+ * @internal
834
+ */
835
+ writeDQT() {
836
+ this.writeWord(0xffdb); // marker
837
+ this.writeWord(132); // length
838
+ this.writeByte(0);
839
+ for (let i = 0; i < 64; i++) {
840
+ this.writeByte(this._yTable[i]);
841
+ }
842
+ this.writeByte(1);
843
+ for (let j = 0; j < 64; j++) {
844
+ this.writeByte(this._uvTable[j]);
845
+ }
846
+ }
847
+ /**
848
+ * @internal
849
+ */
850
+ writeDHT() {
851
+ this.writeWord(0xffc4); // marker
852
+ this.writeWord(0x01a2); // length
853
+ this.writeByte(0); // HTYDc info
854
+ for (let i = 0; i < 16; i++) {
855
+ this.writeByte(JpegEncoder._STD_DC_LUMINANCE_NR_CODES[i + 1]);
856
+ }
857
+ for (let j = 0; j <= 11; j++) {
858
+ this.writeByte(JpegEncoder._STD_DC_LUMINANCE_VALUES[j]);
859
+ }
860
+ this.writeByte(0x10); // HTYAc info
861
+ for (let k = 0; k < 16; k++) {
862
+ this.writeByte(JpegEncoder._STD_AC_LUMINANCE_NR_CODES[k + 1]);
863
+ }
864
+ for (let l = 0; l <= 161; l++) {
865
+ this.writeByte(JpegEncoder._STD_AC_LUMINANCE_VALUES[l]);
866
+ }
867
+ this.writeByte(1); // HTUDc info
868
+ for (let m = 0; m < 16; m++) {
869
+ this.writeByte(JpegEncoder._STD_DC_CHROMINANCE_NR_CODES[m + 1]);
870
+ }
871
+ for (let n = 0; n <= 11; n++) {
872
+ this.writeByte(JpegEncoder._STD_DC_CHROMINANCE_VALUES[n]);
873
+ }
874
+ this.writeByte(0x11); // HTUAc info
875
+ for (let o = 0; o < 16; o++) {
876
+ this.writeByte(JpegEncoder._STD_AC_CHROMINANCE_NR_CODES[o + 1]);
877
+ }
878
+ for (let p = 0; p <= 161; p++) {
879
+ this.writeByte(JpegEncoder._STD_AC_CHROMINANCE_VALUES[p]);
880
+ }
881
+ }
882
+ /**
883
+ * @internal
884
+ */
885
+ writeSOS() {
886
+ this.writeWord(0xffda); // marker
887
+ this.writeWord(12); // length
888
+ this.writeByte(3); // nr of components
889
+ this.writeByte(1); // IdY
890
+ this.writeByte(0); // HTY
891
+ this.writeByte(2); // IdU
892
+ this.writeByte(0x11); // HTU
893
+ this.writeByte(3); // IdV
894
+ this.writeByte(0x11); // HTV
895
+ this.writeByte(0); // Ss
896
+ this.writeByte(0x3f); // Se
897
+ this.writeByte(0); // Bf
898
+ }
899
+ /**
900
+ * @internal
901
+ */
902
+ processDU(CDU, fdTbl, passedDC, HTDc, HTAc) {
903
+ let DC = passedDC;
904
+ const EOB = HTAc[0x00];
905
+ const m16zeroes = HTAc[0xf0];
906
+ let pos;
907
+ const I16 = 16;
908
+ const I63 = 63;
909
+ const I64 = 64;
910
+ const DU_DCT = this.fDCTQuant(CDU, fdTbl);
911
+ // ZigZag reorder
912
+ for (let j = 0; j < I64; ++j) {
913
+ this._du[JpegEncoder._SIG_ZAG[j]] = DU_DCT[j];
914
+ }
915
+ const diff = this._du[0] - DC;
916
+ DC = this._du[0];
917
+ // Encode DC
918
+ if (diff === 0) {
919
+ this.writeBits(HTDc[0]); // Diff might be 0
920
+ }
921
+ else {
922
+ pos = 32767 + diff;
923
+ this.writeBits(HTDc[this._category[pos]]);
924
+ this.writeBits(this._bitCode[pos]);
925
+ }
926
+ // Encode ACs
927
+ let end0pos = 63; // was const... which is crazy
928
+ for (; end0pos > 0 && this._du[end0pos] === 0; end0pos--) { }
929
+ // end0pos = first element in reverse order !=0
930
+ if (end0pos === 0) {
931
+ this.writeBits(EOB);
932
+ return DC;
933
+ }
934
+ let i = 1;
935
+ let lng;
936
+ while (i <= end0pos) {
937
+ const startPos = i;
938
+ for (; this._du[i] === 0 && i <= end0pos; ++i) { }
939
+ let nrZeroes = i - startPos;
940
+ if (nrZeroes >= I16) {
941
+ lng = nrZeroes >> 4;
942
+ for (let nrMarker = 1; nrMarker <= lng; ++nrMarker) {
943
+ this.writeBits(m16zeroes);
944
+ }
945
+ nrZeroes &= 0xf;
946
+ }
947
+ pos = 32767 + this._du[i];
948
+ this.writeBits(HTAc[(nrZeroes << 4) + this._category[pos]]);
949
+ this.writeBits(this._bitCode[pos]);
950
+ i++;
951
+ }
952
+ if (end0pos !== I63) {
953
+ this.writeBits(EOB);
954
+ }
955
+ return DC;
956
+ }
957
+ }
958
+
959
+ // Copyright 2024 IOTA Stiftung.
960
+ // SPDX-License-Identifier: Apache-2.0.
961
+ /* eslint-disable no-bitwise */
962
+ /* eslint-disable no-mixed-operators */
963
+ /* eslint-disable no-continue */
964
+ /* eslint-disable unicorn/prefer-math-trunc */
965
+ /**
966
+ * PNG Encoder.
967
+ * Based on https://github.com/photopea/UPNG.js.
968
+ */
969
+ class PngEncoder {
970
+ /**
971
+ * Encode the image frames to png.
972
+ * @param buffers The frame buffers to encode.
973
+ * @param w The image width.
974
+ * @param h The image height.
975
+ * @returns The data for the image.
976
+ */
977
+ async encode(buffers, w, h) {
978
+ const ps = 0;
979
+ const forbidPlte = false;
980
+ const data = new Uint8Array(buffers[0].byteLength * buffers.length + 100);
981
+ const wr = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
982
+ for (let i = 0; i < 8; i++) {
983
+ data[i] = wr[i];
984
+ }
985
+ let offset = 8;
986
+ const nImg = await this.compressPNG(buffers, w, h, ps, forbidPlte);
987
+ this.writeUint(data, offset, 13);
988
+ offset += 4;
989
+ // cspell:disable-next-line
990
+ this.writeASCII(data, offset, "IHDR");
991
+ offset += 4;
992
+ this.writeUint(data, offset, w);
993
+ offset += 4;
994
+ this.writeUint(data, offset, h);
995
+ offset += 4;
996
+ data[offset] = nImg.depth;
997
+ offset++;
998
+ data[offset] = nImg.cType;
999
+ offset++;
1000
+ data[offset] = 0; // compress
1001
+ offset++;
1002
+ data[offset] = 0; // filter
1003
+ offset++;
1004
+ data[offset] = 0; // interlace
1005
+ offset++;
1006
+ this.writeUint(data, offset, this.crc(data, offset - 17, 17));
1007
+ offset += 4; // crc
1008
+ // 9 bytes to say, that it is sRGB
1009
+ this.writeUint(data, offset, 1);
1010
+ offset += 4;
1011
+ this.writeASCII(data, offset, "sRGB");
1012
+ offset += 4;
1013
+ data[offset] = 1;
1014
+ offset++;
1015
+ this.writeUint(data, offset, this.crc(data, offset - 5, 5));
1016
+ offset += 4; // crc
1017
+ const anim = buffers.length > 1;
1018
+ if (anim) {
1019
+ this.writeUint(data, offset, 8);
1020
+ offset += 4;
1021
+ this.writeASCII(data, offset, "acTL");
1022
+ offset += 4;
1023
+ this.writeUint(data, offset, buffers.length);
1024
+ offset += 4;
1025
+ this.writeUint(data, offset, 0);
1026
+ offset += 4;
1027
+ this.writeUint(data, offset, this.crc(data, offset - 12, 12));
1028
+ offset += 4; // crc
1029
+ }
1030
+ if (nImg.cType === 3) {
1031
+ const dl = nImg.plte.length;
1032
+ this.writeUint(data, offset, dl * 3);
1033
+ offset += 4;
1034
+ this.writeASCII(data, offset, "PLTE");
1035
+ offset += 4;
1036
+ for (let i = 0; i < dl; i++) {
1037
+ const ti = i * 3;
1038
+ const c = nImg.plte[i];
1039
+ const r = c & 255;
1040
+ const g = (c >> 8) & 255;
1041
+ const b = (c >> 16) & 255;
1042
+ data[offset + ti + 0] = r;
1043
+ data[offset + ti + 1] = g;
1044
+ data[offset + ti + 2] = b;
1045
+ }
1046
+ offset += dl * 3;
1047
+ this.writeUint(data, offset, this.crc(data, offset - dl * 3 - 4, dl * 3 + 4));
1048
+ offset += 4; // crc
1049
+ if (nImg.gotAlpha) {
1050
+ this.writeUint(data, offset, dl);
1051
+ offset += 4;
1052
+ this.writeASCII(data, offset, "tRNS");
1053
+ offset += 4;
1054
+ for (let i = 0; i < dl; i++) {
1055
+ data[offset + i] = (nImg.plte[i] >> 24) & 255;
1056
+ }
1057
+ offset += dl;
1058
+ this.writeUint(data, offset, this.crc(data, offset - dl - 4, dl + 4));
1059
+ offset += 4; // crc
1060
+ }
1061
+ }
1062
+ let fi = 0;
1063
+ for (let j = 0; j < nImg.frames.length; j++) {
1064
+ const fr = nImg.frames[j];
1065
+ if (anim) {
1066
+ this.writeUint(data, offset, 26);
1067
+ offset += 4;
1068
+ this.writeASCII(data, offset, "fcTL");
1069
+ offset += 4;
1070
+ this.writeUint(data, offset, fi++);
1071
+ offset += 4;
1072
+ this.writeUint(data, offset, fr.rect.width);
1073
+ offset += 4;
1074
+ this.writeUint(data, offset, fr.rect.height);
1075
+ offset += 4;
1076
+ this.writeUint(data, offset, fr.rect.x);
1077
+ offset += 4;
1078
+ this.writeUint(data, offset, fr.rect.y);
1079
+ offset += 4;
1080
+ this.writeUshort(data, offset, 0);
1081
+ offset += 2;
1082
+ this.writeUshort(data, offset, 1000);
1083
+ offset += 2;
1084
+ data[offset] = fr.dispose;
1085
+ offset++; // dispose
1086
+ data[offset] = fr.blend;
1087
+ offset++; // blend
1088
+ this.writeUint(data, offset, this.crc(data, offset - 30, 30));
1089
+ offset += 4; // crc
1090
+ }
1091
+ const imgD = fr.cImg;
1092
+ const dl = imgD?.length ?? 0;
1093
+ this.writeUint(data, offset, dl + (j === 0 ? 0 : 4));
1094
+ offset += 4;
1095
+ const iOff = offset;
1096
+ // cspell:disable-next-line
1097
+ this.writeASCII(data, offset, j === 0 ? "IDAT" : "fdAT");
1098
+ offset += 4;
1099
+ if (j !== 0) {
1100
+ this.writeUint(data, offset, fi++);
1101
+ offset += 4;
1102
+ }
1103
+ if (imgD) {
1104
+ for (let i = 0; i < dl; i++) {
1105
+ data[offset + i] = imgD[i];
1106
+ }
1107
+ }
1108
+ offset += dl;
1109
+ this.writeUint(data, offset, this.crc(data, iOff, offset - iOff));
1110
+ offset += 4; // crc
1111
+ }
1112
+ this.writeUint(data, offset, 0);
1113
+ offset += 4;
1114
+ // cspell:disable-next-line
1115
+ this.writeASCII(data, offset, "IEND");
1116
+ offset += 4;
1117
+ this.writeUint(data, offset, this.crc(data, offset - 4, 4));
1118
+ offset += 4; // crc
1119
+ return new Uint8Array(data.buffer.slice(0, offset));
1120
+ }
1121
+ /**
1122
+ * @internal
1123
+ */
1124
+ async compressPNG(buffers, w, h, ps, forbidPlte) {
1125
+ const out = this.compress(buffers, w, h, ps, 0, forbidPlte);
1126
+ for (let i = 0; i < buffers.length; i++) {
1127
+ const frm = out.frames[i];
1128
+ const nw = frm.rect.width;
1129
+ const nh = frm.rect.height;
1130
+ const bpl = frm.bpl;
1131
+ const bpp = frm.bpp;
1132
+ const fData = new Uint8Array(nw * bpl + nh);
1133
+ frm.cImg = await this.filterZero(frm.img, nh, bpp, bpl, fData);
1134
+ }
1135
+ return out;
1136
+ }
1137
+ /**
1138
+ * @internal
1139
+ */
1140
+ compress(inBuffers, w, h, inPs, forGIF, forbidPlte) {
1141
+ let cType = 6;
1142
+ let depth = 8;
1143
+ let bpp = 4;
1144
+ let alphaAnd = 255;
1145
+ let ps = inPs;
1146
+ let buffers = inBuffers;
1147
+ for (let j = 0; j < buffers.length; j++) {
1148
+ // when not quantized, other frames can contain colors, that are not in an initial frame
1149
+ const img = new Uint8Array(buffers[j]);
1150
+ const iLen = img.length;
1151
+ for (let i = 0; i < iLen; i += 4) {
1152
+ alphaAnd &= img[i + 3];
1153
+ }
1154
+ }
1155
+ let gotAlpha = alphaAnd !== 255;
1156
+ const cMap = {};
1157
+ const pLte = [];
1158
+ if (buffers.length !== 0) {
1159
+ cMap[0] = 0;
1160
+ pLte.push(0);
1161
+ if (ps !== 0) {
1162
+ ps--;
1163
+ }
1164
+ }
1165
+ if (ps !== 0) {
1166
+ const qRes = this.quantize(buffers, ps, forGIF);
1167
+ buffers = qRes.buffers;
1168
+ for (let i = 0; i < qRes.plte.length; i++) {
1169
+ const c = qRes.plte[i].est?.rgba ?? 0;
1170
+ if (!cMap[c]) {
1171
+ cMap[c] = pLte.length;
1172
+ pLte.push(c);
1173
+ }
1174
+ }
1175
+ }
1176
+ else {
1177
+ // what if ps==0, but there are <=256 colors? we still need to detect, if the palette could be used
1178
+ for (let j = 0; j < buffers.length; j++) {
1179
+ // when not quantized, other frames can contain colors, that are not in an initial frame
1180
+ const img32 = new Uint32Array(buffers[j]);
1181
+ const iLen = img32.length;
1182
+ for (let i = 0; i < iLen; i++) {
1183
+ const c = img32[i];
1184
+ if ((i < w || (c !== img32[i - 1] && c !== img32[i - w])) && !cMap[c]) {
1185
+ cMap[c] = pLte.length;
1186
+ pLte.push(c);
1187
+ if (pLte.length >= 300) {
1188
+ break;
1189
+ }
1190
+ }
1191
+ }
1192
+ }
1193
+ }
1194
+ const brute = gotAlpha ? forGIF : false; // brute : frames can only be copied, not "blended"
1195
+ const cc = pLte.length;
1196
+ if (cc <= 256 && !forbidPlte) {
1197
+ if (cc <= 2) {
1198
+ depth = 1;
1199
+ }
1200
+ else if (cc <= 4) {
1201
+ depth = 2;
1202
+ }
1203
+ else if (cc <= 16) {
1204
+ depth = 4;
1205
+ }
1206
+ else {
1207
+ depth = 8;
1208
+ }
1209
+ if (forGIF) {
1210
+ depth = 8;
1211
+ }
1212
+ gotAlpha = true;
1213
+ }
1214
+ const frames = [];
1215
+ for (let j = 0; j < buffers.length; j++) {
1216
+ let cImg = new Uint8Array(buffers[j]);
1217
+ let cImg32 = new Uint32Array(cImg.buffer);
1218
+ let nx = 0;
1219
+ let ny = 0;
1220
+ let nw = w;
1221
+ let nh = h;
1222
+ let blend = 0;
1223
+ if (j !== 0 && !brute) {
1224
+ const tLim = forGIF || j === 1 || frames[frames.length - 2].dispose === 2 ? 1 : 2;
1225
+ let tStp = 0;
1226
+ let tArea = 1e9;
1227
+ for (let it = 0; it < tLim; it++) {
1228
+ const p32 = new Uint32Array(buffers[j - 1 - it]);
1229
+ let mix = w;
1230
+ let miy = h;
1231
+ let max = -1;
1232
+ let may = -1;
1233
+ for (let y = 0; y < h; y++) {
1234
+ for (let x = 0; x < w; x++) {
1235
+ const i = y * w + x;
1236
+ if (cImg32[i] !== p32[i]) {
1237
+ if (x < mix) {
1238
+ mix = x;
1239
+ }
1240
+ if (x > max) {
1241
+ max = x;
1242
+ }
1243
+ if (y < miy) {
1244
+ miy = y;
1245
+ }
1246
+ if (y > may) {
1247
+ may = y;
1248
+ }
1249
+ }
1250
+ }
1251
+ }
1252
+ const sArea = max === -1 ? 1 : (max - mix + 1) * (may - miy + 1);
1253
+ if (sArea < tArea) {
1254
+ tArea = sArea;
1255
+ tStp = it;
1256
+ if (max === -1) {
1257
+ nx = 0;
1258
+ ny = 0;
1259
+ nw = 1;
1260
+ nh = 1;
1261
+ }
1262
+ else {
1263
+ nx = mix;
1264
+ ny = miy;
1265
+ nw = max - mix + 1;
1266
+ nh = may - miy + 1;
1267
+ }
1268
+ }
1269
+ }
1270
+ const pImg = new Uint8Array(buffers[j - 1 - tStp]);
1271
+ if (tStp === 1) {
1272
+ frames[frames.length - 1].dispose = 2;
1273
+ }
1274
+ const nImg = new Uint8Array(nw * nh * 4);
1275
+ this.copyTile(pImg, w, h, nImg, nw, nh, -nx, -ny, 0);
1276
+ if (this.copyTile(cImg, w, h, nImg, nw, nh, -nx, -ny, 3)) {
1277
+ this.copyTile(cImg, w, h, nImg, nw, nh, -nx, -ny, 2);
1278
+ blend = 1;
1279
+ }
1280
+ else {
1281
+ this.copyTile(cImg, w, h, nImg, nw, nh, -nx, -ny, 0);
1282
+ blend = 0;
1283
+ }
1284
+ cImg = nImg;
1285
+ cImg32 = new Uint32Array(cImg.buffer);
1286
+ }
1287
+ let bpl = 4 * nw;
1288
+ if (cc <= 256 && !forbidPlte) {
1289
+ bpl = Math.ceil((depth * nw) / 8);
1290
+ const nImg = new Uint8Array(bpl * nh);
1291
+ for (let y = 0; y < nh; y++) {
1292
+ const i = y * bpl;
1293
+ const ii = y * nw;
1294
+ if (depth === 8) {
1295
+ for (let x = 0; x < nw; x++) {
1296
+ nImg[i + x] = cMap[cImg32[ii + x]];
1297
+ }
1298
+ }
1299
+ else if (depth === 4) {
1300
+ for (let x = 0; x < nw; x++) {
1301
+ nImg[i + (x >> 1)] |= cMap[cImg32[ii + x]] << (4 - (x & 1) * 4);
1302
+ }
1303
+ }
1304
+ else if (depth === 2) {
1305
+ for (let x = 0; x < nw; x++) {
1306
+ nImg[i + (x >> 2)] |= cMap[cImg32[ii + x]] << (6 - (x & 3) * 2);
1307
+ }
1308
+ }
1309
+ else if (depth === 1) {
1310
+ for (let x = 0; x < nw; x++) {
1311
+ nImg[i + (x >> 3)] |= cMap[cImg32[ii + x]] << (7 - (x & 7) * 1);
1312
+ }
1313
+ }
1314
+ }
1315
+ cImg = nImg;
1316
+ cType = 3;
1317
+ bpp = 1;
1318
+ }
1319
+ else if (!gotAlpha && buffers.length === 1) {
1320
+ // some next "reduced" frames may contain alpha for blending
1321
+ const nImg = new Uint8Array(nw * nh * 3);
1322
+ const area = nw * nh;
1323
+ for (let i = 0; i < area; i++) {
1324
+ const ti = i * 3;
1325
+ const qi = i * 4;
1326
+ nImg[ti] = cImg[qi];
1327
+ nImg[ti + 1] = cImg[qi + 1];
1328
+ nImg[ti + 2] = cImg[qi + 2];
1329
+ }
1330
+ cImg = nImg;
1331
+ cType = 2;
1332
+ bpp = 3;
1333
+ bpl = 3 * nw;
1334
+ }
1335
+ frames.push({
1336
+ rect: {
1337
+ x: nx,
1338
+ y: ny,
1339
+ width: nw,
1340
+ height: nh
1341
+ },
1342
+ img: cImg,
1343
+ bpl,
1344
+ bpp,
1345
+ blend,
1346
+ dispose: brute ? 1 : 0
1347
+ });
1348
+ }
1349
+ return { cType, depth, plte: pLte, gotAlpha, frames };
1350
+ }
1351
+ /**
1352
+ * @internal
1353
+ */
1354
+ async filterZero(img, h, bpp, bpl, data) {
1355
+ const fls = [];
1356
+ for (let t = 0; t < 5; t++) {
1357
+ if (h * bpl > 500000 && (t === 2 || t === 3 || t === 4)) {
1358
+ continue;
1359
+ }
1360
+ for (let y = 0; y < h; y++) {
1361
+ this.filterLine(data, img, y, bpl, bpp, t);
1362
+ }
1363
+ const deflated = await core.Compression.compress(data, "deflate");
1364
+ fls.push(deflated);
1365
+ if (bpp === 1) {
1366
+ break;
1367
+ }
1368
+ }
1369
+ let ti = 0;
1370
+ let tSize = 1e9;
1371
+ for (let i = 0; i < fls.length; i++) {
1372
+ if (fls[i].length < tSize) {
1373
+ ti = i;
1374
+ tSize = fls[i].length;
1375
+ }
1376
+ }
1377
+ return fls[ti];
1378
+ }
1379
+ /**
1380
+ * @internal
1381
+ */
1382
+ filterLine(data, img, y, bpl, bpp, type) {
1383
+ const i = y * bpl;
1384
+ let di = i + y;
1385
+ data[di] = type;
1386
+ di++;
1387
+ if (type === 0) {
1388
+ for (let x = 0; x < bpl; x++) {
1389
+ data[di + x] = img[i + x];
1390
+ }
1391
+ }
1392
+ else if (type === 1) {
1393
+ for (let x = 0; x < bpp; x++) {
1394
+ data[di + x] = img[i + x];
1395
+ }
1396
+ for (let x = bpp; x < bpl; x++) {
1397
+ data[di + x] = (img[i + x] - img[i + x - bpp] + 256) & 255;
1398
+ }
1399
+ }
1400
+ else if (y === 0) {
1401
+ for (let x = 0; x < bpp; x++) {
1402
+ data[di + x] = img[i + x];
1403
+ }
1404
+ if (type === 2) {
1405
+ for (let x = bpp; x < bpl; x++) {
1406
+ data[di + x] = img[i + x];
1407
+ }
1408
+ }
1409
+ if (type === 3) {
1410
+ for (let x = bpp; x < bpl; x++) {
1411
+ data[di + x] = (img[i + x] - (img[i + x - bpp] >> 1) + 256) & 255;
1412
+ }
1413
+ }
1414
+ if (type === 4) {
1415
+ for (let x = bpp; x < bpl; x++) {
1416
+ data[di + x] = (img[i + x] - this.paeth(img[i + x - bpp], 0, 0) + 256) & 255;
1417
+ }
1418
+ }
1419
+ }
1420
+ else {
1421
+ if (type === 2) {
1422
+ for (let x = 0; x < bpl; x++) {
1423
+ data[di + x] = (img[i + x] + 256 - img[i + x - bpl]) & 255;
1424
+ }
1425
+ }
1426
+ if (type === 3) {
1427
+ for (let x = 0; x < bpp; x++) {
1428
+ data[di + x] = (img[i + x] + 256 - (img[i + x - bpl] >> 1)) & 255;
1429
+ }
1430
+ for (let x = bpp; x < bpl; x++) {
1431
+ data[di + x] = (img[i + x] + 256 - ((img[i + x - bpl] + img[i + x - bpp]) >> 1)) & 255;
1432
+ }
1433
+ }
1434
+ if (type === 4) {
1435
+ for (let x = 0; x < bpp; x++) {
1436
+ data[di + x] = (img[i + x] + 256 - this.paeth(0, img[i + x - bpl], 0)) & 255;
1437
+ }
1438
+ for (let x = bpp; x < bpl; x++) {
1439
+ data[di + x] =
1440
+ (img[i + x] +
1441
+ 256 -
1442
+ this.paeth(img[i + x - bpp], img[i + x - bpl], img[i + x - bpp - bpl])) &
1443
+ 255;
1444
+ }
1445
+ }
1446
+ }
1447
+ }
1448
+ /**
1449
+ * @internal
1450
+ */
1451
+ paeth(a, b, c) {
1452
+ const p = a + b - c;
1453
+ const pa = Math.abs(p - a);
1454
+ const pb = Math.abs(p - b);
1455
+ const pc = Math.abs(p - c);
1456
+ if (pa <= pb && pa <= pc) {
1457
+ return a;
1458
+ }
1459
+ if (pb <= pc) {
1460
+ return b;
1461
+ }
1462
+ return c;
1463
+ }
1464
+ /**
1465
+ * @internal
1466
+ */
1467
+ writeASCII(data, p, s) {
1468
+ for (let i = 0; i < s.length; i++) {
1469
+ data[p + i] = s.charCodeAt(i);
1470
+ }
1471
+ }
1472
+ /**
1473
+ * @internal
1474
+ */
1475
+ writeUint(buff, p, n) {
1476
+ buff[p] = (n >> 24) & 255;
1477
+ buff[p + 1] = (n >> 16) & 255;
1478
+ buff[p + 2] = (n >> 8) & 255;
1479
+ buff[p + 3] = n & 255;
1480
+ }
1481
+ /**
1482
+ * @internal
1483
+ */
1484
+ writeUshort(buff, p, n) {
1485
+ buff[p] = (n >> 8) & 255;
1486
+ buff[p + 1] = n & 255;
1487
+ }
1488
+ /**
1489
+ * @internal
1490
+ */
1491
+ copyTile(sb, sw, sh, tb, tw, th, xOffset, yOffset, mode) {
1492
+ const w = Math.min(sw, tw);
1493
+ const h = Math.min(sh, th);
1494
+ let si = 0;
1495
+ let ti = 0;
1496
+ for (let y = 0; y < h; y++) {
1497
+ for (let x = 0; x < w; x++) {
1498
+ if (xOffset >= 0 && yOffset >= 0) {
1499
+ si = (y * sw + x) << 2;
1500
+ ti = ((yOffset + y) * tw + xOffset + x) << 2;
1501
+ }
1502
+ else {
1503
+ si = ((-yOffset + y) * sw - xOffset + x) << 2;
1504
+ ti = (y * tw + x) << 2;
1505
+ }
1506
+ if (mode === 0) {
1507
+ tb[ti] = sb[si];
1508
+ tb[ti + 1] = sb[si + 1];
1509
+ tb[ti + 2] = sb[si + 2];
1510
+ tb[ti + 3] = sb[si + 3];
1511
+ }
1512
+ else if (mode === 1) {
1513
+ const fa = sb[si + 3] * (1 / 255);
1514
+ const fr = sb[si] * fa;
1515
+ const fg = sb[si + 1] * fa;
1516
+ const fb = sb[si + 2] * fa;
1517
+ const ba = tb[ti + 3] * (1 / 255);
1518
+ const br = tb[ti] * ba;
1519
+ const bg = tb[ti + 1] * ba;
1520
+ const bb = tb[ti + 2] * ba;
1521
+ const ifa = 1 - fa;
1522
+ const oa = fa + ba * ifa;
1523
+ const ioa = oa === 0 ? 0 : 1 / oa;
1524
+ tb[ti + 3] = 255 * oa;
1525
+ tb[ti + 0] = (fr + br * ifa) * ioa;
1526
+ tb[ti + 1] = (fg + bg * ifa) * ioa;
1527
+ tb[ti + 2] = (fb + bb * ifa) * ioa;
1528
+ }
1529
+ else if (mode === 2) {
1530
+ // copy only differences, otherwise zero
1531
+ const fa = sb[si + 3];
1532
+ const fr = sb[si];
1533
+ const fg = sb[si + 1];
1534
+ const fb = sb[si + 2];
1535
+ const ba = tb[ti + 3];
1536
+ const br = tb[ti];
1537
+ const bg = tb[ti + 1];
1538
+ const bb = tb[ti + 2];
1539
+ if (fa === ba && fr === br && fg === bg && fb === bb) {
1540
+ tb[ti] = 0;
1541
+ tb[ti + 1] = 0;
1542
+ tb[ti + 2] = 0;
1543
+ tb[ti + 3] = 0;
1544
+ }
1545
+ else {
1546
+ tb[ti] = fr;
1547
+ tb[ti + 1] = fg;
1548
+ tb[ti + 2] = fb;
1549
+ tb[ti + 3] = fa;
1550
+ }
1551
+ }
1552
+ else if (mode === 3) {
1553
+ // check if can be blended
1554
+ const fa = sb[si + 3];
1555
+ const fr = sb[si];
1556
+ const fg = sb[si + 1];
1557
+ const fb = sb[si + 2];
1558
+ const ba = tb[ti + 3];
1559
+ const br = tb[ti];
1560
+ const bg = tb[ti + 1];
1561
+ const bb = tb[ti + 2];
1562
+ if (fa === ba && fr === br && fg === bg && fb === bb) {
1563
+ continue;
1564
+ }
1565
+ if (fa < 220 && ba > 20) {
1566
+ return false;
1567
+ }
1568
+ }
1569
+ }
1570
+ }
1571
+ return true;
1572
+ }
1573
+ /**
1574
+ * @internal
1575
+ */
1576
+ crc(b, o, l) {
1577
+ return this.crcUpdate(0xffffffff, b, o, l) ^ 0xffffffff;
1578
+ }
1579
+ /**
1580
+ * @internal
1581
+ */
1582
+ crcUpdate(c, buf, off, len) {
1583
+ let localC = c;
1584
+ const crcTable = this.crcTable();
1585
+ for (let i = 0; i < len; i++) {
1586
+ localC = crcTable[(localC ^ buf[off + i]) & 0xff] ^ (localC >>> 8);
1587
+ }
1588
+ return localC;
1589
+ }
1590
+ /**
1591
+ * @internal
1592
+ */
1593
+ crcTable() {
1594
+ const tab = new Uint32Array(256);
1595
+ for (let n = 0; n < 256; n++) {
1596
+ let c = n;
1597
+ for (let k = 0; k < 8; k++) {
1598
+ if (c & 1) {
1599
+ c = 0xedb88320 ^ (c >>> 1);
1600
+ }
1601
+ else {
1602
+ c >>>= 1;
1603
+ }
1604
+ }
1605
+ tab[n] = c;
1606
+ }
1607
+ return tab;
1608
+ }
1609
+ /**
1610
+ * @internal
1611
+ */
1612
+ quantize(buffers, ps, roundAlpha) {
1613
+ const imgs = [];
1614
+ let total = 0;
1615
+ for (let i = 0; i < buffers.length; i++) {
1616
+ imgs.push(this.alphaMul(new Uint8Array(buffers[i]), roundAlpha));
1617
+ total += buffers[i].byteLength;
1618
+ }
1619
+ const nImg = new Uint8Array(total);
1620
+ const nImg32 = new Uint32Array(nImg.buffer);
1621
+ let nOff = 0;
1622
+ for (let i = 0; i < imgs.length; i++) {
1623
+ const img = imgs[i];
1624
+ const il = img.length;
1625
+ for (let j = 0; j < il; j++) {
1626
+ nImg[nOff + j] = img[j];
1627
+ }
1628
+ nOff += il;
1629
+ }
1630
+ const root = {
1631
+ i0: 0,
1632
+ i1: nImg.length,
1633
+ bst: null,
1634
+ est: null,
1635
+ tDst: 0,
1636
+ left: null,
1637
+ right: null
1638
+ };
1639
+ root.bst = this.quantizeStats(nImg, root.i0, root.i1);
1640
+ root.est = this.quantizeEStats(root.bst);
1641
+ const leafs = [root];
1642
+ while (leafs.length < ps) {
1643
+ let maxL = 0;
1644
+ let mi = 0;
1645
+ for (let i = 0; i < leafs.length; i++) {
1646
+ const est = leafs[i].est;
1647
+ if (est && est.L > maxL) {
1648
+ maxL = est.L;
1649
+ mi = i;
1650
+ }
1651
+ }
1652
+ if (maxL < 1e-3) {
1653
+ break;
1654
+ }
1655
+ const node = leafs[mi];
1656
+ const s0 = this.quantizeSplitPixels(nImg, nImg32, node.i0, node.i1, node.est?.e ?? [], node.est?.eMq255 ?? 0);
1657
+ const ln = {
1658
+ i0: node.i0,
1659
+ i1: s0,
1660
+ bst: null,
1661
+ est: null,
1662
+ tDst: 0,
1663
+ left: null,
1664
+ right: null
1665
+ };
1666
+ ln.bst = this.quantizeStats(nImg, ln.i0, ln.i1);
1667
+ ln.est = this.quantizeEStats(ln.bst);
1668
+ const rn = {
1669
+ i0: s0,
1670
+ i1: node.i1,
1671
+ bst: null,
1672
+ est: null,
1673
+ tDst: 0,
1674
+ left: null,
1675
+ right: null
1676
+ };
1677
+ rn.bst = {
1678
+ R: [],
1679
+ m: [],
1680
+ N: (node.bst?.N ?? 0) - ln.bst.N
1681
+ };
1682
+ for (let i = 0; i < 16; i++) {
1683
+ rn.bst.R[i] = (node.bst?.R[i] ?? 0) - ln.bst.R[i];
1684
+ }
1685
+ for (let i = 0; i < 4; i++) {
1686
+ rn.bst.m[i] = (node.bst?.m[i] ?? 0) - ln.bst.m[i];
1687
+ }
1688
+ rn.est = this.quantizeEStats(rn.bst);
1689
+ node.left = ln;
1690
+ node.right = rn;
1691
+ leafs[mi] = ln;
1692
+ leafs.push(rn);
1693
+ }
1694
+ leafs.sort((a, b) => (b.bst?.N ?? 0) - (a.bst?.N ?? 0));
1695
+ const outBuffers = [];
1696
+ for (let ii = 0; ii < imgs.length; ii++) {
1697
+ const sb = new Uint8Array(imgs[ii]);
1698
+ const tb = new Uint32Array(imgs[ii]);
1699
+ const len = sb.length;
1700
+ for (let i = 0; i < len; i += 4) {
1701
+ const r = sb[i] * (1 / 255);
1702
+ const g = sb[i + 1] * (1 / 255);
1703
+ const b = sb[i + 2] * (1 / 255);
1704
+ const a = sb[i + 3] * (1 / 255);
1705
+ let nd = root;
1706
+ while (nd?.left) {
1707
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1708
+ nd = this.quantizePlaneDst(nd.est, r, g, b, a) <= 0 ? nd.left : nd.right;
1709
+ }
1710
+ tb[i >> 2] = nd?.est?.rgba ?? 0;
1711
+ }
1712
+ outBuffers[ii] = tb.buffer;
1713
+ }
1714
+ return { buffers: outBuffers, plte: leafs };
1715
+ }
1716
+ /**
1717
+ * @internal
1718
+ */
1719
+ quantizeStats(nImg, i0, i1) {
1720
+ const R = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
1721
+ const m = [0, 0, 0, 0];
1722
+ const N = (i1 - i0) >> 2;
1723
+ for (let i = i0; i < i1; i += 4) {
1724
+ const r = nImg[i] * (1 / 255);
1725
+ const g = nImg[i + 1] * (1 / 255);
1726
+ const b = nImg[i + 2] * (1 / 255);
1727
+ const a = nImg[i + 3] * (1 / 255);
1728
+ m[0] += r;
1729
+ m[1] += g;
1730
+ m[2] += b;
1731
+ m[3] += a;
1732
+ R[0] += r * r;
1733
+ R[1] += r * g;
1734
+ R[2] += r * b;
1735
+ R[3] += r * a;
1736
+ R[5] += g * g;
1737
+ R[6] += g * b;
1738
+ R[7] += g * a;
1739
+ R[10] += b * b;
1740
+ R[11] += b * a;
1741
+ R[15] += a * a;
1742
+ }
1743
+ R[4] = R[1];
1744
+ R[8] = R[2];
1745
+ R[12] = R[3];
1746
+ R[9] = R[6];
1747
+ R[13] = R[7];
1748
+ R[14] = R[11];
1749
+ return { R, m, N };
1750
+ }
1751
+ /**
1752
+ * @internal
1753
+ */
1754
+ quantizeEStats(stats) {
1755
+ const R = stats.R;
1756
+ const m = stats.m;
1757
+ const N = stats.N;
1758
+ const m0 = m[0];
1759
+ const m1 = m[1];
1760
+ const m2 = m[2];
1761
+ const m3 = m[3];
1762
+ const iN = N === 0 ? 0 : 1 / N;
1763
+ const rj = [
1764
+ R[0] - m0 * m0 * iN,
1765
+ R[1] - m0 * m1 * iN,
1766
+ R[2] - m0 * m2 * iN,
1767
+ R[3] - m0 * m3 * iN,
1768
+ R[4] - m1 * m0 * iN,
1769
+ R[5] - m1 * m1 * iN,
1770
+ R[6] - m1 * m2 * iN,
1771
+ R[7] - m1 * m3 * iN,
1772
+ R[8] - m2 * m0 * iN,
1773
+ R[9] - m2 * m1 * iN,
1774
+ R[10] - m2 * m2 * iN,
1775
+ R[11] - m2 * m3 * iN,
1776
+ R[12] - m3 * m0 * iN,
1777
+ R[13] - m3 * m1 * iN,
1778
+ R[14] - m3 * m2 * iN,
1779
+ R[15] - m3 * m3 * iN
1780
+ ];
1781
+ const A = rj;
1782
+ let b = [0.5, 0.5, 0.5, 0.5];
1783
+ let mi = 0;
1784
+ let tmi = 0;
1785
+ if (N !== 0) {
1786
+ for (let i = 0; i < 10; i++) {
1787
+ b = this.m4MultiplyVec(A, b);
1788
+ tmi = Math.sqrt(this.m4Dot(b, b));
1789
+ b = this.m4Sml(1 / tmi, b);
1790
+ if (Math.abs(tmi - mi) < 1e-9) {
1791
+ break;
1792
+ }
1793
+ mi = tmi;
1794
+ }
1795
+ }
1796
+ const q = [m0 * iN, m1 * iN, m2 * iN, m3 * iN];
1797
+ const eMq255 = this.m4Dot(this.m4Sml(255, q), b);
1798
+ const ia = q[3] < 0.001 ? 0 : 1 / q[3];
1799
+ return {
1800
+ Cov: rj,
1801
+ q,
1802
+ e: b,
1803
+ L: mi,
1804
+ eMq255,
1805
+ eMq: this.m4Dot(b, q),
1806
+ rgba: ((Math.round(255 * q[3]) << 24) |
1807
+ (Math.round(255 * q[2] * ia) << 16) |
1808
+ (Math.round(255 * q[1] * ia) << 8) |
1809
+ (Math.round(255 * q[0] * ia) << 0)) >>>
1810
+ 0
1811
+ };
1812
+ }
1813
+ /**
1814
+ * @internal
1815
+ */
1816
+ quantizePlaneDst(est, r, g, b, a) {
1817
+ const e = est.e;
1818
+ return e[0] * r + e[1] * g + e[2] * b + e[3] * a - est.eMq;
1819
+ }
1820
+ /**
1821
+ * @internal
1822
+ */
1823
+ quantizeSplitPixels(nImg, nImg32, i0in, i1in, e, eMq) {
1824
+ let i1 = i1in - 4;
1825
+ let i0 = i0in;
1826
+ while (i0 < i1) {
1827
+ while (this.quantizeVecDot(nImg, i0, e) <= eMq) {
1828
+ i0 += 4;
1829
+ }
1830
+ while (this.quantizeVecDot(nImg, i1, e) > eMq) {
1831
+ i1 -= 4;
1832
+ }
1833
+ if (i0 >= i1) {
1834
+ break;
1835
+ }
1836
+ const t = nImg32[i0 >> 2];
1837
+ nImg32[i0 >> 2] = nImg32[i1 >> 2];
1838
+ nImg32[i1 >> 2] = t;
1839
+ i0 += 4;
1840
+ i1 -= 4;
1841
+ }
1842
+ while (this.quantizeVecDot(nImg, i0, e) > eMq) {
1843
+ i0 -= 4;
1844
+ }
1845
+ return i0 + 4;
1846
+ }
1847
+ /**
1848
+ * @internal
1849
+ */
1850
+ quantizeVecDot(nImg, i, e) {
1851
+ return nImg[i] * e[0] + nImg[i + 1] * e[1] + nImg[i + 2] * e[2] + nImg[i + 3] * e[3];
1852
+ }
1853
+ /**
1854
+ * @internal
1855
+ */
1856
+ m4MultiplyVec(m, v) {
1857
+ return [
1858
+ m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * v[3],
1859
+ m[4] * v[0] + m[5] * v[1] + m[6] * v[2] + m[7] * v[3],
1860
+ m[8] * v[0] + m[9] * v[1] + m[10] * v[2] + m[11] * v[3],
1861
+ m[12] * v[0] + m[13] * v[1] + m[14] * v[2] + m[15] * v[3]
1862
+ ];
1863
+ }
1864
+ /**
1865
+ * @internal
1866
+ */
1867
+ m4Dot(x, y) {
1868
+ return x[0] * y[0] + x[1] * y[1] + x[2] * y[2] + x[3] * y[3];
1869
+ }
1870
+ /**
1871
+ * @internal
1872
+ */
1873
+ m4Sml(a, y) {
1874
+ return [a * y[0], a * y[1], a * y[2], a * y[3]];
1875
+ }
1876
+ /**
1877
+ * @internal
1878
+ */
1879
+ alphaMul(img, roundA) {
1880
+ const nImg = new Uint8Array(img.length);
1881
+ const area = img.length >> 2;
1882
+ for (let i = 0; i < area; i++) {
1883
+ const qi = i << 2;
1884
+ let ia = img[qi + 3];
1885
+ if (roundA) {
1886
+ ia = ia < 128 ? 0 : 255;
1887
+ }
1888
+ const a = ia * (1 / 255);
1889
+ nImg[qi + 0] = img[qi + 0] * a;
1890
+ nImg[qi + 1] = img[qi + 1] * a;
1891
+ nImg[qi + 2] = img[qi + 2] * a;
1892
+ nImg[qi + 3] = ia;
1893
+ }
1894
+ return nImg;
1895
+ }
1896
+ }
1897
+
1898
+ exports.Color = Color;
1899
+ exports.JpegEncoder = JpegEncoder;
1900
+ exports.PngEncoder = PngEncoder;