@li0ard/wsq 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,879 @@
1
+ import { BitmapWithMetadata } from "./bitmap";
2
+ import { ANY_WSQ, BITMASK, buildWSQTrees, COM_WSQ, DHT_WSQ, DQT_WSQ, DTT_WSQ, EOI_WSQ, HeaderFrm, HuffCode, HuffmanTable, MAX_HUFFBITS, MAX_HUFFCOUNTS_WSQ, NCM_COLORSPACE, NCM_COMPRESSION, NCM_HEADER, NCM_LOSSY, NCM_PIX_DEPTH, NCM_PIX_HEIGHT, NCM_PIX_WIDTH, NCM_PPI, NUM_SUBBANDS, Ref, SOB_WSQ, SOF_WSQ, SOI_WSQ, Table_DQT, TBLS_N_SOB, TBLS_N_SOF, Token, W_TREELEN } from "./const";
3
+ import UPNG from "@pdf-lib/upng";
4
+ class WSQDecoder {
5
+ offset = 0;
6
+ decode(data) {
7
+ const token = new Token();
8
+ const dataInput = new DataView(data.buffer, data.byteOffset);
9
+ this.getCMarkerWSQ(dataInput, SOI_WSQ);
10
+ let marker = this.getCMarkerWSQ(dataInput, TBLS_N_SOF);
11
+ while (marker != SOF_WSQ) {
12
+ this.getCTableWSQ(dataInput, token, marker);
13
+ marker = this.getCMarkerWSQ(dataInput, TBLS_N_SOF);
14
+ }
15
+ let frmHeaderWSQ = this.getCFrameHeaderWSQ(dataInput);
16
+ let width = frmHeaderWSQ.width;
17
+ let height = frmHeaderWSQ.height;
18
+ buildWSQTrees(token, width, height);
19
+ let qdata = this.huffmanDecodeDataMem(dataInput, token, width * height);
20
+ let fdata = this.unquantize(token, qdata, width, height);
21
+ this.wsqReconstruct(token, fdata, width, height);
22
+ let cdata = this.convertImageToByte(fdata, width, height, frmHeaderWSQ.mShift, frmHeaderWSQ.rScale);
23
+ const nistcom = new Map();
24
+ const comments = [];
25
+ for (const comment of token.comments) {
26
+ try {
27
+ const parsed = this.stringToFet(comment);
28
+ for (const [k, v] of parsed) {
29
+ nistcom.set(k, v);
30
+ }
31
+ }
32
+ catch (e) {
33
+ comments.push(comment);
34
+ }
35
+ }
36
+ nistcom.delete(NCM_HEADER);
37
+ nistcom.set(NCM_PIX_WIDTH, width.toString());
38
+ nistcom.set(NCM_PIX_HEIGHT, height.toString());
39
+ nistcom.set(NCM_PIX_DEPTH, "8");
40
+ nistcom.set(NCM_LOSSY, "1");
41
+ nistcom.set(NCM_COLORSPACE, "GRAY");
42
+ nistcom.set(NCM_COMPRESSION, "WSQ");
43
+ let ppiOk = false;
44
+ try {
45
+ const ppi = parseInt(nistcom.get(NCM_PPI) ?? "");
46
+ if (ppi > 0)
47
+ ppiOk = true;
48
+ }
49
+ catch { }
50
+ if (!ppiOk)
51
+ nistcom.set(NCM_PPI, "-1");
52
+ return new BitmapWithMetadata(cdata, width, height, parseInt(nistcom.get(NCM_PPI)), 8, 1, nistcom, comments.join(", "));
53
+ }
54
+ stringToFet(comment) {
55
+ if (!comment.startsWith(NCM_HEADER))
56
+ throw new Error("Not a NISTCOM header");
57
+ const result = new Map();
58
+ const lines = comment.split(/\r?\n/);
59
+ for (const line of lines) {
60
+ const split = line.indexOf(" ");
61
+ if (split < 0) {
62
+ console.warn(`Illegal NISTCOM header: Missing separator on line '${line}'`);
63
+ continue;
64
+ }
65
+ const key = decodeURIComponent(line.substring(0, split));
66
+ const value = decodeURIComponent(line.substring(split + 1));
67
+ result.set(key, value);
68
+ }
69
+ return result;
70
+ }
71
+ convertImageToByte(img, width, height, mShift, rScale) {
72
+ let data = new Uint8Array(width * height);
73
+ let idx = 0;
74
+ for (let r = 0; r < height; r++) {
75
+ for (let c = 0; c < width; c++) {
76
+ let pixel = (img[idx] * rScale) + mShift;
77
+ pixel += 0.5;
78
+ if (pixel < 0.0) {
79
+ data[idx] = 0;
80
+ }
81
+ else if (pixel > 255.0) {
82
+ data[idx] = 255;
83
+ }
84
+ else {
85
+ data[idx] = pixel;
86
+ }
87
+ idx++;
88
+ }
89
+ }
90
+ return data;
91
+ }
92
+ wsqReconstruct(token, fdata, width, height) {
93
+ if (token.tableDTT.lodef != 1)
94
+ throw new Error("ERROR: wsq_reconstruct : Lopass filter coefficients not defined");
95
+ if (token.tableDTT.hidef != 1)
96
+ throw new Error("ERROR: wsq_reconstruct : Hipass filter coefficients not defined");
97
+ let numPix = width * height;
98
+ let fdataTemp = new Float32Array(numPix);
99
+ for (let node = W_TREELEN - 1; node >= 0; node--) {
100
+ let fdataBse = (token.wtree[node].y * width) + token.wtree[node].x;
101
+ this.joinLets(fdataTemp, fdata, 0, fdataBse, token.wtree[node].lenx, token.wtree[node].leny, 1, width, token.tableDTT.hifilt, token.tableDTT.hisz, token.tableDTT.lofilt, token.tableDTT.losz, token.wtree[node].invcl);
102
+ this.joinLets(fdata, fdataTemp, fdataBse, 0, token.wtree[node].leny, token.wtree[node].lenx, width, 1, token.tableDTT.hifilt, token.tableDTT.hisz, token.tableDTT.lofilt, token.tableDTT.losz, token.wtree[node].invrw);
103
+ }
104
+ }
105
+ joinLets(newdata, olddata, newIndex, oldIndex, len1, len2, pitch, stride, hi, hsz, lo, lsz, inv) {
106
+ const da_ev = len2 % 2;
107
+ const fi_ev = lsz % 2;
108
+ const pstr = stride;
109
+ const nstr = -pstr;
110
+ let llen, hlen;
111
+ if (da_ev !== 0) {
112
+ llen = Math.floor((len2 + 1) / 2);
113
+ hlen = llen - 1;
114
+ }
115
+ else {
116
+ llen = Math.floor(len2 / 2);
117
+ hlen = llen;
118
+ }
119
+ let asym, ssfac, ofhre;
120
+ let loc, hoc;
121
+ let lotap, hotap;
122
+ let olle, olre, ohle, ohre;
123
+ if (fi_ev !== 0) {
124
+ asym = 0;
125
+ ssfac = 1.0;
126
+ ofhre = 0;
127
+ loc = Math.floor((lsz - 1) / 4);
128
+ hoc = Math.floor((hsz + 1) / 4) - 1;
129
+ lotap = ((lsz - 1) / 2) % 2;
130
+ hotap = ((hsz + 1) / 2) % 2;
131
+ if (da_ev !== 0) {
132
+ olle = 0;
133
+ olre = 0;
134
+ ohle = 1;
135
+ ohre = 1;
136
+ }
137
+ else {
138
+ olle = 0;
139
+ olre = 1;
140
+ ohle = 1;
141
+ ohre = 0;
142
+ }
143
+ }
144
+ else {
145
+ asym = 1;
146
+ ssfac = -1.0;
147
+ ofhre = 2;
148
+ loc = Math.floor(lsz / 4) - 1;
149
+ hoc = Math.floor(hsz / 4) - 1;
150
+ lotap = Math.floor(lsz / 2) % 2;
151
+ hotap = Math.floor(hsz / 2) % 2;
152
+ if (da_ev !== 0) {
153
+ olle = 1;
154
+ olre = 0;
155
+ ohle = 1;
156
+ ohre = 1;
157
+ }
158
+ else {
159
+ olle = 1;
160
+ olre = 1;
161
+ ohle = 1;
162
+ ohre = 1;
163
+ }
164
+ if (loc === -1) {
165
+ loc = 0;
166
+ olle = 0;
167
+ }
168
+ if (hoc === -1) {
169
+ hoc = 0;
170
+ ohle = 0;
171
+ }
172
+ for (let i = 0; i < hsz; i++)
173
+ hi[i] = hi[i] * -1.0;
174
+ }
175
+ let lp0, lp1;
176
+ let hp0, hp1;
177
+ let lopass, hipass;
178
+ let limg, himg;
179
+ let pix, cl_rw;
180
+ let i;
181
+ let lstap, lspx, lspxstr, lpx, lpxstr;
182
+ let lle, lle2, lre, lre2;
183
+ let hspx, hspxstr, hpx, hpxstr;
184
+ let hle, hle2, hre, hre2;
185
+ let hstap, fhre = 0, ofhre_local = ofhre;
186
+ let osfac = ssfac, sfac;
187
+ for (cl_rw = 0; cl_rw < len1; cl_rw++) {
188
+ limg = newIndex + cl_rw * pitch;
189
+ himg = limg;
190
+ newdata[himg] = 0.0;
191
+ newdata[himg + stride] = 0.0;
192
+ if (inv !== 0) {
193
+ hipass = oldIndex + cl_rw * pitch;
194
+ lopass = hipass + stride * hlen;
195
+ }
196
+ else {
197
+ lopass = oldIndex + cl_rw * pitch;
198
+ hipass = lopass + stride * llen;
199
+ }
200
+ lp0 = lopass;
201
+ lp1 = lp0 + (llen - 1) * stride;
202
+ lspx = lp0 + (loc * stride);
203
+ lspxstr = nstr;
204
+ lstap = lotap;
205
+ lle2 = olle;
206
+ lre2 = olre;
207
+ hp0 = hipass;
208
+ hp1 = hp0 + (hlen - 1) * stride;
209
+ hspx = hp0 + (hoc * stride);
210
+ hspxstr = nstr;
211
+ hstap = hotap;
212
+ hle2 = ohle;
213
+ hre2 = ohre;
214
+ osfac = ssfac;
215
+ for (pix = 0; pix < hlen; pix++) {
216
+ for (let tap = lstap; tap >= 0; tap--) {
217
+ lle = lle2;
218
+ lre = lre2;
219
+ lpx = lspx;
220
+ lpxstr = lspxstr;
221
+ let acc = olddata[lpx] * lo[tap];
222
+ for (i = tap + 2; i < lsz; i += 2) {
223
+ if (lpx === lp0) {
224
+ if (lle !== 0) {
225
+ lpxstr = 0;
226
+ lle = 0;
227
+ }
228
+ else {
229
+ lpxstr = pstr;
230
+ }
231
+ }
232
+ if (lpx === lp1) {
233
+ if (lre !== 0) {
234
+ lpxstr = 0;
235
+ lre = 0;
236
+ }
237
+ else {
238
+ lpxstr = nstr;
239
+ }
240
+ }
241
+ lpx += lpxstr;
242
+ acc += olddata[lpx] * lo[i];
243
+ }
244
+ newdata[limg] = acc;
245
+ limg += stride;
246
+ }
247
+ if (lspx === lp0) {
248
+ if (lle2 !== 0) {
249
+ lspxstr = 0;
250
+ lle2 = 0;
251
+ }
252
+ else {
253
+ lspxstr = pstr;
254
+ }
255
+ }
256
+ lspx += lspxstr;
257
+ lstap = 1;
258
+ for (let tap = hstap; tap >= 0; tap--) {
259
+ hle = hle2;
260
+ hre = hre2;
261
+ hpx = hspx;
262
+ hpxstr = hspxstr;
263
+ fhre = ofhre_local;
264
+ sfac = osfac;
265
+ let accH = 0.0;
266
+ for (i = tap; i < hsz; i += 2) {
267
+ if (hpx === hp0) {
268
+ if (hle !== 0) {
269
+ hpxstr = 0;
270
+ hle = 0;
271
+ }
272
+ else {
273
+ hpxstr = pstr;
274
+ sfac = 1.0;
275
+ }
276
+ }
277
+ if (hpx === hp1) {
278
+ if (hre !== 0) {
279
+ hpxstr = 0;
280
+ hre = 0;
281
+ if (asym !== 0 && da_ev !== 0) {
282
+ hre = 1;
283
+ fhre--;
284
+ sfac = fhre;
285
+ if (sfac === 0.0)
286
+ hre = 0;
287
+ }
288
+ }
289
+ else {
290
+ hpxstr = nstr;
291
+ if (asym !== 0)
292
+ sfac = -1.0;
293
+ }
294
+ }
295
+ accH += olddata[hpx] * hi[i] * sfac;
296
+ hpx += hpxstr;
297
+ }
298
+ newdata[himg] += accH;
299
+ himg += stride;
300
+ }
301
+ if (hspx === hp0) {
302
+ if (hle2 !== 0) {
303
+ hspxstr = 0;
304
+ hle2 = 0;
305
+ }
306
+ else {
307
+ hspxstr = pstr;
308
+ osfac = 1.0;
309
+ }
310
+ }
311
+ hspx += hspxstr;
312
+ hstap = 1;
313
+ }
314
+ if (da_ev !== 0) {
315
+ lstap = lotap !== 0 ? 1 : 0;
316
+ }
317
+ else {
318
+ lstap = lotap !== 0 ? 2 : 1;
319
+ }
320
+ for (let tap = 1; tap >= lstap; tap--) {
321
+ lle = lle2;
322
+ lre = lre2;
323
+ lpx = lspx;
324
+ lpxstr = lspxstr;
325
+ let acc = olddata[lpx] * lo[tap];
326
+ for (i = tap + 2; i < lsz; i += 2) {
327
+ if (lpx === lp0) {
328
+ if (lle !== 0) {
329
+ lpxstr = 0;
330
+ lle = 0;
331
+ }
332
+ else {
333
+ lpxstr = pstr;
334
+ }
335
+ }
336
+ if (lpx === lp1) {
337
+ if (lre !== 0) {
338
+ lpxstr = 0;
339
+ lre = 0;
340
+ }
341
+ else {
342
+ lpxstr = nstr;
343
+ }
344
+ }
345
+ lpx += lpxstr;
346
+ acc += olddata[lpx] * lo[i];
347
+ }
348
+ newdata[limg] = acc;
349
+ limg += stride;
350
+ }
351
+ if (da_ev !== 0) {
352
+ hstap = hotap !== 0 ? 1 : 0;
353
+ if (hsz === 2) {
354
+ hspx -= hspxstr;
355
+ fhre = 1;
356
+ }
357
+ }
358
+ else {
359
+ hstap = hotap !== 0 ? 2 : 1;
360
+ }
361
+ for (let tap = 1; tap >= hstap; tap--) {
362
+ hle = hle2;
363
+ hre = hre2;
364
+ hpx = hspx;
365
+ hpxstr = hspxstr;
366
+ sfac = osfac;
367
+ if (hsz !== 2)
368
+ fhre = ofhre_local;
369
+ let accHbase = 0.0;
370
+ for (i = tap; i < hsz; i += 2) {
371
+ if (hpx === hp0) {
372
+ if (hle !== 0) {
373
+ hpxstr = 0;
374
+ hle = 0;
375
+ }
376
+ else {
377
+ hpxstr = pstr;
378
+ sfac = 1.0;
379
+ }
380
+ }
381
+ if (hpx === hp1) {
382
+ if (hre !== 0) {
383
+ hpxstr = 0;
384
+ hre = 0;
385
+ if (asym !== 0 && da_ev !== 0) {
386
+ hre = 1;
387
+ fhre--;
388
+ sfac = fhre;
389
+ if (sfac === 0.0)
390
+ hre = 0;
391
+ }
392
+ }
393
+ else {
394
+ hpxstr = nstr;
395
+ if (asym !== 0)
396
+ sfac = -1.0;
397
+ }
398
+ }
399
+ accHbase += olddata[hpx] * hi[i] * sfac;
400
+ hpx += hpxstr;
401
+ }
402
+ newdata[himg] += accHbase;
403
+ himg += stride;
404
+ }
405
+ }
406
+ if (fi_ev === 0) {
407
+ for (i = 0; i < hsz; i++)
408
+ hi[i] = hi[i] * -1.0;
409
+ }
410
+ }
411
+ unquantize(token, sip, width, height) {
412
+ let fip = new Float32Array(width * height);
413
+ if (token.tableDQT.dqtDef != 1)
414
+ throw new Error("ERROR: unquantize : quantization table parameters not defined!");
415
+ let binCenter = token.tableDQT.binCenter;
416
+ let sptr = 0;
417
+ for (let cnt = 0; cnt < NUM_SUBBANDS; cnt++) {
418
+ if (token.tableDQT.qBin[cnt] == 0.0)
419
+ continue;
420
+ let fptr = (token.qtree[cnt].y * width) + token.qtree[cnt].x;
421
+ for (let row = 0; row < token.qtree[cnt].leny; row++, fptr += width - token.qtree[cnt].lenx) {
422
+ for (let col = 0; col < token.qtree[cnt].lenx; col++) {
423
+ if (sip[sptr] == 0)
424
+ fip[fptr] = 0.0;
425
+ else if (sip[sptr] > 0)
426
+ fip[fptr] = (token.tableDQT.qBin[cnt] * (sip[sptr] - binCenter)) + (token.tableDQT.zBin[cnt] / 2.0);
427
+ else if (sip[sptr] < 0)
428
+ fip[fptr] = (token.tableDQT.qBin[cnt] * (sip[sptr] + binCenter)) - (token.tableDQT.zBin[cnt] / 2.0);
429
+ else
430
+ throw new Error("ERROR : unquantize : invalid quantization pixel value");
431
+ fptr++;
432
+ sptr++;
433
+ }
434
+ }
435
+ }
436
+ return fip;
437
+ }
438
+ huffmanDecodeDataMem(dataInput, token, size) {
439
+ let qdata = new Array(size);
440
+ let maxcode = new Array(MAX_HUFFBITS + 1);
441
+ let mincode = new Array(MAX_HUFFBITS + 1);
442
+ let valptr = new Array(MAX_HUFFBITS + 1);
443
+ let marker = new Ref(this.getCMarkerWSQ(dataInput, TBLS_N_SOB));
444
+ let bitCount = new Ref(0);
445
+ let nextByte = new Ref(0);
446
+ let hufftableId = 0;
447
+ let ip = 0;
448
+ let isPrematureEOF = false;
449
+ while (!isPrematureEOF && (marker.value != EOI_WSQ)) {
450
+ if (marker.value != 0) {
451
+ while (marker.value != SOB_WSQ) {
452
+ this.getCTableWSQ(dataInput, token, marker.value);
453
+ marker.value = this.getCMarkerWSQ(dataInput, TBLS_N_SOB);
454
+ }
455
+ hufftableId = this.getCBlockHeader(dataInput);
456
+ if (token.tableDHT[hufftableId].tabdef != 1)
457
+ throw new Error("ERROR : huffmanDecodeDataMem : huffman table undefined.");
458
+ let hufftable = this.buildHuffsizes(token.tableDHT[hufftableId].huffbits, MAX_HUFFCOUNTS_WSQ);
459
+ this.buildHuffcodes(hufftable);
460
+ this.genDecodeTable(hufftable, maxcode, mincode, valptr, token.tableDHT[hufftableId].huffbits);
461
+ bitCount.value = 0;
462
+ marker.value = 0;
463
+ }
464
+ try {
465
+ let nodeptr = this.decodeDataMem(dataInput, mincode, maxcode, valptr, token.tableDHT[hufftableId].huffvalues, bitCount, marker, nextByte);
466
+ if (nodeptr == -1)
467
+ continue;
468
+ if (nodeptr > 0 && nodeptr <= 100) {
469
+ for (let n = 0; n < nodeptr; n++)
470
+ qdata[ip++] = 0;
471
+ }
472
+ else if (nodeptr > 106 && nodeptr < 0xff)
473
+ qdata[ip++] = nodeptr - 180;
474
+ else if (nodeptr == 101)
475
+ qdata[ip++] = this.getCNextbitsWSQ(dataInput, marker, bitCount, 8, nextByte);
476
+ else if (nodeptr == 102)
477
+ qdata[ip++] = -this.getCNextbitsWSQ(dataInput, marker, bitCount, 8, nextByte);
478
+ else if (nodeptr == 103)
479
+ qdata[ip++] = this.getCNextbitsWSQ(dataInput, marker, bitCount, 16, nextByte);
480
+ else if (nodeptr == 104)
481
+ qdata[ip++] = -this.getCNextbitsWSQ(dataInput, marker, bitCount, 16, nextByte);
482
+ else if (nodeptr == 105) {
483
+ let n = this.getCNextbitsWSQ(dataInput, marker, bitCount, 8, nextByte);
484
+ while (n-- > 0)
485
+ qdata[ip++] = 0;
486
+ }
487
+ else if (nodeptr == 106) {
488
+ let n = this.getCNextbitsWSQ(dataInput, marker, bitCount, 16, nextByte);
489
+ while (n-- > 0)
490
+ qdata[ip++] = 0;
491
+ }
492
+ else
493
+ throw new Error("ERROR: huffman_decode_data_mem : Invalid code (" + nodeptr + ")");
494
+ }
495
+ catch (e) {
496
+ console.warn("DEBUG: MO - ignoring EOF in WSQDecoder");
497
+ isPrematureEOF = true;
498
+ }
499
+ }
500
+ return qdata;
501
+ }
502
+ decodeDataMem(dataInput, mincode, maxcode, valptr, huffvalues, bitCount, marker, nextByte) {
503
+ let code = this.getCNextbitsWSQ(dataInput, marker, bitCount, 1, nextByte);
504
+ if (marker.value != 0)
505
+ return -1;
506
+ let inx;
507
+ for (inx = 1; code > maxcode[inx]; inx++) {
508
+ let tbits = this.getCNextbitsWSQ(dataInput, marker, bitCount, 1, nextByte); /* becomes a huffman code word (one bit at a time)*/
509
+ code = ((code << 1) + tbits);
510
+ if (marker.value != 0)
511
+ return -1;
512
+ }
513
+ let inx2 = valptr[inx] + code - mincode[inx];
514
+ return huffvalues[inx2];
515
+ }
516
+ getCNextbitsWSQ(dataInput, marker, bitCount, bitsReq, nextByte) {
517
+ if (bitCount.value == 0) {
518
+ nextByte.value = dataInput.getUint8(this.offset);
519
+ this.offset += 1;
520
+ bitCount.value = 8;
521
+ if (nextByte.value == 0xFF) {
522
+ let code2 = dataInput.getUint8(this.offset);
523
+ this.offset += 1;
524
+ if (code2 != 0x00 && bitsReq == 1) {
525
+ marker.value = (nextByte.value << 8) | code2;
526
+ return 1;
527
+ }
528
+ if (code2 != 0x00)
529
+ throw new Error("ERROR: getCNextbitsWSQ : No stuffed zeros.");
530
+ }
531
+ }
532
+ let bits, tbits;
533
+ let bitsNeeded;
534
+ if (bitsReq <= bitCount.value) {
535
+ bits = (nextByte.value >> (bitCount.value - bitsReq)) & (BITMASK[bitsReq]);
536
+ bitCount.value -= bitsReq;
537
+ nextByte.value &= BITMASK[bitCount.value];
538
+ }
539
+ else {
540
+ bitsNeeded = bitsReq - bitCount.value;
541
+ bits = nextByte.value << bitsNeeded;
542
+ bitCount.value = 0;
543
+ tbits = this.getCNextbitsWSQ(dataInput, marker, bitCount, bitsNeeded, nextByte);
544
+ bits |= tbits;
545
+ }
546
+ return bits;
547
+ }
548
+ genDecodeTable(huffcodeTable, maxcode, mincode, valptr, huffbits) {
549
+ for (let i = 0; i <= MAX_HUFFBITS; i++) {
550
+ maxcode[i] = 0;
551
+ mincode[i] = 0;
552
+ valptr[i] = 0;
553
+ }
554
+ let i2 = 0;
555
+ for (let i = 1; i <= MAX_HUFFBITS; i++) {
556
+ if (huffbits[i - 1] == 0) {
557
+ maxcode[i] = -1;
558
+ continue;
559
+ }
560
+ valptr[i] = i2;
561
+ mincode[i] = huffcodeTable[i2].code;
562
+ i2 = i2 + huffbits[i - 1] - 1;
563
+ maxcode[i] = huffcodeTable[i2].code;
564
+ i2++;
565
+ }
566
+ }
567
+ buildHuffsizes(huffbits, maxHuffcounts) {
568
+ let numberOfCodes = 1;
569
+ let huffcodeTable = new Array(maxHuffcounts + 1);
570
+ let tempSize = 0;
571
+ for (let codeSize = 1; codeSize <= MAX_HUFFBITS; codeSize++) {
572
+ while (numberOfCodes <= huffbits[codeSize - 1]) {
573
+ huffcodeTable[tempSize] = new HuffCode();
574
+ huffcodeTable[tempSize].size = codeSize;
575
+ tempSize++;
576
+ numberOfCodes++;
577
+ }
578
+ numberOfCodes = 1;
579
+ }
580
+ huffcodeTable[tempSize] = new HuffCode();
581
+ huffcodeTable[tempSize].size = 0;
582
+ return huffcodeTable;
583
+ }
584
+ buildHuffcodes(huffcodeTable) {
585
+ let tempCode = 0;
586
+ let pointer = 0;
587
+ let tempSize = huffcodeTable[0].size;
588
+ if (huffcodeTable[pointer].size == 0)
589
+ return;
590
+ do {
591
+ do {
592
+ huffcodeTable[pointer].code = tempCode;
593
+ tempCode++;
594
+ pointer++;
595
+ } while (huffcodeTable[pointer].size == tempSize);
596
+ if (huffcodeTable[pointer].size == 0)
597
+ return;
598
+ do {
599
+ tempCode <<= 1;
600
+ tempSize++;
601
+ } while (huffcodeTable[pointer].size != tempSize);
602
+ } while (huffcodeTable[pointer].size == tempSize);
603
+ }
604
+ getCBlockHeader(dataInput) {
605
+ dataInput.getUint16(this.offset);
606
+ this.offset += 2;
607
+ let value = dataInput.getUint8(this.offset);
608
+ this.offset += 1;
609
+ return value;
610
+ }
611
+ getCFrameHeaderWSQ(dataInput) {
612
+ let headerFrm = new HeaderFrm();
613
+ dataInput.getUint16(this.offset);
614
+ this.offset += 2;
615
+ headerFrm.black = dataInput.getUint8(this.offset);
616
+ this.offset += 1;
617
+ headerFrm.white = dataInput.getUint8(this.offset);
618
+ this.offset += 1;
619
+ headerFrm.height = dataInput.getUint16(this.offset);
620
+ this.offset += 2;
621
+ headerFrm.width = dataInput.getUint16(this.offset);
622
+ this.offset += 2;
623
+ let scale = dataInput.getUint8(this.offset);
624
+ this.offset += 1;
625
+ let shrtDat = dataInput.getUint16(this.offset);
626
+ this.offset += 2;
627
+ headerFrm.mShift = shrtDat;
628
+ while (scale > 0) {
629
+ headerFrm.mShift /= 10.0;
630
+ scale--;
631
+ }
632
+ scale = dataInput.getUint8(this.offset);
633
+ this.offset += 1;
634
+ shrtDat = dataInput.getUint16(this.offset);
635
+ this.offset += 2;
636
+ headerFrm.rScale = shrtDat;
637
+ while (scale > 0) {
638
+ headerFrm.rScale /= 10.0;
639
+ scale--;
640
+ }
641
+ headerFrm.wsqEncoder = dataInput.getUint8(this.offset);
642
+ this.offset += 1;
643
+ headerFrm.software = dataInput.getUint16(this.offset);
644
+ this.offset += 2;
645
+ return headerFrm;
646
+ }
647
+ getCMarkerWSQ(dataInput, type) {
648
+ const marker = dataInput.getUint16(this.offset);
649
+ this.offset += 2;
650
+ switch (type) {
651
+ case SOI_WSQ:
652
+ if (marker != SOI_WSQ)
653
+ throw new Error("ERROR : getCMarkerWSQ : No SOI marker : " + marker);
654
+ return marker;
655
+ case TBLS_N_SOF:
656
+ if (marker != DTT_WSQ && marker != DQT_WSQ && marker != DHT_WSQ && marker != SOF_WSQ && marker != COM_WSQ)
657
+ throw new Error("ERROR : getc_marker_wsq : No SOF, Table, or comment markers : " + marker);
658
+ return marker;
659
+ case TBLS_N_SOB:
660
+ if (marker != DTT_WSQ && marker != DQT_WSQ && marker != DHT_WSQ && marker != SOB_WSQ && marker != COM_WSQ)
661
+ throw new Error("ERROR : getc_marker_wsq : No SOB, Table, or comment markers : " + marker);
662
+ return marker;
663
+ case ANY_WSQ:
664
+ if ((marker & 0xff00) != 0xff00)
665
+ throw new Error("ERROR : getc_marker_wsq : no marker found : " + marker);
666
+ if ((marker < SOI_WSQ) || (marker > COM_WSQ))
667
+ throw new Error("ERROR : getc_marker_wsq : not a valid marker : " + marker);
668
+ return marker;
669
+ default:
670
+ throw new Error("ERROR : getc_marker_wsq : Invalid marker : " + marker);
671
+ }
672
+ }
673
+ getCTableWSQ(dataInput, token, marker) {
674
+ switch (marker) {
675
+ case DTT_WSQ:
676
+ this.getCTransformTable(dataInput, token);
677
+ return;
678
+ case DQT_WSQ:
679
+ this.getCQuantizationTable(dataInput, token);
680
+ return;
681
+ case DHT_WSQ:
682
+ this.getCHuffmanTableWSQ(dataInput, token);
683
+ return;
684
+ case COM_WSQ:
685
+ token.comments.push(this.getCComment(dataInput));
686
+ return;
687
+ default:
688
+ throw new Error("ERROR: getCTableWSQ : Invalid table defined : " + marker.toString(16));
689
+ }
690
+ }
691
+ getCHuffmanTableWSQ(dataInput, token) {
692
+ let firstHuffmanTable = this.getCHuffmanTable(dataInput, token, MAX_HUFFCOUNTS_WSQ, 0, true);
693
+ let tableId = firstHuffmanTable.tableId;
694
+ token.tableDHT[tableId].huffbits = firstHuffmanTable.huffbits.slice();
695
+ token.tableDHT[tableId].huffvalues = firstHuffmanTable.huffvalues.slice();
696
+ token.tableDHT[tableId].tabdef = 1;
697
+ let bytesLeft = firstHuffmanTable.bytesLeft;
698
+ while (bytesLeft != 0) {
699
+ let huffmantable = this.getCHuffmanTable(dataInput, token, MAX_HUFFCOUNTS_WSQ, bytesLeft, false);
700
+ tableId = huffmantable.tableId;
701
+ if (token.tableDHT[tableId].tabdef != 0)
702
+ throw new Error("ERROR : getCHuffmanTableWSQ : huffman table already defined.");
703
+ token.tableDHT[tableId].huffbits = huffmantable.huffbits.slice();
704
+ token.tableDHT[tableId].huffvalues = huffmantable.huffvalues.slice();
705
+ token.tableDHT[tableId].tabdef = 1;
706
+ bytesLeft = huffmantable.bytesLeft;
707
+ }
708
+ }
709
+ getCHuffmanTable(dataInput, token, maxHuffcounts, bytesLeft, readTableLen) {
710
+ let huffmanTable = new HuffmanTable();
711
+ if (readTableLen) {
712
+ huffmanTable.tableLen = dataInput.getUint16(this.offset);
713
+ this.offset += 2;
714
+ huffmanTable.bytesLeft = huffmanTable.tableLen - 2;
715
+ bytesLeft = huffmanTable.bytesLeft;
716
+ }
717
+ else
718
+ huffmanTable.bytesLeft = bytesLeft;
719
+ if (bytesLeft <= 0)
720
+ throw new Error("ERROR : getCHuffmanTable : no huffman table bytes remaining");
721
+ huffmanTable.tableId = dataInput.getUint8(this.offset);
722
+ this.offset += 1;
723
+ huffmanTable.bytesLeft--;
724
+ huffmanTable.huffbits = new Array(MAX_HUFFBITS);
725
+ let numHufvals = 0;
726
+ for (let i = 0; i < MAX_HUFFBITS; i++) {
727
+ huffmanTable.huffbits[i] = dataInput.getUint8(this.offset);
728
+ this.offset += 1;
729
+ numHufvals += huffmanTable.huffbits[i];
730
+ }
731
+ huffmanTable.bytesLeft -= MAX_HUFFBITS;
732
+ if (numHufvals > maxHuffcounts + 1)
733
+ throw new Error("ERROR : getCHuffmanTable : numHufvals is larger than MAX_HUFFCOUNTS");
734
+ huffmanTable.huffvalues = new Array(maxHuffcounts + 1);
735
+ for (let i = 0; i < numHufvals; i++) {
736
+ huffmanTable.huffvalues[i] = dataInput.getUint8(this.offset);
737
+ this.offset += 1;
738
+ }
739
+ huffmanTable.bytesLeft -= numHufvals;
740
+ return huffmanTable;
741
+ }
742
+ getCQuantizationTable(dataInput, token) {
743
+ dataInput.getUint16(this.offset);
744
+ this.offset += 2;
745
+ let scale = dataInput.getUint8(this.offset);
746
+ this.offset += 1;
747
+ let shrtDat = dataInput.getUint16(this.offset);
748
+ this.offset += 2;
749
+ token.tableDQT.binCenter = shrtDat;
750
+ while (scale > 0) {
751
+ token.tableDQT.binCenter /= 10.0;
752
+ scale--;
753
+ }
754
+ for (let cnt = 0; cnt < Table_DQT.MAX_SUBBANDS; cnt++) {
755
+ scale = dataInput.getUint8(this.offset);
756
+ this.offset += 1;
757
+ shrtDat = dataInput.getUint16(this.offset);
758
+ this.offset += 2;
759
+ token.tableDQT.qBin[cnt] = shrtDat;
760
+ while (scale > 0) {
761
+ token.tableDQT.qBin[cnt] /= 10.0;
762
+ scale--;
763
+ }
764
+ scale = dataInput.getUint8(this.offset);
765
+ this.offset += 1;
766
+ shrtDat = dataInput.getUint16(this.offset);
767
+ this.offset += 2;
768
+ token.tableDQT.zBin[cnt] = shrtDat;
769
+ while (scale > 0) {
770
+ token.tableDQT.zBin[cnt] /= 10.0;
771
+ scale--;
772
+ }
773
+ }
774
+ token.tableDQT.dqtDef = 1;
775
+ }
776
+ getCComment(dataInput) {
777
+ const size = dataInput.getUint16(this.offset, false) - 2;
778
+ this.offset += 2;
779
+ const bytes = new Uint8Array(dataInput.buffer, dataInput.byteOffset + this.offset, size);
780
+ this.offset += size;
781
+ return new TextDecoder("utf-8").decode(bytes);
782
+ }
783
+ getCTransformTable(dataInput, token) {
784
+ dataInput.getUint16(this.offset, false);
785
+ this.offset += 2;
786
+ token.tableDTT.hisz = dataInput.getUint8(this.offset);
787
+ this.offset += 1;
788
+ token.tableDTT.losz = dataInput.getUint8(this.offset);
789
+ this.offset += 1;
790
+ token.tableDTT.hifilt = new Float32Array(token.tableDTT.hisz);
791
+ token.tableDTT.lofilt = new Float32Array(token.tableDTT.losz);
792
+ let aSize;
793
+ if (token.tableDTT.hisz % 2 != 0)
794
+ aSize = (token.tableDTT.hisz + 1) / 2;
795
+ else
796
+ aSize = token.tableDTT.hisz / 2;
797
+ const aLofilt = new Float32Array(aSize);
798
+ aSize--;
799
+ for (let cnt = 0; cnt <= aSize; cnt++) {
800
+ let sign = dataInput.getUint8(this.offset);
801
+ this.offset += 1;
802
+ let scale = dataInput.getUint8(this.offset);
803
+ this.offset += 1;
804
+ let shrtDat = (dataInput.getInt32(this.offset) & 0xFFFFFFFF) >>> 0;
805
+ this.offset += 4;
806
+ aLofilt[cnt] = shrtDat;
807
+ while (scale > 0) {
808
+ aLofilt[cnt] /= 10.0;
809
+ scale--;
810
+ }
811
+ if (sign != 0)
812
+ aLofilt[cnt] *= -1.0;
813
+ if (token.tableDTT.hisz % 2 != 0) {
814
+ token.tableDTT.hifilt[cnt + aSize] = this.intSign(cnt) * aLofilt[cnt];
815
+ if (cnt > 0)
816
+ token.tableDTT.hifilt[aSize - cnt] = token.tableDTT.hifilt[cnt + aSize];
817
+ }
818
+ else {
819
+ token.tableDTT.hifilt[cnt + aSize + 1] = this.intSign(cnt) * aLofilt[cnt];
820
+ token.tableDTT.hifilt[aSize - cnt] = -1 * token.tableDTT.hifilt[cnt + aSize + 1];
821
+ }
822
+ }
823
+ if (token.tableDTT.losz % 2 != 0)
824
+ aSize = (token.tableDTT.losz + 1) / 2;
825
+ else
826
+ aSize = token.tableDTT.losz / 2;
827
+ const aHifilt = new Float32Array(aSize);
828
+ aSize--;
829
+ for (let cnt = 0; cnt <= aSize; cnt++) {
830
+ let sign = dataInput.getUint8(this.offset);
831
+ this.offset += 1;
832
+ let scale = dataInput.getUint8(this.offset);
833
+ this.offset += 1;
834
+ let shrtDat = (dataInput.getInt32(this.offset) & 0xFFFFFFFF) >>> 0;
835
+ this.offset += 4;
836
+ aHifilt[cnt] = shrtDat;
837
+ while (scale > 0) {
838
+ aHifilt[cnt] /= 10.0;
839
+ scale--;
840
+ }
841
+ if (sign != 0)
842
+ aHifilt[cnt] *= -1.0;
843
+ if (token.tableDTT.losz % 2 != 0) {
844
+ token.tableDTT.lofilt[cnt + aSize] = this.intSign(cnt) * aHifilt[cnt];
845
+ if (cnt > 0)
846
+ token.tableDTT.lofilt[aSize - cnt] = token.tableDTT.lofilt[cnt + aSize];
847
+ }
848
+ else {
849
+ token.tableDTT.lofilt[cnt + aSize + 1] = this.intSign(cnt + 1) * aHifilt[cnt];
850
+ token.tableDTT.lofilt[aSize - cnt] = token.tableDTT.lofilt[cnt + aSize + 1];
851
+ }
852
+ }
853
+ token.tableDTT.lodef = 1;
854
+ token.tableDTT.hidef = 1;
855
+ }
856
+ intSign(power) {
857
+ let num = -1;
858
+ if (power == 0)
859
+ return 1;
860
+ for (let cnt = 1; cnt < power; cnt++)
861
+ num *= -1;
862
+ return num;
863
+ }
864
+ }
865
+ /**
866
+ * Convert Wavelet Scalar Quantization (`.wsq`) to PNG
867
+ * @param data WSQ buffer
868
+ */
869
+ export const wsqToPNG = (data) => {
870
+ const grayscale = new WSQDecoder().decode(data);
871
+ const rgba = new Uint8Array(grayscale.width * grayscale.height * 4);
872
+ for (let i = 0; i < grayscale.width * grayscale.height; i++) {
873
+ rgba[i * 4 + 0] = grayscale.pixels[i];
874
+ rgba[i * 4 + 1] = grayscale.pixels[i];
875
+ rgba[i * 4 + 2] = grayscale.pixels[i];
876
+ rgba[i * 4 + 3] = 255;
877
+ }
878
+ return new Uint8Array(UPNG.encode([rgba.buffer], grayscale.width, grayscale.height, 0));
879
+ };