roslib-ts 1.0.0-beta.7 → 1.0.0-beta.8

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.
package/dist/index.cjs.js CHANGED
@@ -60,6 +60,2041 @@ class EventEmitter {
60
60
  }
61
61
  }
62
62
 
63
+ // DEFLATE is a complex format; to read this code, you should probably check the RFC first:
64
+ // https://tools.ietf.org/html/rfc1951
65
+ // You may also wish to take a look at the guide I made about this program:
66
+ // https://gist.github.com/101arrowz/253f31eb5abc3d9275ab943003ffecad
67
+ // Some of the following code is similar to that of UZIP.js:
68
+ // https://github.com/photopea/UZIP.js
69
+ // However, the vast majority of the codebase has diverged from UZIP.js to increase performance and reduce bundle size.
70
+ // Sometimes 0 will appear where -1 would be more appropriate. This is because using a uint
71
+ // is better for memory in most engines (I *think*).
72
+
73
+ // aliases for shorter compressed code (most minifers don't do this)
74
+ var u8 = Uint8Array, u16 = Uint16Array, i32 = Int32Array;
75
+ // fixed length extra bits
76
+ var fleb = new u8([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, /* unused */ 0, 0, /* impossible */ 0]);
77
+ // fixed distance extra bits
78
+ var fdeb = new u8([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, /* unused */ 0, 0]);
79
+ // code length index map
80
+ var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
81
+ // get base, reverse index map from extra bits
82
+ var freb = function (eb, start) {
83
+ var b = new u16(31);
84
+ for (var i = 0; i < 31; ++i) {
85
+ b[i] = start += 1 << eb[i - 1];
86
+ }
87
+ // numbers here are at max 18 bits
88
+ var r = new i32(b[30]);
89
+ for (var i = 1; i < 30; ++i) {
90
+ for (var j = b[i]; j < b[i + 1]; ++j) {
91
+ r[j] = ((j - b[i]) << 5) | i;
92
+ }
93
+ }
94
+ return { b: b, r: r };
95
+ };
96
+ var _a = freb(fleb, 2), fl = _a.b, revfl = _a.r;
97
+ // we can ignore the fact that the other numbers are wrong; they never happen anyway
98
+ fl[28] = 258, revfl[258] = 28;
99
+ var _b = freb(fdeb, 0), fd = _b.b;
100
+ // map of value to reverse (assuming 16 bits)
101
+ var rev = new u16(32768);
102
+ for (var i = 0; i < 32768; ++i) {
103
+ // reverse table algorithm from SO
104
+ var x = ((i & 0xAAAA) >> 1) | ((i & 0x5555) << 1);
105
+ x = ((x & 0xCCCC) >> 2) | ((x & 0x3333) << 2);
106
+ x = ((x & 0xF0F0) >> 4) | ((x & 0x0F0F) << 4);
107
+ rev[i] = (((x & 0xFF00) >> 8) | ((x & 0x00FF) << 8)) >> 1;
108
+ }
109
+ // create huffman tree from u8 "map": index -> code length for code index
110
+ // mb (max bits) must be at most 15
111
+ // TODO: optimize/split up?
112
+ var hMap = (function (cd, mb, r) {
113
+ var s = cd.length;
114
+ // index
115
+ var i = 0;
116
+ // u16 "map": index -> # of codes with bit length = index
117
+ var l = new u16(mb);
118
+ // length of cd must be 288 (total # of codes)
119
+ for (; i < s; ++i) {
120
+ if (cd[i])
121
+ ++l[cd[i] - 1];
122
+ }
123
+ // u16 "map": index -> minimum code for bit length = index
124
+ var le = new u16(mb);
125
+ for (i = 1; i < mb; ++i) {
126
+ le[i] = (le[i - 1] + l[i - 1]) << 1;
127
+ }
128
+ var co;
129
+ if (r) {
130
+ // u16 "map": index -> number of actual bits, symbol for code
131
+ co = new u16(1 << mb);
132
+ // bits to remove for reverser
133
+ var rvb = 15 - mb;
134
+ for (i = 0; i < s; ++i) {
135
+ // ignore 0 lengths
136
+ if (cd[i]) {
137
+ // num encoding both symbol and bits read
138
+ var sv = (i << 4) | cd[i];
139
+ // free bits
140
+ var r_1 = mb - cd[i];
141
+ // start value
142
+ var v = le[cd[i] - 1]++ << r_1;
143
+ // m is end value
144
+ for (var m = v | ((1 << r_1) - 1); v <= m; ++v) {
145
+ // every 16 bit value starting with the code yields the same result
146
+ co[rev[v] >> rvb] = sv;
147
+ }
148
+ }
149
+ }
150
+ }
151
+ else {
152
+ co = new u16(s);
153
+ for (i = 0; i < s; ++i) {
154
+ if (cd[i]) {
155
+ co[i] = rev[le[cd[i] - 1]++] >> (15 - cd[i]);
156
+ }
157
+ }
158
+ }
159
+ return co;
160
+ });
161
+ // fixed length tree
162
+ var flt = new u8(288);
163
+ for (var i = 0; i < 144; ++i)
164
+ flt[i] = 8;
165
+ for (var i = 144; i < 256; ++i)
166
+ flt[i] = 9;
167
+ for (var i = 256; i < 280; ++i)
168
+ flt[i] = 7;
169
+ for (var i = 280; i < 288; ++i)
170
+ flt[i] = 8;
171
+ // fixed distance tree
172
+ var fdt = new u8(32);
173
+ for (var i = 0; i < 32; ++i)
174
+ fdt[i] = 5;
175
+ // fixed length map
176
+ var flrm = /*#__PURE__*/ hMap(flt, 9, 1);
177
+ // fixed distance map
178
+ var fdrm = /*#__PURE__*/ hMap(fdt, 5, 1);
179
+ // find max of array
180
+ var max = function (a) {
181
+ var m = a[0];
182
+ for (var i = 1; i < a.length; ++i) {
183
+ if (a[i] > m)
184
+ m = a[i];
185
+ }
186
+ return m;
187
+ };
188
+ // read d, starting at bit p and mask with m
189
+ var bits = function (d, p, m) {
190
+ var o = (p / 8) | 0;
191
+ return ((d[o] | (d[o + 1] << 8)) >> (p & 7)) & m;
192
+ };
193
+ // read d, starting at bit p continuing for at least 16 bits
194
+ var bits16 = function (d, p) {
195
+ var o = (p / 8) | 0;
196
+ return ((d[o] | (d[o + 1] << 8) | (d[o + 2] << 16)) >> (p & 7));
197
+ };
198
+ // get end of byte
199
+ var shft = function (p) { return ((p + 7) / 8) | 0; };
200
+ // typed array slice - allows garbage collector to free original reference,
201
+ // while being more compatible than .slice
202
+ var slc = function (v, s, e) {
203
+ if (s == null || s < 0)
204
+ s = 0;
205
+ if (e == null || e > v.length)
206
+ e = v.length;
207
+ // can't use .constructor in case user-supplied
208
+ return new u8(v.subarray(s, e));
209
+ };
210
+ // error codes
211
+ var ec = [
212
+ 'unexpected EOF',
213
+ 'invalid block type',
214
+ 'invalid length/literal',
215
+ 'invalid distance',
216
+ 'stream finished',
217
+ 'no stream handler',
218
+ ,
219
+ 'no callback',
220
+ 'invalid UTF-8 data',
221
+ 'extra field too long',
222
+ 'date not in range 1980-2099',
223
+ 'filename too long',
224
+ 'stream finishing',
225
+ 'invalid zip data'
226
+ // determined by unknown compression method
227
+ ];
228
+ var err = function (ind, msg, nt) {
229
+ var e = new Error(msg || ec[ind]);
230
+ e.code = ind;
231
+ if (Error.captureStackTrace)
232
+ Error.captureStackTrace(e, err);
233
+ if (!nt)
234
+ throw e;
235
+ return e;
236
+ };
237
+ // expands raw DEFLATE data
238
+ var inflt = function (dat, st, buf, dict) {
239
+ // source length dict length
240
+ var sl = dat.length, dl = 0;
241
+ if (!sl || st.f && !st.l)
242
+ return buf || new u8(0);
243
+ var noBuf = !buf;
244
+ // have to estimate size
245
+ var resize = noBuf || st.i != 2;
246
+ // no state
247
+ var noSt = st.i;
248
+ // Assumes roughly 33% compression ratio average
249
+ if (noBuf)
250
+ buf = new u8(sl * 3);
251
+ // ensure buffer can fit at least l elements
252
+ var cbuf = function (l) {
253
+ var bl = buf.length;
254
+ // need to increase size to fit
255
+ if (l > bl) {
256
+ // Double or set to necessary, whichever is greater
257
+ var nbuf = new u8(Math.max(bl * 2, l));
258
+ nbuf.set(buf);
259
+ buf = nbuf;
260
+ }
261
+ };
262
+ // last chunk bitpos bytes
263
+ var final = st.f || 0, pos = st.p || 0, bt = st.b || 0, lm = st.l, dm = st.d, lbt = st.m, dbt = st.n;
264
+ // total bits
265
+ var tbts = sl * 8;
266
+ do {
267
+ if (!lm) {
268
+ // BFINAL - this is only 1 when last chunk is next
269
+ final = bits(dat, pos, 1);
270
+ // type: 0 = no compression, 1 = fixed huffman, 2 = dynamic huffman
271
+ var type = bits(dat, pos + 1, 3);
272
+ pos += 3;
273
+ if (!type) {
274
+ // go to end of byte boundary
275
+ var s = shft(pos) + 4, l = dat[s - 4] | (dat[s - 3] << 8), t = s + l;
276
+ if (t > sl) {
277
+ if (noSt)
278
+ err(0);
279
+ break;
280
+ }
281
+ // ensure size
282
+ if (resize)
283
+ cbuf(bt + l);
284
+ // Copy over uncompressed data
285
+ buf.set(dat.subarray(s, t), bt);
286
+ // Get new bitpos, update byte count
287
+ st.b = bt += l, st.p = pos = t * 8, st.f = final;
288
+ continue;
289
+ }
290
+ else if (type == 1)
291
+ lm = flrm, dm = fdrm, lbt = 9, dbt = 5;
292
+ else if (type == 2) {
293
+ // literal lengths
294
+ var hLit = bits(dat, pos, 31) + 257, hcLen = bits(dat, pos + 10, 15) + 4;
295
+ var tl = hLit + bits(dat, pos + 5, 31) + 1;
296
+ pos += 14;
297
+ // length+distance tree
298
+ var ldt = new u8(tl);
299
+ // code length tree
300
+ var clt = new u8(19);
301
+ for (var i = 0; i < hcLen; ++i) {
302
+ // use index map to get real code
303
+ clt[clim[i]] = bits(dat, pos + i * 3, 7);
304
+ }
305
+ pos += hcLen * 3;
306
+ // code lengths bits
307
+ var clb = max(clt), clbmsk = (1 << clb) - 1;
308
+ // code lengths map
309
+ var clm = hMap(clt, clb, 1);
310
+ for (var i = 0; i < tl;) {
311
+ var r = clm[bits(dat, pos, clbmsk)];
312
+ // bits read
313
+ pos += r & 15;
314
+ // symbol
315
+ var s = r >> 4;
316
+ // code length to copy
317
+ if (s < 16) {
318
+ ldt[i++] = s;
319
+ }
320
+ else {
321
+ // copy count
322
+ var c = 0, n = 0;
323
+ if (s == 16)
324
+ n = 3 + bits(dat, pos, 3), pos += 2, c = ldt[i - 1];
325
+ else if (s == 17)
326
+ n = 3 + bits(dat, pos, 7), pos += 3;
327
+ else if (s == 18)
328
+ n = 11 + bits(dat, pos, 127), pos += 7;
329
+ while (n--)
330
+ ldt[i++] = c;
331
+ }
332
+ }
333
+ // length tree distance tree
334
+ var lt = ldt.subarray(0, hLit), dt = ldt.subarray(hLit);
335
+ // max length bits
336
+ lbt = max(lt);
337
+ // max dist bits
338
+ dbt = max(dt);
339
+ lm = hMap(lt, lbt, 1);
340
+ dm = hMap(dt, dbt, 1);
341
+ }
342
+ else
343
+ err(1);
344
+ if (pos > tbts) {
345
+ if (noSt)
346
+ err(0);
347
+ break;
348
+ }
349
+ }
350
+ // Make sure the buffer can hold this + the largest possible addition
351
+ // Maximum chunk size (practically, theoretically infinite) is 2^17
352
+ if (resize)
353
+ cbuf(bt + 131072);
354
+ var lms = (1 << lbt) - 1, dms = (1 << dbt) - 1;
355
+ var lpos = pos;
356
+ for (;; lpos = pos) {
357
+ // bits read, code
358
+ var c = lm[bits16(dat, pos) & lms], sym = c >> 4;
359
+ pos += c & 15;
360
+ if (pos > tbts) {
361
+ if (noSt)
362
+ err(0);
363
+ break;
364
+ }
365
+ if (!c)
366
+ err(2);
367
+ if (sym < 256)
368
+ buf[bt++] = sym;
369
+ else if (sym == 256) {
370
+ lpos = pos, lm = null;
371
+ break;
372
+ }
373
+ else {
374
+ var add = sym - 254;
375
+ // no extra bits needed if less
376
+ if (sym > 264) {
377
+ // index
378
+ var i = sym - 257, b = fleb[i];
379
+ add = bits(dat, pos, (1 << b) - 1) + fl[i];
380
+ pos += b;
381
+ }
382
+ // dist
383
+ var d = dm[bits16(dat, pos) & dms], dsym = d >> 4;
384
+ if (!d)
385
+ err(3);
386
+ pos += d & 15;
387
+ var dt = fd[dsym];
388
+ if (dsym > 3) {
389
+ var b = fdeb[dsym];
390
+ dt += bits16(dat, pos) & (1 << b) - 1, pos += b;
391
+ }
392
+ if (pos > tbts) {
393
+ if (noSt)
394
+ err(0);
395
+ break;
396
+ }
397
+ if (resize)
398
+ cbuf(bt + 131072);
399
+ var end = bt + add;
400
+ if (bt < dt) {
401
+ var shift = dl - dt, dend = Math.min(dt, end);
402
+ if (shift + bt < 0)
403
+ err(3);
404
+ for (; bt < dend; ++bt)
405
+ buf[bt] = dict[shift + bt];
406
+ }
407
+ for (; bt < end; ++bt)
408
+ buf[bt] = buf[bt - dt];
409
+ }
410
+ }
411
+ st.l = lm, st.p = lpos, st.b = bt, st.f = final;
412
+ if (lm)
413
+ final = 1, st.m = lbt, st.d = dm, st.n = dbt;
414
+ } while (!final);
415
+ // don't reallocate for streams or user buffers
416
+ return bt != buf.length && noBuf ? slc(buf, 0, bt) : buf.subarray(0, bt);
417
+ };
418
+ // empty
419
+ var et = /*#__PURE__*/ new u8(0);
420
+ // zlib start
421
+ var zls = function (d, dict) {
422
+ if ((d[0] & 15) != 8 || (d[0] >> 4) > 7 || ((d[0] << 8 | d[1]) % 31))
423
+ err(6, 'invalid zlib data');
424
+ if ((d[1] >> 5 & 1) == +!dict)
425
+ err(6, 'invalid zlib data: ' + (d[1] & 32 ? 'need' : 'unexpected') + ' dictionary');
426
+ return (d[1] >> 3 & 4) + 2;
427
+ };
428
+ /**
429
+ * Streaming DEFLATE decompression
430
+ */
431
+ var Inflate = /*#__PURE__*/ (function () {
432
+ function Inflate(opts, cb) {
433
+ // no StrmOpt here to avoid adding to workerizer
434
+ if (typeof opts == 'function')
435
+ cb = opts, opts = {};
436
+ this.ondata = cb;
437
+ var dict = opts && opts.dictionary && opts.dictionary.subarray(-32768);
438
+ this.s = { i: 0, b: dict ? dict.length : 0 };
439
+ this.o = new u8(32768);
440
+ this.p = new u8(0);
441
+ if (dict)
442
+ this.o.set(dict);
443
+ }
444
+ Inflate.prototype.e = function (c) {
445
+ if (!this.ondata)
446
+ err(5);
447
+ if (this.d)
448
+ err(4);
449
+ if (!this.p.length)
450
+ this.p = c;
451
+ else if (c.length) {
452
+ var n = new u8(this.p.length + c.length);
453
+ n.set(this.p), n.set(c, this.p.length), this.p = n;
454
+ }
455
+ };
456
+ Inflate.prototype.c = function (final) {
457
+ this.s.i = +(this.d = final || false);
458
+ var bts = this.s.b;
459
+ var dt = inflt(this.p, this.s, this.o);
460
+ this.ondata(slc(dt, bts, this.s.b), this.d);
461
+ this.o = slc(dt, this.s.b - 32768), this.s.b = this.o.length;
462
+ this.p = slc(this.p, (this.s.p / 8) | 0), this.s.p &= 7;
463
+ };
464
+ /**
465
+ * Pushes a chunk to be inflated
466
+ * @param chunk The chunk to push
467
+ * @param final Whether this is the final chunk
468
+ */
469
+ Inflate.prototype.push = function (chunk, final) {
470
+ this.e(chunk), this.c(final);
471
+ };
472
+ return Inflate;
473
+ }());
474
+ /**
475
+ * Streaming Zlib decompression
476
+ */
477
+ var Unzlib = /*#__PURE__*/ (function () {
478
+ function Unzlib(opts, cb) {
479
+ Inflate.call(this, opts, cb);
480
+ this.v = opts && opts.dictionary ? 2 : 1;
481
+ }
482
+ /**
483
+ * Pushes a chunk to be unzlibbed
484
+ * @param chunk The chunk to push
485
+ * @param final Whether this is the last chunk
486
+ */
487
+ Unzlib.prototype.push = function (chunk, final) {
488
+ Inflate.prototype.e.call(this, chunk);
489
+ if (this.v) {
490
+ if (this.p.length < 6 && !final)
491
+ return;
492
+ this.p = this.p.subarray(zls(this.p, this.v - 1)), this.v = 0;
493
+ }
494
+ if (final) {
495
+ if (this.p.length < 4)
496
+ err(6, 'invalid zlib data');
497
+ this.p = this.p.subarray(0, -4);
498
+ }
499
+ // necessary to prevent TS from using the closure value
500
+ // This allows for workerization to function correctly
501
+ Inflate.prototype.c.call(this, final);
502
+ };
503
+ return Unzlib;
504
+ }());
505
+ /**
506
+ * Expands Zlib data
507
+ * @param data The data to decompress
508
+ * @param opts The decompression options
509
+ * @returns The decompressed version of the data
510
+ */
511
+ function unzlibSync(data, opts) {
512
+ return inflt(data.subarray(zls(data, opts), -4), { i: 2 }, opts, opts);
513
+ }
514
+ // text decoder
515
+ var td = typeof TextDecoder != 'undefined' && /*#__PURE__*/ new TextDecoder();
516
+ // text decoder stream
517
+ var tds = 0;
518
+ try {
519
+ td.decode(et, { stream: true });
520
+ tds = 1;
521
+ }
522
+ catch (e) { }
523
+
524
+ /**
525
+ * Decode bytes to text
526
+ * @param bytes - Bytes to decode
527
+ * @param encoding - Text encoding
528
+ * @returns The decoded text
529
+ */
530
+ function decode(bytes, encoding = 'utf8') {
531
+ const decoder = new TextDecoder(encoding);
532
+ return decoder.decode(bytes);
533
+ }
534
+ const encoder = new TextEncoder();
535
+ /**
536
+ * Encode text to utf8
537
+ * @param str - Text to encode
538
+ * @returns The encoded bytes
539
+ */
540
+ function encode(str) {
541
+ return encoder.encode(str);
542
+ }
543
+
544
+ const defaultByteLength = 1024 * 8;
545
+ const hostBigEndian = (() => {
546
+ const array = new Uint8Array(4);
547
+ const view = new Uint32Array(array.buffer);
548
+ return !((view[0] = 1) & array[0]);
549
+ })();
550
+ const typedArrays = {
551
+ int8: globalThis.Int8Array,
552
+ uint8: globalThis.Uint8Array,
553
+ int16: globalThis.Int16Array,
554
+ uint16: globalThis.Uint16Array,
555
+ int32: globalThis.Int32Array,
556
+ uint32: globalThis.Uint32Array,
557
+ uint64: globalThis.BigUint64Array,
558
+ int64: globalThis.BigInt64Array,
559
+ float32: globalThis.Float32Array,
560
+ float64: globalThis.Float64Array,
561
+ };
562
+ class IOBuffer {
563
+ /**
564
+ * Reference to the internal ArrayBuffer object.
565
+ */
566
+ buffer;
567
+ /**
568
+ * Byte length of the internal ArrayBuffer.
569
+ */
570
+ byteLength;
571
+ /**
572
+ * Byte offset of the internal ArrayBuffer.
573
+ */
574
+ byteOffset;
575
+ /**
576
+ * Byte length of the internal ArrayBuffer.
577
+ */
578
+ length;
579
+ /**
580
+ * The current offset of the buffer's pointer.
581
+ */
582
+ offset;
583
+ lastWrittenByte;
584
+ littleEndian;
585
+ _data;
586
+ _mark;
587
+ _marks;
588
+ /**
589
+ * Create a new IOBuffer.
590
+ * @param data - The data to construct the IOBuffer with.
591
+ * If data is a number, it will be the new buffer's length<br>
592
+ * If data is `undefined`, the buffer will be initialized with a default length of 8Kb<br>
593
+ * If data is an ArrayBuffer, SharedArrayBuffer, an ArrayBufferView (Typed Array), an IOBuffer instance,
594
+ * or a Node.js Buffer, a view will be created over the underlying ArrayBuffer.
595
+ * @param options - An object for the options.
596
+ * @returns A new IOBuffer instance.
597
+ */
598
+ constructor(data = defaultByteLength, options = {}) {
599
+ let dataIsGiven = false;
600
+ if (typeof data === 'number') {
601
+ data = new ArrayBuffer(data);
602
+ }
603
+ else {
604
+ dataIsGiven = true;
605
+ this.lastWrittenByte = data.byteLength;
606
+ }
607
+ const offset = options.offset ? options.offset >>> 0 : 0;
608
+ const byteLength = data.byteLength - offset;
609
+ let dvOffset = offset;
610
+ if (ArrayBuffer.isView(data) || data instanceof IOBuffer) {
611
+ if (data.byteLength !== data.buffer.byteLength) {
612
+ dvOffset = data.byteOffset + offset;
613
+ }
614
+ data = data.buffer;
615
+ }
616
+ if (dataIsGiven) {
617
+ this.lastWrittenByte = byteLength;
618
+ }
619
+ else {
620
+ this.lastWrittenByte = 0;
621
+ }
622
+ this.buffer = data;
623
+ this.length = byteLength;
624
+ this.byteLength = byteLength;
625
+ this.byteOffset = dvOffset;
626
+ this.offset = 0;
627
+ this.littleEndian = true;
628
+ this._data = new DataView(this.buffer, dvOffset, byteLength);
629
+ this._mark = 0;
630
+ this._marks = [];
631
+ }
632
+ /**
633
+ * Checks if the memory allocated to the buffer is sufficient to store more
634
+ * bytes after the offset.
635
+ * @param byteLength - The needed memory in bytes.
636
+ * @returns `true` if there is sufficient space and `false` otherwise.
637
+ */
638
+ available(byteLength = 1) {
639
+ return this.offset + byteLength <= this.length;
640
+ }
641
+ /**
642
+ * Check if little-endian mode is used for reading and writing multi-byte
643
+ * values.
644
+ * @returns `true` if little-endian mode is used, `false` otherwise.
645
+ */
646
+ isLittleEndian() {
647
+ return this.littleEndian;
648
+ }
649
+ /**
650
+ * Set little-endian mode for reading and writing multi-byte values.
651
+ * @returns This.
652
+ */
653
+ setLittleEndian() {
654
+ this.littleEndian = true;
655
+ return this;
656
+ }
657
+ /**
658
+ * Check if big-endian mode is used for reading and writing multi-byte values.
659
+ * @returns `true` if big-endian mode is used, `false` otherwise.
660
+ */
661
+ isBigEndian() {
662
+ return !this.littleEndian;
663
+ }
664
+ /**
665
+ * Switches to big-endian mode for reading and writing multi-byte values.
666
+ * @returns This.
667
+ */
668
+ setBigEndian() {
669
+ this.littleEndian = false;
670
+ return this;
671
+ }
672
+ /**
673
+ * Move the pointer n bytes forward.
674
+ * @param n - Number of bytes to skip.
675
+ * @returns This.
676
+ */
677
+ skip(n = 1) {
678
+ this.offset += n;
679
+ return this;
680
+ }
681
+ /**
682
+ * Move the pointer n bytes backward.
683
+ * @param n - Number of bytes to move back.
684
+ * @returns This.
685
+ */
686
+ back(n = 1) {
687
+ this.offset -= n;
688
+ return this;
689
+ }
690
+ /**
691
+ * Move the pointer to the given offset.
692
+ * @param offset - The offset to move to.
693
+ * @returns This.
694
+ */
695
+ seek(offset) {
696
+ this.offset = offset;
697
+ return this;
698
+ }
699
+ /**
700
+ * Store the current pointer offset.
701
+ * @see {@link IOBuffer#reset}
702
+ * @returns This.
703
+ */
704
+ mark() {
705
+ this._mark = this.offset;
706
+ return this;
707
+ }
708
+ /**
709
+ * Move the pointer back to the last pointer offset set by mark.
710
+ * @see {@link IOBuffer#mark}
711
+ * @returns This.
712
+ */
713
+ reset() {
714
+ this.offset = this._mark;
715
+ return this;
716
+ }
717
+ /**
718
+ * Push the current pointer offset to the mark stack.
719
+ * @see {@link IOBuffer#popMark}
720
+ * @returns This.
721
+ */
722
+ pushMark() {
723
+ this._marks.push(this.offset);
724
+ return this;
725
+ }
726
+ /**
727
+ * Pop the last pointer offset from the mark stack, and set the current
728
+ * pointer offset to the popped value.
729
+ * @see {@link IOBuffer#pushMark}
730
+ * @returns This.
731
+ */
732
+ popMark() {
733
+ const offset = this._marks.pop();
734
+ if (offset === undefined) {
735
+ throw new Error('Mark stack empty');
736
+ }
737
+ this.seek(offset);
738
+ return this;
739
+ }
740
+ /**
741
+ * Move the pointer offset back to 0.
742
+ * @returns This.
743
+ */
744
+ rewind() {
745
+ this.offset = 0;
746
+ return this;
747
+ }
748
+ /**
749
+ * Make sure the buffer has sufficient memory to write a given byteLength at
750
+ * the current pointer offset.
751
+ * If the buffer's memory is insufficient, this method will create a new
752
+ * buffer (a copy) with a length that is twice (byteLength + current offset).
753
+ * @param byteLength - The needed memory in bytes.
754
+ * @returns This.
755
+ */
756
+ ensureAvailable(byteLength = 1) {
757
+ if (!this.available(byteLength)) {
758
+ const lengthNeeded = this.offset + byteLength;
759
+ const newLength = lengthNeeded * 2;
760
+ const newArray = new Uint8Array(newLength);
761
+ newArray.set(new Uint8Array(this.buffer));
762
+ this.buffer = newArray.buffer;
763
+ this.length = newLength;
764
+ this.byteLength = newLength;
765
+ this._data = new DataView(this.buffer);
766
+ }
767
+ return this;
768
+ }
769
+ /**
770
+ * Read a byte and return false if the byte's value is 0, or true otherwise.
771
+ * Moves pointer forward by one byte.
772
+ * @returns The read boolean.
773
+ */
774
+ readBoolean() {
775
+ return this.readUint8() !== 0;
776
+ }
777
+ /**
778
+ * Read a signed 8-bit integer and move pointer forward by 1 byte.
779
+ * @returns The read byte.
780
+ */
781
+ readInt8() {
782
+ return this._data.getInt8(this.offset++);
783
+ }
784
+ /**
785
+ * Read an unsigned 8-bit integer and move pointer forward by 1 byte.
786
+ * @returns The read byte.
787
+ */
788
+ readUint8() {
789
+ return this._data.getUint8(this.offset++);
790
+ }
791
+ /**
792
+ * Alias for {@link IOBuffer#readUint8}.
793
+ * @returns The read byte.
794
+ */
795
+ readByte() {
796
+ return this.readUint8();
797
+ }
798
+ /**
799
+ * Read `n` bytes and move pointer forward by `n` bytes.
800
+ * @param n - Number of bytes to read.
801
+ * @returns The read bytes.
802
+ */
803
+ readBytes(n = 1) {
804
+ return this.readArray(n, 'uint8');
805
+ }
806
+ /**
807
+ * Creates an array of corresponding to the type `type` and size `size`.
808
+ * For example, type `uint8` will create a `Uint8Array`.
809
+ * @param size - size of the resulting array
810
+ * @param type - number type of elements to read
811
+ * @returns The read array.
812
+ */
813
+ readArray(size, type) {
814
+ const bytes = typedArrays[type].BYTES_PER_ELEMENT * size;
815
+ const offset = this.byteOffset + this.offset;
816
+ const slice = this.buffer.slice(offset, offset + bytes);
817
+ if (this.littleEndian === hostBigEndian &&
818
+ type !== 'uint8' &&
819
+ type !== 'int8') {
820
+ const slice = new Uint8Array(this.buffer.slice(offset, offset + bytes));
821
+ slice.reverse();
822
+ const returnArray = new typedArrays[type](slice.buffer);
823
+ this.offset += bytes;
824
+ returnArray.reverse();
825
+ return returnArray;
826
+ }
827
+ const returnArray = new typedArrays[type](slice);
828
+ this.offset += bytes;
829
+ return returnArray;
830
+ }
831
+ /**
832
+ * Read a 16-bit signed integer and move pointer forward by 2 bytes.
833
+ * @returns The read value.
834
+ */
835
+ readInt16() {
836
+ const value = this._data.getInt16(this.offset, this.littleEndian);
837
+ this.offset += 2;
838
+ return value;
839
+ }
840
+ /**
841
+ * Read a 16-bit unsigned integer and move pointer forward by 2 bytes.
842
+ * @returns The read value.
843
+ */
844
+ readUint16() {
845
+ const value = this._data.getUint16(this.offset, this.littleEndian);
846
+ this.offset += 2;
847
+ return value;
848
+ }
849
+ /**
850
+ * Read a 32-bit signed integer and move pointer forward by 4 bytes.
851
+ * @returns The read value.
852
+ */
853
+ readInt32() {
854
+ const value = this._data.getInt32(this.offset, this.littleEndian);
855
+ this.offset += 4;
856
+ return value;
857
+ }
858
+ /**
859
+ * Read a 32-bit unsigned integer and move pointer forward by 4 bytes.
860
+ * @returns The read value.
861
+ */
862
+ readUint32() {
863
+ const value = this._data.getUint32(this.offset, this.littleEndian);
864
+ this.offset += 4;
865
+ return value;
866
+ }
867
+ /**
868
+ * Read a 32-bit floating number and move pointer forward by 4 bytes.
869
+ * @returns The read value.
870
+ */
871
+ readFloat32() {
872
+ const value = this._data.getFloat32(this.offset, this.littleEndian);
873
+ this.offset += 4;
874
+ return value;
875
+ }
876
+ /**
877
+ * Read a 64-bit floating number and move pointer forward by 8 bytes.
878
+ * @returns The read value.
879
+ */
880
+ readFloat64() {
881
+ const value = this._data.getFloat64(this.offset, this.littleEndian);
882
+ this.offset += 8;
883
+ return value;
884
+ }
885
+ /**
886
+ * Read a 64-bit signed integer number and move pointer forward by 8 bytes.
887
+ * @returns The read value.
888
+ */
889
+ readBigInt64() {
890
+ const value = this._data.getBigInt64(this.offset, this.littleEndian);
891
+ this.offset += 8;
892
+ return value;
893
+ }
894
+ /**
895
+ * Read a 64-bit unsigned integer number and move pointer forward by 8 bytes.
896
+ * @returns The read value.
897
+ */
898
+ readBigUint64() {
899
+ const value = this._data.getBigUint64(this.offset, this.littleEndian);
900
+ this.offset += 8;
901
+ return value;
902
+ }
903
+ /**
904
+ * Read a 1-byte ASCII character and move pointer forward by 1 byte.
905
+ * @returns The read character.
906
+ */
907
+ readChar() {
908
+ // eslint-disable-next-line unicorn/prefer-code-point
909
+ return String.fromCharCode(this.readInt8());
910
+ }
911
+ /**
912
+ * Read `n` 1-byte ASCII characters and move pointer forward by `n` bytes.
913
+ * @param n - Number of characters to read.
914
+ * @returns The read characters.
915
+ */
916
+ readChars(n = 1) {
917
+ let result = '';
918
+ for (let i = 0; i < n; i++) {
919
+ result += this.readChar();
920
+ }
921
+ return result;
922
+ }
923
+ /**
924
+ * Read the next `n` bytes, return a UTF-8 decoded string and move pointer
925
+ * forward by `n` bytes.
926
+ * @param n - Number of bytes to read.
927
+ * @returns The decoded string.
928
+ */
929
+ readUtf8(n = 1) {
930
+ return decode(this.readBytes(n));
931
+ }
932
+ /**
933
+ * Read the next `n` bytes, return a string decoded with `encoding` and move pointer
934
+ * forward by `n` bytes.
935
+ * If no encoding is passed, the function is equivalent to @see {@link IOBuffer#readUtf8}
936
+ * @param n - Number of bytes to read.
937
+ * @param encoding - The encoding to use. Default is 'utf8'.
938
+ * @returns The decoded string.
939
+ */
940
+ decodeText(n = 1, encoding = 'utf8') {
941
+ return decode(this.readBytes(n), encoding);
942
+ }
943
+ /**
944
+ * Write 0xff if the passed value is truthy, 0x00 otherwise and move pointer
945
+ * forward by 1 byte.
946
+ * @param value - The value to write.
947
+ * @returns This.
948
+ */
949
+ writeBoolean(value) {
950
+ this.writeUint8(value ? 0xff : 0x00);
951
+ return this;
952
+ }
953
+ /**
954
+ * Write `value` as an 8-bit signed integer and move pointer forward by 1 byte.
955
+ * @param value - The value to write.
956
+ * @returns This.
957
+ */
958
+ writeInt8(value) {
959
+ this.ensureAvailable(1);
960
+ this._data.setInt8(this.offset++, value);
961
+ this._updateLastWrittenByte();
962
+ return this;
963
+ }
964
+ /**
965
+ * Write `value` as an 8-bit unsigned integer and move pointer forward by 1
966
+ * byte.
967
+ * @param value - The value to write.
968
+ * @returns This.
969
+ */
970
+ writeUint8(value) {
971
+ this.ensureAvailable(1);
972
+ this._data.setUint8(this.offset++, value);
973
+ this._updateLastWrittenByte();
974
+ return this;
975
+ }
976
+ /**
977
+ * An alias for {@link IOBuffer#writeUint8}.
978
+ * @param value - The value to write.
979
+ * @returns This.
980
+ */
981
+ writeByte(value) {
982
+ return this.writeUint8(value);
983
+ }
984
+ /**
985
+ * Write all elements of `bytes` as uint8 values and move pointer forward by
986
+ * `bytes.length` bytes.
987
+ * @param bytes - The array of bytes to write.
988
+ * @returns This.
989
+ */
990
+ writeBytes(bytes) {
991
+ this.ensureAvailable(bytes.length);
992
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
993
+ for (let i = 0; i < bytes.length; i++) {
994
+ this._data.setUint8(this.offset++, bytes[i]);
995
+ }
996
+ this._updateLastWrittenByte();
997
+ return this;
998
+ }
999
+ /**
1000
+ * Write `value` as a 16-bit signed integer and move pointer forward by 2
1001
+ * bytes.
1002
+ * @param value - The value to write.
1003
+ * @returns This.
1004
+ */
1005
+ writeInt16(value) {
1006
+ this.ensureAvailable(2);
1007
+ this._data.setInt16(this.offset, value, this.littleEndian);
1008
+ this.offset += 2;
1009
+ this._updateLastWrittenByte();
1010
+ return this;
1011
+ }
1012
+ /**
1013
+ * Write `value` as a 16-bit unsigned integer and move pointer forward by 2
1014
+ * bytes.
1015
+ * @param value - The value to write.
1016
+ * @returns This.
1017
+ */
1018
+ writeUint16(value) {
1019
+ this.ensureAvailable(2);
1020
+ this._data.setUint16(this.offset, value, this.littleEndian);
1021
+ this.offset += 2;
1022
+ this._updateLastWrittenByte();
1023
+ return this;
1024
+ }
1025
+ /**
1026
+ * Write `value` as a 32-bit signed integer and move pointer forward by 4
1027
+ * bytes.
1028
+ * @param value - The value to write.
1029
+ * @returns This.
1030
+ */
1031
+ writeInt32(value) {
1032
+ this.ensureAvailable(4);
1033
+ this._data.setInt32(this.offset, value, this.littleEndian);
1034
+ this.offset += 4;
1035
+ this._updateLastWrittenByte();
1036
+ return this;
1037
+ }
1038
+ /**
1039
+ * Write `value` as a 32-bit unsigned integer and move pointer forward by 4
1040
+ * bytes.
1041
+ * @param value - The value to write.
1042
+ * @returns This.
1043
+ */
1044
+ writeUint32(value) {
1045
+ this.ensureAvailable(4);
1046
+ this._data.setUint32(this.offset, value, this.littleEndian);
1047
+ this.offset += 4;
1048
+ this._updateLastWrittenByte();
1049
+ return this;
1050
+ }
1051
+ /**
1052
+ * Write `value` as a 32-bit floating number and move pointer forward by 4
1053
+ * bytes.
1054
+ * @param value - The value to write.
1055
+ * @returns This.
1056
+ */
1057
+ writeFloat32(value) {
1058
+ this.ensureAvailable(4);
1059
+ this._data.setFloat32(this.offset, value, this.littleEndian);
1060
+ this.offset += 4;
1061
+ this._updateLastWrittenByte();
1062
+ return this;
1063
+ }
1064
+ /**
1065
+ * Write `value` as a 64-bit floating number and move pointer forward by 8
1066
+ * bytes.
1067
+ * @param value - The value to write.
1068
+ * @returns This.
1069
+ */
1070
+ writeFloat64(value) {
1071
+ this.ensureAvailable(8);
1072
+ this._data.setFloat64(this.offset, value, this.littleEndian);
1073
+ this.offset += 8;
1074
+ this._updateLastWrittenByte();
1075
+ return this;
1076
+ }
1077
+ /**
1078
+ * Write `value` as a 64-bit signed bigint and move pointer forward by 8
1079
+ * bytes.
1080
+ * @param value - The value to write.
1081
+ * @returns This.
1082
+ */
1083
+ writeBigInt64(value) {
1084
+ this.ensureAvailable(8);
1085
+ this._data.setBigInt64(this.offset, value, this.littleEndian);
1086
+ this.offset += 8;
1087
+ this._updateLastWrittenByte();
1088
+ return this;
1089
+ }
1090
+ /**
1091
+ * Write `value` as a 64-bit unsigned bigint and move pointer forward by 8
1092
+ * bytes.
1093
+ * @param value - The value to write.
1094
+ * @returns This.
1095
+ */
1096
+ writeBigUint64(value) {
1097
+ this.ensureAvailable(8);
1098
+ this._data.setBigUint64(this.offset, value, this.littleEndian);
1099
+ this.offset += 8;
1100
+ this._updateLastWrittenByte();
1101
+ return this;
1102
+ }
1103
+ /**
1104
+ * Write the charCode of `str`'s first character as an 8-bit unsigned integer
1105
+ * and move pointer forward by 1 byte.
1106
+ * @param str - The character to write.
1107
+ * @returns This.
1108
+ */
1109
+ writeChar(str) {
1110
+ // eslint-disable-next-line unicorn/prefer-code-point
1111
+ return this.writeUint8(str.charCodeAt(0));
1112
+ }
1113
+ /**
1114
+ * Write the charCodes of all `str`'s characters as 8-bit unsigned integers
1115
+ * and move pointer forward by `str.length` bytes.
1116
+ * @param str - The characters to write.
1117
+ * @returns This.
1118
+ */
1119
+ writeChars(str) {
1120
+ for (let i = 0; i < str.length; i++) {
1121
+ // eslint-disable-next-line unicorn/prefer-code-point
1122
+ this.writeUint8(str.charCodeAt(i));
1123
+ }
1124
+ return this;
1125
+ }
1126
+ /**
1127
+ * UTF-8 encode and write `str` to the current pointer offset and move pointer
1128
+ * forward according to the encoded length.
1129
+ * @param str - The string to write.
1130
+ * @returns This.
1131
+ */
1132
+ writeUtf8(str) {
1133
+ return this.writeBytes(encode(str));
1134
+ }
1135
+ /**
1136
+ * Export a Uint8Array view of the internal buffer.
1137
+ * The view starts at the byte offset and its length
1138
+ * is calculated to stop at the last written byte or the original length.
1139
+ * @returns A new Uint8Array view.
1140
+ */
1141
+ toArray() {
1142
+ return new Uint8Array(this.buffer, this.byteOffset, this.lastWrittenByte);
1143
+ }
1144
+ /**
1145
+ * Get the total number of bytes written so far, regardless of the current offset.
1146
+ * @returns - Total number of bytes.
1147
+ */
1148
+ getWrittenByteLength() {
1149
+ return this.lastWrittenByte - this.byteOffset;
1150
+ }
1151
+ /**
1152
+ * Update the last written byte offset
1153
+ * @private
1154
+ */
1155
+ _updateLastWrittenByte() {
1156
+ if (this.offset > this.lastWrittenByte) {
1157
+ this.lastWrittenByte = this.offset;
1158
+ }
1159
+ }
1160
+ }
1161
+
1162
+ const crcTable = [];
1163
+ for (let n = 0; n < 256; n++) {
1164
+ let c = n;
1165
+ for (let k = 0; k < 8; k++) {
1166
+ if (c & 1) {
1167
+ c = 0xedb88320 ^ (c >>> 1);
1168
+ }
1169
+ else {
1170
+ c = c >>> 1;
1171
+ }
1172
+ }
1173
+ crcTable[n] = c;
1174
+ }
1175
+ const initialCrc = 0xffffffff;
1176
+ function updateCrc(currentCrc, data, length) {
1177
+ let c = currentCrc;
1178
+ for (let n = 0; n < length; n++) {
1179
+ c = crcTable[(c ^ data[n]) & 0xff] ^ (c >>> 8);
1180
+ }
1181
+ return c;
1182
+ }
1183
+ function crc(data, length) {
1184
+ return (updateCrc(initialCrc, data, length) ^ initialCrc) >>> 0;
1185
+ }
1186
+ function checkCrc(buffer, crcLength, chunkName) {
1187
+ const expectedCrc = buffer.readUint32();
1188
+ const actualCrc = crc(new Uint8Array(buffer.buffer, buffer.byteOffset + buffer.offset - crcLength - 4, crcLength), crcLength); // "- 4" because we already advanced by reading the CRC
1189
+ if (actualCrc !== expectedCrc) {
1190
+ throw new Error(`CRC mismatch for chunk ${chunkName}. Expected ${expectedCrc}, found ${actualCrc}`);
1191
+ }
1192
+ }
1193
+
1194
+ function unfilterNone(currentLine, newLine, bytesPerLine) {
1195
+ for (let i = 0; i < bytesPerLine; i++) {
1196
+ newLine[i] = currentLine[i];
1197
+ }
1198
+ }
1199
+ function unfilterSub(currentLine, newLine, bytesPerLine, bytesPerPixel) {
1200
+ let i = 0;
1201
+ for (; i < bytesPerPixel; i++) {
1202
+ // just copy first bytes
1203
+ newLine[i] = currentLine[i];
1204
+ }
1205
+ for (; i < bytesPerLine; i++) {
1206
+ newLine[i] = (currentLine[i] + newLine[i - bytesPerPixel]) & 0xff;
1207
+ }
1208
+ }
1209
+ function unfilterUp(currentLine, newLine, prevLine, bytesPerLine) {
1210
+ let i = 0;
1211
+ if (prevLine.length === 0) {
1212
+ // just copy bytes for first line
1213
+ for (; i < bytesPerLine; i++) {
1214
+ newLine[i] = currentLine[i];
1215
+ }
1216
+ }
1217
+ else {
1218
+ for (; i < bytesPerLine; i++) {
1219
+ newLine[i] = (currentLine[i] + prevLine[i]) & 0xff;
1220
+ }
1221
+ }
1222
+ }
1223
+ function unfilterAverage(currentLine, newLine, prevLine, bytesPerLine, bytesPerPixel) {
1224
+ let i = 0;
1225
+ if (prevLine.length === 0) {
1226
+ for (; i < bytesPerPixel; i++) {
1227
+ newLine[i] = currentLine[i];
1228
+ }
1229
+ for (; i < bytesPerLine; i++) {
1230
+ newLine[i] = (currentLine[i] + (newLine[i - bytesPerPixel] >> 1)) & 0xff;
1231
+ }
1232
+ }
1233
+ else {
1234
+ for (; i < bytesPerPixel; i++) {
1235
+ newLine[i] = (currentLine[i] + (prevLine[i] >> 1)) & 0xff;
1236
+ }
1237
+ for (; i < bytesPerLine; i++) {
1238
+ newLine[i] =
1239
+ (currentLine[i] + ((newLine[i - bytesPerPixel] + prevLine[i]) >> 1)) &
1240
+ 0xff;
1241
+ }
1242
+ }
1243
+ }
1244
+ function unfilterPaeth(currentLine, newLine, prevLine, bytesPerLine, bytesPerPixel) {
1245
+ let i = 0;
1246
+ if (prevLine.length === 0) {
1247
+ for (; i < bytesPerPixel; i++) {
1248
+ newLine[i] = currentLine[i];
1249
+ }
1250
+ for (; i < bytesPerLine; i++) {
1251
+ newLine[i] = (currentLine[i] + newLine[i - bytesPerPixel]) & 0xff;
1252
+ }
1253
+ }
1254
+ else {
1255
+ for (; i < bytesPerPixel; i++) {
1256
+ newLine[i] = (currentLine[i] + prevLine[i]) & 0xff;
1257
+ }
1258
+ for (; i < bytesPerLine; i++) {
1259
+ newLine[i] =
1260
+ (currentLine[i] +
1261
+ paethPredictor(newLine[i - bytesPerPixel], prevLine[i], prevLine[i - bytesPerPixel])) &
1262
+ 0xff;
1263
+ }
1264
+ }
1265
+ }
1266
+ function paethPredictor(a, b, c) {
1267
+ const p = a + b - c;
1268
+ const pa = Math.abs(p - a);
1269
+ const pb = Math.abs(p - b);
1270
+ const pc = Math.abs(p - c);
1271
+ if (pa <= pb && pa <= pc)
1272
+ return a;
1273
+ else if (pb <= pc)
1274
+ return b;
1275
+ else
1276
+ return c;
1277
+ }
1278
+
1279
+ /**
1280
+ * Apllies filter on scanline based on the filter type.
1281
+ * @param filterType - The filter type to apply.
1282
+ * @param currentLine - The current line of pixel data.
1283
+ * @param newLine - The new line of pixel data.
1284
+ * @param prevLine - The previous line of pixel data.
1285
+ * @param passLineBytes - The number of bytes in the pass line.
1286
+ * @param bytesPerPixel - The number of bytes per pixel.
1287
+ */
1288
+ function applyUnfilter(filterType, currentLine, newLine, prevLine, passLineBytes, bytesPerPixel) {
1289
+ switch (filterType) {
1290
+ case 0:
1291
+ unfilterNone(currentLine, newLine, passLineBytes);
1292
+ break;
1293
+ case 1:
1294
+ unfilterSub(currentLine, newLine, passLineBytes, bytesPerPixel);
1295
+ break;
1296
+ case 2:
1297
+ unfilterUp(currentLine, newLine, prevLine, passLineBytes);
1298
+ break;
1299
+ case 3:
1300
+ unfilterAverage(currentLine, newLine, prevLine, passLineBytes, bytesPerPixel);
1301
+ break;
1302
+ case 4:
1303
+ unfilterPaeth(currentLine, newLine, prevLine, passLineBytes, bytesPerPixel);
1304
+ break;
1305
+ default:
1306
+ throw new Error(`Unsupported filter: ${filterType}`);
1307
+ }
1308
+ }
1309
+
1310
+ const uint16$1 = new Uint16Array([0x00ff]);
1311
+ const uint8$1 = new Uint8Array(uint16$1.buffer);
1312
+ const osIsLittleEndian$1 = uint8$1[0] === 0xff;
1313
+ /**
1314
+ * Decodes the Adam7 interlaced PNG data.
1315
+ * @param params - DecodeInterlaceNullParams
1316
+ * @returns - array of pixel data.
1317
+ */
1318
+ function decodeInterlaceAdam7(params) {
1319
+ const { data, width, height, channels, depth } = params;
1320
+ // Adam7 interlacing pattern
1321
+ const passes = [
1322
+ { x: 0, y: 0, xStep: 8, yStep: 8 }, // Pass 1
1323
+ { x: 4, y: 0, xStep: 8, yStep: 8 }, // Pass 2
1324
+ { x: 0, y: 4, xStep: 4, yStep: 8 }, // Pass 3
1325
+ { x: 2, y: 0, xStep: 4, yStep: 4 }, // Pass 4
1326
+ { x: 0, y: 2, xStep: 2, yStep: 4 }, // Pass 5
1327
+ { x: 1, y: 0, xStep: 2, yStep: 2 }, // Pass 6
1328
+ { x: 0, y: 1, xStep: 1, yStep: 2 }, // Pass 7
1329
+ ];
1330
+ const bytesPerPixel = Math.ceil(depth / 8) * channels;
1331
+ const resultData = new Uint8Array(height * width * bytesPerPixel);
1332
+ let offset = 0;
1333
+ // Process each pass
1334
+ for (let passIndex = 0; passIndex < 7; passIndex++) {
1335
+ const pass = passes[passIndex];
1336
+ // Calculate pass dimensions
1337
+ const passWidth = Math.ceil((width - pass.x) / pass.xStep);
1338
+ const passHeight = Math.ceil((height - pass.y) / pass.yStep);
1339
+ if (passWidth <= 0 || passHeight <= 0)
1340
+ continue;
1341
+ const passLineBytes = passWidth * bytesPerPixel;
1342
+ const prevLine = new Uint8Array(passLineBytes);
1343
+ // Process each scanline in this pass
1344
+ for (let y = 0; y < passHeight; y++) {
1345
+ // First byte is the filter type
1346
+ const filterType = data[offset++];
1347
+ const currentLine = data.subarray(offset, offset + passLineBytes);
1348
+ offset += passLineBytes;
1349
+ // Create a new line for the unfiltered data
1350
+ const newLine = new Uint8Array(passLineBytes);
1351
+ // Apply the appropriate unfilter
1352
+ applyUnfilter(filterType, currentLine, newLine, prevLine, passLineBytes, bytesPerPixel);
1353
+ prevLine.set(newLine);
1354
+ for (let x = 0; x < passWidth; x++) {
1355
+ const outputX = pass.x + x * pass.xStep;
1356
+ const outputY = pass.y + y * pass.yStep;
1357
+ if (outputX >= width || outputY >= height)
1358
+ continue;
1359
+ for (let i = 0; i < bytesPerPixel; i++) {
1360
+ resultData[(outputY * width + outputX) * bytesPerPixel + i] =
1361
+ newLine[x * bytesPerPixel + i];
1362
+ }
1363
+ }
1364
+ }
1365
+ }
1366
+ if (depth === 16) {
1367
+ const uint16Data = new Uint16Array(resultData.buffer);
1368
+ if (osIsLittleEndian$1) {
1369
+ for (let k = 0; k < uint16Data.length; k++) {
1370
+ // PNG is always big endian. Swap the bytes.
1371
+ uint16Data[k] = swap16$1(uint16Data[k]);
1372
+ }
1373
+ }
1374
+ return uint16Data;
1375
+ }
1376
+ else {
1377
+ return resultData;
1378
+ }
1379
+ }
1380
+ function swap16$1(val) {
1381
+ return ((val & 0xff) << 8) | ((val >> 8) & 0xff);
1382
+ }
1383
+
1384
+ const uint16 = new Uint16Array([0x00ff]);
1385
+ const uint8 = new Uint8Array(uint16.buffer);
1386
+ const osIsLittleEndian = uint8[0] === 0xff;
1387
+ const empty = new Uint8Array(0);
1388
+ function decodeInterlaceNull(params) {
1389
+ const { data, width, height, channels, depth } = params;
1390
+ const bytesPerPixel = Math.ceil(depth / 8) * channels;
1391
+ const bytesPerLine = Math.ceil((depth / 8) * channels * width);
1392
+ const newData = new Uint8Array(height * bytesPerLine);
1393
+ let prevLine = empty;
1394
+ let offset = 0;
1395
+ let currentLine;
1396
+ let newLine;
1397
+ for (let i = 0; i < height; i++) {
1398
+ currentLine = data.subarray(offset + 1, offset + 1 + bytesPerLine);
1399
+ newLine = newData.subarray(i * bytesPerLine, (i + 1) * bytesPerLine);
1400
+ switch (data[offset]) {
1401
+ case 0:
1402
+ unfilterNone(currentLine, newLine, bytesPerLine);
1403
+ break;
1404
+ case 1:
1405
+ unfilterSub(currentLine, newLine, bytesPerLine, bytesPerPixel);
1406
+ break;
1407
+ case 2:
1408
+ unfilterUp(currentLine, newLine, prevLine, bytesPerLine);
1409
+ break;
1410
+ case 3:
1411
+ unfilterAverage(currentLine, newLine, prevLine, bytesPerLine, bytesPerPixel);
1412
+ break;
1413
+ case 4:
1414
+ unfilterPaeth(currentLine, newLine, prevLine, bytesPerLine, bytesPerPixel);
1415
+ break;
1416
+ default:
1417
+ throw new Error(`Unsupported filter: ${data[offset]}`);
1418
+ }
1419
+ prevLine = newLine;
1420
+ offset += bytesPerLine + 1;
1421
+ }
1422
+ if (depth === 16) {
1423
+ const uint16Data = new Uint16Array(newData.buffer);
1424
+ if (osIsLittleEndian) {
1425
+ for (let k = 0; k < uint16Data.length; k++) {
1426
+ // PNG is always big endian. Swap the bytes.
1427
+ uint16Data[k] = swap16(uint16Data[k]);
1428
+ }
1429
+ }
1430
+ return uint16Data;
1431
+ }
1432
+ else {
1433
+ return newData;
1434
+ }
1435
+ }
1436
+ function swap16(val) {
1437
+ return ((val & 0xff) << 8) | ((val >> 8) & 0xff);
1438
+ }
1439
+
1440
+ // https://www.w3.org/TR/PNG/#5PNG-file-signature
1441
+ const pngSignature = Uint8Array.of(137, 80, 78, 71, 13, 10, 26, 10);
1442
+ function checkSignature(buffer) {
1443
+ if (!hasPngSignature(buffer.readBytes(pngSignature.length))) {
1444
+ throw new Error('wrong PNG signature');
1445
+ }
1446
+ }
1447
+ function hasPngSignature(array) {
1448
+ if (array.length < pngSignature.length) {
1449
+ return false;
1450
+ }
1451
+ for (let i = 0; i < pngSignature.length; i++) {
1452
+ if (array[i] !== pngSignature[i]) {
1453
+ return false;
1454
+ }
1455
+ }
1456
+ return true;
1457
+ }
1458
+
1459
+ // https://www.w3.org/TR/png/#11tEXt
1460
+ const textChunkName = 'tEXt';
1461
+ const NULL = 0;
1462
+ const latin1Decoder = new TextDecoder('latin1');
1463
+ function validateKeyword(keyword) {
1464
+ validateLatin1(keyword);
1465
+ if (keyword.length === 0 || keyword.length > 79) {
1466
+ throw new Error('keyword length must be between 1 and 79');
1467
+ }
1468
+ }
1469
+ // eslint-disable-next-line no-control-regex
1470
+ const latin1Regex = /^[\u0000-\u00FF]*$/;
1471
+ function validateLatin1(text) {
1472
+ if (!latin1Regex.test(text)) {
1473
+ throw new Error('invalid latin1 text');
1474
+ }
1475
+ }
1476
+ function decodetEXt(text, buffer, length) {
1477
+ const keyword = readKeyword(buffer);
1478
+ text[keyword] = readLatin1(buffer, length - keyword.length - 1);
1479
+ }
1480
+ // https://www.w3.org/TR/png/#11keywords
1481
+ function readKeyword(buffer) {
1482
+ buffer.mark();
1483
+ while (buffer.readByte() !== NULL) {
1484
+ /* advance */
1485
+ }
1486
+ const end = buffer.offset;
1487
+ buffer.reset();
1488
+ const keyword = latin1Decoder.decode(buffer.readBytes(end - buffer.offset - 1));
1489
+ // NULL
1490
+ buffer.skip(1);
1491
+ validateKeyword(keyword);
1492
+ return keyword;
1493
+ }
1494
+ function readLatin1(buffer, length) {
1495
+ return latin1Decoder.decode(buffer.readBytes(length));
1496
+ }
1497
+
1498
+ const ColorType = {
1499
+ UNKNOWN: -1,
1500
+ GREYSCALE: 0,
1501
+ TRUECOLOUR: 2,
1502
+ INDEXED_COLOUR: 3,
1503
+ GREYSCALE_ALPHA: 4,
1504
+ TRUECOLOUR_ALPHA: 6,
1505
+ };
1506
+ const CompressionMethod = {
1507
+ UNKNOWN: -1,
1508
+ DEFLATE: 0,
1509
+ };
1510
+ const FilterMethod = {
1511
+ UNKNOWN: -1,
1512
+ ADAPTIVE: 0,
1513
+ };
1514
+ const InterlaceMethod = {
1515
+ UNKNOWN: -1,
1516
+ NO_INTERLACE: 0,
1517
+ ADAM7: 1,
1518
+ };
1519
+ const DisposeOpType = {
1520
+ NONE: 0,
1521
+ BACKGROUND: 1,
1522
+ PREVIOUS: 2,
1523
+ };
1524
+ const BlendOpType = {
1525
+ SOURCE: 0,
1526
+ OVER: 1,
1527
+ };
1528
+
1529
+ class PngDecoder extends IOBuffer {
1530
+ _checkCrc;
1531
+ _inflator;
1532
+ _png;
1533
+ _apng;
1534
+ _end;
1535
+ _hasPalette;
1536
+ _palette;
1537
+ _hasTransparency;
1538
+ _transparency;
1539
+ _compressionMethod;
1540
+ _filterMethod;
1541
+ _interlaceMethod;
1542
+ _colorType;
1543
+ _isAnimated;
1544
+ _numberOfFrames;
1545
+ _numberOfPlays;
1546
+ _frames;
1547
+ _writingDataChunks;
1548
+ _chunks;
1549
+ _inflatorResult;
1550
+ constructor(data, options = {}) {
1551
+ super(data);
1552
+ const { checkCrc = false } = options;
1553
+ this._checkCrc = checkCrc;
1554
+ this._inflator = new Unzlib((chunk, final) => {
1555
+ this._chunks.push(chunk);
1556
+ if (final) {
1557
+ const totalLength = this._chunks.reduce((sum, c) => sum + c.length, 0);
1558
+ this._inflatorResult = new Uint8Array(totalLength);
1559
+ let offset = 0;
1560
+ for (const chunk of this._chunks) {
1561
+ this._inflatorResult.set(chunk, offset);
1562
+ offset += chunk.length;
1563
+ }
1564
+ this._chunks = [];
1565
+ }
1566
+ });
1567
+ this._chunks = [];
1568
+ this._png = {
1569
+ width: -1,
1570
+ height: -1,
1571
+ channels: -1,
1572
+ data: new Uint8Array(0),
1573
+ depth: 1,
1574
+ text: {},
1575
+ };
1576
+ this._apng = {
1577
+ width: -1,
1578
+ height: -1,
1579
+ channels: -1,
1580
+ depth: 1,
1581
+ numberOfFrames: 1,
1582
+ numberOfPlays: 0,
1583
+ text: {},
1584
+ frames: [],
1585
+ };
1586
+ this._end = false;
1587
+ this._hasPalette = false;
1588
+ this._palette = [];
1589
+ this._hasTransparency = false;
1590
+ this._transparency = new Uint16Array(0);
1591
+ this._compressionMethod = CompressionMethod.UNKNOWN;
1592
+ this._filterMethod = FilterMethod.UNKNOWN;
1593
+ this._interlaceMethod = InterlaceMethod.UNKNOWN;
1594
+ this._colorType = ColorType.UNKNOWN;
1595
+ this._isAnimated = false;
1596
+ this._numberOfFrames = 1;
1597
+ this._numberOfPlays = 0;
1598
+ this._frames = [];
1599
+ this._writingDataChunks = false;
1600
+ this._inflatorResult = new Uint8Array(0);
1601
+ // PNG is always big endian
1602
+ // https://www.w3.org/TR/PNG/#7Integers-and-byte-order
1603
+ this.setBigEndian();
1604
+ }
1605
+ decode() {
1606
+ checkSignature(this);
1607
+ while (!this._end) {
1608
+ const length = this.readUint32();
1609
+ const type = this.readChars(4);
1610
+ this.decodeChunk(length, type);
1611
+ }
1612
+ this._inflator.push(new Uint8Array(0), true);
1613
+ this.decodeImage();
1614
+ return this._png;
1615
+ }
1616
+ decodeApng() {
1617
+ checkSignature(this);
1618
+ while (!this._end) {
1619
+ const length = this.readUint32();
1620
+ const type = this.readChars(4);
1621
+ this.decodeApngChunk(length, type);
1622
+ }
1623
+ this.decodeApngImage();
1624
+ return this._apng;
1625
+ }
1626
+ // https://www.w3.org/TR/PNG/#5Chunk-layout
1627
+ decodeChunk(length, type) {
1628
+ const offset = this.offset;
1629
+ switch (type) {
1630
+ // 11.2 Critical chunks
1631
+ case 'IHDR': // 11.2.2 IHDR Image header
1632
+ this.decodeIHDR();
1633
+ break;
1634
+ case 'PLTE': // 11.2.3 PLTE Palette
1635
+ this.decodePLTE(length);
1636
+ break;
1637
+ case 'IDAT': // 11.2.4 IDAT Image data
1638
+ this.decodeIDAT(length);
1639
+ break;
1640
+ case 'IEND': // 11.2.5 IEND Image trailer
1641
+ this._end = true;
1642
+ break;
1643
+ // 11.3 Ancillary chunks
1644
+ case 'tRNS': // 11.3.2.1 tRNS Transparency
1645
+ this.decodetRNS(length);
1646
+ break;
1647
+ case 'iCCP': // 11.3.3.3 iCCP Embedded ICC profile
1648
+ this.decodeiCCP(length);
1649
+ break;
1650
+ case textChunkName: // 11.3.4.3 tEXt Textual data
1651
+ decodetEXt(this._png.text, this, length);
1652
+ break;
1653
+ case 'pHYs': // 11.3.5.3 pHYs Physical pixel dimensions
1654
+ this.decodepHYs();
1655
+ break;
1656
+ default:
1657
+ this.skip(length);
1658
+ break;
1659
+ }
1660
+ if (this.offset - offset !== length) {
1661
+ throw new Error(`Length mismatch while decoding chunk ${type}`);
1662
+ }
1663
+ if (this._checkCrc) {
1664
+ checkCrc(this, length + 4, type);
1665
+ }
1666
+ else {
1667
+ this.skip(4);
1668
+ }
1669
+ }
1670
+ decodeApngChunk(length, type) {
1671
+ const offset = this.offset;
1672
+ if (type !== 'fdAT' && type !== 'IDAT' && this._writingDataChunks) {
1673
+ this.pushDataToFrame();
1674
+ }
1675
+ switch (type) {
1676
+ case 'acTL':
1677
+ this.decodeACTL();
1678
+ break;
1679
+ case 'fcTL':
1680
+ this.decodeFCTL();
1681
+ break;
1682
+ case 'fdAT':
1683
+ this.decodeFDAT(length);
1684
+ break;
1685
+ default:
1686
+ this.decodeChunk(length, type);
1687
+ this.offset = offset + length;
1688
+ break;
1689
+ }
1690
+ if (this.offset - offset !== length) {
1691
+ throw new Error(`Length mismatch while decoding chunk ${type}`);
1692
+ }
1693
+ if (this._checkCrc) {
1694
+ checkCrc(this, length + 4, type);
1695
+ }
1696
+ else {
1697
+ this.skip(4);
1698
+ }
1699
+ }
1700
+ // https://www.w3.org/TR/PNG/#11IHDR
1701
+ decodeIHDR() {
1702
+ const image = this._png;
1703
+ image.width = this.readUint32();
1704
+ image.height = this.readUint32();
1705
+ image.depth = checkBitDepth(this.readUint8());
1706
+ const colorType = this.readUint8();
1707
+ this._colorType = colorType;
1708
+ let channels;
1709
+ switch (colorType) {
1710
+ case ColorType.GREYSCALE:
1711
+ channels = 1;
1712
+ break;
1713
+ case ColorType.TRUECOLOUR:
1714
+ channels = 3;
1715
+ break;
1716
+ case ColorType.INDEXED_COLOUR:
1717
+ channels = 1;
1718
+ break;
1719
+ case ColorType.GREYSCALE_ALPHA:
1720
+ channels = 2;
1721
+ break;
1722
+ case ColorType.TRUECOLOUR_ALPHA:
1723
+ channels = 4;
1724
+ break;
1725
+ // Kept for exhaustiveness.
1726
+ // eslint-disable-next-line unicorn/no-useless-switch-case
1727
+ case ColorType.UNKNOWN:
1728
+ default:
1729
+ throw new Error(`Unknown color type: ${colorType}`);
1730
+ }
1731
+ this._png.channels = channels;
1732
+ this._compressionMethod = this.readUint8();
1733
+ if (this._compressionMethod !== CompressionMethod.DEFLATE) {
1734
+ throw new Error(`Unsupported compression method: ${this._compressionMethod}`);
1735
+ }
1736
+ this._filterMethod = this.readUint8();
1737
+ this._interlaceMethod = this.readUint8();
1738
+ }
1739
+ decodeACTL() {
1740
+ this._numberOfFrames = this.readUint32();
1741
+ this._numberOfPlays = this.readUint32();
1742
+ this._isAnimated = true;
1743
+ }
1744
+ decodeFCTL() {
1745
+ const image = {
1746
+ sequenceNumber: this.readUint32(),
1747
+ width: this.readUint32(),
1748
+ height: this.readUint32(),
1749
+ xOffset: this.readUint32(),
1750
+ yOffset: this.readUint32(),
1751
+ delayNumber: this.readUint16(),
1752
+ delayDenominator: this.readUint16(),
1753
+ disposeOp: this.readUint8(),
1754
+ blendOp: this.readUint8(),
1755
+ data: new Uint8Array(0),
1756
+ };
1757
+ this._frames.push(image);
1758
+ }
1759
+ // https://www.w3.org/TR/PNG/#11PLTE
1760
+ decodePLTE(length) {
1761
+ if (length % 3 !== 0) {
1762
+ throw new RangeError(`PLTE field length must be a multiple of 3. Got ${length}`);
1763
+ }
1764
+ const l = length / 3;
1765
+ this._hasPalette = true;
1766
+ const palette = [];
1767
+ this._palette = palette;
1768
+ for (let i = 0; i < l; i++) {
1769
+ palette.push([this.readUint8(), this.readUint8(), this.readUint8()]);
1770
+ }
1771
+ }
1772
+ // https://www.w3.org/TR/PNG/#11IDAT
1773
+ decodeIDAT(length) {
1774
+ this._writingDataChunks = true;
1775
+ const dataLength = length;
1776
+ const dataOffset = this.offset + this.byteOffset;
1777
+ try {
1778
+ this._inflator.push(new Uint8Array(this.buffer, dataOffset, dataLength), false);
1779
+ }
1780
+ catch (error) {
1781
+ throw new Error('Error while decompressing the data:', { cause: error });
1782
+ }
1783
+ this.skip(length);
1784
+ }
1785
+ decodeFDAT(length) {
1786
+ this._writingDataChunks = true;
1787
+ let dataLength = length;
1788
+ let dataOffset = this.offset + this.byteOffset;
1789
+ dataOffset += 4;
1790
+ dataLength -= 4;
1791
+ try {
1792
+ this._inflator.push(new Uint8Array(this.buffer, dataOffset, dataLength), false);
1793
+ }
1794
+ catch (error) {
1795
+ throw new Error('Error while decompressing the data:', { cause: error });
1796
+ }
1797
+ this.skip(length);
1798
+ }
1799
+ // https://www.w3.org/TR/PNG/#11tRNS
1800
+ decodetRNS(length) {
1801
+ switch (this._colorType) {
1802
+ case ColorType.GREYSCALE:
1803
+ case ColorType.TRUECOLOUR: {
1804
+ if (length % 2 !== 0) {
1805
+ throw new RangeError(`tRNS chunk length must be a multiple of 2. Got ${length}`);
1806
+ }
1807
+ if (length / 2 > this._png.width * this._png.height) {
1808
+ throw new Error(`tRNS chunk contains more alpha values than there are pixels (${length / 2} vs ${this._png.width * this._png.height})`);
1809
+ }
1810
+ this._hasTransparency = true;
1811
+ this._transparency = new Uint16Array(length / 2);
1812
+ for (let i = 0; i < length / 2; i++) {
1813
+ this._transparency[i] = this.readUint16();
1814
+ }
1815
+ break;
1816
+ }
1817
+ case ColorType.INDEXED_COLOUR: {
1818
+ if (length > this._palette.length) {
1819
+ throw new Error(`tRNS chunk contains more alpha values than there are palette colors (${length} vs ${this._palette.length})`);
1820
+ }
1821
+ let i = 0;
1822
+ for (; i < length; i++) {
1823
+ const alpha = this.readByte();
1824
+ this._palette[i].push(alpha);
1825
+ }
1826
+ for (; i < this._palette.length; i++) {
1827
+ this._palette[i].push(255);
1828
+ }
1829
+ break;
1830
+ }
1831
+ // Kept for exhaustiveness.
1832
+ /* eslint-disable unicorn/no-useless-switch-case */
1833
+ case ColorType.UNKNOWN:
1834
+ case ColorType.GREYSCALE_ALPHA:
1835
+ case ColorType.TRUECOLOUR_ALPHA:
1836
+ default: {
1837
+ throw new Error(`tRNS chunk is not supported for color type ${this._colorType}`);
1838
+ }
1839
+ /* eslint-enable unicorn/no-useless-switch-case */
1840
+ }
1841
+ }
1842
+ // https://www.w3.org/TR/PNG/#11iCCP
1843
+ decodeiCCP(length) {
1844
+ const name = readKeyword(this);
1845
+ const compressionMethod = this.readUint8();
1846
+ if (compressionMethod !== CompressionMethod.DEFLATE) {
1847
+ throw new Error(`Unsupported iCCP compression method: ${compressionMethod}`);
1848
+ }
1849
+ const compressedProfile = this.readBytes(length - name.length - 2);
1850
+ this._png.iccEmbeddedProfile = {
1851
+ name,
1852
+ profile: unzlibSync(compressedProfile),
1853
+ };
1854
+ }
1855
+ // https://www.w3.org/TR/PNG/#11pHYs
1856
+ decodepHYs() {
1857
+ const ppuX = this.readUint32();
1858
+ const ppuY = this.readUint32();
1859
+ const unitSpecifier = this.readByte();
1860
+ this._png.resolution = {
1861
+ x: ppuX,
1862
+ y: ppuY,
1863
+ unit: unitSpecifier,
1864
+ };
1865
+ }
1866
+ decodeApngImage() {
1867
+ this._apng.width = this._png.width;
1868
+ this._apng.height = this._png.height;
1869
+ this._apng.channels = this._png.channels;
1870
+ this._apng.depth = this._png.depth;
1871
+ this._apng.numberOfFrames = this._numberOfFrames;
1872
+ this._apng.numberOfPlays = this._numberOfPlays;
1873
+ this._apng.text = this._png.text;
1874
+ this._apng.resolution = this._png.resolution;
1875
+ for (let i = 0; i < this._numberOfFrames; i++) {
1876
+ const newFrame = {
1877
+ sequenceNumber: this._frames[i].sequenceNumber,
1878
+ delayNumber: this._frames[i].delayNumber,
1879
+ delayDenominator: this._frames[i].delayDenominator,
1880
+ data: this._apng.depth === 8
1881
+ ? new Uint8Array(this._apng.width * this._apng.height * this._apng.channels)
1882
+ : new Uint16Array(this._apng.width * this._apng.height * this._apng.channels),
1883
+ };
1884
+ const frame = this._frames.at(i);
1885
+ if (frame) {
1886
+ frame.data = decodeInterlaceNull({
1887
+ data: frame.data,
1888
+ width: frame.width,
1889
+ height: frame.height,
1890
+ channels: this._apng.channels,
1891
+ depth: this._apng.depth,
1892
+ });
1893
+ if (this._hasPalette) {
1894
+ this._apng.palette = this._palette;
1895
+ }
1896
+ if (this._hasTransparency) {
1897
+ this._apng.transparency = this._transparency;
1898
+ }
1899
+ if (i === 0 ||
1900
+ (frame.xOffset === 0 &&
1901
+ frame.yOffset === 0 &&
1902
+ frame.width === this._png.width &&
1903
+ frame.height === this._png.height)) {
1904
+ newFrame.data = frame.data;
1905
+ }
1906
+ else {
1907
+ const prevFrame = this._apng.frames.at(i - 1);
1908
+ this.disposeFrame(frame, prevFrame, newFrame);
1909
+ this.addFrameDataToCanvas(newFrame, frame);
1910
+ }
1911
+ this._apng.frames.push(newFrame);
1912
+ }
1913
+ }
1914
+ return this._apng;
1915
+ }
1916
+ disposeFrame(frame, prevFrame, imageFrame) {
1917
+ switch (frame.disposeOp) {
1918
+ case DisposeOpType.NONE:
1919
+ break;
1920
+ case DisposeOpType.BACKGROUND:
1921
+ for (let row = 0; row < this._png.height; row++) {
1922
+ for (let col = 0; col < this._png.width; col++) {
1923
+ const index = (row * frame.width + col) * this._png.channels;
1924
+ for (let channel = 0; channel < this._png.channels; channel++) {
1925
+ imageFrame.data[index + channel] = 0;
1926
+ }
1927
+ }
1928
+ }
1929
+ break;
1930
+ case DisposeOpType.PREVIOUS:
1931
+ imageFrame.data.set(prevFrame.data);
1932
+ break;
1933
+ default:
1934
+ throw new Error('Unknown disposeOp');
1935
+ }
1936
+ }
1937
+ addFrameDataToCanvas(imageFrame, frame) {
1938
+ const maxValue = 1 << this._png.depth;
1939
+ const calculatePixelIndices = (row, col) => {
1940
+ const index = ((row + frame.yOffset) * this._png.width + frame.xOffset + col) *
1941
+ this._png.channels;
1942
+ const frameIndex = (row * frame.width + col) * this._png.channels;
1943
+ return { index, frameIndex };
1944
+ };
1945
+ switch (frame.blendOp) {
1946
+ case BlendOpType.SOURCE:
1947
+ for (let row = 0; row < frame.height; row++) {
1948
+ for (let col = 0; col < frame.width; col++) {
1949
+ const { index, frameIndex } = calculatePixelIndices(row, col);
1950
+ for (let channel = 0; channel < this._png.channels; channel++) {
1951
+ imageFrame.data[index + channel] =
1952
+ frame.data[frameIndex + channel];
1953
+ }
1954
+ }
1955
+ }
1956
+ break;
1957
+ // https://www.w3.org/TR/png-3/#13Alpha-channel-processing
1958
+ case BlendOpType.OVER:
1959
+ for (let row = 0; row < frame.height; row++) {
1960
+ for (let col = 0; col < frame.width; col++) {
1961
+ const { index, frameIndex } = calculatePixelIndices(row, col);
1962
+ for (let channel = 0; channel < this._png.channels; channel++) {
1963
+ const sourceAlpha = frame.data[frameIndex + this._png.channels - 1] / maxValue;
1964
+ const foregroundValue = channel % (this._png.channels - 1) === 0
1965
+ ? 1
1966
+ : frame.data[frameIndex + channel];
1967
+ const value = Math.floor(sourceAlpha * foregroundValue +
1968
+ (1 - sourceAlpha) * imageFrame.data[index + channel]);
1969
+ imageFrame.data[index + channel] += value;
1970
+ }
1971
+ }
1972
+ }
1973
+ break;
1974
+ default:
1975
+ throw new Error('Unknown blendOp');
1976
+ }
1977
+ }
1978
+ decodeImage() {
1979
+ const data = this._inflatorResult;
1980
+ if (this._filterMethod !== FilterMethod.ADAPTIVE) {
1981
+ throw new Error(`Filter method ${this._filterMethod} not supported`);
1982
+ }
1983
+ if (this._interlaceMethod === InterlaceMethod.NO_INTERLACE) {
1984
+ this._png.data = decodeInterlaceNull({
1985
+ data,
1986
+ width: this._png.width,
1987
+ height: this._png.height,
1988
+ channels: this._png.channels,
1989
+ depth: this._png.depth,
1990
+ });
1991
+ }
1992
+ else if (this._interlaceMethod === InterlaceMethod.ADAM7) {
1993
+ this._png.data = decodeInterlaceAdam7({
1994
+ data,
1995
+ width: this._png.width,
1996
+ height: this._png.height,
1997
+ channels: this._png.channels,
1998
+ depth: this._png.depth,
1999
+ });
2000
+ }
2001
+ else {
2002
+ throw new Error(`Interlace method ${this._interlaceMethod} not supported`);
2003
+ }
2004
+ if (this._hasPalette) {
2005
+ this._png.palette = this._palette;
2006
+ }
2007
+ if (this._hasTransparency) {
2008
+ this._png.transparency = this._transparency;
2009
+ }
2010
+ }
2011
+ pushDataToFrame() {
2012
+ // Finalize the current stream
2013
+ this._inflator.push(new Uint8Array(0), true); // This triggers final=true in callback
2014
+ const result = this._inflatorResult;
2015
+ const lastFrame = this._frames.at(-1);
2016
+ if (lastFrame) {
2017
+ lastFrame.data = result;
2018
+ }
2019
+ else {
2020
+ this._frames.push({
2021
+ sequenceNumber: 0,
2022
+ width: this._png.width,
2023
+ height: this._png.height,
2024
+ xOffset: 0,
2025
+ yOffset: 0,
2026
+ delayNumber: 0,
2027
+ delayDenominator: 0,
2028
+ disposeOp: DisposeOpType.NONE,
2029
+ blendOp: BlendOpType.SOURCE,
2030
+ data: result,
2031
+ });
2032
+ }
2033
+ // Create new inflator for next frame
2034
+ this._inflator = new Unzlib((chunk, final) => {
2035
+ this._chunks.push(chunk);
2036
+ if (final) {
2037
+ const totalLength = this._chunks.reduce((sum, c) => sum + c.length, 0);
2038
+ this._inflatorResult = new Uint8Array(totalLength);
2039
+ let offset = 0;
2040
+ for (const chunk of this._chunks) {
2041
+ this._inflatorResult.set(chunk, offset);
2042
+ offset += chunk.length;
2043
+ }
2044
+ this._chunks = [];
2045
+ }
2046
+ });
2047
+ this._chunks = [];
2048
+ this._writingDataChunks = false;
2049
+ }
2050
+ }
2051
+ function checkBitDepth(value) {
2052
+ if (value !== 1 &&
2053
+ value !== 2 &&
2054
+ value !== 4 &&
2055
+ value !== 8 &&
2056
+ value !== 16) {
2057
+ throw new Error(`invalid bit depth: ${value}`);
2058
+ }
2059
+ return value;
2060
+ }
2061
+
2062
+ function decodePng(data, options) {
2063
+ const decoder = new PngDecoder(data, options);
2064
+ return decoder.decode();
2065
+ }
2066
+
2067
+ /**
2068
+ * @fileOverview
2069
+ * @author Ramon Wijnands - rayman747@hotmail.com
2070
+ */
2071
+ const textDecoder = new TextDecoder();
2072
+ /**
2073
+ * If a message was compressed as a PNG image (a compression hack since
2074
+ * gzipping over WebSockets * is not supported yet), this function decodes
2075
+ * the "image" as a Base64 string.
2076
+ *
2077
+ * @param data - An object containing the PNG data.
2078
+ */
2079
+ function decompressPng(data) {
2080
+ const buffer = Uint8Array.from(atob(data), (char) => char.charCodeAt(0));
2081
+ const decoded = tryDecodeBuffer(buffer);
2082
+ try {
2083
+ return JSON.parse(textDecoder.decode(decoded.data));
2084
+ }
2085
+ catch (error) {
2086
+ throw new Error("Error parsing PNG JSON contents", { cause: error });
2087
+ }
2088
+ }
2089
+ function tryDecodeBuffer(buffer) {
2090
+ try {
2091
+ return decodePng(buffer);
2092
+ }
2093
+ catch (error) {
2094
+ throw new Error("Error decoding PNG buffer", { cause: error });
2095
+ }
2096
+ }
2097
+
63
2098
  class Ros extends EventEmitter {
64
2099
  constructor(options = {}) {
65
2100
  super();
@@ -97,7 +2132,8 @@ class Ros extends EventEmitter {
97
2132
  this.emit('error', error);
98
2133
  };
99
2134
  this.socket.onmessage = (event) => {
100
- this.handleMessage(event.data);
2135
+ var message = JSON.parse(typeof event === 'string' ? event : event.data);
2136
+ this.handlePng(message);
101
2137
  };
102
2138
  }
103
2139
  catch (error) {
@@ -111,9 +2147,8 @@ class Ros extends EventEmitter {
111
2147
  }
112
2148
  this._isConnected = false;
113
2149
  }
114
- handleMessage(data) {
2150
+ handleMessage(message) {
115
2151
  try {
116
- const message = JSON.parse(data);
117
2152
  if (message.op === 'publish') {
118
2153
  // 发布消息到对应的 topic
119
2154
  this.emit(message.topic, message.msg);
@@ -136,6 +2171,14 @@ class Ros extends EventEmitter {
136
2171
  console.error('Error parsing message:', error);
137
2172
  }
138
2173
  }
2174
+ handlePng(message) {
2175
+ if (message.op === 'png') {
2176
+ this.handleMessage(decompressPng(message.data));
2177
+ }
2178
+ else {
2179
+ this.handleMessage(message);
2180
+ }
2181
+ }
139
2182
  callOnConnection(message) {
140
2183
  const messageStr = JSON.stringify(message);
141
2184
  if (this._isConnected && this.socket) {
@@ -341,7 +2384,8 @@ class EnhancedRos extends EventEmitter {
341
2384
  };
342
2385
  this.socket.onmessage = (event) => {
343
2386
  this.lastServerMessageAtMs = Date.now();
344
- this.handleMessage(event.data);
2387
+ var message = JSON.parse(typeof event === 'string' ? event : event.data);
2388
+ this.handlePng(message);
345
2389
  };
346
2390
  }
347
2391
  catch (error) {
@@ -468,9 +2512,8 @@ class EnhancedRos extends EventEmitter {
468
2512
  this.socket.send(messageStr);
469
2513
  }
470
2514
  /** 解析并分发服务端消息 */
471
- handleMessage(data) {
2515
+ handleMessage(message) {
472
2516
  try {
473
- const message = JSON.parse(data);
474
2517
  if (message.op === 'publish') {
475
2518
  // 普通话题消息
476
2519
  this.emit(message.topic, message.msg);
@@ -497,6 +2540,14 @@ class EnhancedRos extends EventEmitter {
497
2540
  console.error('Error parsing message:', error);
498
2541
  }
499
2542
  }
2543
+ handlePng(message) {
2544
+ if (message.op === 'png') {
2545
+ this.handleMessage(decompressPng(message.data));
2546
+ }
2547
+ else {
2548
+ this.handleMessage(message);
2549
+ }
2550
+ }
500
2551
  }
501
2552
 
502
2553
  class Topic extends EventEmitter {