From 12b3e69aba006960c6fd625592d5184b6a4da8c5 Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Tue, 8 Sep 2026 12:47:26 -0400 Subject: [PATCH 1/2] fix(dicom-codec): decode the last pixel of byte-aligned JPEG Lossless scans Published jpeg-lossless-decoder-js 2.1.2 drops the final sample of any frame whose last Huffman code ends exactly on a byte boundary: its end-of-scan guards read the 0xFF introducing EOI as entropy coded data and abandon the scan one sample early. T.81 B.1.1.2 pads only an incomplete final byte, so a scan that tiles its last byte exactly is legal and common - DCMTK emits one whenever a frame ends in a run of a single value, which CT slices routinely do. Transfer syntaxes 1.2.840.10008.1.2.4.57 and .70 decoded those frames with a wrong last pixel. The fix is upstream in cornerstonejs/JPEGLosslessDecoderJS (a fork of rii-mango/JPEGLosslessDecoderJS) at 03bb80c0, which replaces the three `index < markerIndex` guards with a named `readPastEntropyData` putting the boundary at `index < 8`, and drops the `isLastPixel` special case that was papering over the same off-by-one at one of the three sites. That fork is not published to npm, so its CJS build is vendored under src/vendor/jpeg-lossless-decoder-js (verbatim build output plus the MIT LICENSE and a README recording the commit and how to re-vendor) and the jpeg-lossless-decoder-js dependency is dropped. When a release carries the fix, delete the directory and go back to a normal dependency. Both JPEG Lossless fixtures now compare byte-for-byte against CT-512x512.raw, replacing the it.fails pair that pinned the broken last pixel. tools/fixture-verification agrees on both (12/12 byte-exact), as does the vendored build's own suite (54 tests, including the regression fixture). Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 2 +- packages/dicom-codec/package.json | 3 +- .../dicom-codec/src/codecs/jpegLossless.js | 6 +- .../vendor/jpeg-lossless-decoder-js/LICENSE | 19 + .../vendor/jpeg-lossless-decoder-js/README.md | 48 + .../jpeg-lossless-decoder-js/lossless.cjs | 1216 +++++++++++++++++ .../jpeg-lossless-decoder-js/lossless.cjs.map | 1 + packages/dicom-codec/test/integration.test.js | 36 +- pnpm-lock.yaml | 19 - tools/fixture-verification/README.md | 26 +- 10 files changed, 1319 insertions(+), 57 deletions(-) create mode 100644 packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/LICENSE create mode 100644 packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/README.md create mode 100644 packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs create mode 100644 packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs.map diff --git a/README.md b/README.md index 66b55c45..56fc8d7c 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Transfer Syntax is the language used in DICOM to describe the DICOM file format - \*\*\* - Unlike all other DICOM transfer syntaxes, the deflate transfer syntaxes compress the whole of the DICOM data (tags, lengths, VR etc.) rather than just the pixel data - this is done using the standard “deflate” mechanism as used in gzip etc.) It is therefore most suitable for non-pixel objects such as structured reports, presentation states etc. - 5: [JS Decoder](https://github.com/cornerstonejs/cornerstoneWADOImageLoader/blob/4bfa04759412d58647cc5d6bd0204aa37e4542e3/src/shared/decoders/decodeRLE.js) -- 57 & 70: [JS Decoder](https://github.com/cornerstonejs/cornerstoneWADOImageLoader/blob/4bfa04759412d58647cc5d6bd0204aa37e4542e3/codecs/jpegLossless.js) +- 57 & 70: [JS Decoder](https://github.com/cornerstonejs/JPEGLosslessDecoderJS) — built from the `main` branch of that fork and vendored into `packages/dicom-codec/src/vendor/`, since the published `jpeg-lossless-decoder-js` predates its end-of-scan fix - 1.2 & 2.1 & 99: [JS Decoder](https://github.com/cornerstonejs/cornerstoneWADOImageLoader/blob/4bfa04759412d58647cc5d6bd0204aa37e4542e3/src/shared/decoders/decodeLittleEndian.js) - 2.2: [JS Decoder](https://github.com/cornerstonejs/cornerstoneWADOImageLoader/blob/4bfa04759412d58647cc5d6bd0204aa37e4542e3/src/shared/decoders/decodeBigEndian.js) diff --git a/packages/dicom-codec/package.json b/packages/dicom-codec/package.json index 0ad5f702..1a7c4646 100644 --- a/packages/dicom-codec/package.json +++ b/packages/dicom-codec/package.json @@ -39,7 +39,6 @@ "@cornerstonejs/codec-little-endian": "^0.0.9", "@cornerstonejs/codec-openjpeg": "^1.3.5", "@cornerstonejs/codec-openjph": "^2.4.11", - "browser-or-node": "^2.0.0", - "jpeg-lossless-decoder-js": "^2.0.4" + "browser-or-node": "^2.0.0" } } diff --git a/packages/dicom-codec/src/codecs/jpegLossless.js b/packages/dicom-codec/src/codecs/jpegLossless.js index 31a14604..304d83d2 100644 --- a/packages/dicom-codec/src/codecs/jpegLossless.js +++ b/packages/dicom-codec/src/codecs/jpegLossless.js @@ -1,4 +1,8 @@ -const codecModule = require("jpeg-lossless-decoder-js"); +// Vendored build of the cornerstonejs fork of jpeg-lossless-decoder-js rather +// than the published 2.1.2: that release loses the last sample of any frame +// whose final Huffman code ends on a byte boundary. See +// ../vendor/jpeg-lossless-decoder-js/README.md. +const codecModule = require("../vendor/jpeg-lossless-decoder-js/lossless.cjs"); const codecFactory = require("./codecFactory"); /** diff --git a/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/LICENSE b/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/LICENSE new file mode 100644 index 00000000..e24e431d --- /dev/null +++ b/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2015 RII-UTHSCSA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/README.md b/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/README.md new file mode 100644 index 00000000..6af694c3 --- /dev/null +++ b/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/README.md @@ -0,0 +1,48 @@ +# Vendored: jpeg-lossless-decoder-js + +`lossless.cjs` and `lossless.cjs.map` are **build output, not source**. Do not +edit them here — fix the upstream repository and re-vendor. + +| | | +| --- | --- | +| Upstream | (fork of `rii-mango/JPEGLosslessDecoderJS`) | +| Branch | `main` — upstream `master` plus the byte-aligned-end-of-scan fix | +| Commit | `03bb80c073e34369893e468b615dac9dcb0dcee9` | +| Version | 2.1.2 + the fix | +| License | MIT, retained alongside as `LICENSE` | + +## Why this is vendored rather than a dependency + +dicom-codec used the published `jpeg-lossless-decoder-js@2.1.2` for transfer +syntaxes 1.2.840.10008.1.2.4.57 and .70. That release drops the final sample of +any frame whose last Huffman code ends exactly on a byte boundary — the +`markerIndex` guards read the 0xFF that introduces EOI as if it were entropy +coded data and abandon the scan one sample early. DCMTK produces exactly that +layout whenever a frame ends in a run of one value, so real CT images decoded +with a wrong last pixel. + +The fix lives in the fork above. The fork is not published to npm, so the built +CJS bundle is committed here and required directly by +[`../../codecs/jpegLossless.js`](../../codecs/jpegLossless.js). When a release +of `jpeg-lossless-decoder-js` carries the fix, delete this directory and go back +to a normal dependency. + +The bundle is a single self-contained file with no runtime dependencies, and its +source map embeds the TypeScript sources, so debugging still lands in +`decoder.ts` rather than in minified output. + +## Re-vendoring + +```bash +git clone -b main https://github.com/cornerstonejs/JPEGLosslessDecoderJS.git +cd JPEGLosslessDecoderJS +npm install +npm test # 54 tests, includes the byte-aligned-end regression +npm run build +cp release/cjs/lossless.cjs release/cjs/lossless.cjs.map LICENSE \ + /packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/ +``` + +Copy the files verbatim and record the new commit in the table above — an +unmodified copy is what makes "is this really what that commit builds?" +answerable by rebuilding. diff --git a/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs b/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs new file mode 100644 index 00000000..4a8d909b --- /dev/null +++ b/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs @@ -0,0 +1,1216 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/main.ts +var main_exports = {}; +__export(main_exports, { + ComponentSpec: () => ComponentSpec, + DataStream: () => DataStream, + Decoder: () => Decoder, + FrameHeader: () => FrameHeader, + HuffmanTable: () => HuffmanTable, + QuantizationTable: () => QuantizationTable, + ScanComponent: () => ScanComponent, + ScanHeader: () => ScanHeader, + Utils: () => utils_exports +}); +module.exports = __toCommonJS(main_exports); + +// src/component-spec.ts +var ComponentSpec = { + hSamp: 0, + quantTableSel: 0, + vSamp: 0 +}; + +// src/data-stream.ts +var DataStream = class { + buffer; + index; + constructor(data, offset, length) { + this.buffer = new Uint8Array(data, offset, length); + this.index = 0; + } + get16() { + const value = (this.buffer[this.index] << 8) + this.buffer[this.index + 1]; + this.index += 2; + return value; + } + get8() { + const value = this.buffer[this.index]; + this.index += 1; + return value; + } +}; + +// src/frame-header.ts +var FrameHeader = class { + dimX = 0; + dimY = 0; + numComp = 0; + precision = 0; + components = []; + read(data) { + let count = 0; + let temp; + const length = data.get16(); + count += 2; + this.precision = data.get8(); + count += 1; + this.dimY = data.get16(); + count += 2; + this.dimX = data.get16(); + count += 2; + this.numComp = data.get8(); + count += 1; + for (let i = 1; i <= this.numComp; i += 1) { + if (count > length) { + throw new Error("ERROR: frame format error"); + } + const c = data.get8(); + count += 1; + if (count >= length) { + throw new Error("ERROR: frame format error [c>=Lf]"); + } + temp = data.get8(); + count += 1; + if (!this.components[c]) { + this.components[c] = { ...ComponentSpec }; + } + this.components[c].hSamp = temp >> 4; + this.components[c].vSamp = temp & 15; + this.components[c].quantTableSel = data.get8(); + count += 1; + } + if (count !== length) { + throw new Error("ERROR: frame format error [Lf!=count]"); + } + return 1; + } +}; + +// src/utils.ts +var utils_exports = {}; +__export(utils_exports, { + crc32: () => crc32, + crcTable: () => crcTable, + createArray: () => createArray, + makeCRCTable: () => makeCRCTable +}); +var createArray = (...dimensions) => { + if (dimensions.length > 1) { + const dim = dimensions[0]; + const rest = dimensions.slice(1); + const newArray = []; + for (let i = 0; i < dim; i++) { + newArray[i] = createArray(...rest); + } + return newArray; + } else { + return Array(dimensions[0]).fill(void 0); + } +}; +var makeCRCTable = function() { + let c; + const crcTable2 = []; + for (let n = 0; n < 256; n++) { + c = n; + for (let k = 0; k < 8; k++) { + c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1; + } + crcTable2[n] = c; + } + return crcTable2; +}; +var crcTable = makeCRCTable(); +var crc32 = function(buffer) { + const uint8view = new Uint8Array(buffer); + let crc = 0 ^ -1; + for (let i = 0; i < uint8view.length; i++) { + crc = crc >>> 8 ^ crcTable[(crc ^ uint8view[i]) & 255]; + } + return (crc ^ -1) >>> 0; +}; + +// src/huffman-table.ts +var HuffmanTable = class _HuffmanTable { + static MSB = 2147483648; + l; + th; + v; + tc; + constructor() { + this.l = createArray(4, 2, 16); + this.th = [0, 0, 0, 0]; + this.v = createArray(4, 2, 16, 200); + this.tc = [ + [0, 0], + [0, 0], + [0, 0], + [0, 0] + ]; + } + read(data, HuffTab) { + let count = 0; + let temp; + let t; + let c; + let i; + let j; + const length = data.get16(); + count += 2; + while (count < length) { + temp = data.get8(); + count += 1; + t = temp & 15; + if (t > 3) { + throw new Error("ERROR: Huffman table ID > 3"); + } + c = temp >> 4; + if (c > 2) { + throw new Error("ERROR: Huffman table [Table class > 2 ]"); + } + this.th[t] = 1; + this.tc[t][c] = 1; + for (i = 0; i < 16; i += 1) { + this.l[t][c][i] = data.get8(); + count += 1; + } + for (i = 0; i < 16; i += 1) { + for (j = 0; j < this.l[t][c][i]; j += 1) { + if (count > length) { + throw new Error("ERROR: Huffman table format error [count>Lh]"); + } + this.v[t][c][i][j] = data.get8(); + count += 1; + } + } + } + if (count !== length) { + throw new Error("ERROR: Huffman table format error [count!=Lf]"); + } + for (i = 0; i < 4; i += 1) { + for (j = 0; j < 2; j += 1) { + if (this.tc[i][j] !== 0) { + this.buildHuffTable(HuffTab[i][j], this.l[i][j], this.v[i][j]); + } + } + } + return 1; + } + // Build_HuffTab() + // Parameter: t table ID + // c table class ( 0 for DC, 1 for AC ) + // L[i] # of codewords which length is i + // V[i][j] Huffman Value (length=i) + // Effect: + // build up HuffTab[t][c] using L and V. + buildHuffTable(tab, L, V) { + let currentTable, k, i, j, n; + const temp = 256; + k = 0; + for (i = 0; i < 8; i += 1) { + for (j = 0; j < L[i]; j += 1) { + for (n = 0; n < temp >> i + 1; n += 1) { + tab[k] = V[i][j] | i + 1 << 8; + k += 1; + } + } + } + for (i = 1; k < 256; i += 1, k += 1) { + tab[k] = i | _HuffmanTable.MSB; + } + currentTable = 1; + k = 0; + for (i = 8; i < 16; i += 1) { + for (j = 0; j < L[i]; j += 1) { + for (n = 0; n < temp >> i - 7; n += 1) { + tab[currentTable * 256 + k] = V[i][j] | i + 1 << 8; + k += 1; + } + if (k >= 256) { + if (k > 256) { + throw new Error("ERROR: Huffman table error(1)!"); + } + k = 0; + currentTable += 1; + } + } + } + } +}; + +// src/quantization-table.ts +var QuantizationTable = class _QuantizationTable { + precision = []; + // Quantization precision 8 or 16 + tq = [0, 0, 0, 0]; + // 1: this table is presented + quantTables = createArray(4, 64); + // Tables + static enhanceQuantizationTable = function(qtab, table) { + for (let i = 0; i < 8; i += 1) { + qtab[table[0 * 8 + i]] *= 90; + qtab[table[4 * 8 + i]] *= 90; + qtab[table[2 * 8 + i]] *= 118; + qtab[table[6 * 8 + i]] *= 49; + qtab[table[5 * 8 + i]] *= 71; + qtab[table[1 * 8 + i]] *= 126; + qtab[table[7 * 8 + i]] *= 25; + qtab[table[3 * 8 + i]] *= 106; + } + for (let i = 0; i < 8; i += 1) { + qtab[table[0 + 8 * i]] *= 90; + qtab[table[4 + 8 * i]] *= 90; + qtab[table[2 + 8 * i]] *= 118; + qtab[table[6 + 8 * i]] *= 49; + qtab[table[5 + 8 * i]] *= 71; + qtab[table[1 + 8 * i]] *= 126; + qtab[table[7 + 8 * i]] *= 25; + qtab[table[3 + 8 * i]] *= 106; + } + for (let i = 0; i < 64; i += 1) { + qtab[i] >>= 6; + } + }; + read(data, table) { + let count = 0; + let temp; + let t; + let i; + const length = data.get16(); + count += 2; + while (count < length) { + temp = data.get8(); + count += 1; + t = temp & 15; + if (t > 3) { + throw new Error("ERROR: Quantization table ID > 3"); + } + this.precision[t] = temp >> 4; + if (this.precision[t] === 0) { + this.precision[t] = 8; + } else if (this.precision[t] === 1) { + this.precision[t] = 16; + } else { + throw new Error("ERROR: Quantization table precision error"); + } + this.tq[t] = 1; + if (this.precision[t] === 8) { + for (i = 0; i < 64; i += 1) { + if (count > length) { + throw new Error("ERROR: Quantization table format error"); + } + this.quantTables[t][i] = data.get8(); + count += 1; + } + _QuantizationTable.enhanceQuantizationTable(this.quantTables[t], table); + } else { + for (i = 0; i < 64; i += 1) { + if (count > length) { + throw new Error("ERROR: Quantization table format error"); + } + this.quantTables[t][i] = data.get16(); + count += 2; + } + _QuantizationTable.enhanceQuantizationTable(this.quantTables[t], table); + } + } + if (count !== length) { + throw new Error("ERROR: Quantization table error [count!=Lq]"); + } + return 1; + } +}; + +// src/scan-component.ts +var ScanComponent = { + acTabSel: 0, + // AC table selector + dcTabSel: 0, + // DC table selector + scanCompSel: 0 + // Scan component selector +}; + +// src/scan-header.ts +var ScanHeader = class { + ah = 0; + al = 0; + numComp = 0; + // Number of components in the scan + selection = 0; + // Start of spectral or predictor selection + spectralEnd = 0; + // End of spectral selection + components = []; + read(data) { + let count = 0; + let i; + let temp; + const length = data.get16(); + count += 2; + this.numComp = data.get8(); + count += 1; + for (i = 0; i < this.numComp; i += 1) { + this.components[i] = { ...ScanComponent }; + if (count > length) { + throw new Error("ERROR: scan header format error"); + } + this.components[i].scanCompSel = data.get8(); + count += 1; + temp = data.get8(); + count += 1; + this.components[i].dcTabSel = temp >> 4; + this.components[i].acTabSel = temp & 15; + } + this.selection = data.get8(); + count += 1; + this.spectralEnd = data.get8(); + count += 1; + temp = data.get8(); + this.ah = temp >> 4; + this.al = temp & 15; + count += 1; + if (count !== length) { + throw new Error("ERROR: scan header format error [count!=Ns]"); + } + return 1; + } +}; + +// src/decoder.ts +var littleEndian = function() { + const buffer = new ArrayBuffer(2); + new DataView(buffer).setInt16( + 0, + 256, + true + /* littleEndian */ + ); + return new Int16Array(buffer)[0] === 256; +}(); +var Decoder = class _Decoder { + static IDCT_P = [ + 0, + 5, + 40, + 16, + 45, + 2, + 7, + 42, + 21, + 56, + 8, + 61, + 18, + 47, + 1, + 4, + 41, + 23, + 58, + 13, + 32, + 24, + 37, + 10, + 63, + 17, + 44, + 3, + 6, + 43, + 20, + 57, + 15, + 34, + 29, + 48, + 53, + 26, + 39, + 9, + 60, + 19, + 46, + 22, + 59, + 12, + 33, + 31, + 50, + 55, + 25, + 36, + 11, + 62, + 14, + 35, + 28, + 49, + 52, + 27, + 38, + 30, + 51, + 54 + ]; + static TABLE = [ + 0, + 1, + 5, + 6, + 14, + 15, + 27, + 28, + 2, + 4, + 7, + 13, + 16, + 26, + 29, + 42, + 3, + 8, + 12, + 17, + 25, + 30, + 41, + 43, + 9, + 11, + 18, + 24, + 31, + 40, + 44, + 53, + 10, + 19, + 23, + 32, + 39, + 45, + 52, + 54, + 20, + 22, + 33, + 38, + 46, + 51, + 55, + 60, + 21, + 34, + 37, + 47, + 50, + 56, + 59, + 61, + 35, + 36, + 48, + 49, + 57, + 58, + 62, + 63 + ]; + static MAX_HUFFMAN_SUBTREE = 50; + static MSB = 2147483648; + static RESTART_MARKER_BEGIN = 65488; + static RESTART_MARKER_END = 65495; + // Value `markerIndex` takes once the 0xFF that introduces a marker has been + // shifted into `temp`. Only `!== 0` is ever tested - see `readPastEntropyData` + // for what the bit count itself means. + static MARKER_SEEN = 9; + // How many bits of `temp` that 0xFF occupies. Bits below it are the marker, + // not entropy coded data. + static MARKER_BITS = 8; + buffer = null; + stream = null; + frame = new FrameHeader(); + huffTable = new HuffmanTable(); + quantTable = new QuantizationTable(); + scan = new ScanHeader(); + DU = createArray(10, 4, 64); + // at most 10 data units in a MCU, at most 4 data units in one component + HuffTab = createArray(4, 2, 50 * 256); + IDCT_Source = []; + nBlock = []; + // number of blocks in the i-th Comp in a scan + acTab = createArray(10, 1); + // ac HuffTab for the i-th Comp in a scan + dcTab = createArray(10, 1); + // dc HuffTab for the i-th Comp in a scan + qTab = createArray(10, 1); + // quantization table for the i-th Comp in a scan + marker = 0; + markerIndex = 0; + numComp = 0; + restartInterval = 0; + selection = 0; + xDim = 0; + yDim = 0; + xLoc = 0; + yLoc = 0; + outputData = null; + restarting = false; + mask = 0; + numBytes = 0; + precision = void 0; + components = []; + getter = null; + setter = null; + output = null; + selector = null; + /** + * The Decoder constructor. + * @property {number} numBytes - number of bytes per component + * @type {Function} + */ + constructor(buffer, numBytes) { + this.buffer = buffer ?? null; + this.numBytes = numBytes ?? 0; + } + /** + * Returns decompressed data. + */ + decompress(buffer, offset, length) { + const result = this.decode(buffer, offset, length); + return result.buffer; + } + decode(buffer, offset, length, numBytes) { + let scanNum = 0; + const pred = []; + let i; + let compN; + const temp = []; + const index = []; + let mcuNum; + if (buffer) { + this.buffer = buffer; + } + if (numBytes !== void 0) { + this.numBytes = numBytes; + } + this.stream = new DataStream(this.buffer, offset, length); + this.buffer = null; + this.xLoc = 0; + this.yLoc = 0; + let current = this.stream.get16(); + if (current !== 65496) { + throw new Error("Not a JPEG file"); + } + current = this.stream.get16(); + while (current >> 4 !== 4092 || current === 65476) { + switch (current) { + case 65476: + this.huffTable.read(this.stream, this.HuffTab); + break; + case 65484: + throw new Error("Program doesn't support arithmetic coding. (format throw new IOException)"); + case 65499: + this.quantTable.read(this.stream, _Decoder.TABLE); + break; + case 65501: + this.restartInterval = this.readNumber() ?? 0; + break; + case 65504: + case 65505: + case 65506: + case 65507: + case 65508: + case 65509: + case 65510: + case 65511: + case 65512: + case 65513: + case 65514: + case 65515: + case 65516: + case 65517: + case 65518: + case 65519: + this.readApp(); + break; + case 65534: + this.readComment(); + break; + default: + if (current >> 8 !== 255) { + throw new Error("ERROR: format throw new IOException! (decode)"); + } + } + current = this.stream.get16(); + } + if (current < 65472 || current > 65479) { + throw new Error("ERROR: could not handle arithmetic code!"); + } + this.frame.read(this.stream); + current = this.stream.get16(); + do { + while (current !== 65498) { + switch (current) { + case 65476: + this.huffTable.read(this.stream, this.HuffTab); + break; + case 65484: + throw new Error("Program doesn't support arithmetic coding. (format throw new IOException)"); + case 65499: + this.quantTable.read(this.stream, _Decoder.TABLE); + break; + case 65501: + this.restartInterval = this.readNumber() ?? 0; + break; + case 65504: + case 65505: + case 65506: + case 65507: + case 65508: + case 65509: + case 65510: + case 65511: + case 65512: + case 65513: + case 65514: + case 65515: + case 65516: + case 65517: + case 65518: + case 65519: + this.readApp(); + break; + case 65534: + this.readComment(); + break; + default: + if (current >> 8 !== 255) { + throw new Error("ERROR: format throw new IOException! (Parser.decode)"); + } + } + current = this.stream.get16(); + } + this.precision = this.frame.precision; + this.components = this.frame.components; + if (!this.numBytes) { + this.numBytes = Math.round(Math.ceil(this.precision / 8)); + } + if (this.numBytes === 1) { + this.mask = 255; + } else { + this.mask = 65535; + } + this.scan.read(this.stream); + this.numComp = this.scan.numComp; + this.selection = this.scan.selection; + if (this.numBytes === 1) { + if (this.numComp === 3) { + this.getter = this.getValueRGB; + this.setter = this.setValueRGB; + this.output = this.outputRGB; + } else { + this.getter = this.getValue8; + this.setter = this.setValue8; + this.output = this.outputSingle; + } + } else { + this.getter = this.getValue8; + this.setter = this.setValue8; + this.output = this.outputSingle; + } + switch (this.selection) { + case 2: + this.selector = this.select2; + break; + case 3: + this.selector = this.select3; + break; + case 4: + this.selector = this.select4; + break; + case 5: + this.selector = this.select5; + break; + case 6: + this.selector = this.select6; + break; + case 7: + this.selector = this.select7; + break; + default: + this.selector = this.select1; + break; + } + for (i = 0; i < this.numComp; i += 1) { + compN = this.scan.components[i].scanCompSel; + this.qTab[i] = this.quantTable.quantTables[this.components[compN].quantTableSel]; + this.nBlock[i] = this.components[compN].vSamp * this.components[compN].hSamp; + this.dcTab[i] = this.HuffTab[this.scan.components[i].dcTabSel][0]; + this.acTab[i] = this.HuffTab[this.scan.components[i].acTabSel][1]; + } + this.xDim = this.frame.dimX; + this.yDim = this.frame.dimY; + if (this.numBytes === 1) { + this.outputData = new Uint8Array(new ArrayBuffer(this.xDim * this.yDim * this.numBytes * this.numComp)); + } else { + this.outputData = new Uint16Array(new ArrayBuffer(this.xDim * this.yDim * this.numBytes * this.numComp)); + } + scanNum += 1; + while (true) { + temp[0] = 0; + index[0] = 0; + for (i = 0; i < 10; i += 1) { + pred[i] = 1 << this.precision - 1; + } + if (this.restartInterval === 0) { + current = this.decodeUnit(pred, temp, index); + while (current === 0 && this.xLoc < this.xDim && this.yLoc < this.yDim) { + this.output(pred); + current = this.decodeUnit(pred, temp, index); + } + break; + } + for (mcuNum = 0; mcuNum < this.restartInterval; mcuNum += 1) { + this.restarting = mcuNum === 0; + current = this.decodeUnit(pred, temp, index); + this.output(pred); + if (current !== 0) { + break; + } + } + if (current === 0) { + if (this.markerIndex !== 0) { + current = 65280 | this.marker; + this.markerIndex = 0; + } else { + current = this.stream.get16(); + } + } + if (!(current >= _Decoder.RESTART_MARKER_BEGIN && current <= _Decoder.RESTART_MARKER_END)) { + break; + } + } + if (current === 65500 && scanNum === 1) { + this.readNumber(); + current = this.stream.get16(); + } + } while (current !== 65497 && this.xLoc < this.xDim && this.yLoc < this.yDim && scanNum === 0); + return this.outputData; + } + decodeUnit(prev, temp, index) { + if (this.numComp === 1) { + return this.decodeSingle(prev, temp, index); + } else if (this.numComp === 3) { + return this.decodeRGB(prev, temp, index); + } else { + return -1; + } + } + select1(compOffset) { + return this.getPreviousX(compOffset); + } + select2(compOffset) { + return this.getPreviousY(compOffset); + } + select3(compOffset) { + return this.getPreviousXY(compOffset); + } + select4(compOffset) { + return this.getPreviousX(compOffset) + this.getPreviousY(compOffset) - this.getPreviousXY(compOffset); + } + select5(compOffset) { + return this.getPreviousX(compOffset) + (this.getPreviousY(compOffset) - this.getPreviousXY(compOffset) >> 1); + } + select6(compOffset) { + return this.getPreviousY(compOffset) + (this.getPreviousX(compOffset) - this.getPreviousXY(compOffset) >> 1); + } + select7(compOffset) { + return (this.getPreviousX(compOffset) + this.getPreviousY(compOffset)) / 2; + } + decodeRGB(prev, temp, index) { + if (this.selector === null) + throw new Error("decode hasn't run yet"); + let actab, dctab, qtab, ctrC, i, k, j; + prev[0] = this.selector(0); + prev[1] = this.selector(1); + prev[2] = this.selector(2); + for (ctrC = 0; ctrC < this.numComp; ctrC += 1) { + qtab = this.qTab[ctrC]; + actab = this.acTab[ctrC]; + dctab = this.dcTab[ctrC]; + for (i = 0; i < this.nBlock[ctrC]; i += 1) { + for (k = 0; k < this.IDCT_Source.length; k += 1) { + this.IDCT_Source[k] = 0; + } + let value = this.getHuffmanValue(dctab, temp, index); + if (value >= 65280) { + return value; + } + prev[ctrC] = this.IDCT_Source[0] = prev[ctrC] + this.getn(index, value, temp, index); + this.IDCT_Source[0] *= qtab[0]; + for (j = 1; j < 64; j += 1) { + value = this.getHuffmanValue(actab, temp, index); + if (value >= 65280) { + return value; + } + j += value >> 4; + if ((value & 15) === 0) { + if (value >> 4 === 0) { + break; + } + } else { + this.IDCT_Source[_Decoder.IDCT_P[j]] = this.getn(index, value & 15, temp, index) * qtab[j]; + } + } + } + } + return 0; + } + decodeSingle(prev, temp, index) { + if (this.selector === null) + throw new Error("decode hasn't run yet"); + let value, i, n, nRestart; + if (this.restarting) { + this.restarting = false; + prev[0] = 1 << this.frame.precision - 1; + } else { + prev[0] = this.selector(); + } + for (i = 0; i < this.nBlock[0]; i += 1) { + value = this.getHuffmanValue(this.dcTab[0], temp, index); + if (value >= 65280) { + return value; + } + n = this.getn(prev, value, temp, index); + nRestart = n >> 8; + if (nRestart >= _Decoder.RESTART_MARKER_BEGIN && nRestart <= _Decoder.RESTART_MARKER_END) { + return nRestart; + } + prev[0] += n; + } + return 0; + } + // Huffman table for fast search: (HuffTab) 8-bit Look up table 2-layer search architecture, 1st-layer represent 256 node (8 bits) if codeword-length > 8 + // bits, then the entry of 1st-layer = (# of 2nd-layer table) | MSB and it is stored in the 2nd-layer Size of tables in each layer are 256. + // HuffTab[*][*][0-256] is always the only 1st-layer table. + // + // An entry can be: (1) (# of 2nd-layer table) | MSB , for code length > 8 in 1st-layer (2) (Code length) << 8 | HuffVal + // + // HuffmanValue(table HuffTab[x][y] (ex) HuffmanValue(HuffTab[1][0],...) + // ): + // return: Huffman Value of table + // 0xFF?? if it receives a MARKER + // Parameter: table HuffTab[x][y] (ex) HuffmanValue(HuffTab[1][0],...) + // temp temp storage for remainded bits + // index index to bit of temp + // in FILE pointer + // Effect: + // temp store new remainded bits + // index change to new index + // in change to new position + // NOTE: + // Initial by temp=0; index=0; + // NOTE: (explain temp and index) + // temp: is always in the form at calling time or returning time + // | byte 4 | byte 3 | byte 2 | byte 1 | + // | 0 | 0 | 00000000 | 00000??? | if not a MARKER + // ^index=3 (from 0 to 15) + // 321 + // NOTE (marker and marker_index): + // If get a MARKER from 'in', marker=the low-byte of the MARKER + // and marker_index=9 + // If marker_index=9 then index is always > 8, or HuffmanValue() + // will not be called + getHuffmanValue(table, temp, index) { + let code, input; + const mask = 65535; + if (!this.stream) + throw new Error("stream not initialized"); + if (index[0] < 8) { + temp[0] <<= 8; + input = this.stream.get8(); + if (input === 255) { + this.marker = this.stream.get8(); + if (this.marker !== 0) { + this.markerIndex = _Decoder.MARKER_SEEN; + } + } + temp[0] |= input; + } else { + index[0] -= 8; + } + code = table[temp[0] >> index[0]]; + if ((code & _Decoder.MSB) !== 0) { + if (this.markerIndex !== 0) { + this.markerIndex = 0; + return 65280 | this.marker; + } + temp[0] &= mask >> 16 - index[0]; + temp[0] <<= 8; + input = this.stream.get8(); + if (input === 255) { + this.marker = this.stream.get8(); + if (this.marker !== 0) { + this.markerIndex = _Decoder.MARKER_SEEN; + } + } + temp[0] |= input; + code = table[(code & 255) * 256 + (temp[0] >> index[0])]; + index[0] += 8; + } + index[0] += 8 - (code >> 8); + if (index[0] < 0) { + throw new Error("index=" + index[0] + " temp=" + temp[0] + " code=" + code + " in HuffmanValue()"); + } + if (this.readPastEntropyData(index)) { + this.markerIndex = 0; + return 65280 | this.marker; + } + temp[0] &= mask >> 16 - index[0]; + return code & 255; + } + getn(PRED, n, temp, index) { + let result, input; + const one = 1; + const n_one = -1; + const mask = 65535; + if (this.stream === null) + throw new Error("stream not initialized"); + if (n === 0) { + return 0; + } + if (n === 16) { + if (PRED[0] >= 0) { + return -32768; + } else { + return 32768; + } + } + index[0] -= n; + if (index[0] >= 0) { + if (this.readPastEntropyData(index)) { + this.markerIndex = 0; + return (65280 | this.marker) << 8; + } + result = temp[0] >> index[0]; + temp[0] &= mask >> 16 - index[0]; + } else { + temp[0] <<= 8; + input = this.stream.get8(); + if (input === 255) { + this.marker = this.stream.get8(); + if (this.marker !== 0) { + this.markerIndex = _Decoder.MARKER_SEEN; + } + } + temp[0] |= input; + index[0] += 8; + if (index[0] < 0) { + if (this.markerIndex !== 0) { + this.markerIndex = 0; + return (65280 | this.marker) << 8; + } + temp[0] <<= 8; + input = this.stream.get8(); + if (input === 255) { + this.marker = this.stream.get8(); + if (this.marker !== 0) { + this.markerIndex = _Decoder.MARKER_SEEN; + } + } + temp[0] |= input; + index[0] += 8; + } + if (index[0] < 0) { + throw new Error("index=" + index[0] + " in getn()"); + } + if (this.readPastEntropyData(index)) { + this.markerIndex = 0; + return (65280 | this.marker) << 8; + } + result = temp[0] >> index[0]; + temp[0] &= mask >> 16 - index[0]; + } + if (result < one << n - 1) { + result += (n_one << n) + 1; + } + return result; + } + getPreviousX(compOffset = 0) { + if (this.getter === null) + throw new Error("decode hasn't run yet"); + if (this.xLoc > 0) { + return this.getter(this.yLoc * this.xDim + this.xLoc - 1, compOffset); + } else if (this.yLoc > 0) { + return this.getPreviousY(compOffset); + } else { + return 1 << this.frame.precision - 1; + } + } + getPreviousXY(compOffset = 0) { + if (this.getter === null) + throw new Error("decode hasn't run yet"); + if (this.xLoc > 0 && this.yLoc > 0) { + return this.getter((this.yLoc - 1) * this.xDim + this.xLoc - 1, compOffset); + } else { + return this.getPreviousY(compOffset); + } + } + getPreviousY(compOffset = 0) { + if (this.getter === null) + throw new Error("decode hasn't run yet"); + if (this.yLoc > 0) { + return this.getter((this.yLoc - 1) * this.xDim + this.xLoc, compOffset); + } else { + return this.getPreviousX(compOffset); + } + } + /** + * True when the bits just consumed came out of a marker rather than out of + * the entropy coded segment. + * + * `temp` holds `index` unconsumed bits (see the `getHuffmanValue` notes). The + * moment `getHuffmanValue`/`getn` shifts in a byte that turns out to be the + * 0xFF introducing a marker, those 8 bits stop being data: only the + * `index - MARKER_BITS` bits above them are real, and the scan is over once + * they run out. + * + * Consuming the last real bit leaves `index === MARKER_BITS`, and that is + * still a valid decode - it is a scan whose final Huffman code ends exactly + * on a byte boundary, so the encoder had no partial byte left to pad (T.81 + * B.1.1.2 pads only an incomplete final byte). Testing `index < markerIndex` + * rejected that case and dropped the frame's last sample, which is what + * DCMTK produces when a frame ends in a long run of one value: the run's + * short codes tile the final byte exactly. + */ + readPastEntropyData(index) { + return this.markerIndex !== 0 && index[0] < _Decoder.MARKER_BITS; + } + outputSingle(PRED) { + if (this.setter === null) + throw new Error("decode hasn't run yet"); + if (this.xLoc < this.xDim && this.yLoc < this.yDim) { + this.setter(this.yLoc * this.xDim + this.xLoc, this.mask & PRED[0]); + this.xLoc += 1; + if (this.xLoc >= this.xDim) { + this.yLoc += 1; + this.xLoc = 0; + } + } + } + outputRGB(PRED) { + if (this.setter === null) + throw new Error("decode hasn't run yet"); + const offset = this.yLoc * this.xDim + this.xLoc; + if (this.xLoc < this.xDim && this.yLoc < this.yDim) { + this.setter(offset, PRED[0], 0); + this.setter(offset, PRED[1], 1); + this.setter(offset, PRED[2], 2); + this.xLoc += 1; + if (this.xLoc >= this.xDim) { + this.yLoc += 1; + this.xLoc = 0; + } + } + } + setValue8(index, val) { + if (!this.outputData) + throw new Error("output data not ready"); + if (littleEndian) { + this.outputData[index] = val; + } else { + this.outputData[index] = (val & 255) << 8 | val >> 8 & 255; + } + } + getValue8(index) { + if (this.outputData === null) + throw new Error("output data not ready"); + if (littleEndian) { + return this.outputData[index]; + } else { + const val = this.outputData[index]; + return (val & 255) << 8 | val >> 8 & 255; + } + } + setValueRGB(index, val, compOffset = 0) { + if (this.outputData === null) + return; + this.outputData[index * 3 + compOffset] = val; + } + getValueRGB(index, compOffset) { + if (this.outputData === null) + throw new Error("output data not ready"); + return this.outputData[index * 3 + compOffset]; + } + readApp() { + if (this.stream === null) + return null; + let count = 0; + const length = this.stream.get16(); + count += 2; + while (count < length) { + this.stream.get8(); + count += 1; + } + return length; + } + readComment() { + if (this.stream === null) + return null; + let sb = ""; + let count = 0; + const length = this.stream.get16(); + count += 2; + while (count < length) { + sb += this.stream.get8(); + count += 1; + } + return sb; + } + readNumber() { + if (this.stream === null) + return null; + const Ld = this.stream.get16(); + if (Ld !== 4) { + throw new Error("ERROR: Define number format throw new IOException [Ld!=4]"); + } + return this.stream.get16(); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + ComponentSpec, + DataStream, + Decoder, + FrameHeader, + HuffmanTable, + QuantizationTable, + ScanComponent, + ScanHeader, + Utils +}); +//# sourceMappingURL=lossless.cjs.map \ No newline at end of file diff --git a/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs.map b/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs.map new file mode 100644 index 00000000..5be02734 --- /dev/null +++ b/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../src/main.ts","../../src/component-spec.ts","../../src/data-stream.ts","../../src/frame-header.ts","../../src/utils.ts","../../src/huffman-table.ts","../../src/quantization-table.ts","../../src/scan-component.ts","../../src/scan-header.ts","../../src/decoder.ts"],"sourcesContent":["export { ComponentSpec } from './component-spec.js'\nexport { DataStream } from './data-stream.js'\nexport { Decoder } from './decoder.js'\nexport { FrameHeader } from './frame-header.js'\nexport { HuffmanTable } from './huffman-table.js'\nexport { QuantizationTable } from './quantization-table.js'\nexport { ScanComponent } from './scan-component.js'\nexport { ScanHeader } from './scan-header.js'\nexport * as Utils from './utils.js'\n","export const ComponentSpec = {\n hSamp: 0,\n quantTableSel: 0,\n vSamp: 0\n}\n","export class DataStream {\n buffer: Uint8Array\n index: number\n\n constructor(data: ArrayBuffer, offset?: number, length?: number) {\n this.buffer = new Uint8Array(data, offset, length)\n this.index = 0\n }\n\n get16() {\n // var value = this.buffer.getUint16(this.index, false);\n const value = (this.buffer[this.index] << 8) + this.buffer[this.index + 1] // DataView is big-endian by default\n this.index += 2\n return value\n }\n\n get8() {\n // var value = this.buffer.getUint8(this.index);\n const value = this.buffer[this.index]\n this.index += 1\n return value\n }\n}\n","import { ComponentSpec } from './component-spec.js'\nimport { DataStream } from './data-stream.js'\n\nexport class FrameHeader {\n dimX = 0\n dimY = 0\n numComp = 0\n precision = 0\n components: Array = []\n\n read(data: DataStream) {\n let count = 0\n let temp\n\n const length = data.get16()\n count += 2\n\n this.precision = data.get8()\n count += 1\n\n this.dimY = data.get16()\n count += 2\n\n this.dimX = data.get16()\n count += 2\n\n this.numComp = data.get8()\n count += 1\n for (let i = 1; i <= this.numComp; i += 1) {\n if (count > length) {\n throw new Error('ERROR: frame format error')\n }\n\n const c = data.get8()\n count += 1\n\n if (count >= length) {\n throw new Error('ERROR: frame format error [c>=Lf]')\n }\n\n temp = data.get8()\n count += 1\n\n if (!this.components[c]) {\n this.components[c] = { ...ComponentSpec }\n }\n\n this.components[c].hSamp = temp >> 4\n this.components[c].vSamp = temp & 0x0f\n this.components[c].quantTableSel = data.get8()\n count += 1\n }\n\n if (count !== length) {\n throw new Error('ERROR: frame format error [Lf!=count]')\n }\n\n return 1\n }\n}\n","type NestedArray = Array>\n\n// https://stackoverflow.com/a/12588826\nexport const createArray = (...dimensions: number[]): NestedArray => {\n if (dimensions.length > 1) {\n const dim = dimensions[0]\n const rest = dimensions.slice(1)\n const newArray = []\n for (let i = 0; i < dim; i++) {\n newArray[i] = createArray(...rest)\n }\n return newArray\n } else {\n return Array(dimensions[0]).fill(undefined)\n }\n}\n\n// http://stackoverflow.com/questions/18638900/javascript-crc32\nexport const makeCRCTable = function () {\n let c\n const crcTable = []\n for (let n = 0; n < 256; n++) {\n c = n\n for (let k = 0; k < 8; k++) {\n c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1\n }\n crcTable[n] = c\n }\n return crcTable\n}\n\nexport const crcTable = makeCRCTable()\n\nexport const crc32 = function (buffer: ArrayBuffer) {\n const uint8view = new Uint8Array(buffer)\n let crc = 0 ^ -1\n\n for (let i = 0; i < uint8view.length; i++) {\n crc = (crc >>> 8) ^ crcTable[(crc ^ uint8view[i]) & 0xff]\n }\n\n return (crc ^ -1) >>> 0\n}\n","import { DataStream } from './data-stream.js'\nimport { createArray } from './utils.js'\n\nexport class HuffmanTable {\n static MSB = 0x80000000\n\n l: number[][][]\n th: number[]\n v: number[][][][]\n tc: number[][]\n\n constructor() {\n this.l = createArray(4, 2, 16) as number[][][]\n this.th = [0, 0, 0, 0]\n this.v = createArray(4, 2, 16, 200) as number[][][][]\n this.tc = [\n [0, 0],\n [0, 0],\n [0, 0],\n [0, 0]\n ]\n }\n\n read(data: DataStream, HuffTab: number[][][]) {\n let count = 0\n let temp\n let t\n let c\n let i\n let j\n\n const length = data.get16()\n count += 2\n\n while (count < length) {\n temp = data.get8()\n count += 1\n t = temp & 0x0f\n if (t > 3) {\n throw new Error('ERROR: Huffman table ID > 3')\n }\n\n c = temp >> 4\n if (c > 2) {\n throw new Error('ERROR: Huffman table [Table class > 2 ]')\n }\n\n this.th[t] = 1\n this.tc[t][c] = 1\n\n for (i = 0; i < 16; i += 1) {\n this.l[t][c][i] = data.get8()\n count += 1\n }\n\n for (i = 0; i < 16; i += 1) {\n for (j = 0; j < this.l[t][c][i]; j += 1) {\n if (count > length) {\n throw new Error('ERROR: Huffman table format error [count>Lh]')\n }\n\n this.v[t][c][i][j] = data.get8()\n count += 1\n }\n }\n }\n\n if (count !== length) {\n throw new Error('ERROR: Huffman table format error [count!=Lf]')\n }\n\n for (i = 0; i < 4; i += 1) {\n for (j = 0; j < 2; j += 1) {\n if (this.tc[i][j] !== 0) {\n this.buildHuffTable(HuffTab[i][j], this.l[i][j], this.v[i][j])\n }\n }\n }\n\n return 1\n }\n\n //\tBuild_HuffTab()\n //\tParameter: t table ID\n //\t c table class ( 0 for DC, 1 for AC )\n //\t L[i] # of codewords which length is i\n //\t V[i][j] Huffman Value (length=i)\n //\tEffect:\n //\t build up HuffTab[t][c] using L and V.\n buildHuffTable(tab: number[], L: number[], V: number[][]) {\n let currentTable, k, i, j, n\n const temp = 256\n k = 0\n\n for (i = 0; i < 8; i += 1) {\n // i+1 is Code length\n for (j = 0; j < L[i]; j += 1) {\n for (n = 0; n < temp >> (i + 1); n += 1) {\n tab[k] = V[i][j] | ((i + 1) << 8)\n k += 1\n }\n }\n }\n\n for (i = 1; k < 256; i += 1, k += 1) {\n tab[k] = i | HuffmanTable.MSB\n }\n\n currentTable = 1\n k = 0\n\n for (i = 8; i < 16; i += 1) {\n // i+1 is Code length\n for (j = 0; j < L[i]; j += 1) {\n for (n = 0; n < temp >> (i - 7); n += 1) {\n tab[currentTable * 256 + k] = V[i][j] | ((i + 1) << 8)\n k += 1\n }\n\n if (k >= 256) {\n if (k > 256) {\n throw new Error('ERROR: Huffman table error(1)!')\n }\n\n k = 0\n currentTable += 1\n }\n }\n }\n }\n}\n","import { DataStream } from './data-stream.js'\nimport { createArray } from './utils.js'\n\nexport class QuantizationTable {\n precision: number[] = [] // Quantization precision 8 or 16\n tq = [0, 0, 0, 0] // 1: this table is presented\n quantTables: number[][] = createArray(4, 64) as number[][] // Tables\n\n static enhanceQuantizationTable = function (qtab: number[], table: number[]) {\n for (let i = 0; i < 8; i += 1) {\n qtab[table[0 * 8 + i]] *= 90\n qtab[table[4 * 8 + i]] *= 90\n qtab[table[2 * 8 + i]] *= 118\n qtab[table[6 * 8 + i]] *= 49\n qtab[table[5 * 8 + i]] *= 71\n qtab[table[1 * 8 + i]] *= 126\n qtab[table[7 * 8 + i]] *= 25\n qtab[table[3 * 8 + i]] *= 106\n }\n\n for (let i = 0; i < 8; i += 1) {\n qtab[table[0 + 8 * i]] *= 90\n qtab[table[4 + 8 * i]] *= 90\n qtab[table[2 + 8 * i]] *= 118\n qtab[table[6 + 8 * i]] *= 49\n qtab[table[5 + 8 * i]] *= 71\n qtab[table[1 + 8 * i]] *= 126\n qtab[table[7 + 8 * i]] *= 25\n qtab[table[3 + 8 * i]] *= 106\n }\n\n for (let i = 0; i < 64; i += 1) {\n qtab[i] >>= 6\n }\n }\n\n read(data: DataStream, table: number[]) {\n let count = 0\n let temp\n let t\n let i\n\n const length = data.get16()\n count += 2\n\n while (count < length) {\n temp = data.get8()\n count += 1\n t = temp & 0x0f\n\n if (t > 3) {\n throw new Error('ERROR: Quantization table ID > 3')\n }\n\n this.precision[t] = temp >> 4\n\n if (this.precision[t] === 0) {\n this.precision[t] = 8\n } else if (this.precision[t] === 1) {\n this.precision[t] = 16\n } else {\n throw new Error('ERROR: Quantization table precision error')\n }\n\n this.tq[t] = 1\n\n if (this.precision[t] === 8) {\n for (i = 0; i < 64; i += 1) {\n if (count > length) {\n throw new Error('ERROR: Quantization table format error')\n }\n\n this.quantTables[t][i] = data.get8()\n count += 1\n }\n\n QuantizationTable.enhanceQuantizationTable(this.quantTables[t], table)\n } else {\n for (i = 0; i < 64; i += 1) {\n if (count > length) {\n throw new Error('ERROR: Quantization table format error')\n }\n\n this.quantTables[t][i] = data.get16()\n count += 2\n }\n\n QuantizationTable.enhanceQuantizationTable(this.quantTables[t], table)\n }\n }\n\n if (count !== length) {\n throw new Error('ERROR: Quantization table error [count!=Lq]')\n }\n\n return 1\n }\n}\n","export const ScanComponent = {\n acTabSel: 0, // AC table selector\n dcTabSel: 0, // DC table selector\n scanCompSel: 0 // Scan component selector\n}\n","import { DataStream } from './data-stream.js'\nimport { ScanComponent } from './scan-component.js'\n\nexport class ScanHeader {\n ah = 0\n al = 0\n numComp = 0 // Number of components in the scan\n selection = 0 // Start of spectral or predictor selection\n spectralEnd = 0 // End of spectral selection\n components: Array = []\n\n read(data: DataStream) {\n let count = 0\n let i\n let temp\n\n const length = data.get16()\n count += 2\n\n this.numComp = data.get8()\n count += 1\n\n for (i = 0; i < this.numComp; i += 1) {\n this.components[i] = { ...ScanComponent }\n\n if (count > length) {\n throw new Error('ERROR: scan header format error')\n }\n\n this.components[i].scanCompSel = data.get8()\n count += 1\n\n temp = data.get8()\n count += 1\n\n this.components[i].dcTabSel = temp >> 4\n this.components[i].acTabSel = temp & 0x0f\n }\n\n this.selection = data.get8()\n count += 1\n\n this.spectralEnd = data.get8()\n count += 1\n\n temp = data.get8()\n this.ah = temp >> 4\n this.al = temp & 0x0f\n count += 1\n\n if (count !== length) {\n throw new Error('ERROR: scan header format error [count!=Ns]')\n }\n\n return 1\n }\n}\n","import { ComponentSpec } from './component-spec.js'\nimport { DataStream } from './data-stream.js'\nimport { FrameHeader } from './frame-header.js'\nimport { HuffmanTable } from './huffman-table.js'\nimport { QuantizationTable } from './quantization-table.js'\nimport { ScanHeader } from './scan-header.js'\nimport { createArray } from './utils.js'\n\nconst littleEndian = (function () {\n const buffer = new ArrayBuffer(2)\n new DataView(buffer).setInt16(0, 256, true /* littleEndian */)\n // Int16Array uses the platform's endianness.\n return new Int16Array(buffer)[0] === 256\n})()\n\nexport class Decoder {\n static IDCT_P = [\n 0, 5, 40, 16, 45, 2, 7, 42, 21, 56, 8, 61, 18, 47, 1, 4, 41, 23, 58, 13, 32, 24, 37, 10, 63, 17, 44, 3, 6, 43, 20,\n 57, 15, 34, 29, 48, 53, 26, 39, 9, 60, 19, 46, 22, 59, 12, 33, 31, 50, 55, 25, 36, 11, 62, 14, 35, 28, 49, 52, 27,\n 38, 30, 51, 54\n ]\n\n static TABLE = [\n 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44,\n 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49,\n 57, 58, 62, 63\n ]\n\n static MAX_HUFFMAN_SUBTREE = 50\n static MSB = 0x80000000\n static RESTART_MARKER_BEGIN = 0xffd0\n static RESTART_MARKER_END = 0xffd7\n\n // Value `markerIndex` takes once the 0xFF that introduces a marker has been\n // shifted into `temp`. Only `!== 0` is ever tested - see `readPastEntropyData`\n // for what the bit count itself means.\n static MARKER_SEEN = 9\n\n // How many bits of `temp` that 0xFF occupies. Bits below it are the marker,\n // not entropy coded data.\n static MARKER_BITS = 8\n\n buffer: ArrayBuffer | null = null\n stream: DataStream | null = null\n frame = new FrameHeader()\n huffTable = new HuffmanTable()\n quantTable = new QuantizationTable()\n scan = new ScanHeader()\n DU: number[][][] = createArray(10, 4, 64) as number[][][] // at most 10 data units in a MCU, at most 4 data units in one component\n HuffTab: number[][][] = createArray(4, 2, 50 * 256) as number[][][]\n IDCT_Source: number[] = []\n nBlock: number[] = [] // number of blocks in the i-th Comp in a scan\n acTab: number[][] = createArray(10, 1) as number[][] // ac HuffTab for the i-th Comp in a scan\n dcTab: number[][] = createArray(10, 1) as number[][] // dc HuffTab for the i-th Comp in a scan\n qTab: number[][] = createArray(10, 1) as number[][] // quantization table for the i-th Comp in a scan\n marker = 0\n markerIndex = 0\n numComp = 0\n restartInterval = 0\n selection = 0\n xDim = 0\n yDim = 0\n xLoc = 0\n yLoc = 0\n outputData: Uint8Array | Uint16Array | null = null\n restarting = false\n mask = 0\n numBytes = 0\n\n precision: number | undefined = undefined\n components: Array = []\n\n getter: null | ((index: number, compOffset: number) => number) = null\n setter: null | ((index: number, val: number, compOffset?: number) => void) = null\n output: null | ((PRED: number[]) => void) = null\n selector: null | ((compOffset?: number) => number) = null\n\n /**\n * The Decoder constructor.\n * @property {number} numBytes - number of bytes per component\n * @type {Function}\n */\n constructor(buffer?: ArrayBuffer | null, numBytes?: number) {\n this.buffer = buffer ?? null\n this.numBytes = numBytes ?? 0\n }\n\n /**\n * Returns decompressed data.\n */\n decompress(buffer: ArrayBuffer, offset: number, length: number): ArrayBuffer {\n const result = this.decode(buffer, offset, length)\n return result.buffer\n }\n\n decode(buffer?: ArrayBuffer, offset?: number, length?: number, numBytes?: number) {\n let scanNum = 0\n const pred = []\n let i\n let compN\n const temp = []\n const index = []\n let mcuNum\n\n if (buffer) {\n this.buffer = buffer\n }\n\n if (numBytes !== undefined) {\n this.numBytes = numBytes\n }\n\n this.stream = new DataStream(this.buffer as ArrayBuffer, offset, length)\n this.buffer = null\n\n this.xLoc = 0\n this.yLoc = 0\n let current = this.stream.get16()\n\n if (current !== 0xffd8) {\n // SOI\n throw new Error('Not a JPEG file')\n }\n\n current = this.stream.get16()\n\n while (current >> 4 !== 0x0ffc || current === 0xffc4) {\n // SOF 0~15\n switch (current) {\n case 0xffc4: // DHT\n this.huffTable.read(this.stream, this.HuffTab)\n break\n case 0xffcc: // DAC\n throw new Error(\"Program doesn't support arithmetic coding. (format throw new IOException)\")\n case 0xffdb:\n this.quantTable.read(this.stream, Decoder.TABLE)\n break\n case 0xffdd:\n this.restartInterval = this.readNumber() ?? 0\n break\n case 0xffe0:\n case 0xffe1:\n case 0xffe2:\n case 0xffe3:\n case 0xffe4:\n case 0xffe5:\n case 0xffe6:\n case 0xffe7:\n case 0xffe8:\n case 0xffe9:\n case 0xffea:\n case 0xffeb:\n case 0xffec:\n case 0xffed:\n case 0xffee:\n case 0xffef:\n this.readApp()\n break\n case 0xfffe:\n this.readComment()\n break\n default:\n if (current >> 8 !== 0xff) {\n throw new Error('ERROR: format throw new IOException! (decode)')\n }\n }\n\n current = this.stream.get16()\n }\n\n if (current < 0xffc0 || current > 0xffc7) {\n throw new Error('ERROR: could not handle arithmetic code!')\n }\n\n this.frame.read(this.stream)\n current = this.stream.get16()\n\n do {\n while (current !== 0x0ffda) {\n // SOS\n switch (current) {\n case 0xffc4: // DHT\n this.huffTable.read(this.stream, this.HuffTab)\n break\n case 0xffcc: // DAC\n throw new Error(\"Program doesn't support arithmetic coding. (format throw new IOException)\")\n case 0xffdb:\n this.quantTable.read(this.stream, Decoder.TABLE)\n break\n case 0xffdd:\n this.restartInterval = this.readNumber() ?? 0\n break\n case 0xffe0:\n case 0xffe1:\n case 0xffe2:\n case 0xffe3:\n case 0xffe4:\n case 0xffe5:\n case 0xffe6:\n case 0xffe7:\n case 0xffe8:\n case 0xffe9:\n case 0xffea:\n case 0xffeb:\n case 0xffec:\n case 0xffed:\n case 0xffee:\n case 0xffef:\n this.readApp()\n break\n case 0xfffe:\n this.readComment()\n break\n default:\n if (current >> 8 !== 0xff) {\n throw new Error('ERROR: format throw new IOException! (Parser.decode)')\n }\n }\n\n current = this.stream.get16()\n }\n\n this.precision = this.frame.precision\n this.components = this.frame.components\n\n if (!this.numBytes) {\n this.numBytes = Math.round(Math.ceil(this.precision / 8))\n }\n\n if (this.numBytes === 1) {\n this.mask = 0xff\n } else {\n this.mask = 0xffff\n }\n\n this.scan.read(this.stream)\n this.numComp = this.scan.numComp\n this.selection = this.scan.selection\n\n if (this.numBytes === 1) {\n if (this.numComp === 3) {\n this.getter = this.getValueRGB\n this.setter = this.setValueRGB\n this.output = this.outputRGB\n } else {\n this.getter = this.getValue8\n this.setter = this.setValue8\n this.output = this.outputSingle\n }\n } else {\n this.getter = this.getValue8\n this.setter = this.setValue8\n this.output = this.outputSingle\n }\n\n switch (this.selection) {\n case 2:\n this.selector = this.select2\n break\n case 3:\n this.selector = this.select3\n break\n case 4:\n this.selector = this.select4\n break\n case 5:\n this.selector = this.select5\n break\n case 6:\n this.selector = this.select6\n break\n case 7:\n this.selector = this.select7\n break\n default:\n this.selector = this.select1\n break\n }\n\n // this.scanComps = this.scan.components\n // this.quantTables = this.quantTable.quantTables\n\n for (i = 0; i < this.numComp; i += 1) {\n compN = this.scan.components[i].scanCompSel\n this.qTab[i] = this.quantTable.quantTables[this.components[compN].quantTableSel]\n this.nBlock[i] = this.components[compN].vSamp * this.components[compN].hSamp\n this.dcTab[i] = this.HuffTab[this.scan.components[i].dcTabSel][0]\n this.acTab[i] = this.HuffTab[this.scan.components[i].acTabSel][1]\n }\n\n this.xDim = this.frame.dimX\n this.yDim = this.frame.dimY\n if (this.numBytes === 1) {\n this.outputData = new Uint8Array(new ArrayBuffer(this.xDim * this.yDim * this.numBytes * this.numComp))\n } else {\n this.outputData = new Uint16Array(new ArrayBuffer(this.xDim * this.yDim * this.numBytes * this.numComp))\n }\n\n scanNum += 1\n\n while (true) {\n // Decode one scan\n temp[0] = 0\n index[0] = 0\n\n for (i = 0; i < 10; i += 1) {\n pred[i] = 1 << (this.precision - 1)\n }\n\n if (this.restartInterval === 0) {\n current = this.decodeUnit(pred, temp, index)\n\n while (current === 0 && this.xLoc < this.xDim && this.yLoc < this.yDim) {\n this.output(pred)\n current = this.decodeUnit(pred, temp, index)\n }\n\n break // current=MARKER\n }\n\n for (mcuNum = 0; mcuNum < this.restartInterval; mcuNum += 1) {\n this.restarting = mcuNum === 0\n current = this.decodeUnit(pred, temp, index)\n this.output(pred)\n\n if (current !== 0) {\n break\n }\n }\n\n if (current === 0) {\n if (this.markerIndex !== 0) {\n current = 0xff00 | this.marker\n this.markerIndex = 0\n } else {\n current = this.stream.get16()\n }\n }\n\n if (!(current >= Decoder.RESTART_MARKER_BEGIN && current <= Decoder.RESTART_MARKER_END)) {\n break // current=MARKER\n }\n }\n\n if (current === 0xffdc && scanNum === 1) {\n // DNL\n this.readNumber()\n current = this.stream.get16()\n }\n } while (current !== 0xffd9 && this.xLoc < this.xDim && this.yLoc < this.yDim && scanNum === 0)\n\n return this.outputData\n }\n\n decodeUnit(prev: number[], temp: number[], index: number[]): number {\n if (this.numComp === 1) {\n return this.decodeSingle(prev, temp, index)\n } else if (this.numComp === 3) {\n return this.decodeRGB(prev, temp, index)\n } else {\n return -1\n }\n }\n\n select1(compOffset?: number) {\n return this.getPreviousX(compOffset)\n }\n\n select2(compOffset?: number) {\n return this.getPreviousY(compOffset)\n }\n\n select3(compOffset?: number) {\n return this.getPreviousXY(compOffset)\n }\n\n select4(compOffset?: number) {\n return this.getPreviousX(compOffset) + this.getPreviousY(compOffset) - this.getPreviousXY(compOffset)\n }\n\n select5(compOffset?: number) {\n return this.getPreviousX(compOffset) + ((this.getPreviousY(compOffset) - this.getPreviousXY(compOffset)) >> 1)\n }\n\n select6(compOffset?: number) {\n return this.getPreviousY(compOffset) + ((this.getPreviousX(compOffset) - this.getPreviousXY(compOffset)) >> 1)\n }\n\n select7(compOffset?: number) {\n return (this.getPreviousX(compOffset) + this.getPreviousY(compOffset)) / 2\n }\n\n decodeRGB(prev: number[], temp: number[], index: number[]) {\n if (this.selector === null) throw new Error(\"decode hasn't run yet\")\n\n let actab, dctab, qtab, ctrC, i, k, j\n\n prev[0] = this.selector(0)\n prev[1] = this.selector(1)\n prev[2] = this.selector(2)\n\n for (ctrC = 0; ctrC < this.numComp; ctrC += 1) {\n qtab = this.qTab[ctrC]\n actab = this.acTab[ctrC]\n dctab = this.dcTab[ctrC]\n for (i = 0; i < this.nBlock[ctrC]; i += 1) {\n for (k = 0; k < this.IDCT_Source.length; k += 1) {\n this.IDCT_Source[k] = 0\n }\n\n let value = this.getHuffmanValue(dctab, temp, index)\n\n if (value >= 0xff00) {\n return value\n }\n\n prev[ctrC] = this.IDCT_Source[0] = prev[ctrC] + this.getn(index, value, temp, index)\n this.IDCT_Source[0] *= qtab[0]\n\n for (j = 1; j < 64; j += 1) {\n value = this.getHuffmanValue(actab, temp, index)\n\n if (value >= 0xff00) {\n return value\n }\n\n j += value >> 4\n\n if ((value & 0x0f) === 0) {\n if (value >> 4 === 0) {\n break\n }\n } else {\n this.IDCT_Source[Decoder.IDCT_P[j]] = this.getn(index, value & 0x0f, temp, index) * qtab[j]\n }\n }\n }\n }\n\n return 0\n }\n\n decodeSingle(prev: number[], temp: number[], index: number[]) {\n if (this.selector === null) throw new Error(\"decode hasn't run yet\")\n\n let value, i, n, nRestart\n\n if (this.restarting) {\n this.restarting = false\n prev[0] = 1 << (this.frame.precision - 1)\n } else {\n prev[0] = this.selector()\n }\n\n for (i = 0; i < this.nBlock[0]; i += 1) {\n value = this.getHuffmanValue(this.dcTab[0], temp, index)\n if (value >= 0xff00) {\n return value\n }\n\n n = this.getn(prev, value, temp, index)\n nRestart = n >> 8\n\n if (nRestart >= Decoder.RESTART_MARKER_BEGIN && nRestart <= Decoder.RESTART_MARKER_END) {\n return nRestart\n }\n\n prev[0] += n\n }\n\n return 0\n }\n\n //\tHuffman table for fast search: (HuffTab) 8-bit Look up table 2-layer search architecture, 1st-layer represent 256 node (8 bits) if codeword-length > 8\n //\tbits, then the entry of 1st-layer = (# of 2nd-layer table) | MSB and it is stored in the 2nd-layer Size of tables in each layer are 256.\n //\tHuffTab[*][*][0-256] is always the only 1st-layer table.\n //\n //\tAn entry can be: (1) (# of 2nd-layer table) | MSB , for code length > 8 in 1st-layer (2) (Code length) << 8 | HuffVal\n //\n //\tHuffmanValue(table HuffTab[x][y] (ex) HuffmanValue(HuffTab[1][0],...)\n //\t ):\n //\t return: Huffman Value of table\n //\t 0xFF?? if it receives a MARKER\n //\t Parameter: table HuffTab[x][y] (ex) HuffmanValue(HuffTab[1][0],...)\n //\t temp temp storage for remainded bits\n //\t index index to bit of temp\n //\t in FILE pointer\n //\t Effect:\n //\t temp store new remainded bits\n //\t index change to new index\n //\t in change to new position\n //\t NOTE:\n //\t Initial by temp=0; index=0;\n //\t NOTE: (explain temp and index)\n //\t temp: is always in the form at calling time or returning time\n //\t | byte 4 | byte 3 | byte 2 | byte 1 |\n //\t | 0 | 0 | 00000000 | 00000??? | if not a MARKER\n //\t ^index=3 (from 0 to 15)\n //\t 321\n //\t NOTE (marker and marker_index):\n //\t If get a MARKER from 'in', marker=the low-byte of the MARKER\n //\t and marker_index=9\n //\t If marker_index=9 then index is always > 8, or HuffmanValue()\n //\t will not be called\n getHuffmanValue(table: number[], temp: number[], index: number[]): number {\n let code, input\n const mask = 0xffff\n\n if (!this.stream) throw new Error('stream not initialized')\n\n if (index[0] < 8) {\n temp[0] <<= 8\n input = this.stream.get8()\n if (input === 0xff) {\n this.marker = this.stream.get8()\n if (this.marker !== 0) {\n this.markerIndex = Decoder.MARKER_SEEN\n }\n }\n temp[0] |= input\n } else {\n index[0] -= 8\n }\n\n code = table[temp[0] >> index[0]]\n\n if ((code & Decoder.MSB) !== 0) {\n if (this.markerIndex !== 0) {\n this.markerIndex = 0\n return 0xff00 | this.marker\n }\n\n temp[0] &= mask >> (16 - index[0])\n temp[0] <<= 8\n input = this.stream.get8()\n\n if (input === 0xff) {\n this.marker = this.stream.get8()\n if (this.marker !== 0) {\n this.markerIndex = Decoder.MARKER_SEEN\n }\n }\n\n temp[0] |= input\n code = table[(code & 0xff) * 256 + (temp[0] >> index[0])]\n index[0] += 8\n }\n\n index[0] += 8 - (code >> 8)\n\n if (index[0] < 0) {\n throw new Error('index=' + index[0] + ' temp=' + temp[0] + ' code=' + code + ' in HuffmanValue()')\n }\n\n if (this.readPastEntropyData(index)) {\n this.markerIndex = 0\n return 0xff00 | this.marker\n }\n\n temp[0] &= mask >> (16 - index[0])\n return code & 0xff\n }\n\n getn(PRED: number[], n: number, temp: number[], index: number[]) {\n let result, input\n const one = 1\n const n_one = -1\n const mask = 0xffff\n\n if (this.stream === null) throw new Error('stream not initialized')\n\n if (n === 0) {\n return 0\n }\n\n if (n === 16) {\n if (PRED[0] >= 0) {\n return -32768\n } else {\n return 32768\n }\n }\n\n index[0] -= n\n\n if (index[0] >= 0) {\n if (this.readPastEntropyData(index)) {\n this.markerIndex = 0\n return (0xff00 | this.marker) << 8\n }\n\n result = temp[0] >> index[0]\n temp[0] &= mask >> (16 - index[0])\n } else {\n temp[0] <<= 8\n input = this.stream.get8()\n\n if (input === 0xff) {\n this.marker = this.stream.get8()\n if (this.marker !== 0) {\n this.markerIndex = Decoder.MARKER_SEEN\n }\n }\n\n temp[0] |= input\n index[0] += 8\n\n if (index[0] < 0) {\n if (this.markerIndex !== 0) {\n this.markerIndex = 0\n return (0xff00 | this.marker) << 8\n }\n\n temp[0] <<= 8\n input = this.stream.get8()\n\n if (input === 0xff) {\n this.marker = this.stream.get8()\n if (this.marker !== 0) {\n this.markerIndex = Decoder.MARKER_SEEN\n }\n }\n\n temp[0] |= input\n index[0] += 8\n }\n\n if (index[0] < 0) {\n throw new Error('index=' + index[0] + ' in getn()')\n }\n\n if (this.readPastEntropyData(index)) {\n this.markerIndex = 0\n return (0xff00 | this.marker) << 8\n }\n\n result = temp[0] >> index[0]\n temp[0] &= mask >> (16 - index[0])\n }\n\n if (result < one << (n - 1)) {\n result += (n_one << n) + 1\n }\n\n return result\n }\n\n getPreviousX(compOffset = 0): number {\n if (this.getter === null) throw new Error(\"decode hasn't run yet\")\n\n if (this.xLoc > 0) {\n return this.getter(this.yLoc * this.xDim + this.xLoc - 1, compOffset)\n } else if (this.yLoc > 0) {\n return this.getPreviousY(compOffset)\n } else {\n return 1 << (this.frame.precision - 1)\n }\n }\n\n getPreviousXY(compOffset = 0) {\n if (this.getter === null) throw new Error(\"decode hasn't run yet\")\n\n if (this.xLoc > 0 && this.yLoc > 0) {\n return this.getter((this.yLoc - 1) * this.xDim + this.xLoc - 1, compOffset)\n } else {\n return this.getPreviousY(compOffset)\n }\n }\n\n getPreviousY(compOffset = 0) {\n if (this.getter === null) throw new Error(\"decode hasn't run yet\")\n\n if (this.yLoc > 0) {\n return this.getter((this.yLoc - 1) * this.xDim + this.xLoc, compOffset)\n } else {\n return this.getPreviousX(compOffset)\n }\n }\n\n /**\n * True when the bits just consumed came out of a marker rather than out of\n * the entropy coded segment.\n *\n * `temp` holds `index` unconsumed bits (see the `getHuffmanValue` notes). The\n * moment `getHuffmanValue`/`getn` shifts in a byte that turns out to be the\n * 0xFF introducing a marker, those 8 bits stop being data: only the\n * `index - MARKER_BITS` bits above them are real, and the scan is over once\n * they run out.\n *\n * Consuming the last real bit leaves `index === MARKER_BITS`, and that is\n * still a valid decode - it is a scan whose final Huffman code ends exactly\n * on a byte boundary, so the encoder had no partial byte left to pad (T.81\n * B.1.1.2 pads only an incomplete final byte). Testing `index < markerIndex`\n * rejected that case and dropped the frame's last sample, which is what\n * DCMTK produces when a frame ends in a long run of one value: the run's\n * short codes tile the final byte exactly.\n */\n readPastEntropyData(index: number[]): boolean {\n return this.markerIndex !== 0 && index[0] < Decoder.MARKER_BITS\n }\n\n outputSingle(PRED: number[]) {\n if (this.setter === null) throw new Error(\"decode hasn't run yet\")\n\n if (this.xLoc < this.xDim && this.yLoc < this.yDim) {\n this.setter(this.yLoc * this.xDim + this.xLoc, this.mask & PRED[0])\n\n this.xLoc += 1\n\n if (this.xLoc >= this.xDim) {\n this.yLoc += 1\n this.xLoc = 0\n }\n }\n }\n\n outputRGB(PRED: number[]) {\n if (this.setter === null) throw new Error(\"decode hasn't run yet\")\n\n const offset = this.yLoc * this.xDim + this.xLoc\n\n if (this.xLoc < this.xDim && this.yLoc < this.yDim) {\n this.setter(offset, PRED[0], 0)\n this.setter(offset, PRED[1], 1)\n this.setter(offset, PRED[2], 2)\n\n this.xLoc += 1\n\n if (this.xLoc >= this.xDim) {\n this.yLoc += 1\n this.xLoc = 0\n }\n }\n }\n\n setValue8(index: number, val: number) {\n if (!this.outputData) throw new Error('output data not ready')\n\n if (littleEndian) {\n this.outputData[index] = val\n } else {\n this.outputData[index] = ((val & 0xff) << 8) | ((val >> 8) & 0xff)\n }\n }\n\n getValue8(index: number) {\n if (this.outputData === null) throw new Error('output data not ready')\n if (littleEndian) {\n return this.outputData[index] // mask should not be necessary because outputData is either Int8Array or Int16Array\n } else {\n const val = this.outputData[index]\n return ((val & 0xff) << 8) | ((val >> 8) & 0xff)\n }\n }\n\n setValueRGB(index: number, val: number, compOffset = 0) {\n if (this.outputData === null) return\n this.outputData[index * 3 + compOffset] = val\n }\n\n getValueRGB(index: number, compOffset: number) {\n if (this.outputData === null) throw new Error('output data not ready')\n return this.outputData[index * 3 + compOffset]\n }\n\n readApp() {\n if (this.stream === null) return null\n\n let count = 0\n const length = this.stream.get16()\n count += 2\n\n while (count < length) {\n this.stream.get8()\n count += 1\n }\n\n return length\n }\n\n readComment() {\n if (this.stream === null) return null\n\n let sb = ''\n let count = 0\n\n const length = this.stream.get16()\n count += 2\n\n while (count < length) {\n sb += this.stream.get8()\n count += 1\n }\n\n return sb\n }\n\n readNumber() {\n if (this.stream === null) return null\n\n const Ld = this.stream.get16()\n\n if (Ld !== 4) {\n throw new Error('ERROR: Define number format throw new IOException [Ld!=4]')\n }\n\n return this.stream.get16()\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,IAAM,gBAAgB;AAAA,EAC3B,OAAO;AAAA,EACP,eAAe;AAAA,EACf,OAAO;AACT;;;ACJO,IAAM,aAAN,MAAiB;AAAA,EACtB;AAAA,EACA;AAAA,EAEA,YAAY,MAAmB,QAAiB,QAAiB;AAC/D,SAAK,SAAS,IAAI,WAAW,MAAM,QAAQ,MAAM;AACjD,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,QAAQ;AAEN,UAAM,SAAS,KAAK,OAAO,KAAK,KAAK,KAAK,KAAK,KAAK,OAAO,KAAK,QAAQ,CAAC;AACzE,SAAK,SAAS;AACd,WAAO;AAAA,EACT;AAAA,EAEA,OAAO;AAEL,UAAM,QAAQ,KAAK,OAAO,KAAK,KAAK;AACpC,SAAK,SAAS;AACd,WAAO;AAAA,EACT;AACF;;;ACnBO,IAAM,cAAN,MAAkB;AAAA,EACvB,OAAO;AAAA,EACP,OAAO;AAAA,EACP,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,aAA0C,CAAC;AAAA,EAE3C,KAAK,MAAkB;AACrB,QAAI,QAAQ;AACZ,QAAI;AAEJ,UAAM,SAAS,KAAK,MAAM;AAC1B,aAAS;AAET,SAAK,YAAY,KAAK,KAAK;AAC3B,aAAS;AAET,SAAK,OAAO,KAAK,MAAM;AACvB,aAAS;AAET,SAAK,OAAO,KAAK,MAAM;AACvB,aAAS;AAET,SAAK,UAAU,KAAK,KAAK;AACzB,aAAS;AACT,aAAS,IAAI,GAAG,KAAK,KAAK,SAAS,KAAK,GAAG;AACzC,UAAI,QAAQ,QAAQ;AAClB,cAAM,IAAI,MAAM,2BAA2B;AAAA,MAC7C;AAEA,YAAM,IAAI,KAAK,KAAK;AACpB,eAAS;AAET,UAAI,SAAS,QAAQ;AACnB,cAAM,IAAI,MAAM,mCAAmC;AAAA,MACrD;AAEA,aAAO,KAAK,KAAK;AACjB,eAAS;AAET,UAAI,CAAC,KAAK,WAAW,CAAC,GAAG;AACvB,aAAK,WAAW,CAAC,IAAI,EAAE,GAAG,cAAc;AAAA,MAC1C;AAEA,WAAK,WAAW,CAAC,EAAE,QAAQ,QAAQ;AACnC,WAAK,WAAW,CAAC,EAAE,QAAQ,OAAO;AAClC,WAAK,WAAW,CAAC,EAAE,gBAAgB,KAAK,KAAK;AAC7C,eAAS;AAAA,IACX;AAEA,QAAI,UAAU,QAAQ;AACpB,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAEA,WAAO;AAAA,EACT;AACF;;;AC3DA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGO,IAAM,cAAc,IAAI,eAA8C;AAC3E,MAAI,WAAW,SAAS,GAAG;AACzB,UAAM,MAAM,WAAW,CAAC;AACxB,UAAM,OAAO,WAAW,MAAM,CAAC;AAC/B,UAAM,WAAW,CAAC;AAClB,aAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,eAAS,CAAC,IAAI,YAAY,GAAG,IAAI;AAAA,IACnC;AACA,WAAO;AAAA,EACT,OAAO;AACL,WAAO,MAAM,WAAW,CAAC,CAAC,EAAE,KAAK,MAAS;AAAA,EAC5C;AACF;AAGO,IAAM,eAAe,WAAY;AACtC,MAAI;AACJ,QAAMA,YAAW,CAAC;AAClB,WAAS,IAAI,GAAG,IAAI,KAAK,KAAK;AAC5B,QAAI;AACJ,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAI,IAAI,IAAI,aAAc,MAAM,IAAK,MAAM;AAAA,IAC7C;AACA,IAAAA,UAAS,CAAC,IAAI;AAAA,EAChB;AACA,SAAOA;AACT;AAEO,IAAM,WAAW,aAAa;AAE9B,IAAM,QAAQ,SAAU,QAAqB;AAClD,QAAM,YAAY,IAAI,WAAW,MAAM;AACvC,MAAI,MAAM,IAAI;AAEd,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,UAAO,QAAQ,IAAK,UAAU,MAAM,UAAU,CAAC,KAAK,GAAI;AAAA,EAC1D;AAEA,UAAQ,MAAM,QAAQ;AACxB;;;ACvCO,IAAM,eAAN,MAAM,cAAa;AAAA,EACxB,OAAO,MAAM;AAAA,EAEb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,cAAc;AACZ,SAAK,IAAI,YAAY,GAAG,GAAG,EAAE;AAC7B,SAAK,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC;AACrB,SAAK,IAAI,YAAY,GAAG,GAAG,IAAI,GAAG;AAClC,SAAK,KAAK;AAAA,MACR,CAAC,GAAG,CAAC;AAAA,MACL,CAAC,GAAG,CAAC;AAAA,MACL,CAAC,GAAG,CAAC;AAAA,MACL,CAAC,GAAG,CAAC;AAAA,IACP;AAAA,EACF;AAAA,EAEA,KAAK,MAAkB,SAAuB;AAC5C,QAAI,QAAQ;AACZ,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,QAAI;AAEJ,UAAM,SAAS,KAAK,MAAM;AAC1B,aAAS;AAET,WAAO,QAAQ,QAAQ;AACrB,aAAO,KAAK,KAAK;AACjB,eAAS;AACT,UAAI,OAAO;AACX,UAAI,IAAI,GAAG;AACT,cAAM,IAAI,MAAM,6BAA6B;AAAA,MAC/C;AAEA,UAAI,QAAQ;AACZ,UAAI,IAAI,GAAG;AACT,cAAM,IAAI,MAAM,yCAAyC;AAAA,MAC3D;AAEA,WAAK,GAAG,CAAC,IAAI;AACb,WAAK,GAAG,CAAC,EAAE,CAAC,IAAI;AAEhB,WAAK,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG;AAC1B,aAAK,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,KAAK;AAC5B,iBAAS;AAAA,MACX;AAEA,WAAK,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG;AAC1B,aAAK,IAAI,GAAG,IAAI,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,GAAG;AACvC,cAAI,QAAQ,QAAQ;AAClB,kBAAM,IAAI,MAAM,8CAA8C;AAAA,UAChE;AAEA,eAAK,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,KAAK,KAAK;AAC/B,mBAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAEA,QAAI,UAAU,QAAQ;AACpB,YAAM,IAAI,MAAM,+CAA+C;AAAA,IACjE;AAEA,SAAK,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG;AACzB,WAAK,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG;AACzB,YAAI,KAAK,GAAG,CAAC,EAAE,CAAC,MAAM,GAAG;AACvB,eAAK,eAAe,QAAQ,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;AAAA,QAC/D;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,eAAe,KAAe,GAAa,GAAe;AACxD,QAAI,cAAc,GAAG,GAAG,GAAG;AAC3B,UAAM,OAAO;AACb,QAAI;AAEJ,SAAK,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG;AAEzB,WAAK,IAAI,GAAG,IAAI,EAAE,CAAC,GAAG,KAAK,GAAG;AAC5B,aAAK,IAAI,GAAG,IAAI,QAAS,IAAI,GAAI,KAAK,GAAG;AACvC,cAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,IAAM,IAAI,KAAM;AAC/B,eAAK;AAAA,QACP;AAAA,MACF;AAAA,IACF;AAEA,SAAK,IAAI,GAAG,IAAI,KAAK,KAAK,GAAG,KAAK,GAAG;AACnC,UAAI,CAAC,IAAI,IAAI,cAAa;AAAA,IAC5B;AAEA,mBAAe;AACf,QAAI;AAEJ,SAAK,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG;AAE1B,WAAK,IAAI,GAAG,IAAI,EAAE,CAAC,GAAG,KAAK,GAAG;AAC5B,aAAK,IAAI,GAAG,IAAI,QAAS,IAAI,GAAI,KAAK,GAAG;AACvC,cAAI,eAAe,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,IAAM,IAAI,KAAM;AACpD,eAAK;AAAA,QACP;AAEA,YAAI,KAAK,KAAK;AACZ,cAAI,IAAI,KAAK;AACX,kBAAM,IAAI,MAAM,gCAAgC;AAAA,UAClD;AAEA,cAAI;AACJ,0BAAgB;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AC/HO,IAAM,oBAAN,MAAM,mBAAkB;AAAA,EAC7B,YAAsB,CAAC;AAAA;AAAA,EACvB,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EAChB,cAA0B,YAAY,GAAG,EAAE;AAAA;AAAA,EAE3C,OAAO,2BAA2B,SAAU,MAAgB,OAAiB;AAC3E,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG;AAC7B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAC1B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAC1B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAC1B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAC1B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAC1B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAC1B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAC1B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAAA,IAC5B;AAEA,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG;AAC7B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAC1B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAC1B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAC1B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAC1B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAC1B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAC1B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAC1B,WAAK,MAAM,IAAI,IAAI,CAAC,CAAC,KAAK;AAAA,IAC5B;AAEA,aAAS,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG;AAC9B,WAAK,CAAC,MAAM;AAAA,IACd;AAAA,EACF;AAAA,EAEA,KAAK,MAAkB,OAAiB;AACtC,QAAI,QAAQ;AACZ,QAAI;AACJ,QAAI;AACJ,QAAI;AAEJ,UAAM,SAAS,KAAK,MAAM;AAC1B,aAAS;AAET,WAAO,QAAQ,QAAQ;AACrB,aAAO,KAAK,KAAK;AACjB,eAAS;AACT,UAAI,OAAO;AAEX,UAAI,IAAI,GAAG;AACT,cAAM,IAAI,MAAM,kCAAkC;AAAA,MACpD;AAEA,WAAK,UAAU,CAAC,IAAI,QAAQ;AAE5B,UAAI,KAAK,UAAU,CAAC,MAAM,GAAG;AAC3B,aAAK,UAAU,CAAC,IAAI;AAAA,MACtB,WAAW,KAAK,UAAU,CAAC,MAAM,GAAG;AAClC,aAAK,UAAU,CAAC,IAAI;AAAA,MACtB,OAAO;AACL,cAAM,IAAI,MAAM,2CAA2C;AAAA,MAC7D;AAEA,WAAK,GAAG,CAAC,IAAI;AAEb,UAAI,KAAK,UAAU,CAAC,MAAM,GAAG;AAC3B,aAAK,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG;AAC1B,cAAI,QAAQ,QAAQ;AAClB,kBAAM,IAAI,MAAM,wCAAwC;AAAA,UAC1D;AAEA,eAAK,YAAY,CAAC,EAAE,CAAC,IAAI,KAAK,KAAK;AACnC,mBAAS;AAAA,QACX;AAEA,2BAAkB,yBAAyB,KAAK,YAAY,CAAC,GAAG,KAAK;AAAA,MACvE,OAAO;AACL,aAAK,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG;AAC1B,cAAI,QAAQ,QAAQ;AAClB,kBAAM,IAAI,MAAM,wCAAwC;AAAA,UAC1D;AAEA,eAAK,YAAY,CAAC,EAAE,CAAC,IAAI,KAAK,MAAM;AACpC,mBAAS;AAAA,QACX;AAEA,2BAAkB,yBAAyB,KAAK,YAAY,CAAC,GAAG,KAAK;AAAA,MACvE;AAAA,IACF;AAEA,QAAI,UAAU,QAAQ;AACpB,YAAM,IAAI,MAAM,6CAA6C;AAAA,IAC/D;AAEA,WAAO;AAAA,EACT;AACF;;;ACjGO,IAAM,gBAAgB;AAAA,EAC3B,UAAU;AAAA;AAAA,EACV,UAAU;AAAA;AAAA,EACV,aAAa;AAAA;AACf;;;ACDO,IAAM,aAAN,MAAiB;AAAA,EACtB,KAAK;AAAA,EACL,KAAK;AAAA,EACL,UAAU;AAAA;AAAA,EACV,YAAY;AAAA;AAAA,EACZ,cAAc;AAAA;AAAA,EACd,aAA0C,CAAC;AAAA,EAE3C,KAAK,MAAkB;AACrB,QAAI,QAAQ;AACZ,QAAI;AACJ,QAAI;AAEJ,UAAM,SAAS,KAAK,MAAM;AAC1B,aAAS;AAET,SAAK,UAAU,KAAK,KAAK;AACzB,aAAS;AAET,SAAK,IAAI,GAAG,IAAI,KAAK,SAAS,KAAK,GAAG;AACpC,WAAK,WAAW,CAAC,IAAI,EAAE,GAAG,cAAc;AAExC,UAAI,QAAQ,QAAQ;AAClB,cAAM,IAAI,MAAM,iCAAiC;AAAA,MACnD;AAEA,WAAK,WAAW,CAAC,EAAE,cAAc,KAAK,KAAK;AAC3C,eAAS;AAET,aAAO,KAAK,KAAK;AACjB,eAAS;AAET,WAAK,WAAW,CAAC,EAAE,WAAW,QAAQ;AACtC,WAAK,WAAW,CAAC,EAAE,WAAW,OAAO;AAAA,IACvC;AAEA,SAAK,YAAY,KAAK,KAAK;AAC3B,aAAS;AAET,SAAK,cAAc,KAAK,KAAK;AAC7B,aAAS;AAET,WAAO,KAAK,KAAK;AACjB,SAAK,KAAK,QAAQ;AAClB,SAAK,KAAK,OAAO;AACjB,aAAS;AAET,QAAI,UAAU,QAAQ;AACpB,YAAM,IAAI,MAAM,6CAA6C;AAAA,IAC/D;AAEA,WAAO;AAAA,EACT;AACF;;;AChDA,IAAM,eAAgB,WAAY;AAChC,QAAM,SAAS,IAAI,YAAY,CAAC;AAChC,MAAI,SAAS,MAAM,EAAE;AAAA,IAAS;AAAA,IAAG;AAAA,IAAK;AAAA;AAAA,EAAuB;AAE7D,SAAO,IAAI,WAAW,MAAM,EAAE,CAAC,MAAM;AACvC,EAAG;AAEI,IAAM,UAAN,MAAM,SAAQ;AAAA,EACnB,OAAO,SAAS;AAAA,IACd;AAAA,IAAG;AAAA,IAAG;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAG;AAAA,IAAG;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAG;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAG;AAAA,IAAG;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAG;AAAA,IAAG;AAAA,IAAI;AAAA,IAC/G;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAG;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAC/G;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,EACd;AAAA,EAEA,OAAO,QAAQ;AAAA,IACb;AAAA,IAAG;AAAA,IAAG;AAAA,IAAG;AAAA,IAAG;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAG;AAAA,IAAG;AAAA,IAAG;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAG;AAAA,IAAG;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAG;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAC9G;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,IAChH;AAAA,IAAI;AAAA,IAAI;AAAA,IAAI;AAAA,EACd;AAAA,EAEA,OAAO,sBAAsB;AAAA,EAC7B,OAAO,MAAM;AAAA,EACb,OAAO,uBAAuB;AAAA,EAC9B,OAAO,qBAAqB;AAAA;AAAA;AAAA;AAAA,EAK5B,OAAO,cAAc;AAAA;AAAA;AAAA,EAIrB,OAAO,cAAc;AAAA,EAErB,SAA6B;AAAA,EAC7B,SAA4B;AAAA,EAC5B,QAAQ,IAAI,YAAY;AAAA,EACxB,YAAY,IAAI,aAAa;AAAA,EAC7B,aAAa,IAAI,kBAAkB;AAAA,EACnC,OAAO,IAAI,WAAW;AAAA,EACtB,KAAmB,YAAY,IAAI,GAAG,EAAE;AAAA;AAAA,EACxC,UAAwB,YAAY,GAAG,GAAG,KAAK,GAAG;AAAA,EAClD,cAAwB,CAAC;AAAA,EACzB,SAAmB,CAAC;AAAA;AAAA,EACpB,QAAoB,YAAY,IAAI,CAAC;AAAA;AAAA,EACrC,QAAoB,YAAY,IAAI,CAAC;AAAA;AAAA,EACrC,OAAmB,YAAY,IAAI,CAAC;AAAA;AAAA,EACpC,SAAS;AAAA,EACT,cAAc;AAAA,EACd,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,aAA8C;AAAA,EAC9C,aAAa;AAAA,EACb,OAAO;AAAA,EACP,WAAW;AAAA,EAEX,YAAgC;AAAA,EAChC,aAA0C,CAAC;AAAA,EAE3C,SAAiE;AAAA,EACjE,SAA6E;AAAA,EAC7E,SAA4C;AAAA,EAC5C,WAAqD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOrD,YAAY,QAA6B,UAAmB;AAC1D,SAAK,SAAS,UAAU;AACxB,SAAK,WAAW,YAAY;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,QAAqB,QAAgB,QAA6B;AAC3E,UAAM,SAAS,KAAK,OAAO,QAAQ,QAAQ,MAAM;AACjD,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,OAAO,QAAsB,QAAiB,QAAiB,UAAmB;AAChF,QAAI,UAAU;AACd,UAAM,OAAO,CAAC;AACd,QAAI;AACJ,QAAI;AACJ,UAAM,OAAO,CAAC;AACd,UAAM,QAAQ,CAAC;AACf,QAAI;AAEJ,QAAI,QAAQ;AACV,WAAK,SAAS;AAAA,IAChB;AAEA,QAAI,aAAa,QAAW;AAC1B,WAAK,WAAW;AAAA,IAClB;AAEA,SAAK,SAAS,IAAI,WAAW,KAAK,QAAuB,QAAQ,MAAM;AACvE,SAAK,SAAS;AAEd,SAAK,OAAO;AACZ,SAAK,OAAO;AACZ,QAAI,UAAU,KAAK,OAAO,MAAM;AAEhC,QAAI,YAAY,OAAQ;AAEtB,YAAM,IAAI,MAAM,iBAAiB;AAAA,IACnC;AAEA,cAAU,KAAK,OAAO,MAAM;AAE5B,WAAO,WAAW,MAAM,QAAU,YAAY,OAAQ;AAEpD,cAAQ,SAAS;AAAA,QACf,KAAK;AACH,eAAK,UAAU,KAAK,KAAK,QAAQ,KAAK,OAAO;AAC7C;AAAA,QACF,KAAK;AACH,gBAAM,IAAI,MAAM,2EAA2E;AAAA,QAC7F,KAAK;AACH,eAAK,WAAW,KAAK,KAAK,QAAQ,SAAQ,KAAK;AAC/C;AAAA,QACF,KAAK;AACH,eAAK,kBAAkB,KAAK,WAAW,KAAK;AAC5C;AAAA,QACF,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AAAA,QACL,KAAK;AACH,eAAK,QAAQ;AACb;AAAA,QACF,KAAK;AACH,eAAK,YAAY;AACjB;AAAA,QACF;AACE,cAAI,WAAW,MAAM,KAAM;AACzB,kBAAM,IAAI,MAAM,+CAA+C;AAAA,UACjE;AAAA,MACJ;AAEA,gBAAU,KAAK,OAAO,MAAM;AAAA,IAC9B;AAEA,QAAI,UAAU,SAAU,UAAU,OAAQ;AACxC,YAAM,IAAI,MAAM,0CAA0C;AAAA,IAC5D;AAEA,SAAK,MAAM,KAAK,KAAK,MAAM;AAC3B,cAAU,KAAK,OAAO,MAAM;AAE5B,OAAG;AACD,aAAO,YAAY,OAAS;AAE1B,gBAAQ,SAAS;AAAA,UACf,KAAK;AACH,iBAAK,UAAU,KAAK,KAAK,QAAQ,KAAK,OAAO;AAC7C;AAAA,UACF,KAAK;AACH,kBAAM,IAAI,MAAM,2EAA2E;AAAA,UAC7F,KAAK;AACH,iBAAK,WAAW,KAAK,KAAK,QAAQ,SAAQ,KAAK;AAC/C;AAAA,UACF,KAAK;AACH,iBAAK,kBAAkB,KAAK,WAAW,KAAK;AAC5C;AAAA,UACF,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AAAA,UACL,KAAK;AACH,iBAAK,QAAQ;AACb;AAAA,UACF,KAAK;AACH,iBAAK,YAAY;AACjB;AAAA,UACF;AACE,gBAAI,WAAW,MAAM,KAAM;AACzB,oBAAM,IAAI,MAAM,sDAAsD;AAAA,YACxE;AAAA,QACJ;AAEA,kBAAU,KAAK,OAAO,MAAM;AAAA,MAC9B;AAEA,WAAK,YAAY,KAAK,MAAM;AAC5B,WAAK,aAAa,KAAK,MAAM;AAE7B,UAAI,CAAC,KAAK,UAAU;AAClB,aAAK,WAAW,KAAK,MAAM,KAAK,KAAK,KAAK,YAAY,CAAC,CAAC;AAAA,MAC1D;AAEA,UAAI,KAAK,aAAa,GAAG;AACvB,aAAK,OAAO;AAAA,MACd,OAAO;AACL,aAAK,OAAO;AAAA,MACd;AAEA,WAAK,KAAK,KAAK,KAAK,MAAM;AAC1B,WAAK,UAAU,KAAK,KAAK;AACzB,WAAK,YAAY,KAAK,KAAK;AAE3B,UAAI,KAAK,aAAa,GAAG;AACvB,YAAI,KAAK,YAAY,GAAG;AACtB,eAAK,SAAS,KAAK;AACnB,eAAK,SAAS,KAAK;AACnB,eAAK,SAAS,KAAK;AAAA,QACrB,OAAO;AACL,eAAK,SAAS,KAAK;AACnB,eAAK,SAAS,KAAK;AACnB,eAAK,SAAS,KAAK;AAAA,QACrB;AAAA,MACF,OAAO;AACL,aAAK,SAAS,KAAK;AACnB,aAAK,SAAS,KAAK;AACnB,aAAK,SAAS,KAAK;AAAA,MACrB;AAEA,cAAQ,KAAK,WAAW;AAAA,QACtB,KAAK;AACH,eAAK,WAAW,KAAK;AACrB;AAAA,QACF,KAAK;AACH,eAAK,WAAW,KAAK;AACrB;AAAA,QACF,KAAK;AACH,eAAK,WAAW,KAAK;AACrB;AAAA,QACF,KAAK;AACH,eAAK,WAAW,KAAK;AACrB;AAAA,QACF,KAAK;AACH,eAAK,WAAW,KAAK;AACrB;AAAA,QACF,KAAK;AACH,eAAK,WAAW,KAAK;AACrB;AAAA,QACF;AACE,eAAK,WAAW,KAAK;AACrB;AAAA,MACJ;AAKA,WAAK,IAAI,GAAG,IAAI,KAAK,SAAS,KAAK,GAAG;AACpC,gBAAQ,KAAK,KAAK,WAAW,CAAC,EAAE;AAChC,aAAK,KAAK,CAAC,IAAI,KAAK,WAAW,YAAY,KAAK,WAAW,KAAK,EAAE,aAAa;AAC/E,aAAK,OAAO,CAAC,IAAI,KAAK,WAAW,KAAK,EAAE,QAAQ,KAAK,WAAW,KAAK,EAAE;AACvE,aAAK,MAAM,CAAC,IAAI,KAAK,QAAQ,KAAK,KAAK,WAAW,CAAC,EAAE,QAAQ,EAAE,CAAC;AAChE,aAAK,MAAM,CAAC,IAAI,KAAK,QAAQ,KAAK,KAAK,WAAW,CAAC,EAAE,QAAQ,EAAE,CAAC;AAAA,MAClE;AAEA,WAAK,OAAO,KAAK,MAAM;AACvB,WAAK,OAAO,KAAK,MAAM;AACvB,UAAI,KAAK,aAAa,GAAG;AACvB,aAAK,aAAa,IAAI,WAAW,IAAI,YAAY,KAAK,OAAO,KAAK,OAAO,KAAK,WAAW,KAAK,OAAO,CAAC;AAAA,MACxG,OAAO;AACL,aAAK,aAAa,IAAI,YAAY,IAAI,YAAY,KAAK,OAAO,KAAK,OAAO,KAAK,WAAW,KAAK,OAAO,CAAC;AAAA,MACzG;AAEA,iBAAW;AAEX,aAAO,MAAM;AAEX,aAAK,CAAC,IAAI;AACV,cAAM,CAAC,IAAI;AAEX,aAAK,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG;AAC1B,eAAK,CAAC,IAAI,KAAM,KAAK,YAAY;AAAA,QACnC;AAEA,YAAI,KAAK,oBAAoB,GAAG;AAC9B,oBAAU,KAAK,WAAW,MAAM,MAAM,KAAK;AAE3C,iBAAO,YAAY,KAAK,KAAK,OAAO,KAAK,QAAQ,KAAK,OAAO,KAAK,MAAM;AACtE,iBAAK,OAAO,IAAI;AAChB,sBAAU,KAAK,WAAW,MAAM,MAAM,KAAK;AAAA,UAC7C;AAEA;AAAA,QACF;AAEA,aAAK,SAAS,GAAG,SAAS,KAAK,iBAAiB,UAAU,GAAG;AAC3D,eAAK,aAAa,WAAW;AAC7B,oBAAU,KAAK,WAAW,MAAM,MAAM,KAAK;AAC3C,eAAK,OAAO,IAAI;AAEhB,cAAI,YAAY,GAAG;AACjB;AAAA,UACF;AAAA,QACF;AAEA,YAAI,YAAY,GAAG;AACjB,cAAI,KAAK,gBAAgB,GAAG;AAC1B,sBAAU,QAAS,KAAK;AACxB,iBAAK,cAAc;AAAA,UACrB,OAAO;AACL,sBAAU,KAAK,OAAO,MAAM;AAAA,UAC9B;AAAA,QACF;AAEA,YAAI,EAAE,WAAW,SAAQ,wBAAwB,WAAW,SAAQ,qBAAqB;AACvF;AAAA,QACF;AAAA,MACF;AAEA,UAAI,YAAY,SAAU,YAAY,GAAG;AAEvC,aAAK,WAAW;AAChB,kBAAU,KAAK,OAAO,MAAM;AAAA,MAC9B;AAAA,IACF,SAAS,YAAY,SAAU,KAAK,OAAO,KAAK,QAAQ,KAAK,OAAO,KAAK,QAAQ,YAAY;AAE7F,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAAW,MAAgB,MAAgB,OAAyB;AAClE,QAAI,KAAK,YAAY,GAAG;AACtB,aAAO,KAAK,aAAa,MAAM,MAAM,KAAK;AAAA,IAC5C,WAAW,KAAK,YAAY,GAAG;AAC7B,aAAO,KAAK,UAAU,MAAM,MAAM,KAAK;AAAA,IACzC,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,QAAQ,YAAqB;AAC3B,WAAO,KAAK,aAAa,UAAU;AAAA,EACrC;AAAA,EAEA,QAAQ,YAAqB;AAC3B,WAAO,KAAK,aAAa,UAAU;AAAA,EACrC;AAAA,EAEA,QAAQ,YAAqB;AAC3B,WAAO,KAAK,cAAc,UAAU;AAAA,EACtC;AAAA,EAEA,QAAQ,YAAqB;AAC3B,WAAO,KAAK,aAAa,UAAU,IAAI,KAAK,aAAa,UAAU,IAAI,KAAK,cAAc,UAAU;AAAA,EACtG;AAAA,EAEA,QAAQ,YAAqB;AAC3B,WAAO,KAAK,aAAa,UAAU,KAAM,KAAK,aAAa,UAAU,IAAI,KAAK,cAAc,UAAU,KAAM;AAAA,EAC9G;AAAA,EAEA,QAAQ,YAAqB;AAC3B,WAAO,KAAK,aAAa,UAAU,KAAM,KAAK,aAAa,UAAU,IAAI,KAAK,cAAc,UAAU,KAAM;AAAA,EAC9G;AAAA,EAEA,QAAQ,YAAqB;AAC3B,YAAQ,KAAK,aAAa,UAAU,IAAI,KAAK,aAAa,UAAU,KAAK;AAAA,EAC3E;AAAA,EAEA,UAAU,MAAgB,MAAgB,OAAiB;AACzD,QAAI,KAAK,aAAa;AAAM,YAAM,IAAI,MAAM,uBAAuB;AAEnE,QAAI,OAAO,OAAO,MAAM,MAAM,GAAG,GAAG;AAEpC,SAAK,CAAC,IAAI,KAAK,SAAS,CAAC;AACzB,SAAK,CAAC,IAAI,KAAK,SAAS,CAAC;AACzB,SAAK,CAAC,IAAI,KAAK,SAAS,CAAC;AAEzB,SAAK,OAAO,GAAG,OAAO,KAAK,SAAS,QAAQ,GAAG;AAC7C,aAAO,KAAK,KAAK,IAAI;AACrB,cAAQ,KAAK,MAAM,IAAI;AACvB,cAAQ,KAAK,MAAM,IAAI;AACvB,WAAK,IAAI,GAAG,IAAI,KAAK,OAAO,IAAI,GAAG,KAAK,GAAG;AACzC,aAAK,IAAI,GAAG,IAAI,KAAK,YAAY,QAAQ,KAAK,GAAG;AAC/C,eAAK,YAAY,CAAC,IAAI;AAAA,QACxB;AAEA,YAAI,QAAQ,KAAK,gBAAgB,OAAO,MAAM,KAAK;AAEnD,YAAI,SAAS,OAAQ;AACnB,iBAAO;AAAA,QACT;AAEA,aAAK,IAAI,IAAI,KAAK,YAAY,CAAC,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,OAAO,OAAO,MAAM,KAAK;AACnF,aAAK,YAAY,CAAC,KAAK,KAAK,CAAC;AAE7B,aAAK,IAAI,GAAG,IAAI,IAAI,KAAK,GAAG;AAC1B,kBAAQ,KAAK,gBAAgB,OAAO,MAAM,KAAK;AAE/C,cAAI,SAAS,OAAQ;AACnB,mBAAO;AAAA,UACT;AAEA,eAAK,SAAS;AAEd,eAAK,QAAQ,QAAU,GAAG;AACxB,gBAAI,SAAS,MAAM,GAAG;AACpB;AAAA,YACF;AAAA,UACF,OAAO;AACL,iBAAK,YAAY,SAAQ,OAAO,CAAC,CAAC,IAAI,KAAK,KAAK,OAAO,QAAQ,IAAM,MAAM,KAAK,IAAI,KAAK,CAAC;AAAA,UAC5F;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,aAAa,MAAgB,MAAgB,OAAiB;AAC5D,QAAI,KAAK,aAAa;AAAM,YAAM,IAAI,MAAM,uBAAuB;AAEnE,QAAI,OAAO,GAAG,GAAG;AAEjB,QAAI,KAAK,YAAY;AACnB,WAAK,aAAa;AAClB,WAAK,CAAC,IAAI,KAAM,KAAK,MAAM,YAAY;AAAA,IACzC,OAAO;AACL,WAAK,CAAC,IAAI,KAAK,SAAS;AAAA,IAC1B;AAEA,SAAK,IAAI,GAAG,IAAI,KAAK,OAAO,CAAC,GAAG,KAAK,GAAG;AACtC,cAAQ,KAAK,gBAAgB,KAAK,MAAM,CAAC,GAAG,MAAM,KAAK;AACvD,UAAI,SAAS,OAAQ;AACnB,eAAO;AAAA,MACT;AAEA,UAAI,KAAK,KAAK,MAAM,OAAO,MAAM,KAAK;AACtC,iBAAW,KAAK;AAEhB,UAAI,YAAY,SAAQ,wBAAwB,YAAY,SAAQ,oBAAoB;AACtF,eAAO;AAAA,MACT;AAEA,WAAK,CAAC,KAAK;AAAA,IACb;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiCA,gBAAgB,OAAiB,MAAgB,OAAyB;AACxE,QAAI,MAAM;AACV,UAAM,OAAO;AAEb,QAAI,CAAC,KAAK;AAAQ,YAAM,IAAI,MAAM,wBAAwB;AAE1D,QAAI,MAAM,CAAC,IAAI,GAAG;AAChB,WAAK,CAAC,MAAM;AACZ,cAAQ,KAAK,OAAO,KAAK;AACzB,UAAI,UAAU,KAAM;AAClB,aAAK,SAAS,KAAK,OAAO,KAAK;AAC/B,YAAI,KAAK,WAAW,GAAG;AACrB,eAAK,cAAc,SAAQ;AAAA,QAC7B;AAAA,MACF;AACA,WAAK,CAAC,KAAK;AAAA,IACb,OAAO;AACL,YAAM,CAAC,KAAK;AAAA,IACd;AAEA,WAAO,MAAM,KAAK,CAAC,KAAK,MAAM,CAAC,CAAC;AAEhC,SAAK,OAAO,SAAQ,SAAS,GAAG;AAC9B,UAAI,KAAK,gBAAgB,GAAG;AAC1B,aAAK,cAAc;AACnB,eAAO,QAAS,KAAK;AAAA,MACvB;AAEA,WAAK,CAAC,KAAK,QAAS,KAAK,MAAM,CAAC;AAChC,WAAK,CAAC,MAAM;AACZ,cAAQ,KAAK,OAAO,KAAK;AAEzB,UAAI,UAAU,KAAM;AAClB,aAAK,SAAS,KAAK,OAAO,KAAK;AAC/B,YAAI,KAAK,WAAW,GAAG;AACrB,eAAK,cAAc,SAAQ;AAAA,QAC7B;AAAA,MACF;AAEA,WAAK,CAAC,KAAK;AACX,aAAO,OAAO,OAAO,OAAQ,OAAO,KAAK,CAAC,KAAK,MAAM,CAAC,EAAE;AACxD,YAAM,CAAC,KAAK;AAAA,IACd;AAEA,UAAM,CAAC,KAAK,KAAK,QAAQ;AAEzB,QAAI,MAAM,CAAC,IAAI,GAAG;AAChB,YAAM,IAAI,MAAM,WAAW,MAAM,CAAC,IAAI,WAAW,KAAK,CAAC,IAAI,WAAW,OAAO,oBAAoB;AAAA,IACnG;AAEA,QAAI,KAAK,oBAAoB,KAAK,GAAG;AACnC,WAAK,cAAc;AACnB,aAAO,QAAS,KAAK;AAAA,IACvB;AAEA,SAAK,CAAC,KAAK,QAAS,KAAK,MAAM,CAAC;AAChC,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,KAAK,MAAgB,GAAW,MAAgB,OAAiB;AAC/D,QAAI,QAAQ;AACZ,UAAM,MAAM;AACZ,UAAM,QAAQ;AACd,UAAM,OAAO;AAEb,QAAI,KAAK,WAAW;AAAM,YAAM,IAAI,MAAM,wBAAwB;AAElE,QAAI,MAAM,GAAG;AACX,aAAO;AAAA,IACT;AAEA,QAAI,MAAM,IAAI;AACZ,UAAI,KAAK,CAAC,KAAK,GAAG;AAChB,eAAO;AAAA,MACT,OAAO;AACL,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,CAAC,KAAK;AAEZ,QAAI,MAAM,CAAC,KAAK,GAAG;AACjB,UAAI,KAAK,oBAAoB,KAAK,GAAG;AACnC,aAAK,cAAc;AACnB,gBAAQ,QAAS,KAAK,WAAW;AAAA,MACnC;AAEA,eAAS,KAAK,CAAC,KAAK,MAAM,CAAC;AAC3B,WAAK,CAAC,KAAK,QAAS,KAAK,MAAM,CAAC;AAAA,IAClC,OAAO;AACL,WAAK,CAAC,MAAM;AACZ,cAAQ,KAAK,OAAO,KAAK;AAEzB,UAAI,UAAU,KAAM;AAClB,aAAK,SAAS,KAAK,OAAO,KAAK;AAC/B,YAAI,KAAK,WAAW,GAAG;AACrB,eAAK,cAAc,SAAQ;AAAA,QAC7B;AAAA,MACF;AAEA,WAAK,CAAC,KAAK;AACX,YAAM,CAAC,KAAK;AAEZ,UAAI,MAAM,CAAC,IAAI,GAAG;AAChB,YAAI,KAAK,gBAAgB,GAAG;AAC1B,eAAK,cAAc;AACnB,kBAAQ,QAAS,KAAK,WAAW;AAAA,QACnC;AAEA,aAAK,CAAC,MAAM;AACZ,gBAAQ,KAAK,OAAO,KAAK;AAEzB,YAAI,UAAU,KAAM;AAClB,eAAK,SAAS,KAAK,OAAO,KAAK;AAC/B,cAAI,KAAK,WAAW,GAAG;AACrB,iBAAK,cAAc,SAAQ;AAAA,UAC7B;AAAA,QACF;AAEA,aAAK,CAAC,KAAK;AACX,cAAM,CAAC,KAAK;AAAA,MACd;AAEA,UAAI,MAAM,CAAC,IAAI,GAAG;AAChB,cAAM,IAAI,MAAM,WAAW,MAAM,CAAC,IAAI,YAAY;AAAA,MACpD;AAEA,UAAI,KAAK,oBAAoB,KAAK,GAAG;AACnC,aAAK,cAAc;AACnB,gBAAQ,QAAS,KAAK,WAAW;AAAA,MACnC;AAEA,eAAS,KAAK,CAAC,KAAK,MAAM,CAAC;AAC3B,WAAK,CAAC,KAAK,QAAS,KAAK,MAAM,CAAC;AAAA,IAClC;AAEA,QAAI,SAAS,OAAQ,IAAI,GAAI;AAC3B,iBAAW,SAAS,KAAK;AAAA,IAC3B;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,aAAa,aAAa,GAAW;AACnC,QAAI,KAAK,WAAW;AAAM,YAAM,IAAI,MAAM,uBAAuB;AAEjE,QAAI,KAAK,OAAO,GAAG;AACjB,aAAO,KAAK,OAAO,KAAK,OAAO,KAAK,OAAO,KAAK,OAAO,GAAG,UAAU;AAAA,IACtE,WAAW,KAAK,OAAO,GAAG;AACxB,aAAO,KAAK,aAAa,UAAU;AAAA,IACrC,OAAO;AACL,aAAO,KAAM,KAAK,MAAM,YAAY;AAAA,IACtC;AAAA,EACF;AAAA,EAEA,cAAc,aAAa,GAAG;AAC5B,QAAI,KAAK,WAAW;AAAM,YAAM,IAAI,MAAM,uBAAuB;AAEjE,QAAI,KAAK,OAAO,KAAK,KAAK,OAAO,GAAG;AAClC,aAAO,KAAK,QAAQ,KAAK,OAAO,KAAK,KAAK,OAAO,KAAK,OAAO,GAAG,UAAU;AAAA,IAC5E,OAAO;AACL,aAAO,KAAK,aAAa,UAAU;AAAA,IACrC;AAAA,EACF;AAAA,EAEA,aAAa,aAAa,GAAG;AAC3B,QAAI,KAAK,WAAW;AAAM,YAAM,IAAI,MAAM,uBAAuB;AAEjE,QAAI,KAAK,OAAO,GAAG;AACjB,aAAO,KAAK,QAAQ,KAAK,OAAO,KAAK,KAAK,OAAO,KAAK,MAAM,UAAU;AAAA,IACxE,OAAO;AACL,aAAO,KAAK,aAAa,UAAU;AAAA,IACrC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,oBAAoB,OAA0B;AAC5C,WAAO,KAAK,gBAAgB,KAAK,MAAM,CAAC,IAAI,SAAQ;AAAA,EACtD;AAAA,EAEA,aAAa,MAAgB;AAC3B,QAAI,KAAK,WAAW;AAAM,YAAM,IAAI,MAAM,uBAAuB;AAEjE,QAAI,KAAK,OAAO,KAAK,QAAQ,KAAK,OAAO,KAAK,MAAM;AAClD,WAAK,OAAO,KAAK,OAAO,KAAK,OAAO,KAAK,MAAM,KAAK,OAAO,KAAK,CAAC,CAAC;AAElE,WAAK,QAAQ;AAEb,UAAI,KAAK,QAAQ,KAAK,MAAM;AAC1B,aAAK,QAAQ;AACb,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAAA,EAEA,UAAU,MAAgB;AACxB,QAAI,KAAK,WAAW;AAAM,YAAM,IAAI,MAAM,uBAAuB;AAEjE,UAAM,SAAS,KAAK,OAAO,KAAK,OAAO,KAAK;AAE5C,QAAI,KAAK,OAAO,KAAK,QAAQ,KAAK,OAAO,KAAK,MAAM;AAClD,WAAK,OAAO,QAAQ,KAAK,CAAC,GAAG,CAAC;AAC9B,WAAK,OAAO,QAAQ,KAAK,CAAC,GAAG,CAAC;AAC9B,WAAK,OAAO,QAAQ,KAAK,CAAC,GAAG,CAAC;AAE9B,WAAK,QAAQ;AAEb,UAAI,KAAK,QAAQ,KAAK,MAAM;AAC1B,aAAK,QAAQ;AACb,aAAK,OAAO;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAAA,EAEA,UAAU,OAAe,KAAa;AACpC,QAAI,CAAC,KAAK;AAAY,YAAM,IAAI,MAAM,uBAAuB;AAE7D,QAAI,cAAc;AAChB,WAAK,WAAW,KAAK,IAAI;AAAA,IAC3B,OAAO;AACL,WAAK,WAAW,KAAK,KAAM,MAAM,QAAS,IAAO,OAAO,IAAK;AAAA,IAC/D;AAAA,EACF;AAAA,EAEA,UAAU,OAAe;AACvB,QAAI,KAAK,eAAe;AAAM,YAAM,IAAI,MAAM,uBAAuB;AACrE,QAAI,cAAc;AAChB,aAAO,KAAK,WAAW,KAAK;AAAA,IAC9B,OAAO;AACL,YAAM,MAAM,KAAK,WAAW,KAAK;AACjC,cAAS,MAAM,QAAS,IAAO,OAAO,IAAK;AAAA,IAC7C;AAAA,EACF;AAAA,EAEA,YAAY,OAAe,KAAa,aAAa,GAAG;AACtD,QAAI,KAAK,eAAe;AAAM;AAC9B,SAAK,WAAW,QAAQ,IAAI,UAAU,IAAI;AAAA,EAC5C;AAAA,EAEA,YAAY,OAAe,YAAoB;AAC7C,QAAI,KAAK,eAAe;AAAM,YAAM,IAAI,MAAM,uBAAuB;AACrE,WAAO,KAAK,WAAW,QAAQ,IAAI,UAAU;AAAA,EAC/C;AAAA,EAEA,UAAU;AACR,QAAI,KAAK,WAAW;AAAM,aAAO;AAEjC,QAAI,QAAQ;AACZ,UAAM,SAAS,KAAK,OAAO,MAAM;AACjC,aAAS;AAET,WAAO,QAAQ,QAAQ;AACrB,WAAK,OAAO,KAAK;AACjB,eAAS;AAAA,IACX;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,cAAc;AACZ,QAAI,KAAK,WAAW;AAAM,aAAO;AAEjC,QAAI,KAAK;AACT,QAAI,QAAQ;AAEZ,UAAM,SAAS,KAAK,OAAO,MAAM;AACjC,aAAS;AAET,WAAO,QAAQ,QAAQ;AACrB,YAAM,KAAK,OAAO,KAAK;AACvB,eAAS;AAAA,IACX;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,aAAa;AACX,QAAI,KAAK,WAAW;AAAM,aAAO;AAEjC,UAAM,KAAK,KAAK,OAAO,MAAM;AAE7B,QAAI,OAAO,GAAG;AACZ,YAAM,IAAI,MAAM,2DAA2D;AAAA,IAC7E;AAEA,WAAO,KAAK,OAAO,MAAM;AAAA,EAC3B;AACF;","names":["crcTable"]} \ No newline at end of file diff --git a/packages/dicom-codec/test/integration.test.js b/packages/dicom-codec/test/integration.test.js index c1f95873..e4a4b739 100644 --- a/packages/dicom-codec/test/integration.test.js +++ b/packages/dicom-codec/test/integration.test.js @@ -217,9 +217,9 @@ describe.skipIf(!ALL_BUILT)("dicom-codec integration", () => { }) describe("JPEG Lossless (1.2.840.10008.1.2.4.57 / .70)", () => { - // These go through dicom-codec's internal jpegLosslessCodec - // (jpeg-lossless-decoder-js, pure JS — no separate wasm package). Both - // fixtures encode the same 512x512x16 CT slice; the reference + // These go through dicom-codec's internal jpegLosslessCodec (the vendored + // jpeg-lossless-decoder-js build, pure JS — no separate wasm package). + // Both fixtures encode the same 512x512x16 CT slice; the reference // fixtures/raw/CT-512x512.raw was cross-validated three ways: the RLE // decoder, the Process 14 path of jpeg-lossless-decoder-js and DCMTK's // dcmdjpeg all produce these exact bytes. @@ -258,33 +258,21 @@ describe.skipIf(!ALL_BUILT)("dicom-codec integration", () => { expect(frameBytes(result.imageFrame).equals(ctRaw)).toBe(true) }) - // KNOWN UPSTREAM BUG (jpeg-lossless-decoder-js): the SV1 path decodes - // the final pixel of this fixture as 0 instead of -2000. DCMTK's - // dcmdjpeg confirms the fixture itself is correct (its decode matches - // CT-512x512.raw exactly, last pixel included), so the defect is in the - // JS decoder. The test below pins today's behavior: every sample except - // the last matches the reference. When the upstream bug is fixed, the - // paired `it.fails` test starts passing and vitest will flag it — then - // fold these two tests into a single exact comparison. - it("decodes Process 14 SV1 through the dispatcher (.70) — all but the last pixel match", async () => { + // This fixture is the regression case for the vendored decoder: its scan + // ends in a long run of the image minimum whose zero-difference codes tile + // the final byte exactly, and published jpeg-lossless-decoder-js 2.1.2 + // decoded that last sample as 0 instead of -2000 (it read the 0xFF + // introducing EOI as entropy coded data and stopped one sample early). + // DCMTK's dcmdjpeg always decoded this fixture to CT-512x512.raw exactly, + // last pixel included. An exact comparison here fails on any decoder + // without the fix. + it("decodes Process 14 SV1 through the dispatcher (.70) to the exact reference pixels", async () => { const result = await dicomCodec.decode( jpllProcess14Sv1, ctImageInfo, "1.2.840.10008.1.2.4.70" ) expect(result.imageFrame.byteLength).toBe(ctRaw.length) - - const actual = frameBytes(result.imageFrame) - // Everything up to the final 16-bit sample must match exactly. - expect(actual.subarray(0, ctRaw.length - 2).equals(ctRaw.subarray(0, ctRaw.length - 2))).toBe(true) - }) - - it.fails("decodes Process 14 SV1 (.70) to the exact reference pixels (known upstream last-pixel bug)", async () => { - const result = await dicomCodec.decode( - jpllProcess14Sv1, - ctImageInfo, - "1.2.840.10008.1.2.4.70" - ) expect(frameBytes(result.imageFrame).equals(ctRaw)).toBe(true) }) }) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e32899c5..33a9e090 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -88,9 +88,6 @@ importers: browser-or-node: specifier: ^2.0.0 version: 2.1.1 - jpeg-lossless-decoder-js: - specifier: ^2.0.4 - version: 2.1.2 devDependencies: clean-webpack-plugin: specifier: ^4.0.0 @@ -1127,12 +1124,6 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.13.0': - resolution: {integrity: sha512-yUD/8wMffnTKuiIsl6xU+4IA8UNhQ/f1sAnQebmE/lyQ8abjsVyDkyRkWop0kdMhKMprpNIhPmYlCxgHrPoXoA==} - cpu: [x64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.62.2': resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} cpu: [x64] @@ -2120,9 +2111,6 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} - jpeg-lossless-decoder-js@2.1.2: - resolution: {integrity: sha512-fYf/plymnuKwVw+s8gJ8O/BPw8y8OLaN11MBvEcT05kuy3m45o7BEC4J0JbxMNUYyO+MdK/I/jq0q1gkVYZm2Q==} - js-tokens@10.0.0: resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} @@ -3880,9 +3868,6 @@ snapshots: '@rollup/rollup-linux-s390x-gnu@4.62.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.13.0': - optional: true - '@rollup/rollup-linux-x64-gnu@4.62.2': optional: true @@ -5078,10 +5063,6 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jpeg-lossless-decoder-js@2.1.2: - optionalDependencies: - '@rollup/rollup-linux-x64-gnu': 4.13.0 - js-tokens@10.0.0: {} js-tokens@4.0.0: {} diff --git a/tools/fixture-verification/README.md b/tools/fixture-verification/README.md index f7e6e47a..d902a872 100644 --- a/tools/fixture-verification/README.md +++ b/tools/fixture-verification/README.md @@ -3,9 +3,9 @@ Independent, from-scratch decoders used to verify that the `.raw`/`.RAW` pixel references committed under `packages/*/test/fixtures` are correct. They share **no code** with the codecs under test (the emscripten wasm -builds, `jpeg-lossless-decoder-js`, or dicom-codec's `rleLossless.js`), so -byte-exact agreement means two independent implementations produce the -same pixels from the same codestream. +builds, the vendored `jpeg-lossless-decoder-js`, or dicom-codec's +`rleLossless.js`), so byte-exact agreement means two independent +implementations produce the same pixels from the same codestream. ``` node tools/fixture-verification/run-all.js @@ -98,11 +98,17 @@ context, with CharLS and pylibjpeg agreeing with each other. The bug is in remain valid (independently confirmed by DCMTK/RLE cross-checks). Fixing the 12-bit run-interruption path here is a TODO. -## Bug found by this verification +## Bug found by this verification (now fixed) -`jpeg-lossless-decoder-js` (used by dicom-codec for transfer syntaxes -.57/.70) decodes the final pixel of the SV1 fixture as 0 instead of -2000. -Four independent decoders agree the fixture is correct: the from-scratch -`jpll.js` here, DCMTK's `dcmdjpeg`, the RLE decode of the same slice, and -the library's own Process-14 path. See the pinned `it.fails` test in -`packages/dicom-codec/test/integration.test.js`. +Published `jpeg-lossless-decoder-js` 2.1.2 (used by dicom-codec for transfer +syntaxes .57/.70) decoded the final pixel of the SV1 fixture as 0 instead of +-2000. Four independent decoders agreed the fixture was correct: the +from-scratch `jpll.js` here, DCMTK's `dcmdjpeg`, the RLE decode of the same +slice, and the library's own Process-14 path. + +The cause was an off-by-one in the end-of-scan guards, which read the 0xFF +introducing EOI as entropy coded data whenever the last Huffman code ended +exactly on a byte boundary. dicom-codec now uses a vendored build carrying the +fix — see `packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/README.md` +— and `packages/dicom-codec/test/integration.test.js` compares both fixtures +byte-for-byte. From 73b8e1f66cf8ac3f0f284eb07998668a4ea6dcd4 Mon Sep 17 00:00:00 2001 From: Bill Wallace Date: Thu, 10 Sep 2026 17:25:41 -0400 Subject: [PATCH 2/2] refactor(dicom-codec): depend on the published fork, not a vendored build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fix for transfer syntaxes 1.2.840.10008.1.2.4.57 and .70 lived only in the cornerstonejs fork of jpeg-lossless-decoder-js, and the fork was not on npm, so this repository committed a build of it under packages/dicom-codec/src/vendor/. The fork now publishes as @cornerstonejs/jpeg-lossless-decoder-js, so dicom-codec takes a normal dependency and the vendored directory goes. This answers all three review comments on the pull request: - The vendor directory is removed, which is what the reviewer asked to happen once the scoped package existed. - README.md said the decoder was "built from the `main` branch". The branch moves, so that sentence had a short life. It now names the published package. - CodeRabbit read the fork's committed release/cjs/lossless.cjs at 03bb80c0 and reported the vendored file as modified. The reviewer had already established that the fork's committed artifact was the stale 2.1.2 build. The question cannot arise again, because there is no vendored file. The decoder bytes do not change. @cornerstonejs/jpeg-lossless-decoder-js@2.2.0 resolves to release/cjs/lossless.cjs, and that file is byte-identical to the packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs that this commit deletes. The API shape is unchanged too: codecFactory looks up `Decoder` on the module, and the package exports `Decoder` at the top level. pnpm-workspace.yaml gains a minimumReleaseAgeExclude entry. pnpm refuses a dependency version that the registry published very recently, and 2.2.0 is new. The measure guards against third-party code, and this organisation publishes this package, so waiting out the window would delay a decode fix and protect nothing. The entry names the package and no version: pnpm writes a version-pinned entry itself, and such an entry goes stale at every release of the package. Verified: the 102 dicom-codec tests pass, 7 skipped, including both byte-exact JPEG Lossless comparisons — the .70 SV1 fixture is the regression case, and it fails on any decoder without the fix. `pnpm csp:source` passes. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 2 +- packages/dicom-codec/package.json | 1 + .../dicom-codec/src/codecs/jpegLossless.js | 9 +- .../vendor/jpeg-lossless-decoder-js/LICENSE | 19 - .../vendor/jpeg-lossless-decoder-js/README.md | 48 - .../jpeg-lossless-decoder-js/lossless.cjs | 1216 ----------------- .../jpeg-lossless-decoder-js/lossless.cjs.map | 1 - packages/dicom-codec/test/integration.test.js | 6 +- pnpm-lock.yaml | 8 + pnpm-workspace.yaml | 17 + tools/fixture-verification/README.md | 10 +- 11 files changed, 39 insertions(+), 1298 deletions(-) delete mode 100644 packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/LICENSE delete mode 100644 packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/README.md delete mode 100644 packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs delete mode 100644 packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs.map diff --git a/README.md b/README.md index 56fc8d7c..a411e0fe 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Transfer Syntax is the language used in DICOM to describe the DICOM file format - \*\*\* - Unlike all other DICOM transfer syntaxes, the deflate transfer syntaxes compress the whole of the DICOM data (tags, lengths, VR etc.) rather than just the pixel data - this is done using the standard “deflate” mechanism as used in gzip etc.) It is therefore most suitable for non-pixel objects such as structured reports, presentation states etc. - 5: [JS Decoder](https://github.com/cornerstonejs/cornerstoneWADOImageLoader/blob/4bfa04759412d58647cc5d6bd0204aa37e4542e3/src/shared/decoders/decodeRLE.js) -- 57 & 70: [JS Decoder](https://github.com/cornerstonejs/JPEGLosslessDecoderJS) — built from the `main` branch of that fork and vendored into `packages/dicom-codec/src/vendor/`, since the published `jpeg-lossless-decoder-js` predates its end-of-scan fix +- 57 & 70: [JS Decoder](https://github.com/cornerstonejs/JPEGLosslessDecoderJS) — used as the published [`@cornerstonejs/jpeg-lossless-decoder-js`](https://www.npmjs.com/package/@cornerstonejs/jpeg-lossless-decoder-js), a fork that carries the end-of-scan fix the unscoped `jpeg-lossless-decoder-js` still lacks - 1.2 & 2.1 & 99: [JS Decoder](https://github.com/cornerstonejs/cornerstoneWADOImageLoader/blob/4bfa04759412d58647cc5d6bd0204aa37e4542e3/src/shared/decoders/decodeLittleEndian.js) - 2.2: [JS Decoder](https://github.com/cornerstonejs/cornerstoneWADOImageLoader/blob/4bfa04759412d58647cc5d6bd0204aa37e4542e3/src/shared/decoders/decodeBigEndian.js) diff --git a/packages/dicom-codec/package.json b/packages/dicom-codec/package.json index 311feda9..c8de5c84 100644 --- a/packages/dicom-codec/package.json +++ b/packages/dicom-codec/package.json @@ -39,6 +39,7 @@ "@cornerstonejs/codec-little-endian": "^0.0.9", "@cornerstonejs/codec-openjpeg": "^1.3.6", "@cornerstonejs/codec-openjph": "^2.4.11", + "@cornerstonejs/jpeg-lossless-decoder-js": "^2.2.0", "browser-or-node": "^2.0.0" } } diff --git a/packages/dicom-codec/src/codecs/jpegLossless.js b/packages/dicom-codec/src/codecs/jpegLossless.js index 304d83d2..d69d7c93 100644 --- a/packages/dicom-codec/src/codecs/jpegLossless.js +++ b/packages/dicom-codec/src/codecs/jpegLossless.js @@ -1,8 +1,7 @@ -// Vendored build of the cornerstonejs fork of jpeg-lossless-decoder-js rather -// than the published 2.1.2: that release loses the last sample of any frame -// whose final Huffman code ends on a byte boundary. See -// ../vendor/jpeg-lossless-decoder-js/README.md. -const codecModule = require("../vendor/jpeg-lossless-decoder-js/lossless.cjs"); +// @cornerstonejs/jpeg-lossless-decoder-js, not the unscoped +// jpeg-lossless-decoder-js: the published 2.1.2 of that package loses the last +// sample of any frame whose final Huffman code ends on a byte boundary. +const codecModule = require("@cornerstonejs/jpeg-lossless-decoder-js"); const codecFactory = require("./codecFactory"); /** diff --git a/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/LICENSE b/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/LICENSE deleted file mode 100644 index e24e431d..00000000 --- a/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2015 RII-UTHSCSA - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/README.md b/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/README.md deleted file mode 100644 index 6af694c3..00000000 --- a/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# Vendored: jpeg-lossless-decoder-js - -`lossless.cjs` and `lossless.cjs.map` are **build output, not source**. Do not -edit them here — fix the upstream repository and re-vendor. - -| | | -| --- | --- | -| Upstream | (fork of `rii-mango/JPEGLosslessDecoderJS`) | -| Branch | `main` — upstream `master` plus the byte-aligned-end-of-scan fix | -| Commit | `03bb80c073e34369893e468b615dac9dcb0dcee9` | -| Version | 2.1.2 + the fix | -| License | MIT, retained alongside as `LICENSE` | - -## Why this is vendored rather than a dependency - -dicom-codec used the published `jpeg-lossless-decoder-js@2.1.2` for transfer -syntaxes 1.2.840.10008.1.2.4.57 and .70. That release drops the final sample of -any frame whose last Huffman code ends exactly on a byte boundary — the -`markerIndex` guards read the 0xFF that introduces EOI as if it were entropy -coded data and abandon the scan one sample early. DCMTK produces exactly that -layout whenever a frame ends in a run of one value, so real CT images decoded -with a wrong last pixel. - -The fix lives in the fork above. The fork is not published to npm, so the built -CJS bundle is committed here and required directly by -[`../../codecs/jpegLossless.js`](../../codecs/jpegLossless.js). When a release -of `jpeg-lossless-decoder-js` carries the fix, delete this directory and go back -to a normal dependency. - -The bundle is a single self-contained file with no runtime dependencies, and its -source map embeds the TypeScript sources, so debugging still lands in -`decoder.ts` rather than in minified output. - -## Re-vendoring - -```bash -git clone -b main https://github.com/cornerstonejs/JPEGLosslessDecoderJS.git -cd JPEGLosslessDecoderJS -npm install -npm test # 54 tests, includes the byte-aligned-end regression -npm run build -cp release/cjs/lossless.cjs release/cjs/lossless.cjs.map LICENSE \ - /packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/ -``` - -Copy the files verbatim and record the new commit in the table above — an -unmodified copy is what makes "is this really what that commit builds?" -answerable by rebuilding. diff --git a/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs b/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs deleted file mode 100644 index 4a8d909b..00000000 --- a/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs +++ /dev/null @@ -1,1216 +0,0 @@ -"use strict"; -var __defProp = Object.defineProperty; -var __getOwnPropDesc = Object.getOwnPropertyDescriptor; -var __getOwnPropNames = Object.getOwnPropertyNames; -var __hasOwnProp = Object.prototype.hasOwnProperty; -var __export = (target, all) => { - for (var name in all) - __defProp(target, name, { get: all[name], enumerable: true }); -}; -var __copyProps = (to, from, except, desc) => { - if (from && typeof from === "object" || typeof from === "function") { - for (let key of __getOwnPropNames(from)) - if (!__hasOwnProp.call(to, key) && key !== except) - __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); - } - return to; -}; -var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); - -// src/main.ts -var main_exports = {}; -__export(main_exports, { - ComponentSpec: () => ComponentSpec, - DataStream: () => DataStream, - Decoder: () => Decoder, - FrameHeader: () => FrameHeader, - HuffmanTable: () => HuffmanTable, - QuantizationTable: () => QuantizationTable, - ScanComponent: () => ScanComponent, - ScanHeader: () => ScanHeader, - Utils: () => utils_exports -}); -module.exports = __toCommonJS(main_exports); - -// src/component-spec.ts -var ComponentSpec = { - hSamp: 0, - quantTableSel: 0, - vSamp: 0 -}; - -// src/data-stream.ts -var DataStream = class { - buffer; - index; - constructor(data, offset, length) { - this.buffer = new Uint8Array(data, offset, length); - this.index = 0; - } - get16() { - const value = (this.buffer[this.index] << 8) + this.buffer[this.index + 1]; - this.index += 2; - return value; - } - get8() { - const value = this.buffer[this.index]; - this.index += 1; - return value; - } -}; - -// src/frame-header.ts -var FrameHeader = class { - dimX = 0; - dimY = 0; - numComp = 0; - precision = 0; - components = []; - read(data) { - let count = 0; - let temp; - const length = data.get16(); - count += 2; - this.precision = data.get8(); - count += 1; - this.dimY = data.get16(); - count += 2; - this.dimX = data.get16(); - count += 2; - this.numComp = data.get8(); - count += 1; - for (let i = 1; i <= this.numComp; i += 1) { - if (count > length) { - throw new Error("ERROR: frame format error"); - } - const c = data.get8(); - count += 1; - if (count >= length) { - throw new Error("ERROR: frame format error [c>=Lf]"); - } - temp = data.get8(); - count += 1; - if (!this.components[c]) { - this.components[c] = { ...ComponentSpec }; - } - this.components[c].hSamp = temp >> 4; - this.components[c].vSamp = temp & 15; - this.components[c].quantTableSel = data.get8(); - count += 1; - } - if (count !== length) { - throw new Error("ERROR: frame format error [Lf!=count]"); - } - return 1; - } -}; - -// src/utils.ts -var utils_exports = {}; -__export(utils_exports, { - crc32: () => crc32, - crcTable: () => crcTable, - createArray: () => createArray, - makeCRCTable: () => makeCRCTable -}); -var createArray = (...dimensions) => { - if (dimensions.length > 1) { - const dim = dimensions[0]; - const rest = dimensions.slice(1); - const newArray = []; - for (let i = 0; i < dim; i++) { - newArray[i] = createArray(...rest); - } - return newArray; - } else { - return Array(dimensions[0]).fill(void 0); - } -}; -var makeCRCTable = function() { - let c; - const crcTable2 = []; - for (let n = 0; n < 256; n++) { - c = n; - for (let k = 0; k < 8; k++) { - c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1; - } - crcTable2[n] = c; - } - return crcTable2; -}; -var crcTable = makeCRCTable(); -var crc32 = function(buffer) { - const uint8view = new Uint8Array(buffer); - let crc = 0 ^ -1; - for (let i = 0; i < uint8view.length; i++) { - crc = crc >>> 8 ^ crcTable[(crc ^ uint8view[i]) & 255]; - } - return (crc ^ -1) >>> 0; -}; - -// src/huffman-table.ts -var HuffmanTable = class _HuffmanTable { - static MSB = 2147483648; - l; - th; - v; - tc; - constructor() { - this.l = createArray(4, 2, 16); - this.th = [0, 0, 0, 0]; - this.v = createArray(4, 2, 16, 200); - this.tc = [ - [0, 0], - [0, 0], - [0, 0], - [0, 0] - ]; - } - read(data, HuffTab) { - let count = 0; - let temp; - let t; - let c; - let i; - let j; - const length = data.get16(); - count += 2; - while (count < length) { - temp = data.get8(); - count += 1; - t = temp & 15; - if (t > 3) { - throw new Error("ERROR: Huffman table ID > 3"); - } - c = temp >> 4; - if (c > 2) { - throw new Error("ERROR: Huffman table [Table class > 2 ]"); - } - this.th[t] = 1; - this.tc[t][c] = 1; - for (i = 0; i < 16; i += 1) { - this.l[t][c][i] = data.get8(); - count += 1; - } - for (i = 0; i < 16; i += 1) { - for (j = 0; j < this.l[t][c][i]; j += 1) { - if (count > length) { - throw new Error("ERROR: Huffman table format error [count>Lh]"); - } - this.v[t][c][i][j] = data.get8(); - count += 1; - } - } - } - if (count !== length) { - throw new Error("ERROR: Huffman table format error [count!=Lf]"); - } - for (i = 0; i < 4; i += 1) { - for (j = 0; j < 2; j += 1) { - if (this.tc[i][j] !== 0) { - this.buildHuffTable(HuffTab[i][j], this.l[i][j], this.v[i][j]); - } - } - } - return 1; - } - // Build_HuffTab() - // Parameter: t table ID - // c table class ( 0 for DC, 1 for AC ) - // L[i] # of codewords which length is i - // V[i][j] Huffman Value (length=i) - // Effect: - // build up HuffTab[t][c] using L and V. - buildHuffTable(tab, L, V) { - let currentTable, k, i, j, n; - const temp = 256; - k = 0; - for (i = 0; i < 8; i += 1) { - for (j = 0; j < L[i]; j += 1) { - for (n = 0; n < temp >> i + 1; n += 1) { - tab[k] = V[i][j] | i + 1 << 8; - k += 1; - } - } - } - for (i = 1; k < 256; i += 1, k += 1) { - tab[k] = i | _HuffmanTable.MSB; - } - currentTable = 1; - k = 0; - for (i = 8; i < 16; i += 1) { - for (j = 0; j < L[i]; j += 1) { - for (n = 0; n < temp >> i - 7; n += 1) { - tab[currentTable * 256 + k] = V[i][j] | i + 1 << 8; - k += 1; - } - if (k >= 256) { - if (k > 256) { - throw new Error("ERROR: Huffman table error(1)!"); - } - k = 0; - currentTable += 1; - } - } - } - } -}; - -// src/quantization-table.ts -var QuantizationTable = class _QuantizationTable { - precision = []; - // Quantization precision 8 or 16 - tq = [0, 0, 0, 0]; - // 1: this table is presented - quantTables = createArray(4, 64); - // Tables - static enhanceQuantizationTable = function(qtab, table) { - for (let i = 0; i < 8; i += 1) { - qtab[table[0 * 8 + i]] *= 90; - qtab[table[4 * 8 + i]] *= 90; - qtab[table[2 * 8 + i]] *= 118; - qtab[table[6 * 8 + i]] *= 49; - qtab[table[5 * 8 + i]] *= 71; - qtab[table[1 * 8 + i]] *= 126; - qtab[table[7 * 8 + i]] *= 25; - qtab[table[3 * 8 + i]] *= 106; - } - for (let i = 0; i < 8; i += 1) { - qtab[table[0 + 8 * i]] *= 90; - qtab[table[4 + 8 * i]] *= 90; - qtab[table[2 + 8 * i]] *= 118; - qtab[table[6 + 8 * i]] *= 49; - qtab[table[5 + 8 * i]] *= 71; - qtab[table[1 + 8 * i]] *= 126; - qtab[table[7 + 8 * i]] *= 25; - qtab[table[3 + 8 * i]] *= 106; - } - for (let i = 0; i < 64; i += 1) { - qtab[i] >>= 6; - } - }; - read(data, table) { - let count = 0; - let temp; - let t; - let i; - const length = data.get16(); - count += 2; - while (count < length) { - temp = data.get8(); - count += 1; - t = temp & 15; - if (t > 3) { - throw new Error("ERROR: Quantization table ID > 3"); - } - this.precision[t] = temp >> 4; - if (this.precision[t] === 0) { - this.precision[t] = 8; - } else if (this.precision[t] === 1) { - this.precision[t] = 16; - } else { - throw new Error("ERROR: Quantization table precision error"); - } - this.tq[t] = 1; - if (this.precision[t] === 8) { - for (i = 0; i < 64; i += 1) { - if (count > length) { - throw new Error("ERROR: Quantization table format error"); - } - this.quantTables[t][i] = data.get8(); - count += 1; - } - _QuantizationTable.enhanceQuantizationTable(this.quantTables[t], table); - } else { - for (i = 0; i < 64; i += 1) { - if (count > length) { - throw new Error("ERROR: Quantization table format error"); - } - this.quantTables[t][i] = data.get16(); - count += 2; - } - _QuantizationTable.enhanceQuantizationTable(this.quantTables[t], table); - } - } - if (count !== length) { - throw new Error("ERROR: Quantization table error [count!=Lq]"); - } - return 1; - } -}; - -// src/scan-component.ts -var ScanComponent = { - acTabSel: 0, - // AC table selector - dcTabSel: 0, - // DC table selector - scanCompSel: 0 - // Scan component selector -}; - -// src/scan-header.ts -var ScanHeader = class { - ah = 0; - al = 0; - numComp = 0; - // Number of components in the scan - selection = 0; - // Start of spectral or predictor selection - spectralEnd = 0; - // End of spectral selection - components = []; - read(data) { - let count = 0; - let i; - let temp; - const length = data.get16(); - count += 2; - this.numComp = data.get8(); - count += 1; - for (i = 0; i < this.numComp; i += 1) { - this.components[i] = { ...ScanComponent }; - if (count > length) { - throw new Error("ERROR: scan header format error"); - } - this.components[i].scanCompSel = data.get8(); - count += 1; - temp = data.get8(); - count += 1; - this.components[i].dcTabSel = temp >> 4; - this.components[i].acTabSel = temp & 15; - } - this.selection = data.get8(); - count += 1; - this.spectralEnd = data.get8(); - count += 1; - temp = data.get8(); - this.ah = temp >> 4; - this.al = temp & 15; - count += 1; - if (count !== length) { - throw new Error("ERROR: scan header format error [count!=Ns]"); - } - return 1; - } -}; - -// src/decoder.ts -var littleEndian = function() { - const buffer = new ArrayBuffer(2); - new DataView(buffer).setInt16( - 0, - 256, - true - /* littleEndian */ - ); - return new Int16Array(buffer)[0] === 256; -}(); -var Decoder = class _Decoder { - static IDCT_P = [ - 0, - 5, - 40, - 16, - 45, - 2, - 7, - 42, - 21, - 56, - 8, - 61, - 18, - 47, - 1, - 4, - 41, - 23, - 58, - 13, - 32, - 24, - 37, - 10, - 63, - 17, - 44, - 3, - 6, - 43, - 20, - 57, - 15, - 34, - 29, - 48, - 53, - 26, - 39, - 9, - 60, - 19, - 46, - 22, - 59, - 12, - 33, - 31, - 50, - 55, - 25, - 36, - 11, - 62, - 14, - 35, - 28, - 49, - 52, - 27, - 38, - 30, - 51, - 54 - ]; - static TABLE = [ - 0, - 1, - 5, - 6, - 14, - 15, - 27, - 28, - 2, - 4, - 7, - 13, - 16, - 26, - 29, - 42, - 3, - 8, - 12, - 17, - 25, - 30, - 41, - 43, - 9, - 11, - 18, - 24, - 31, - 40, - 44, - 53, - 10, - 19, - 23, - 32, - 39, - 45, - 52, - 54, - 20, - 22, - 33, - 38, - 46, - 51, - 55, - 60, - 21, - 34, - 37, - 47, - 50, - 56, - 59, - 61, - 35, - 36, - 48, - 49, - 57, - 58, - 62, - 63 - ]; - static MAX_HUFFMAN_SUBTREE = 50; - static MSB = 2147483648; - static RESTART_MARKER_BEGIN = 65488; - static RESTART_MARKER_END = 65495; - // Value `markerIndex` takes once the 0xFF that introduces a marker has been - // shifted into `temp`. Only `!== 0` is ever tested - see `readPastEntropyData` - // for what the bit count itself means. - static MARKER_SEEN = 9; - // How many bits of `temp` that 0xFF occupies. Bits below it are the marker, - // not entropy coded data. - static MARKER_BITS = 8; - buffer = null; - stream = null; - frame = new FrameHeader(); - huffTable = new HuffmanTable(); - quantTable = new QuantizationTable(); - scan = new ScanHeader(); - DU = createArray(10, 4, 64); - // at most 10 data units in a MCU, at most 4 data units in one component - HuffTab = createArray(4, 2, 50 * 256); - IDCT_Source = []; - nBlock = []; - // number of blocks in the i-th Comp in a scan - acTab = createArray(10, 1); - // ac HuffTab for the i-th Comp in a scan - dcTab = createArray(10, 1); - // dc HuffTab for the i-th Comp in a scan - qTab = createArray(10, 1); - // quantization table for the i-th Comp in a scan - marker = 0; - markerIndex = 0; - numComp = 0; - restartInterval = 0; - selection = 0; - xDim = 0; - yDim = 0; - xLoc = 0; - yLoc = 0; - outputData = null; - restarting = false; - mask = 0; - numBytes = 0; - precision = void 0; - components = []; - getter = null; - setter = null; - output = null; - selector = null; - /** - * The Decoder constructor. - * @property {number} numBytes - number of bytes per component - * @type {Function} - */ - constructor(buffer, numBytes) { - this.buffer = buffer ?? null; - this.numBytes = numBytes ?? 0; - } - /** - * Returns decompressed data. - */ - decompress(buffer, offset, length) { - const result = this.decode(buffer, offset, length); - return result.buffer; - } - decode(buffer, offset, length, numBytes) { - let scanNum = 0; - const pred = []; - let i; - let compN; - const temp = []; - const index = []; - let mcuNum; - if (buffer) { - this.buffer = buffer; - } - if (numBytes !== void 0) { - this.numBytes = numBytes; - } - this.stream = new DataStream(this.buffer, offset, length); - this.buffer = null; - this.xLoc = 0; - this.yLoc = 0; - let current = this.stream.get16(); - if (current !== 65496) { - throw new Error("Not a JPEG file"); - } - current = this.stream.get16(); - while (current >> 4 !== 4092 || current === 65476) { - switch (current) { - case 65476: - this.huffTable.read(this.stream, this.HuffTab); - break; - case 65484: - throw new Error("Program doesn't support arithmetic coding. (format throw new IOException)"); - case 65499: - this.quantTable.read(this.stream, _Decoder.TABLE); - break; - case 65501: - this.restartInterval = this.readNumber() ?? 0; - break; - case 65504: - case 65505: - case 65506: - case 65507: - case 65508: - case 65509: - case 65510: - case 65511: - case 65512: - case 65513: - case 65514: - case 65515: - case 65516: - case 65517: - case 65518: - case 65519: - this.readApp(); - break; - case 65534: - this.readComment(); - break; - default: - if (current >> 8 !== 255) { - throw new Error("ERROR: format throw new IOException! (decode)"); - } - } - current = this.stream.get16(); - } - if (current < 65472 || current > 65479) { - throw new Error("ERROR: could not handle arithmetic code!"); - } - this.frame.read(this.stream); - current = this.stream.get16(); - do { - while (current !== 65498) { - switch (current) { - case 65476: - this.huffTable.read(this.stream, this.HuffTab); - break; - case 65484: - throw new Error("Program doesn't support arithmetic coding. (format throw new IOException)"); - case 65499: - this.quantTable.read(this.stream, _Decoder.TABLE); - break; - case 65501: - this.restartInterval = this.readNumber() ?? 0; - break; - case 65504: - case 65505: - case 65506: - case 65507: - case 65508: - case 65509: - case 65510: - case 65511: - case 65512: - case 65513: - case 65514: - case 65515: - case 65516: - case 65517: - case 65518: - case 65519: - this.readApp(); - break; - case 65534: - this.readComment(); - break; - default: - if (current >> 8 !== 255) { - throw new Error("ERROR: format throw new IOException! (Parser.decode)"); - } - } - current = this.stream.get16(); - } - this.precision = this.frame.precision; - this.components = this.frame.components; - if (!this.numBytes) { - this.numBytes = Math.round(Math.ceil(this.precision / 8)); - } - if (this.numBytes === 1) { - this.mask = 255; - } else { - this.mask = 65535; - } - this.scan.read(this.stream); - this.numComp = this.scan.numComp; - this.selection = this.scan.selection; - if (this.numBytes === 1) { - if (this.numComp === 3) { - this.getter = this.getValueRGB; - this.setter = this.setValueRGB; - this.output = this.outputRGB; - } else { - this.getter = this.getValue8; - this.setter = this.setValue8; - this.output = this.outputSingle; - } - } else { - this.getter = this.getValue8; - this.setter = this.setValue8; - this.output = this.outputSingle; - } - switch (this.selection) { - case 2: - this.selector = this.select2; - break; - case 3: - this.selector = this.select3; - break; - case 4: - this.selector = this.select4; - break; - case 5: - this.selector = this.select5; - break; - case 6: - this.selector = this.select6; - break; - case 7: - this.selector = this.select7; - break; - default: - this.selector = this.select1; - break; - } - for (i = 0; i < this.numComp; i += 1) { - compN = this.scan.components[i].scanCompSel; - this.qTab[i] = this.quantTable.quantTables[this.components[compN].quantTableSel]; - this.nBlock[i] = this.components[compN].vSamp * this.components[compN].hSamp; - this.dcTab[i] = this.HuffTab[this.scan.components[i].dcTabSel][0]; - this.acTab[i] = this.HuffTab[this.scan.components[i].acTabSel][1]; - } - this.xDim = this.frame.dimX; - this.yDim = this.frame.dimY; - if (this.numBytes === 1) { - this.outputData = new Uint8Array(new ArrayBuffer(this.xDim * this.yDim * this.numBytes * this.numComp)); - } else { - this.outputData = new Uint16Array(new ArrayBuffer(this.xDim * this.yDim * this.numBytes * this.numComp)); - } - scanNum += 1; - while (true) { - temp[0] = 0; - index[0] = 0; - for (i = 0; i < 10; i += 1) { - pred[i] = 1 << this.precision - 1; - } - if (this.restartInterval === 0) { - current = this.decodeUnit(pred, temp, index); - while (current === 0 && this.xLoc < this.xDim && this.yLoc < this.yDim) { - this.output(pred); - current = this.decodeUnit(pred, temp, index); - } - break; - } - for (mcuNum = 0; mcuNum < this.restartInterval; mcuNum += 1) { - this.restarting = mcuNum === 0; - current = this.decodeUnit(pred, temp, index); - this.output(pred); - if (current !== 0) { - break; - } - } - if (current === 0) { - if (this.markerIndex !== 0) { - current = 65280 | this.marker; - this.markerIndex = 0; - } else { - current = this.stream.get16(); - } - } - if (!(current >= _Decoder.RESTART_MARKER_BEGIN && current <= _Decoder.RESTART_MARKER_END)) { - break; - } - } - if (current === 65500 && scanNum === 1) { - this.readNumber(); - current = this.stream.get16(); - } - } while (current !== 65497 && this.xLoc < this.xDim && this.yLoc < this.yDim && scanNum === 0); - return this.outputData; - } - decodeUnit(prev, temp, index) { - if (this.numComp === 1) { - return this.decodeSingle(prev, temp, index); - } else if (this.numComp === 3) { - return this.decodeRGB(prev, temp, index); - } else { - return -1; - } - } - select1(compOffset) { - return this.getPreviousX(compOffset); - } - select2(compOffset) { - return this.getPreviousY(compOffset); - } - select3(compOffset) { - return this.getPreviousXY(compOffset); - } - select4(compOffset) { - return this.getPreviousX(compOffset) + this.getPreviousY(compOffset) - this.getPreviousXY(compOffset); - } - select5(compOffset) { - return this.getPreviousX(compOffset) + (this.getPreviousY(compOffset) - this.getPreviousXY(compOffset) >> 1); - } - select6(compOffset) { - return this.getPreviousY(compOffset) + (this.getPreviousX(compOffset) - this.getPreviousXY(compOffset) >> 1); - } - select7(compOffset) { - return (this.getPreviousX(compOffset) + this.getPreviousY(compOffset)) / 2; - } - decodeRGB(prev, temp, index) { - if (this.selector === null) - throw new Error("decode hasn't run yet"); - let actab, dctab, qtab, ctrC, i, k, j; - prev[0] = this.selector(0); - prev[1] = this.selector(1); - prev[2] = this.selector(2); - for (ctrC = 0; ctrC < this.numComp; ctrC += 1) { - qtab = this.qTab[ctrC]; - actab = this.acTab[ctrC]; - dctab = this.dcTab[ctrC]; - for (i = 0; i < this.nBlock[ctrC]; i += 1) { - for (k = 0; k < this.IDCT_Source.length; k += 1) { - this.IDCT_Source[k] = 0; - } - let value = this.getHuffmanValue(dctab, temp, index); - if (value >= 65280) { - return value; - } - prev[ctrC] = this.IDCT_Source[0] = prev[ctrC] + this.getn(index, value, temp, index); - this.IDCT_Source[0] *= qtab[0]; - for (j = 1; j < 64; j += 1) { - value = this.getHuffmanValue(actab, temp, index); - if (value >= 65280) { - return value; - } - j += value >> 4; - if ((value & 15) === 0) { - if (value >> 4 === 0) { - break; - } - } else { - this.IDCT_Source[_Decoder.IDCT_P[j]] = this.getn(index, value & 15, temp, index) * qtab[j]; - } - } - } - } - return 0; - } - decodeSingle(prev, temp, index) { - if (this.selector === null) - throw new Error("decode hasn't run yet"); - let value, i, n, nRestart; - if (this.restarting) { - this.restarting = false; - prev[0] = 1 << this.frame.precision - 1; - } else { - prev[0] = this.selector(); - } - for (i = 0; i < this.nBlock[0]; i += 1) { - value = this.getHuffmanValue(this.dcTab[0], temp, index); - if (value >= 65280) { - return value; - } - n = this.getn(prev, value, temp, index); - nRestart = n >> 8; - if (nRestart >= _Decoder.RESTART_MARKER_BEGIN && nRestart <= _Decoder.RESTART_MARKER_END) { - return nRestart; - } - prev[0] += n; - } - return 0; - } - // Huffman table for fast search: (HuffTab) 8-bit Look up table 2-layer search architecture, 1st-layer represent 256 node (8 bits) if codeword-length > 8 - // bits, then the entry of 1st-layer = (# of 2nd-layer table) | MSB and it is stored in the 2nd-layer Size of tables in each layer are 256. - // HuffTab[*][*][0-256] is always the only 1st-layer table. - // - // An entry can be: (1) (# of 2nd-layer table) | MSB , for code length > 8 in 1st-layer (2) (Code length) << 8 | HuffVal - // - // HuffmanValue(table HuffTab[x][y] (ex) HuffmanValue(HuffTab[1][0],...) - // ): - // return: Huffman Value of table - // 0xFF?? if it receives a MARKER - // Parameter: table HuffTab[x][y] (ex) HuffmanValue(HuffTab[1][0],...) - // temp temp storage for remainded bits - // index index to bit of temp - // in FILE pointer - // Effect: - // temp store new remainded bits - // index change to new index - // in change to new position - // NOTE: - // Initial by temp=0; index=0; - // NOTE: (explain temp and index) - // temp: is always in the form at calling time or returning time - // | byte 4 | byte 3 | byte 2 | byte 1 | - // | 0 | 0 | 00000000 | 00000??? | if not a MARKER - // ^index=3 (from 0 to 15) - // 321 - // NOTE (marker and marker_index): - // If get a MARKER from 'in', marker=the low-byte of the MARKER - // and marker_index=9 - // If marker_index=9 then index is always > 8, or HuffmanValue() - // will not be called - getHuffmanValue(table, temp, index) { - let code, input; - const mask = 65535; - if (!this.stream) - throw new Error("stream not initialized"); - if (index[0] < 8) { - temp[0] <<= 8; - input = this.stream.get8(); - if (input === 255) { - this.marker = this.stream.get8(); - if (this.marker !== 0) { - this.markerIndex = _Decoder.MARKER_SEEN; - } - } - temp[0] |= input; - } else { - index[0] -= 8; - } - code = table[temp[0] >> index[0]]; - if ((code & _Decoder.MSB) !== 0) { - if (this.markerIndex !== 0) { - this.markerIndex = 0; - return 65280 | this.marker; - } - temp[0] &= mask >> 16 - index[0]; - temp[0] <<= 8; - input = this.stream.get8(); - if (input === 255) { - this.marker = this.stream.get8(); - if (this.marker !== 0) { - this.markerIndex = _Decoder.MARKER_SEEN; - } - } - temp[0] |= input; - code = table[(code & 255) * 256 + (temp[0] >> index[0])]; - index[0] += 8; - } - index[0] += 8 - (code >> 8); - if (index[0] < 0) { - throw new Error("index=" + index[0] + " temp=" + temp[0] + " code=" + code + " in HuffmanValue()"); - } - if (this.readPastEntropyData(index)) { - this.markerIndex = 0; - return 65280 | this.marker; - } - temp[0] &= mask >> 16 - index[0]; - return code & 255; - } - getn(PRED, n, temp, index) { - let result, input; - const one = 1; - const n_one = -1; - const mask = 65535; - if (this.stream === null) - throw new Error("stream not initialized"); - if (n === 0) { - return 0; - } - if (n === 16) { - if (PRED[0] >= 0) { - return -32768; - } else { - return 32768; - } - } - index[0] -= n; - if (index[0] >= 0) { - if (this.readPastEntropyData(index)) { - this.markerIndex = 0; - return (65280 | this.marker) << 8; - } - result = temp[0] >> index[0]; - temp[0] &= mask >> 16 - index[0]; - } else { - temp[0] <<= 8; - input = this.stream.get8(); - if (input === 255) { - this.marker = this.stream.get8(); - if (this.marker !== 0) { - this.markerIndex = _Decoder.MARKER_SEEN; - } - } - temp[0] |= input; - index[0] += 8; - if (index[0] < 0) { - if (this.markerIndex !== 0) { - this.markerIndex = 0; - return (65280 | this.marker) << 8; - } - temp[0] <<= 8; - input = this.stream.get8(); - if (input === 255) { - this.marker = this.stream.get8(); - if (this.marker !== 0) { - this.markerIndex = _Decoder.MARKER_SEEN; - } - } - temp[0] |= input; - index[0] += 8; - } - if (index[0] < 0) { - throw new Error("index=" + index[0] + " in getn()"); - } - if (this.readPastEntropyData(index)) { - this.markerIndex = 0; - return (65280 | this.marker) << 8; - } - result = temp[0] >> index[0]; - temp[0] &= mask >> 16 - index[0]; - } - if (result < one << n - 1) { - result += (n_one << n) + 1; - } - return result; - } - getPreviousX(compOffset = 0) { - if (this.getter === null) - throw new Error("decode hasn't run yet"); - if (this.xLoc > 0) { - return this.getter(this.yLoc * this.xDim + this.xLoc - 1, compOffset); - } else if (this.yLoc > 0) { - return this.getPreviousY(compOffset); - } else { - return 1 << this.frame.precision - 1; - } - } - getPreviousXY(compOffset = 0) { - if (this.getter === null) - throw new Error("decode hasn't run yet"); - if (this.xLoc > 0 && this.yLoc > 0) { - return this.getter((this.yLoc - 1) * this.xDim + this.xLoc - 1, compOffset); - } else { - return this.getPreviousY(compOffset); - } - } - getPreviousY(compOffset = 0) { - if (this.getter === null) - throw new Error("decode hasn't run yet"); - if (this.yLoc > 0) { - return this.getter((this.yLoc - 1) * this.xDim + this.xLoc, compOffset); - } else { - return this.getPreviousX(compOffset); - } - } - /** - * True when the bits just consumed came out of a marker rather than out of - * the entropy coded segment. - * - * `temp` holds `index` unconsumed bits (see the `getHuffmanValue` notes). The - * moment `getHuffmanValue`/`getn` shifts in a byte that turns out to be the - * 0xFF introducing a marker, those 8 bits stop being data: only the - * `index - MARKER_BITS` bits above them are real, and the scan is over once - * they run out. - * - * Consuming the last real bit leaves `index === MARKER_BITS`, and that is - * still a valid decode - it is a scan whose final Huffman code ends exactly - * on a byte boundary, so the encoder had no partial byte left to pad (T.81 - * B.1.1.2 pads only an incomplete final byte). Testing `index < markerIndex` - * rejected that case and dropped the frame's last sample, which is what - * DCMTK produces when a frame ends in a long run of one value: the run's - * short codes tile the final byte exactly. - */ - readPastEntropyData(index) { - return this.markerIndex !== 0 && index[0] < _Decoder.MARKER_BITS; - } - outputSingle(PRED) { - if (this.setter === null) - throw new Error("decode hasn't run yet"); - if (this.xLoc < this.xDim && this.yLoc < this.yDim) { - this.setter(this.yLoc * this.xDim + this.xLoc, this.mask & PRED[0]); - this.xLoc += 1; - if (this.xLoc >= this.xDim) { - this.yLoc += 1; - this.xLoc = 0; - } - } - } - outputRGB(PRED) { - if (this.setter === null) - throw new Error("decode hasn't run yet"); - const offset = this.yLoc * this.xDim + this.xLoc; - if (this.xLoc < this.xDim && this.yLoc < this.yDim) { - this.setter(offset, PRED[0], 0); - this.setter(offset, PRED[1], 1); - this.setter(offset, PRED[2], 2); - this.xLoc += 1; - if (this.xLoc >= this.xDim) { - this.yLoc += 1; - this.xLoc = 0; - } - } - } - setValue8(index, val) { - if (!this.outputData) - throw new Error("output data not ready"); - if (littleEndian) { - this.outputData[index] = val; - } else { - this.outputData[index] = (val & 255) << 8 | val >> 8 & 255; - } - } - getValue8(index) { - if (this.outputData === null) - throw new Error("output data not ready"); - if (littleEndian) { - return this.outputData[index]; - } else { - const val = this.outputData[index]; - return (val & 255) << 8 | val >> 8 & 255; - } - } - setValueRGB(index, val, compOffset = 0) { - if (this.outputData === null) - return; - this.outputData[index * 3 + compOffset] = val; - } - getValueRGB(index, compOffset) { - if (this.outputData === null) - throw new Error("output data not ready"); - return this.outputData[index * 3 + compOffset]; - } - readApp() { - if (this.stream === null) - return null; - let count = 0; - const length = this.stream.get16(); - count += 2; - while (count < length) { - this.stream.get8(); - count += 1; - } - return length; - } - readComment() { - if (this.stream === null) - return null; - let sb = ""; - let count = 0; - const length = this.stream.get16(); - count += 2; - while (count < length) { - sb += this.stream.get8(); - count += 1; - } - return sb; - } - readNumber() { - if (this.stream === null) - return null; - const Ld = this.stream.get16(); - if (Ld !== 4) { - throw new Error("ERROR: Define number format throw new IOException [Ld!=4]"); - } - return this.stream.get16(); - } -}; -// Annotate the CommonJS export names for ESM import in node: -0 && (module.exports = { - ComponentSpec, - DataStream, - Decoder, - FrameHeader, - HuffmanTable, - QuantizationTable, - ScanComponent, - ScanHeader, - Utils -}); -//# sourceMappingURL=lossless.cjs.map \ No newline at end of file diff --git a/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs.map b/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs.map deleted file mode 100644 index 5be02734..00000000 --- a/packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/lossless.cjs.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sources":["../../src/main.ts","../../src/component-spec.ts","../../src/data-stream.ts","../../src/frame-header.ts","../../src/utils.ts","../../src/huffman-table.ts","../../src/quantization-table.ts","../../src/scan-component.ts","../../src/scan-header.ts","../../src/decoder.ts"],"sourcesContent":["export { ComponentSpec } from './component-spec.js'\nexport { DataStream } from './data-stream.js'\nexport { Decoder } from './decoder.js'\nexport { FrameHeader } from './frame-header.js'\nexport { HuffmanTable } from './huffman-table.js'\nexport { QuantizationTable } from './quantization-table.js'\nexport { ScanComponent } from './scan-component.js'\nexport { ScanHeader } from './scan-header.js'\nexport * as Utils from './utils.js'\n","export const ComponentSpec = {\n hSamp: 0,\n quantTableSel: 0,\n vSamp: 0\n}\n","export class DataStream {\n buffer: Uint8Array\n index: number\n\n constructor(data: ArrayBuffer, offset?: number, length?: number) {\n this.buffer = new Uint8Array(data, offset, length)\n this.index = 0\n }\n\n get16() {\n // var value = this.buffer.getUint16(this.index, false);\n const value = (this.buffer[this.index] << 8) + this.buffer[this.index + 1] // DataView is big-endian by default\n this.index += 2\n return value\n }\n\n get8() {\n // var value = this.buffer.getUint8(this.index);\n const value = this.buffer[this.index]\n this.index += 1\n return value\n }\n}\n","import { ComponentSpec } from './component-spec.js'\nimport { DataStream } from './data-stream.js'\n\nexport class FrameHeader {\n dimX = 0\n dimY = 0\n numComp = 0\n precision = 0\n components: Array = []\n\n read(data: DataStream) {\n let count = 0\n let temp\n\n const length = data.get16()\n count += 2\n\n this.precision = data.get8()\n count += 1\n\n this.dimY = data.get16()\n count += 2\n\n this.dimX = data.get16()\n count += 2\n\n this.numComp = data.get8()\n count += 1\n for (let i = 1; i <= this.numComp; i += 1) {\n if (count > length) {\n throw new Error('ERROR: frame format error')\n }\n\n const c = data.get8()\n count += 1\n\n if (count >= length) {\n throw new Error('ERROR: frame format error [c>=Lf]')\n }\n\n temp = data.get8()\n count += 1\n\n if (!this.components[c]) {\n this.components[c] = { ...ComponentSpec }\n }\n\n this.components[c].hSamp = temp >> 4\n this.components[c].vSamp = temp & 0x0f\n this.components[c].quantTableSel = data.get8()\n count += 1\n }\n\n if (count !== length) {\n throw new Error('ERROR: frame format error [Lf!=count]')\n }\n\n return 1\n }\n}\n","type NestedArray = Array>\n\n// https://stackoverflow.com/a/12588826\nexport const createArray = (...dimensions: number[]): NestedArray => {\n if (dimensions.length > 1) {\n const dim = dimensions[0]\n const rest = dimensions.slice(1)\n const newArray = []\n for (let i = 0; i < dim; i++) {\n newArray[i] = createArray(...rest)\n }\n return newArray\n } else {\n return Array(dimensions[0]).fill(undefined)\n }\n}\n\n// http://stackoverflow.com/questions/18638900/javascript-crc32\nexport const makeCRCTable = function () {\n let c\n const crcTable = []\n for (let n = 0; n < 256; n++) {\n c = n\n for (let k = 0; k < 8; k++) {\n c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1\n }\n crcTable[n] = c\n }\n return crcTable\n}\n\nexport const crcTable = makeCRCTable()\n\nexport const crc32 = function (buffer: ArrayBuffer) {\n const uint8view = new Uint8Array(buffer)\n let crc = 0 ^ -1\n\n for (let i = 0; i < uint8view.length; i++) {\n crc = (crc >>> 8) ^ crcTable[(crc ^ uint8view[i]) & 0xff]\n }\n\n return (crc ^ -1) >>> 0\n}\n","import { DataStream } from './data-stream.js'\nimport { createArray } from './utils.js'\n\nexport class HuffmanTable {\n static MSB = 0x80000000\n\n l: number[][][]\n th: number[]\n v: number[][][][]\n tc: number[][]\n\n constructor() {\n this.l = createArray(4, 2, 16) as number[][][]\n this.th = [0, 0, 0, 0]\n this.v = createArray(4, 2, 16, 200) as number[][][][]\n this.tc = [\n [0, 0],\n [0, 0],\n [0, 0],\n [0, 0]\n ]\n }\n\n read(data: DataStream, HuffTab: number[][][]) {\n let count = 0\n let temp\n let t\n let c\n let i\n let j\n\n const length = data.get16()\n count += 2\n\n while (count < length) {\n temp = data.get8()\n count += 1\n t = temp & 0x0f\n if (t > 3) {\n throw new Error('ERROR: Huffman table ID > 3')\n }\n\n c = temp >> 4\n if (c > 2) {\n throw new Error('ERROR: Huffman table [Table class > 2 ]')\n }\n\n this.th[t] = 1\n this.tc[t][c] = 1\n\n for (i = 0; i < 16; i += 1) {\n this.l[t][c][i] = data.get8()\n count += 1\n }\n\n for (i = 0; i < 16; i += 1) {\n for (j = 0; j < this.l[t][c][i]; j += 1) {\n if (count > length) {\n throw new Error('ERROR: Huffman table format error [count>Lh]')\n }\n\n this.v[t][c][i][j] = data.get8()\n count += 1\n }\n }\n }\n\n if (count !== length) {\n throw new Error('ERROR: Huffman table format error [count!=Lf]')\n }\n\n for (i = 0; i < 4; i += 1) {\n for (j = 0; j < 2; j += 1) {\n if (this.tc[i][j] !== 0) {\n this.buildHuffTable(HuffTab[i][j], this.l[i][j], this.v[i][j])\n }\n }\n }\n\n return 1\n }\n\n //\tBuild_HuffTab()\n //\tParameter: t table ID\n //\t c table class ( 0 for DC, 1 for AC )\n //\t L[i] # of codewords which length is i\n //\t V[i][j] Huffman Value (length=i)\n //\tEffect:\n //\t build up HuffTab[t][c] using L and V.\n buildHuffTable(tab: number[], L: number[], V: number[][]) {\n let currentTable, k, i, j, n\n const temp = 256\n k = 0\n\n for (i = 0; i < 8; i += 1) {\n // i+1 is Code length\n for (j = 0; j < L[i]; j += 1) {\n for (n = 0; n < temp >> (i + 1); n += 1) {\n tab[k] = V[i][j] | ((i + 1) << 8)\n k += 1\n }\n }\n }\n\n for (i = 1; k < 256; i += 1, k += 1) {\n tab[k] = i | HuffmanTable.MSB\n }\n\n currentTable = 1\n k = 0\n\n for (i = 8; i < 16; i += 1) {\n // i+1 is Code length\n for (j = 0; j < L[i]; j += 1) {\n for (n = 0; n < temp >> (i - 7); n += 1) {\n tab[currentTable * 256 + k] = V[i][j] | ((i + 1) << 8)\n k += 1\n }\n\n if (k >= 256) {\n if (k > 256) {\n throw new Error('ERROR: Huffman table error(1)!')\n }\n\n k = 0\n currentTable += 1\n }\n }\n }\n }\n}\n","import { DataStream } from './data-stream.js'\nimport { createArray } from './utils.js'\n\nexport class QuantizationTable {\n precision: number[] = [] // Quantization precision 8 or 16\n tq = [0, 0, 0, 0] // 1: this table is presented\n quantTables: number[][] = createArray(4, 64) as number[][] // Tables\n\n static enhanceQuantizationTable = function (qtab: number[], table: number[]) {\n for (let i = 0; i < 8; i += 1) {\n qtab[table[0 * 8 + i]] *= 90\n qtab[table[4 * 8 + i]] *= 90\n qtab[table[2 * 8 + i]] *= 118\n qtab[table[6 * 8 + i]] *= 49\n qtab[table[5 * 8 + i]] *= 71\n qtab[table[1 * 8 + i]] *= 126\n qtab[table[7 * 8 + i]] *= 25\n qtab[table[3 * 8 + i]] *= 106\n }\n\n for (let i = 0; i < 8; i += 1) {\n qtab[table[0 + 8 * i]] *= 90\n qtab[table[4 + 8 * i]] *= 90\n qtab[table[2 + 8 * i]] *= 118\n qtab[table[6 + 8 * i]] *= 49\n qtab[table[5 + 8 * i]] *= 71\n qtab[table[1 + 8 * i]] *= 126\n qtab[table[7 + 8 * i]] *= 25\n qtab[table[3 + 8 * i]] *= 106\n }\n\n for (let i = 0; i < 64; i += 1) {\n qtab[i] >>= 6\n }\n }\n\n read(data: DataStream, table: number[]) {\n let count = 0\n let temp\n let t\n let i\n\n const length = data.get16()\n count += 2\n\n while (count < length) {\n temp = data.get8()\n count += 1\n t = temp & 0x0f\n\n if (t > 3) {\n throw new Error('ERROR: Quantization table ID > 3')\n }\n\n this.precision[t] = temp >> 4\n\n if (this.precision[t] === 0) {\n this.precision[t] = 8\n } else if (this.precision[t] === 1) {\n this.precision[t] = 16\n } else {\n throw new Error('ERROR: Quantization table precision error')\n }\n\n this.tq[t] = 1\n\n if (this.precision[t] === 8) {\n for (i = 0; i < 64; i += 1) {\n if (count > length) {\n throw new Error('ERROR: Quantization table format error')\n }\n\n this.quantTables[t][i] = data.get8()\n count += 1\n }\n\n QuantizationTable.enhanceQuantizationTable(this.quantTables[t], table)\n } else {\n for (i = 0; i < 64; i += 1) {\n if (count > length) {\n throw new Error('ERROR: Quantization table format error')\n }\n\n this.quantTables[t][i] = data.get16()\n count += 2\n }\n\n QuantizationTable.enhanceQuantizationTable(this.quantTables[t], table)\n }\n }\n\n if (count !== length) {\n throw new Error('ERROR: Quantization table error [count!=Lq]')\n }\n\n return 1\n }\n}\n","export const ScanComponent = {\n acTabSel: 0, // AC table selector\n dcTabSel: 0, // DC table selector\n scanCompSel: 0 // Scan component selector\n}\n","import { DataStream } from './data-stream.js'\nimport { ScanComponent } from './scan-component.js'\n\nexport class ScanHeader {\n ah = 0\n al = 0\n numComp = 0 // Number of components in the scan\n selection = 0 // Start of spectral or predictor selection\n spectralEnd = 0 // End of spectral selection\n components: Array = []\n\n read(data: DataStream) {\n let count = 0\n let i\n let temp\n\n const length = data.get16()\n count += 2\n\n this.numComp = data.get8()\n count += 1\n\n for (i = 0; i < this.numComp; i += 1) {\n this.components[i] = { ...ScanComponent }\n\n if (count > length) {\n throw new Error('ERROR: scan header format error')\n }\n\n this.components[i].scanCompSel = data.get8()\n count += 1\n\n temp = data.get8()\n count += 1\n\n this.components[i].dcTabSel = temp >> 4\n this.components[i].acTabSel = temp & 0x0f\n }\n\n this.selection = data.get8()\n count += 1\n\n this.spectralEnd = data.get8()\n count += 1\n\n temp = data.get8()\n this.ah = temp >> 4\n this.al = temp & 0x0f\n count += 1\n\n if (count !== length) {\n throw new Error('ERROR: scan header format error [count!=Ns]')\n }\n\n return 1\n }\n}\n","import { ComponentSpec } from './component-spec.js'\nimport { DataStream } from './data-stream.js'\nimport { FrameHeader } from './frame-header.js'\nimport { HuffmanTable } from './huffman-table.js'\nimport { QuantizationTable } from './quantization-table.js'\nimport { ScanHeader } from './scan-header.js'\nimport { createArray } from './utils.js'\n\nconst littleEndian = (function () {\n const buffer = new ArrayBuffer(2)\n new DataView(buffer).setInt16(0, 256, true /* littleEndian */)\n // Int16Array uses the platform's endianness.\n return new Int16Array(buffer)[0] === 256\n})()\n\nexport class Decoder {\n static IDCT_P = [\n 0, 5, 40, 16, 45, 2, 7, 42, 21, 56, 8, 61, 18, 47, 1, 4, 41, 23, 58, 13, 32, 24, 37, 10, 63, 17, 44, 3, 6, 43, 20,\n 57, 15, 34, 29, 48, 53, 26, 39, 9, 60, 19, 46, 22, 59, 12, 33, 31, 50, 55, 25, 36, 11, 62, 14, 35, 28, 49, 52, 27,\n 38, 30, 51, 54\n ]\n\n static TABLE = [\n 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44,\n 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49,\n 57, 58, 62, 63\n ]\n\n static MAX_HUFFMAN_SUBTREE = 50\n static MSB = 0x80000000\n static RESTART_MARKER_BEGIN = 0xffd0\n static RESTART_MARKER_END = 0xffd7\n\n // Value `markerIndex` takes once the 0xFF that introduces a marker has been\n // shifted into `temp`. Only `!== 0` is ever tested - see `readPastEntropyData`\n // for what the bit count itself means.\n static MARKER_SEEN = 9\n\n // How many bits of `temp` that 0xFF occupies. Bits below it are the marker,\n // not entropy coded data.\n static MARKER_BITS = 8\n\n buffer: ArrayBuffer | null = null\n stream: DataStream | null = null\n frame = new FrameHeader()\n huffTable = new HuffmanTable()\n quantTable = new QuantizationTable()\n scan = new ScanHeader()\n DU: number[][][] = createArray(10, 4, 64) as number[][][] // at most 10 data units in a MCU, at most 4 data units in one component\n HuffTab: number[][][] = createArray(4, 2, 50 * 256) as number[][][]\n IDCT_Source: number[] = []\n nBlock: number[] = [] // number of blocks in the i-th Comp in a scan\n acTab: number[][] = createArray(10, 1) as number[][] // ac HuffTab for the i-th Comp in a scan\n dcTab: number[][] = createArray(10, 1) as number[][] // dc HuffTab for the i-th Comp in a scan\n qTab: number[][] = createArray(10, 1) as number[][] // quantization table for the i-th Comp in a scan\n marker = 0\n markerIndex = 0\n numComp = 0\n restartInterval = 0\n selection = 0\n xDim = 0\n yDim = 0\n xLoc = 0\n yLoc = 0\n outputData: Uint8Array | Uint16Array | null = null\n restarting = false\n mask = 0\n numBytes = 0\n\n precision: number | undefined = undefined\n components: Array = []\n\n getter: null | ((index: number, compOffset: number) => number) = null\n setter: null | ((index: number, val: number, compOffset?: number) => void) = null\n output: null | ((PRED: number[]) => void) = null\n selector: null | ((compOffset?: number) => number) = null\n\n /**\n * The Decoder constructor.\n * @property {number} numBytes - number of bytes per component\n * @type {Function}\n */\n constructor(buffer?: ArrayBuffer | null, numBytes?: number) {\n this.buffer = buffer ?? null\n this.numBytes = numBytes ?? 0\n }\n\n /**\n * Returns decompressed data.\n */\n decompress(buffer: ArrayBuffer, offset: number, length: number): ArrayBuffer {\n const result = this.decode(buffer, offset, length)\n return result.buffer\n }\n\n decode(buffer?: ArrayBuffer, offset?: number, length?: number, numBytes?: number) {\n let scanNum = 0\n const pred = []\n let i\n let compN\n const temp = []\n const index = []\n let mcuNum\n\n if (buffer) {\n this.buffer = buffer\n }\n\n if (numBytes !== undefined) {\n this.numBytes = numBytes\n }\n\n this.stream = new DataStream(this.buffer as ArrayBuffer, offset, length)\n this.buffer = null\n\n this.xLoc = 0\n this.yLoc = 0\n let current = this.stream.get16()\n\n if (current !== 0xffd8) {\n // SOI\n throw new Error('Not a JPEG file')\n }\n\n current = this.stream.get16()\n\n while (current >> 4 !== 0x0ffc || current === 0xffc4) {\n // SOF 0~15\n switch (current) {\n case 0xffc4: // DHT\n this.huffTable.read(this.stream, this.HuffTab)\n break\n case 0xffcc: // DAC\n throw new Error(\"Program doesn't support arithmetic coding. (format throw new IOException)\")\n case 0xffdb:\n this.quantTable.read(this.stream, Decoder.TABLE)\n break\n case 0xffdd:\n this.restartInterval = this.readNumber() ?? 0\n break\n case 0xffe0:\n case 0xffe1:\n case 0xffe2:\n case 0xffe3:\n case 0xffe4:\n case 0xffe5:\n case 0xffe6:\n case 0xffe7:\n case 0xffe8:\n case 0xffe9:\n case 0xffea:\n case 0xffeb:\n case 0xffec:\n case 0xffed:\n case 0xffee:\n case 0xffef:\n this.readApp()\n break\n case 0xfffe:\n this.readComment()\n break\n default:\n if (current >> 8 !== 0xff) {\n throw new Error('ERROR: format throw new IOException! (decode)')\n }\n }\n\n current = this.stream.get16()\n }\n\n if (current < 0xffc0 || current > 0xffc7) {\n throw new Error('ERROR: could not handle arithmetic code!')\n }\n\n this.frame.read(this.stream)\n current = this.stream.get16()\n\n do {\n while (current !== 0x0ffda) {\n // SOS\n switch (current) {\n case 0xffc4: // DHT\n this.huffTable.read(this.stream, this.HuffTab)\n break\n case 0xffcc: // DAC\n throw new Error(\"Program doesn't support arithmetic coding. (format throw new IOException)\")\n case 0xffdb:\n this.quantTable.read(this.stream, Decoder.TABLE)\n break\n case 0xffdd:\n this.restartInterval = this.readNumber() ?? 0\n break\n case 0xffe0:\n case 0xffe1:\n case 0xffe2:\n case 0xffe3:\n case 0xffe4:\n case 0xffe5:\n case 0xffe6:\n case 0xffe7:\n case 0xffe8:\n case 0xffe9:\n case 0xffea:\n case 0xffeb:\n case 0xffec:\n case 0xffed:\n case 0xffee:\n case 0xffef:\n this.readApp()\n break\n case 0xfffe:\n this.readComment()\n break\n default:\n if (current >> 8 !== 0xff) {\n throw new Error('ERROR: format throw new IOException! (Parser.decode)')\n }\n }\n\n current = this.stream.get16()\n }\n\n this.precision = this.frame.precision\n this.components = this.frame.components\n\n if (!this.numBytes) {\n this.numBytes = Math.round(Math.ceil(this.precision / 8))\n }\n\n if (this.numBytes === 1) {\n this.mask = 0xff\n } else {\n this.mask = 0xffff\n }\n\n this.scan.read(this.stream)\n this.numComp = this.scan.numComp\n this.selection = this.scan.selection\n\n if (this.numBytes === 1) {\n if (this.numComp === 3) {\n this.getter = this.getValueRGB\n this.setter = this.setValueRGB\n this.output = this.outputRGB\n } else {\n this.getter = this.getValue8\n this.setter = this.setValue8\n this.output = this.outputSingle\n }\n } else {\n this.getter = this.getValue8\n this.setter = this.setValue8\n this.output = this.outputSingle\n }\n\n switch (this.selection) {\n case 2:\n this.selector = this.select2\n break\n case 3:\n this.selector = this.select3\n break\n case 4:\n this.selector = this.select4\n break\n case 5:\n this.selector = this.select5\n break\n case 6:\n this.selector = this.select6\n break\n case 7:\n this.selector = this.select7\n break\n default:\n this.selector = this.select1\n break\n }\n\n // this.scanComps = this.scan.components\n // this.quantTables = this.quantTable.quantTables\n\n for (i = 0; i < this.numComp; i += 1) {\n compN = this.scan.components[i].scanCompSel\n this.qTab[i] = this.quantTable.quantTables[this.components[compN].quantTableSel]\n this.nBlock[i] = this.components[compN].vSamp * this.components[compN].hSamp\n this.dcTab[i] = this.HuffTab[this.scan.components[i].dcTabSel][0]\n this.acTab[i] = this.HuffTab[this.scan.components[i].acTabSel][1]\n }\n\n this.xDim = this.frame.dimX\n this.yDim = this.frame.dimY\n if (this.numBytes === 1) {\n this.outputData = new Uint8Array(new ArrayBuffer(this.xDim * this.yDim * this.numBytes * this.numComp))\n } else {\n this.outputData = new Uint16Array(new ArrayBuffer(this.xDim * this.yDim * this.numBytes * this.numComp))\n }\n\n scanNum += 1\n\n while (true) {\n // Decode one scan\n temp[0] = 0\n index[0] = 0\n\n for (i = 0; i < 10; i += 1) {\n pred[i] = 1 << (this.precision - 1)\n }\n\n if (this.restartInterval === 0) {\n current = this.decodeUnit(pred, temp, index)\n\n while (current === 0 && this.xLoc < this.xDim && this.yLoc < this.yDim) {\n this.output(pred)\n current = this.decodeUnit(pred, temp, index)\n }\n\n break // current=MARKER\n }\n\n for (mcuNum = 0; mcuNum < this.restartInterval; mcuNum += 1) {\n this.restarting = mcuNum === 0\n current = this.decodeUnit(pred, temp, index)\n this.output(pred)\n\n if (current !== 0) {\n break\n }\n }\n\n if (current === 0) {\n if (this.markerIndex !== 0) {\n current = 0xff00 | this.marker\n this.markerIndex = 0\n } else {\n current = this.stream.get16()\n }\n }\n\n if (!(current >= Decoder.RESTART_MARKER_BEGIN && current <= Decoder.RESTART_MARKER_END)) {\n break // current=MARKER\n }\n }\n\n if (current === 0xffdc && scanNum === 1) {\n // DNL\n this.readNumber()\n current = this.stream.get16()\n }\n } while (current !== 0xffd9 && this.xLoc < this.xDim && this.yLoc < this.yDim && scanNum === 0)\n\n return this.outputData\n }\n\n decodeUnit(prev: number[], temp: number[], index: number[]): number {\n if (this.numComp === 1) {\n return this.decodeSingle(prev, temp, index)\n } else if (this.numComp === 3) {\n return this.decodeRGB(prev, temp, index)\n } else {\n return -1\n }\n }\n\n select1(compOffset?: number) {\n return this.getPreviousX(compOffset)\n }\n\n select2(compOffset?: number) {\n return this.getPreviousY(compOffset)\n }\n\n select3(compOffset?: number) {\n return this.getPreviousXY(compOffset)\n }\n\n select4(compOffset?: number) {\n return this.getPreviousX(compOffset) + this.getPreviousY(compOffset) - this.getPreviousXY(compOffset)\n }\n\n select5(compOffset?: number) {\n return this.getPreviousX(compOffset) + ((this.getPreviousY(compOffset) - this.getPreviousXY(compOffset)) >> 1)\n }\n\n select6(compOffset?: number) {\n return this.getPreviousY(compOffset) + ((this.getPreviousX(compOffset) - this.getPreviousXY(compOffset)) >> 1)\n }\n\n select7(compOffset?: number) {\n return (this.getPreviousX(compOffset) + this.getPreviousY(compOffset)) / 2\n }\n\n decodeRGB(prev: number[], temp: number[], index: number[]) {\n if (this.selector === null) throw new Error(\"decode hasn't run yet\")\n\n let actab, dctab, qtab, ctrC, i, k, j\n\n prev[0] = this.selector(0)\n prev[1] = this.selector(1)\n prev[2] = this.selector(2)\n\n for (ctrC = 0; ctrC < this.numComp; ctrC += 1) {\n qtab = this.qTab[ctrC]\n actab = this.acTab[ctrC]\n dctab = this.dcTab[ctrC]\n for (i = 0; i < this.nBlock[ctrC]; i += 1) {\n for (k = 0; k < this.IDCT_Source.length; k += 1) {\n this.IDCT_Source[k] = 0\n }\n\n let value = this.getHuffmanValue(dctab, temp, index)\n\n if (value >= 0xff00) {\n return value\n }\n\n prev[ctrC] = this.IDCT_Source[0] = prev[ctrC] + this.getn(index, value, temp, index)\n this.IDCT_Source[0] *= qtab[0]\n\n for (j = 1; j < 64; j += 1) {\n value = this.getHuffmanValue(actab, temp, index)\n\n if (value >= 0xff00) {\n return value\n }\n\n j += value >> 4\n\n if ((value & 0x0f) === 0) {\n if (value >> 4 === 0) {\n break\n }\n } else {\n this.IDCT_Source[Decoder.IDCT_P[j]] = this.getn(index, value & 0x0f, temp, index) * qtab[j]\n }\n }\n }\n }\n\n return 0\n }\n\n decodeSingle(prev: number[], temp: number[], index: number[]) {\n if (this.selector === null) throw new Error(\"decode hasn't run yet\")\n\n let value, i, n, nRestart\n\n if (this.restarting) {\n this.restarting = false\n prev[0] = 1 << (this.frame.precision - 1)\n } else {\n prev[0] = this.selector()\n }\n\n for (i = 0; i < this.nBlock[0]; i += 1) {\n value = this.getHuffmanValue(this.dcTab[0], temp, index)\n if (value >= 0xff00) {\n return value\n }\n\n n = this.getn(prev, value, temp, index)\n nRestart = n >> 8\n\n if (nRestart >= Decoder.RESTART_MARKER_BEGIN && nRestart <= Decoder.RESTART_MARKER_END) {\n return nRestart\n }\n\n prev[0] += n\n }\n\n return 0\n }\n\n //\tHuffman table for fast search: (HuffTab) 8-bit Look up table 2-layer search architecture, 1st-layer represent 256 node (8 bits) if codeword-length > 8\n //\tbits, then the entry of 1st-layer = (# of 2nd-layer table) | MSB and it is stored in the 2nd-layer Size of tables in each layer are 256.\n //\tHuffTab[*][*][0-256] is always the only 1st-layer table.\n //\n //\tAn entry can be: (1) (# of 2nd-layer table) | MSB , for code length > 8 in 1st-layer (2) (Code length) << 8 | HuffVal\n //\n //\tHuffmanValue(table HuffTab[x][y] (ex) HuffmanValue(HuffTab[1][0],...)\n //\t ):\n //\t return: Huffman Value of table\n //\t 0xFF?? if it receives a MARKER\n //\t Parameter: table HuffTab[x][y] (ex) HuffmanValue(HuffTab[1][0],...)\n //\t temp temp storage for remainded bits\n //\t index index to bit of temp\n //\t in FILE pointer\n //\t Effect:\n //\t temp store new remainded bits\n //\t index change to new index\n //\t in change to new position\n //\t NOTE:\n //\t Initial by temp=0; index=0;\n //\t NOTE: (explain temp and index)\n //\t temp: is always in the form at calling time or returning time\n //\t | byte 4 | byte 3 | byte 2 | byte 1 |\n //\t | 0 | 0 | 00000000 | 00000??? | if not a MARKER\n //\t ^index=3 (from 0 to 15)\n //\t 321\n //\t NOTE (marker and marker_index):\n //\t If get a MARKER from 'in', marker=the low-byte of the MARKER\n //\t and marker_index=9\n //\t If marker_index=9 then index is always > 8, or HuffmanValue()\n //\t will not be called\n getHuffmanValue(table: number[], temp: number[], index: number[]): number {\n let code, input\n const mask = 0xffff\n\n if (!this.stream) throw new Error('stream not initialized')\n\n if (index[0] < 8) {\n temp[0] <<= 8\n input = this.stream.get8()\n if (input === 0xff) {\n this.marker = this.stream.get8()\n if (this.marker !== 0) {\n this.markerIndex = Decoder.MARKER_SEEN\n }\n }\n temp[0] |= input\n } else {\n index[0] -= 8\n }\n\n code = table[temp[0] >> index[0]]\n\n if ((code & Decoder.MSB) !== 0) {\n if (this.markerIndex !== 0) {\n this.markerIndex = 0\n return 0xff00 | this.marker\n }\n\n temp[0] &= mask >> (16 - index[0])\n temp[0] <<= 8\n input = this.stream.get8()\n\n if (input === 0xff) {\n this.marker = this.stream.get8()\n if (this.marker !== 0) {\n this.markerIndex = Decoder.MARKER_SEEN\n }\n }\n\n temp[0] |= input\n code = table[(code & 0xff) * 256 + (temp[0] >> index[0])]\n index[0] += 8\n }\n\n index[0] += 8 - (code >> 8)\n\n if (index[0] < 0) {\n throw new Error('index=' + index[0] + ' temp=' + temp[0] + ' code=' + code + ' in HuffmanValue()')\n }\n\n if (this.readPastEntropyData(index)) {\n this.markerIndex = 0\n return 0xff00 | this.marker\n }\n\n temp[0] &= mask >> (16 - index[0])\n return code & 0xff\n }\n\n getn(PRED: number[], n: number, temp: number[], index: number[]) {\n let result, input\n const one = 1\n const n_one = -1\n const mask = 0xffff\n\n if (this.stream === null) throw new Error('stream not initialized')\n\n if (n === 0) {\n return 0\n }\n\n if (n === 16) {\n if (PRED[0] >= 0) {\n return -32768\n } else {\n return 32768\n }\n }\n\n index[0] -= n\n\n if (index[0] >= 0) {\n if (this.readPastEntropyData(index)) {\n this.markerIndex = 0\n return (0xff00 | this.marker) << 8\n }\n\n result = temp[0] >> index[0]\n temp[0] &= mask >> (16 - index[0])\n } else {\n temp[0] <<= 8\n input = this.stream.get8()\n\n if (input === 0xff) {\n this.marker = this.stream.get8()\n if (this.marker !== 0) {\n this.markerIndex = Decoder.MARKER_SEEN\n }\n }\n\n temp[0] |= input\n index[0] += 8\n\n if (index[0] < 0) {\n if (this.markerIndex !== 0) {\n this.markerIndex = 0\n return (0xff00 | this.marker) << 8\n }\n\n temp[0] <<= 8\n input = this.stream.get8()\n\n if (input === 0xff) {\n this.marker = this.stream.get8()\n if (this.marker !== 0) {\n this.markerIndex = Decoder.MARKER_SEEN\n }\n }\n\n temp[0] |= input\n index[0] += 8\n }\n\n if (index[0] < 0) {\n throw new Error('index=' + index[0] + ' in getn()')\n }\n\n if (this.readPastEntropyData(index)) {\n this.markerIndex = 0\n return (0xff00 | this.marker) << 8\n }\n\n result = temp[0] >> index[0]\n temp[0] &= mask >> (16 - index[0])\n }\n\n if (result < one << (n - 1)) {\n result += (n_one << n) + 1\n }\n\n return result\n }\n\n getPreviousX(compOffset = 0): number {\n if (this.getter === null) throw new Error(\"decode hasn't run yet\")\n\n if (this.xLoc > 0) {\n return this.getter(this.yLoc * this.xDim + this.xLoc - 1, compOffset)\n } else if (this.yLoc > 0) {\n return this.getPreviousY(compOffset)\n } else {\n return 1 << (this.frame.precision - 1)\n }\n }\n\n getPreviousXY(compOffset = 0) {\n if (this.getter === null) throw new Error(\"decode hasn't run yet\")\n\n if (this.xLoc > 0 && this.yLoc > 0) {\n return this.getter((this.yLoc - 1) * this.xDim + this.xLoc - 1, compOffset)\n } else {\n return this.getPreviousY(compOffset)\n }\n }\n\n getPreviousY(compOffset = 0) {\n if (this.getter === null) throw new Error(\"decode hasn't run yet\")\n\n if (this.yLoc > 0) {\n return this.getter((this.yLoc - 1) * this.xDim + this.xLoc, compOffset)\n } else {\n return this.getPreviousX(compOffset)\n }\n }\n\n /**\n * True when the bits just consumed came out of a marker rather than out of\n * the entropy coded segment.\n *\n * `temp` holds `index` unconsumed bits (see the `getHuffmanValue` notes). The\n * moment `getHuffmanValue`/`getn` shifts in a byte that turns out to be the\n * 0xFF introducing a marker, those 8 bits stop being data: only the\n * `index - MARKER_BITS` bits above them are real, and the scan is over once\n * they run out.\n *\n * Consuming the last real bit leaves `index === MARKER_BITS`, and that is\n * still a valid decode - it is a scan whose final Huffman code ends exactly\n * on a byte boundary, so the encoder had no partial byte left to pad (T.81\n * B.1.1.2 pads only an incomplete final byte). Testing `index < markerIndex`\n * rejected that case and dropped the frame's last sample, which is what\n * DCMTK produces when a frame ends in a long run of one value: the run's\n * short codes tile the final byte exactly.\n */\n readPastEntropyData(index: number[]): boolean {\n return this.markerIndex !== 0 && index[0] < Decoder.MARKER_BITS\n }\n\n outputSingle(PRED: number[]) {\n if (this.setter === null) throw new Error(\"decode hasn't run yet\")\n\n if (this.xLoc < this.xDim && this.yLoc < this.yDim) {\n this.setter(this.yLoc * this.xDim + this.xLoc, this.mask & PRED[0])\n\n this.xLoc += 1\n\n if (this.xLoc >= this.xDim) {\n this.yLoc += 1\n this.xLoc = 0\n }\n }\n }\n\n outputRGB(PRED: number[]) {\n if (this.setter === null) throw new Error(\"decode hasn't run yet\")\n\n const offset = this.yLoc * this.xDim + this.xLoc\n\n if (this.xLoc < this.xDim && this.yLoc < this.yDim) {\n this.setter(offset, PRED[0], 0)\n this.setter(offset, PRED[1], 1)\n this.setter(offset, PRED[2], 2)\n\n this.xLoc += 1\n\n if (this.xLoc >= this.xDim) {\n this.yLoc += 1\n this.xLoc = 0\n }\n }\n }\n\n setValue8(index: number, val: number) {\n if (!this.outputData) throw new Error('output data not ready')\n\n if (littleEndian) {\n this.outputData[index] = val\n } else {\n this.outputData[index] = ((val & 0xff) << 8) | ((val >> 8) & 0xff)\n }\n }\n\n getValue8(index: number) {\n if (this.outputData === null) throw new Error('output data not ready')\n if (littleEndian) {\n return this.outputData[index] // mask should not be necessary because outputData is either Int8Array or Int16Array\n } else {\n const val = this.outputData[index]\n return ((val & 0xff) << 8) | ((val >> 8) & 0xff)\n }\n }\n\n setValueRGB(index: number, val: number, compOffset = 0) {\n if (this.outputData === null) return\n this.outputData[index * 3 + compOffset] = val\n }\n\n getValueRGB(index: number, compOffset: number) {\n if (this.outputData === null) throw new Error('output data not ready')\n return this.outputData[index * 3 + compOffset]\n }\n\n readApp() {\n if (this.stream === null) return null\n\n let count = 0\n const length = this.stream.get16()\n count += 2\n\n while (count < length) {\n this.stream.get8()\n count += 1\n }\n\n return length\n }\n\n readComment() {\n if (this.stream === null) return null\n\n let sb = ''\n let count = 0\n\n const length = this.stream.get16()\n count += 2\n\n while (count < length) {\n sb += this.stream.get8()\n count += 1\n }\n\n return sb\n }\n\n readNumber() {\n if (this.stream === null) return null\n\n const Ld = this.stream.get16()\n\n if (Ld !== 4) {\n throw new Error('ERROR: Define number format throw new IOException [Ld!=4]')\n }\n\n return this.stream.get16()\n }\n}\n"],"mappings":"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","names":["crcTable"]} \ No newline at end of file diff --git a/packages/dicom-codec/test/integration.test.js b/packages/dicom-codec/test/integration.test.js index e4a4b739..89c69f97 100644 --- a/packages/dicom-codec/test/integration.test.js +++ b/packages/dicom-codec/test/integration.test.js @@ -217,8 +217,8 @@ describe.skipIf(!ALL_BUILT)("dicom-codec integration", () => { }) describe("JPEG Lossless (1.2.840.10008.1.2.4.57 / .70)", () => { - // These go through dicom-codec's internal jpegLosslessCodec (the vendored - // jpeg-lossless-decoder-js build, pure JS — no separate wasm package). + // These go through dicom-codec's internal jpegLosslessCodec + // (@cornerstonejs/jpeg-lossless-decoder-js, pure JS — no wasm package). // Both fixtures encode the same 512x512x16 CT slice; the reference // fixtures/raw/CT-512x512.raw was cross-validated three ways: the RLE // decoder, the Process 14 path of jpeg-lossless-decoder-js and DCMTK's @@ -258,7 +258,7 @@ describe.skipIf(!ALL_BUILT)("dicom-codec integration", () => { expect(frameBytes(result.imageFrame).equals(ctRaw)).toBe(true) }) - // This fixture is the regression case for the vendored decoder: its scan + // This fixture is the regression case for the forked decoder: its scan // ends in a long run of the image minimum whose zero-difference codes tile // the final byte exactly, and published jpeg-lossless-decoder-js 2.1.2 // decoded that last sample as 0 instead of -2000 (it read the 0xFF diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fda79116..ca482534 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -85,6 +85,9 @@ importers: '@cornerstonejs/codec-openjph': specifier: ^2.4.11 version: link:../openjphjs + '@cornerstonejs/jpeg-lossless-decoder-js': + specifier: ^2.2.0 + version: 2.2.0 browser-or-node: specifier: ^2.0.0 version: 2.1.1 @@ -681,6 +684,9 @@ packages: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 vitest: ^3.2 || ^4 + '@cornerstonejs/jpeg-lossless-decoder-js@2.2.0': + resolution: {integrity: sha512-41y2/PDEsH8FGr6zZZ58TFSak8DLtEEInCuIGJb/sQ3aAPHx7dmZZEtyPamRS6eW3jp9hWiA59KziPiyXSSXSg==} + '@discoveryjs/json-ext@0.5.7': resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} engines: {node: '>=10.0.0'} @@ -3621,6 +3627,8 @@ snapshots: - debug - supports-color + '@cornerstonejs/jpeg-lossless-decoder-js@2.2.0': {} + '@discoveryjs/json-ext@0.5.7': {} '@esbuild/aix-ppc64@0.28.1': diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index d76285a3..0c386e75 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -62,3 +62,20 @@ frozenLockfile: true # local decision. allowBuilds: esbuild: true + +# pnpm refuses a dependency version that the registry published very recently, +# which is a supply-chain measure: it buys time for a compromised release to be +# found and unpublished before this repo installs it. That measure is aimed at +# third-party code, and it cannot judge our own. +# +# @cornerstonejs/jpeg-lossless-decoder-js is published by this organisation from +# cornerstonejs/JPEGLosslessDecoderJS, and dicom-codec needs it for transfer +# syntaxes 1.2.840.10008.1.2.4.57 and .70. Waiting out the age window on our own +# release would block a decode fix behind a delay that protects nothing here. +# +# The entry names the package and no version on purpose. pnpm writes a +# version-pinned entry when it adds one itself, and that entry goes stale at +# every release of the package, which turns a routine version bump into an +# install failure. Keep this list to packages that this organisation publishes. +minimumReleaseAgeExclude: + - '@cornerstonejs/jpeg-lossless-decoder-js' diff --git a/tools/fixture-verification/README.md b/tools/fixture-verification/README.md index d902a872..c180a5e4 100644 --- a/tools/fixture-verification/README.md +++ b/tools/fixture-verification/README.md @@ -3,7 +3,7 @@ Independent, from-scratch decoders used to verify that the `.raw`/`.RAW` pixel references committed under `packages/*/test/fixtures` are correct. They share **no code** with the codecs under test (the emscripten wasm -builds, the vendored `jpeg-lossless-decoder-js`, or dicom-codec's +builds, `@cornerstonejs/jpeg-lossless-decoder-js`, or dicom-codec's `rleLossless.js`), so byte-exact agreement means two independent implementations produce the same pixels from the same codestream. @@ -108,7 +108,7 @@ slice, and the library's own Process-14 path. The cause was an off-by-one in the end-of-scan guards, which read the 0xFF introducing EOI as entropy coded data whenever the last Huffman code ended -exactly on a byte boundary. dicom-codec now uses a vendored build carrying the -fix — see `packages/dicom-codec/src/vendor/jpeg-lossless-decoder-js/README.md` -— and `packages/dicom-codec/test/integration.test.js` compares both fixtures -byte-for-byte. +exactly on a byte boundary. dicom-codec now depends on +`@cornerstonejs/jpeg-lossless-decoder-js`, a fork of the library that carries +the fix, and `packages/dicom-codec/test/integration.test.js` compares both +fixtures byte-for-byte.