@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.
@@ -0,0 +1,939 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ /* eslint-disable no-bitwise */
4
+ /* eslint-disable no-mixed-operators */
5
+ /* eslint-disable no-continue */
6
+ import { Compression } from "@twin.org/core";
7
+ /**
8
+ * PNG Encoder.
9
+ * Based on https://github.com/photopea/UPNG.js.
10
+ */
11
+ export class PngEncoder {
12
+ /**
13
+ * Encode the image frames to png.
14
+ * @param buffers The frame buffers to encode.
15
+ * @param w The image width.
16
+ * @param h The image height.
17
+ * @returns The data for the image.
18
+ */
19
+ async encode(buffers, w, h) {
20
+ const ps = 0;
21
+ const forbidPlte = false;
22
+ const data = new Uint8Array(buffers[0].byteLength * buffers.length + 100);
23
+ const wr = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
24
+ for (let i = 0; i < 8; i++) {
25
+ data[i] = wr[i];
26
+ }
27
+ let offset = 8;
28
+ const nImg = await this.compressPNG(buffers, w, h, ps, forbidPlte);
29
+ this.writeUint(data, offset, 13);
30
+ offset += 4;
31
+ // cspell:disable-next-line
32
+ this.writeASCII(data, offset, "IHDR");
33
+ offset += 4;
34
+ this.writeUint(data, offset, w);
35
+ offset += 4;
36
+ this.writeUint(data, offset, h);
37
+ offset += 4;
38
+ data[offset] = nImg.depth;
39
+ offset++;
40
+ data[offset] = nImg.cType;
41
+ offset++;
42
+ data[offset] = 0; // compress
43
+ offset++;
44
+ data[offset] = 0; // filter
45
+ offset++;
46
+ data[offset] = 0; // interlace
47
+ offset++;
48
+ this.writeUint(data, offset, this.crc(data, offset - 17, 17));
49
+ offset += 4; // crc
50
+ // 9 bytes to say, that it is sRGB
51
+ this.writeUint(data, offset, 1);
52
+ offset += 4;
53
+ this.writeASCII(data, offset, "sRGB");
54
+ offset += 4;
55
+ data[offset] = 1;
56
+ offset++;
57
+ this.writeUint(data, offset, this.crc(data, offset - 5, 5));
58
+ offset += 4; // crc
59
+ const anim = buffers.length > 1;
60
+ if (anim) {
61
+ this.writeUint(data, offset, 8);
62
+ offset += 4;
63
+ this.writeASCII(data, offset, "acTL");
64
+ offset += 4;
65
+ this.writeUint(data, offset, buffers.length);
66
+ offset += 4;
67
+ this.writeUint(data, offset, 0);
68
+ offset += 4;
69
+ this.writeUint(data, offset, this.crc(data, offset - 12, 12));
70
+ offset += 4; // crc
71
+ }
72
+ if (nImg.cType === 3) {
73
+ const dl = nImg.plte.length;
74
+ this.writeUint(data, offset, dl * 3);
75
+ offset += 4;
76
+ this.writeASCII(data, offset, "PLTE");
77
+ offset += 4;
78
+ for (let i = 0; i < dl; i++) {
79
+ const ti = i * 3;
80
+ const c = nImg.plte[i];
81
+ const r = c & 255;
82
+ const g = (c >> 8) & 255;
83
+ const b = (c >> 16) & 255;
84
+ data[offset + ti + 0] = r;
85
+ data[offset + ti + 1] = g;
86
+ data[offset + ti + 2] = b;
87
+ }
88
+ offset += dl * 3;
89
+ this.writeUint(data, offset, this.crc(data, offset - dl * 3 - 4, dl * 3 + 4));
90
+ offset += 4; // crc
91
+ if (nImg.gotAlpha) {
92
+ this.writeUint(data, offset, dl);
93
+ offset += 4;
94
+ this.writeASCII(data, offset, "tRNS");
95
+ offset += 4;
96
+ for (let i = 0; i < dl; i++) {
97
+ data[offset + i] = (nImg.plte[i] >> 24) & 255;
98
+ }
99
+ offset += dl;
100
+ this.writeUint(data, offset, this.crc(data, offset - dl - 4, dl + 4));
101
+ offset += 4; // crc
102
+ }
103
+ }
104
+ let fi = 0;
105
+ for (let j = 0; j < nImg.frames.length; j++) {
106
+ const fr = nImg.frames[j];
107
+ if (anim) {
108
+ this.writeUint(data, offset, 26);
109
+ offset += 4;
110
+ this.writeASCII(data, offset, "fcTL");
111
+ offset += 4;
112
+ this.writeUint(data, offset, fi++);
113
+ offset += 4;
114
+ this.writeUint(data, offset, fr.rect.width);
115
+ offset += 4;
116
+ this.writeUint(data, offset, fr.rect.height);
117
+ offset += 4;
118
+ this.writeUint(data, offset, fr.rect.x);
119
+ offset += 4;
120
+ this.writeUint(data, offset, fr.rect.y);
121
+ offset += 4;
122
+ this.writeUshort(data, offset, 0);
123
+ offset += 2;
124
+ this.writeUshort(data, offset, 1000);
125
+ offset += 2;
126
+ data[offset] = fr.dispose;
127
+ offset++; // dispose
128
+ data[offset] = fr.blend;
129
+ offset++; // blend
130
+ this.writeUint(data, offset, this.crc(data, offset - 30, 30));
131
+ offset += 4; // crc
132
+ }
133
+ const imgD = fr.cImg;
134
+ const dl = imgD?.length ?? 0;
135
+ this.writeUint(data, offset, dl + (j === 0 ? 0 : 4));
136
+ offset += 4;
137
+ const iOff = offset;
138
+ // cspell:disable-next-line
139
+ this.writeASCII(data, offset, j === 0 ? "IDAT" : "fdAT");
140
+ offset += 4;
141
+ if (j !== 0) {
142
+ this.writeUint(data, offset, fi++);
143
+ offset += 4;
144
+ }
145
+ if (imgD) {
146
+ for (let i = 0; i < dl; i++) {
147
+ data[offset + i] = imgD[i];
148
+ }
149
+ }
150
+ offset += dl;
151
+ this.writeUint(data, offset, this.crc(data, iOff, offset - iOff));
152
+ offset += 4; // crc
153
+ }
154
+ this.writeUint(data, offset, 0);
155
+ offset += 4;
156
+ // cspell:disable-next-line
157
+ this.writeASCII(data, offset, "IEND");
158
+ offset += 4;
159
+ this.writeUint(data, offset, this.crc(data, offset - 4, 4));
160
+ offset += 4; // crc
161
+ return new Uint8Array(data.buffer.slice(0, offset));
162
+ }
163
+ /**
164
+ * @internal
165
+ */
166
+ async compressPNG(buffers, w, h, ps, forbidPlte) {
167
+ const out = this.compress(buffers, w, h, ps, 0, forbidPlte);
168
+ for (let i = 0; i < buffers.length; i++) {
169
+ const frm = out.frames[i];
170
+ const nw = frm.rect.width;
171
+ const nh = frm.rect.height;
172
+ const bpl = frm.bpl;
173
+ const bpp = frm.bpp;
174
+ const fData = new Uint8Array(nw * bpl + nh);
175
+ frm.cImg = await this.filterZero(frm.img, nh, bpp, bpl, fData);
176
+ }
177
+ return out;
178
+ }
179
+ /**
180
+ * @internal
181
+ */
182
+ compress(inBuffers, w, h, inPs, forGIF, forbidPlte) {
183
+ let cType = 6;
184
+ let depth = 8;
185
+ let bpp = 4;
186
+ let alphaAnd = 255;
187
+ let ps = inPs;
188
+ let buffers = inBuffers;
189
+ for (let j = 0; j < buffers.length; j++) {
190
+ // when not quantized, other frames can contain colors, that are not in an initial frame
191
+ const img = new Uint8Array(buffers[j]);
192
+ const iLen = img.length;
193
+ for (let i = 0; i < iLen; i += 4) {
194
+ alphaAnd &= img[i + 3];
195
+ }
196
+ }
197
+ let gotAlpha = alphaAnd !== 255;
198
+ const cMap = {};
199
+ const pLte = [];
200
+ if (buffers.length !== 0) {
201
+ cMap[0] = 0;
202
+ pLte.push(0);
203
+ if (ps !== 0) {
204
+ ps--;
205
+ }
206
+ }
207
+ if (ps !== 0) {
208
+ const qRes = this.quantize(buffers, ps, forGIF);
209
+ buffers = qRes.buffers;
210
+ for (let i = 0; i < qRes.plte.length; i++) {
211
+ const c = qRes.plte[i].est?.rgba ?? 0;
212
+ if (!cMap[c]) {
213
+ cMap[c] = pLte.length;
214
+ pLte.push(c);
215
+ }
216
+ }
217
+ }
218
+ else {
219
+ // what if ps==0, but there are <=256 colors? we still need to detect, if the palette could be used
220
+ for (let j = 0; j < buffers.length; j++) {
221
+ // when not quantized, other frames can contain colors, that are not in an initial frame
222
+ const img32 = new Uint32Array(buffers[j]);
223
+ const iLen = img32.length;
224
+ for (let i = 0; i < iLen; i++) {
225
+ const c = img32[i];
226
+ if ((i < w || (c !== img32[i - 1] && c !== img32[i - w])) && !cMap[c]) {
227
+ cMap[c] = pLte.length;
228
+ pLte.push(c);
229
+ if (pLte.length >= 300) {
230
+ break;
231
+ }
232
+ }
233
+ }
234
+ }
235
+ }
236
+ const brute = gotAlpha ? forGIF : false; // brute : frames can only be copied, not "blended"
237
+ const cc = pLte.length;
238
+ if (cc <= 256 && !forbidPlte) {
239
+ if (cc <= 2) {
240
+ depth = 1;
241
+ }
242
+ else if (cc <= 4) {
243
+ depth = 2;
244
+ }
245
+ else if (cc <= 16) {
246
+ depth = 4;
247
+ }
248
+ else {
249
+ depth = 8;
250
+ }
251
+ if (forGIF) {
252
+ depth = 8;
253
+ }
254
+ gotAlpha = true;
255
+ }
256
+ const frames = [];
257
+ for (let j = 0; j < buffers.length; j++) {
258
+ let cImg = new Uint8Array(buffers[j]);
259
+ let cImg32 = new Uint32Array(cImg.buffer);
260
+ let nx = 0;
261
+ let ny = 0;
262
+ let nw = w;
263
+ let nh = h;
264
+ let blend = 0;
265
+ if (j !== 0 && !brute) {
266
+ const tLim = forGIF || j === 1 || frames[frames.length - 2].dispose === 2 ? 1 : 2;
267
+ let tStp = 0;
268
+ let tArea = 1e9;
269
+ for (let it = 0; it < tLim; it++) {
270
+ const p32 = new Uint32Array(buffers[j - 1 - it]);
271
+ let mix = w;
272
+ let miy = h;
273
+ let max = -1;
274
+ let may = -1;
275
+ for (let y = 0; y < h; y++) {
276
+ for (let x = 0; x < w; x++) {
277
+ const i = y * w + x;
278
+ if (cImg32[i] !== p32[i]) {
279
+ if (x < mix) {
280
+ mix = x;
281
+ }
282
+ if (x > max) {
283
+ max = x;
284
+ }
285
+ if (y < miy) {
286
+ miy = y;
287
+ }
288
+ if (y > may) {
289
+ may = y;
290
+ }
291
+ }
292
+ }
293
+ }
294
+ const sArea = max === -1 ? 1 : (max - mix + 1) * (may - miy + 1);
295
+ if (sArea < tArea) {
296
+ tArea = sArea;
297
+ tStp = it;
298
+ if (max === -1) {
299
+ nx = 0;
300
+ ny = 0;
301
+ nw = 1;
302
+ nh = 1;
303
+ }
304
+ else {
305
+ nx = mix;
306
+ ny = miy;
307
+ nw = max - mix + 1;
308
+ nh = may - miy + 1;
309
+ }
310
+ }
311
+ }
312
+ const pImg = new Uint8Array(buffers[j - 1 - tStp]);
313
+ if (tStp === 1) {
314
+ frames[frames.length - 1].dispose = 2;
315
+ }
316
+ const nImg = new Uint8Array(nw * nh * 4);
317
+ this.copyTile(pImg, w, h, nImg, nw, nh, -nx, -ny, 0);
318
+ if (this.copyTile(cImg, w, h, nImg, nw, nh, -nx, -ny, 3)) {
319
+ this.copyTile(cImg, w, h, nImg, nw, nh, -nx, -ny, 2);
320
+ blend = 1;
321
+ }
322
+ else {
323
+ this.copyTile(cImg, w, h, nImg, nw, nh, -nx, -ny, 0);
324
+ blend = 0;
325
+ }
326
+ cImg = nImg;
327
+ cImg32 = new Uint32Array(cImg.buffer);
328
+ }
329
+ let bpl = 4 * nw;
330
+ if (cc <= 256 && !forbidPlte) {
331
+ bpl = Math.ceil((depth * nw) / 8);
332
+ const nImg = new Uint8Array(bpl * nh);
333
+ for (let y = 0; y < nh; y++) {
334
+ const i = y * bpl;
335
+ const ii = y * nw;
336
+ if (depth === 8) {
337
+ for (let x = 0; x < nw; x++) {
338
+ nImg[i + x] = cMap[cImg32[ii + x]];
339
+ }
340
+ }
341
+ else if (depth === 4) {
342
+ for (let x = 0; x < nw; x++) {
343
+ nImg[i + (x >> 1)] |= cMap[cImg32[ii + x]] << (4 - (x & 1) * 4);
344
+ }
345
+ }
346
+ else if (depth === 2) {
347
+ for (let x = 0; x < nw; x++) {
348
+ nImg[i + (x >> 2)] |= cMap[cImg32[ii + x]] << (6 - (x & 3) * 2);
349
+ }
350
+ }
351
+ else if (depth === 1) {
352
+ for (let x = 0; x < nw; x++) {
353
+ nImg[i + (x >> 3)] |= cMap[cImg32[ii + x]] << (7 - (x & 7) * 1);
354
+ }
355
+ }
356
+ }
357
+ cImg = nImg;
358
+ cType = 3;
359
+ bpp = 1;
360
+ }
361
+ else if (!gotAlpha && buffers.length === 1) {
362
+ // some next "reduced" frames may contain alpha for blending
363
+ const nImg = new Uint8Array(nw * nh * 3);
364
+ const area = nw * nh;
365
+ for (let i = 0; i < area; i++) {
366
+ const ti = i * 3;
367
+ const qi = i * 4;
368
+ nImg[ti] = cImg[qi];
369
+ nImg[ti + 1] = cImg[qi + 1];
370
+ nImg[ti + 2] = cImg[qi + 2];
371
+ }
372
+ cImg = nImg;
373
+ cType = 2;
374
+ bpp = 3;
375
+ bpl = 3 * nw;
376
+ }
377
+ frames.push({
378
+ rect: {
379
+ x: nx,
380
+ y: ny,
381
+ width: nw,
382
+ height: nh
383
+ },
384
+ img: cImg,
385
+ bpl,
386
+ bpp,
387
+ blend,
388
+ dispose: brute ? 1 : 0
389
+ });
390
+ }
391
+ return { cType, depth, plte: pLte, gotAlpha, frames };
392
+ }
393
+ /**
394
+ * @internal
395
+ */
396
+ async filterZero(img, h, bpp, bpl, data) {
397
+ const fls = [];
398
+ for (let t = 0; t < 5; t++) {
399
+ if (h * bpl > 500000 && (t === 2 || t === 3 || t === 4)) {
400
+ continue;
401
+ }
402
+ for (let y = 0; y < h; y++) {
403
+ this.filterLine(data, img, y, bpl, bpp, t);
404
+ }
405
+ const deflated = await Compression.compress(data, "deflate");
406
+ fls.push(deflated);
407
+ if (bpp === 1) {
408
+ break;
409
+ }
410
+ }
411
+ let ti = 0;
412
+ let tSize = 1e9;
413
+ for (let i = 0; i < fls.length; i++) {
414
+ if (fls[i].length < tSize) {
415
+ ti = i;
416
+ tSize = fls[i].length;
417
+ }
418
+ }
419
+ return fls[ti];
420
+ }
421
+ /**
422
+ * @internal
423
+ */
424
+ filterLine(data, img, y, bpl, bpp, type) {
425
+ const i = y * bpl;
426
+ let di = i + y;
427
+ data[di] = type;
428
+ di++;
429
+ if (type === 0) {
430
+ for (let x = 0; x < bpl; x++) {
431
+ data[di + x] = img[i + x];
432
+ }
433
+ }
434
+ else if (type === 1) {
435
+ for (let x = 0; x < bpp; x++) {
436
+ data[di + x] = img[i + x];
437
+ }
438
+ for (let x = bpp; x < bpl; x++) {
439
+ data[di + x] = (img[i + x] - img[i + x - bpp] + 256) & 255;
440
+ }
441
+ }
442
+ else if (y === 0) {
443
+ for (let x = 0; x < bpp; x++) {
444
+ data[di + x] = img[i + x];
445
+ }
446
+ if (type === 2) {
447
+ for (let x = bpp; x < bpl; x++) {
448
+ data[di + x] = img[i + x];
449
+ }
450
+ }
451
+ if (type === 3) {
452
+ for (let x = bpp; x < bpl; x++) {
453
+ data[di + x] = (img[i + x] - (img[i + x - bpp] >> 1) + 256) & 255;
454
+ }
455
+ }
456
+ if (type === 4) {
457
+ for (let x = bpp; x < bpl; x++) {
458
+ data[di + x] = (img[i + x] - this.paeth(img[i + x - bpp], 0, 0) + 256) & 255;
459
+ }
460
+ }
461
+ }
462
+ else {
463
+ if (type === 2) {
464
+ for (let x = 0; x < bpl; x++) {
465
+ data[di + x] = (img[i + x] + 256 - img[i + x - bpl]) & 255;
466
+ }
467
+ }
468
+ if (type === 3) {
469
+ for (let x = 0; x < bpp; x++) {
470
+ data[di + x] = (img[i + x] + 256 - (img[i + x - bpl] >> 1)) & 255;
471
+ }
472
+ for (let x = bpp; x < bpl; x++) {
473
+ data[di + x] = (img[i + x] + 256 - ((img[i + x - bpl] + img[i + x - bpp]) >> 1)) & 255;
474
+ }
475
+ }
476
+ if (type === 4) {
477
+ for (let x = 0; x < bpp; x++) {
478
+ data[di + x] = (img[i + x] + 256 - this.paeth(0, img[i + x - bpl], 0)) & 255;
479
+ }
480
+ for (let x = bpp; x < bpl; x++) {
481
+ data[di + x] =
482
+ (img[i + x] +
483
+ 256 -
484
+ this.paeth(img[i + x - bpp], img[i + x - bpl], img[i + x - bpp - bpl])) &
485
+ 255;
486
+ }
487
+ }
488
+ }
489
+ }
490
+ /**
491
+ * @internal
492
+ */
493
+ paeth(a, b, c) {
494
+ const p = a + b - c;
495
+ const pa = Math.abs(p - a);
496
+ const pb = Math.abs(p - b);
497
+ const pc = Math.abs(p - c);
498
+ if (pa <= pb && pa <= pc) {
499
+ return a;
500
+ }
501
+ if (pb <= pc) {
502
+ return b;
503
+ }
504
+ return c;
505
+ }
506
+ /**
507
+ * @internal
508
+ */
509
+ writeASCII(data, p, s) {
510
+ for (let i = 0; i < s.length; i++) {
511
+ data[p + i] = s.charCodeAt(i);
512
+ }
513
+ }
514
+ /**
515
+ * @internal
516
+ */
517
+ writeUint(buff, p, n) {
518
+ buff[p] = (n >> 24) & 255;
519
+ buff[p + 1] = (n >> 16) & 255;
520
+ buff[p + 2] = (n >> 8) & 255;
521
+ buff[p + 3] = n & 255;
522
+ }
523
+ /**
524
+ * @internal
525
+ */
526
+ writeUshort(buff, p, n) {
527
+ buff[p] = (n >> 8) & 255;
528
+ buff[p + 1] = n & 255;
529
+ }
530
+ /**
531
+ * @internal
532
+ */
533
+ copyTile(sb, sw, sh, tb, tw, th, xOffset, yOffset, mode) {
534
+ const w = Math.min(sw, tw);
535
+ const h = Math.min(sh, th);
536
+ let si = 0;
537
+ let ti = 0;
538
+ for (let y = 0; y < h; y++) {
539
+ for (let x = 0; x < w; x++) {
540
+ if (xOffset >= 0 && yOffset >= 0) {
541
+ si = (y * sw + x) << 2;
542
+ ti = ((yOffset + y) * tw + xOffset + x) << 2;
543
+ }
544
+ else {
545
+ si = ((-yOffset + y) * sw - xOffset + x) << 2;
546
+ ti = (y * tw + x) << 2;
547
+ }
548
+ if (mode === 0) {
549
+ tb[ti] = sb[si];
550
+ tb[ti + 1] = sb[si + 1];
551
+ tb[ti + 2] = sb[si + 2];
552
+ tb[ti + 3] = sb[si + 3];
553
+ }
554
+ else if (mode === 1) {
555
+ const fa = sb[si + 3] * (1 / 255);
556
+ const fr = sb[si] * fa;
557
+ const fg = sb[si + 1] * fa;
558
+ const fb = sb[si + 2] * fa;
559
+ const ba = tb[ti + 3] * (1 / 255);
560
+ const br = tb[ti] * ba;
561
+ const bg = tb[ti + 1] * ba;
562
+ const bb = tb[ti + 2] * ba;
563
+ const ifa = 1 - fa;
564
+ const oa = fa + ba * ifa;
565
+ const ioa = oa === 0 ? 0 : 1 / oa;
566
+ tb[ti + 3] = 255 * oa;
567
+ tb[ti + 0] = (fr + br * ifa) * ioa;
568
+ tb[ti + 1] = (fg + bg * ifa) * ioa;
569
+ tb[ti + 2] = (fb + bb * ifa) * ioa;
570
+ }
571
+ else if (mode === 2) {
572
+ // copy only differences, otherwise zero
573
+ const fa = sb[si + 3];
574
+ const fr = sb[si];
575
+ const fg = sb[si + 1];
576
+ const fb = sb[si + 2];
577
+ const ba = tb[ti + 3];
578
+ const br = tb[ti];
579
+ const bg = tb[ti + 1];
580
+ const bb = tb[ti + 2];
581
+ if (fa === ba && fr === br && fg === bg && fb === bb) {
582
+ tb[ti] = 0;
583
+ tb[ti + 1] = 0;
584
+ tb[ti + 2] = 0;
585
+ tb[ti + 3] = 0;
586
+ }
587
+ else {
588
+ tb[ti] = fr;
589
+ tb[ti + 1] = fg;
590
+ tb[ti + 2] = fb;
591
+ tb[ti + 3] = fa;
592
+ }
593
+ }
594
+ else if (mode === 3) {
595
+ // check if can be blended
596
+ const fa = sb[si + 3];
597
+ const fr = sb[si];
598
+ const fg = sb[si + 1];
599
+ const fb = sb[si + 2];
600
+ const ba = tb[ti + 3];
601
+ const br = tb[ti];
602
+ const bg = tb[ti + 1];
603
+ const bb = tb[ti + 2];
604
+ if (fa === ba && fr === br && fg === bg && fb === bb) {
605
+ continue;
606
+ }
607
+ if (fa < 220 && ba > 20) {
608
+ return false;
609
+ }
610
+ }
611
+ }
612
+ }
613
+ return true;
614
+ }
615
+ /**
616
+ * @internal
617
+ */
618
+ crc(b, o, l) {
619
+ return this.crcUpdate(0xffffffff, b, o, l) ^ 0xffffffff;
620
+ }
621
+ /**
622
+ * @internal
623
+ */
624
+ crcUpdate(c, buf, off, len) {
625
+ let localC = c;
626
+ const crcTable = this.crcTable();
627
+ for (let i = 0; i < len; i++) {
628
+ localC = crcTable[(localC ^ buf[off + i]) & 0xff] ^ (localC >>> 8);
629
+ }
630
+ return localC;
631
+ }
632
+ /**
633
+ * @internal
634
+ */
635
+ crcTable() {
636
+ const tab = new Uint32Array(256);
637
+ for (let n = 0; n < 256; n++) {
638
+ let c = n;
639
+ for (let k = 0; k < 8; k++) {
640
+ if (c & 1) {
641
+ c = 0xedb88320 ^ (c >>> 1);
642
+ }
643
+ else {
644
+ c >>>= 1;
645
+ }
646
+ }
647
+ tab[n] = c;
648
+ }
649
+ return tab;
650
+ }
651
+ /**
652
+ * @internal
653
+ */
654
+ quantize(buffers, ps, roundAlpha) {
655
+ const imgs = [];
656
+ let total = 0;
657
+ for (let i = 0; i < buffers.length; i++) {
658
+ imgs.push(this.alphaMul(new Uint8Array(buffers[i]), roundAlpha));
659
+ total += buffers[i].byteLength;
660
+ }
661
+ const nImg = new Uint8Array(total);
662
+ const nImg32 = new Uint32Array(nImg.buffer);
663
+ let nOff = 0;
664
+ for (let i = 0; i < imgs.length; i++) {
665
+ const img = imgs[i];
666
+ const il = img.length;
667
+ for (let j = 0; j < il; j++) {
668
+ nImg[nOff + j] = img[j];
669
+ }
670
+ nOff += il;
671
+ }
672
+ const root = {
673
+ i0: 0,
674
+ i1: nImg.length,
675
+ bst: null,
676
+ est: null,
677
+ tDst: 0,
678
+ left: null,
679
+ right: null
680
+ };
681
+ root.bst = this.quantizeStats(nImg, root.i0, root.i1);
682
+ root.est = this.quantizeEStats(root.bst);
683
+ const leafs = [root];
684
+ while (leafs.length < ps) {
685
+ let maxL = 0;
686
+ let mi = 0;
687
+ for (let i = 0; i < leafs.length; i++) {
688
+ const est = leafs[i].est;
689
+ if (est && est.L > maxL) {
690
+ maxL = est.L;
691
+ mi = i;
692
+ }
693
+ }
694
+ if (maxL < 1e-3) {
695
+ break;
696
+ }
697
+ const node = leafs[mi];
698
+ const s0 = this.quantizeSplitPixels(nImg, nImg32, node.i0, node.i1, node.est?.e ?? [], node.est?.eMq255 ?? 0);
699
+ const ln = {
700
+ i0: node.i0,
701
+ i1: s0,
702
+ bst: null,
703
+ est: null,
704
+ tDst: 0,
705
+ left: null,
706
+ right: null
707
+ };
708
+ ln.bst = this.quantizeStats(nImg, ln.i0, ln.i1);
709
+ ln.est = this.quantizeEStats(ln.bst);
710
+ const rn = {
711
+ i0: s0,
712
+ i1: node.i1,
713
+ bst: null,
714
+ est: null,
715
+ tDst: 0,
716
+ left: null,
717
+ right: null
718
+ };
719
+ rn.bst = {
720
+ R: [],
721
+ m: [],
722
+ N: (node.bst?.N ?? 0) - ln.bst.N
723
+ };
724
+ for (let i = 0; i < 16; i++) {
725
+ rn.bst.R[i] = (node.bst?.R[i] ?? 0) - ln.bst.R[i];
726
+ }
727
+ for (let i = 0; i < 4; i++) {
728
+ rn.bst.m[i] = (node.bst?.m[i] ?? 0) - ln.bst.m[i];
729
+ }
730
+ rn.est = this.quantizeEStats(rn.bst);
731
+ node.left = ln;
732
+ node.right = rn;
733
+ leafs[mi] = ln;
734
+ leafs.push(rn);
735
+ }
736
+ leafs.sort((a, b) => (b.bst?.N ?? 0) - (a.bst?.N ?? 0));
737
+ const outBuffers = [];
738
+ for (let ii = 0; ii < imgs.length; ii++) {
739
+ const sb = new Uint8Array(imgs[ii]);
740
+ const tb = new Uint32Array(imgs[ii]);
741
+ const len = sb.length;
742
+ for (let i = 0; i < len; i += 4) {
743
+ const r = sb[i] * (1 / 255);
744
+ const g = sb[i + 1] * (1 / 255);
745
+ const b = sb[i + 2] * (1 / 255);
746
+ const a = sb[i + 3] * (1 / 255);
747
+ let nd = root;
748
+ while (nd?.left) {
749
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
750
+ nd = this.quantizePlaneDst(nd.est, r, g, b, a) <= 0 ? nd.left : nd.right;
751
+ }
752
+ tb[i >> 2] = nd?.est?.rgba ?? 0;
753
+ }
754
+ outBuffers[ii] = tb.buffer;
755
+ }
756
+ return { buffers: outBuffers, plte: leafs };
757
+ }
758
+ /**
759
+ * @internal
760
+ */
761
+ quantizeStats(nImg, i0, i1) {
762
+ const R = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
763
+ const m = [0, 0, 0, 0];
764
+ const N = (i1 - i0) >> 2;
765
+ for (let i = i0; i < i1; i += 4) {
766
+ const r = nImg[i] * (1 / 255);
767
+ const g = nImg[i + 1] * (1 / 255);
768
+ const b = nImg[i + 2] * (1 / 255);
769
+ const a = nImg[i + 3] * (1 / 255);
770
+ m[0] += r;
771
+ m[1] += g;
772
+ m[2] += b;
773
+ m[3] += a;
774
+ R[0] += r * r;
775
+ R[1] += r * g;
776
+ R[2] += r * b;
777
+ R[3] += r * a;
778
+ R[5] += g * g;
779
+ R[6] += g * b;
780
+ R[7] += g * a;
781
+ R[10] += b * b;
782
+ R[11] += b * a;
783
+ R[15] += a * a;
784
+ }
785
+ R[4] = R[1];
786
+ R[8] = R[2];
787
+ R[12] = R[3];
788
+ R[9] = R[6];
789
+ R[13] = R[7];
790
+ R[14] = R[11];
791
+ return { R, m, N };
792
+ }
793
+ /**
794
+ * @internal
795
+ */
796
+ quantizeEStats(stats) {
797
+ const R = stats.R;
798
+ const m = stats.m;
799
+ const N = stats.N;
800
+ const m0 = m[0];
801
+ const m1 = m[1];
802
+ const m2 = m[2];
803
+ const m3 = m[3];
804
+ const iN = N === 0 ? 0 : 1 / N;
805
+ const rj = [
806
+ R[0] - m0 * m0 * iN,
807
+ R[1] - m0 * m1 * iN,
808
+ R[2] - m0 * m2 * iN,
809
+ R[3] - m0 * m3 * iN,
810
+ R[4] - m1 * m0 * iN,
811
+ R[5] - m1 * m1 * iN,
812
+ R[6] - m1 * m2 * iN,
813
+ R[7] - m1 * m3 * iN,
814
+ R[8] - m2 * m0 * iN,
815
+ R[9] - m2 * m1 * iN,
816
+ R[10] - m2 * m2 * iN,
817
+ R[11] - m2 * m3 * iN,
818
+ R[12] - m3 * m0 * iN,
819
+ R[13] - m3 * m1 * iN,
820
+ R[14] - m3 * m2 * iN,
821
+ R[15] - m3 * m3 * iN
822
+ ];
823
+ const A = rj;
824
+ let b = [0.5, 0.5, 0.5, 0.5];
825
+ let mi = 0;
826
+ let tmi = 0;
827
+ if (N !== 0) {
828
+ for (let i = 0; i < 10; i++) {
829
+ b = this.m4MultiplyVec(A, b);
830
+ tmi = Math.sqrt(this.m4Dot(b, b));
831
+ b = this.m4Sml(1 / tmi, b);
832
+ if (Math.abs(tmi - mi) < 1e-9) {
833
+ break;
834
+ }
835
+ mi = tmi;
836
+ }
837
+ }
838
+ const q = [m0 * iN, m1 * iN, m2 * iN, m3 * iN];
839
+ const eMq255 = this.m4Dot(this.m4Sml(255, q), b);
840
+ const ia = q[3] < 0.001 ? 0 : 1 / q[3];
841
+ return {
842
+ Cov: rj,
843
+ q,
844
+ e: b,
845
+ L: mi,
846
+ eMq255,
847
+ eMq: this.m4Dot(b, q),
848
+ rgba: ((Math.round(255 * q[3]) << 24) |
849
+ (Math.round(255 * q[2] * ia) << 16) |
850
+ (Math.round(255 * q[1] * ia) << 8) |
851
+ (Math.round(255 * q[0] * ia) << 0)) >>>
852
+ 0
853
+ };
854
+ }
855
+ /**
856
+ * @internal
857
+ */
858
+ quantizePlaneDst(est, r, g, b, a) {
859
+ const e = est.e;
860
+ return e[0] * r + e[1] * g + e[2] * b + e[3] * a - est.eMq;
861
+ }
862
+ /**
863
+ * @internal
864
+ */
865
+ quantizeSplitPixels(nImg, nImg32, i0in, i1in, e, eMq) {
866
+ let i1 = i1in - 4;
867
+ let i0 = i0in;
868
+ while (i0 < i1) {
869
+ while (this.quantizeVecDot(nImg, i0, e) <= eMq) {
870
+ i0 += 4;
871
+ }
872
+ while (this.quantizeVecDot(nImg, i1, e) > eMq) {
873
+ i1 -= 4;
874
+ }
875
+ if (i0 >= i1) {
876
+ break;
877
+ }
878
+ const t = nImg32[i0 >> 2];
879
+ nImg32[i0 >> 2] = nImg32[i1 >> 2];
880
+ nImg32[i1 >> 2] = t;
881
+ i0 += 4;
882
+ i1 -= 4;
883
+ }
884
+ while (this.quantizeVecDot(nImg, i0, e) > eMq) {
885
+ i0 -= 4;
886
+ }
887
+ return i0 + 4;
888
+ }
889
+ /**
890
+ * @internal
891
+ */
892
+ quantizeVecDot(nImg, i, e) {
893
+ return nImg[i] * e[0] + nImg[i + 1] * e[1] + nImg[i + 2] * e[2] + nImg[i + 3] * e[3];
894
+ }
895
+ /**
896
+ * @internal
897
+ */
898
+ m4MultiplyVec(m, v) {
899
+ return [
900
+ m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * v[3],
901
+ m[4] * v[0] + m[5] * v[1] + m[6] * v[2] + m[7] * v[3],
902
+ m[8] * v[0] + m[9] * v[1] + m[10] * v[2] + m[11] * v[3],
903
+ m[12] * v[0] + m[13] * v[1] + m[14] * v[2] + m[15] * v[3]
904
+ ];
905
+ }
906
+ /**
907
+ * @internal
908
+ */
909
+ m4Dot(x, y) {
910
+ return x[0] * y[0] + x[1] * y[1] + x[2] * y[2] + x[3] * y[3];
911
+ }
912
+ /**
913
+ * @internal
914
+ */
915
+ m4Sml(a, y) {
916
+ return [a * y[0], a * y[1], a * y[2], a * y[3]];
917
+ }
918
+ /**
919
+ * @internal
920
+ */
921
+ alphaMul(img, roundA) {
922
+ const nImg = new Uint8Array(img.length);
923
+ const area = img.length >> 2;
924
+ for (let i = 0; i < area; i++) {
925
+ const qi = i << 2;
926
+ let ia = img[qi + 3];
927
+ if (roundA) {
928
+ ia = ia < 128 ? 0 : 255;
929
+ }
930
+ const a = ia * (1 / 255);
931
+ nImg[qi + 0] = img[qi + 0] * a;
932
+ nImg[qi + 1] = img[qi + 1] * a;
933
+ nImg[qi + 2] = img[qi + 2] * a;
934
+ nImg[qi + 3] = ia;
935
+ }
936
+ return nImg;
937
+ }
938
+ }
939
+ //# sourceMappingURL=pngEncoder.js.map