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