@niivue/nv-ext-tile 1.0.0-rc.7 → 1.0.0-rc.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -101,7 +101,8 @@ const slide = NVSlide.fromSource(new DicomWsiSource(index.slides[0], files))
101
101
  that changed since indexing fails rather than decoding garbage.
102
102
  - Supported transfer syntaxes: JPEG baseline/extended (browser), JPEG 2000 and JPEG-LS
103
103
  (`registerDcm2niixDecoders`, with `@niivue/dcm2niix` as an optional peer dependency;
104
- a decoder the host already registered wins).
104
+ a decoder the host already registered wins). A dcm2niix pool whose workers die is
105
+ replaced, and the tile or profile retried once.
105
106
  - Slides are shown with row 0 of the total pixel matrix at the top (`displayYAxis: 'down'`),
106
107
  as OpenSlide shows them.
107
108
  - Index speed for the 8 OpenSlide DICOM-WSI test sets (local SSD; Chrome reads through
@@ -1,3 +1,10 @@
1
+ import { FrameDecoder } from '@niivue/dcm2niix';
2
+ import { SlideIccTransform, SlideTileDecoder } from '@niivue/niivue';
3
+ /** Tile decoder and ICC transform over one lazily created dcm2niix pool, replaced when its workers die. */
4
+ export declare function createDcm2niixDecoders(createDecoder?: () => Promise<FrameDecoder>): {
5
+ decodeTile: SlideTileDecoder;
6
+ bakeProfile: SlideIccTransform;
7
+ };
1
8
  /**
2
9
  * Register JPEG 2000 and JPEG-LS tile decoders for NVSlide, backed by dcm2niix's
3
10
  * WASM worker pool (loaded on the first such tile), and its LittleCMS ICC
package/dist/dicom.js CHANGED
@@ -1,117 +1,151 @@
1
1
  import { NVSlide as q } from "@niivue/niivue";
2
- let J = null;
3
- const wt = () => (J ?? (J = import("@niivue/dcm2niix").then((e) => e.createFrameDecoder({ workers: 2 })).catch((e) => {
4
- throw J = null, e;
5
- })), J), ft = 33, Et = async (e, t) => {
6
- const s = await (await wt()).iccLut(e, { size: ft, signal: t });
7
- return s && { size: ft, data: s };
8
- }, xt = async (e, { level: t, signal: r }) => {
9
- var o;
10
- const s = await wt(), n = {
11
- photometric: t.photometric,
12
- bitsStored: t.bitsStored,
13
- signal: r
2
+ function $t(e) {
3
+ return e instanceof Error && e.name === "Error" && !e.message.startsWith("frame decoder ");
4
+ }
5
+ const ct = 33;
6
+ function Mt(e = () => import("@niivue/dcm2niix").then(
7
+ (t) => t.createFrameDecoder({ workers: 2 })
8
+ )) {
9
+ let t = null;
10
+ const r = () => {
11
+ const a = {
12
+ active: 0,
13
+ retired: !1,
14
+ decoder: e().catch((n) => {
15
+ throw t === a && (t = null), n;
16
+ })
17
+ };
18
+ return a;
19
+ }, s = async (a, n, l = !1) => {
20
+ t ?? (t = r());
21
+ const f = t;
22
+ f.active++;
23
+ try {
24
+ return await n(await f.decoder);
25
+ } catch (d) {
26
+ if (d !== (a == null ? void 0 : a.reason) && $t(d) && (f.retired = !0, t === f && (t = null), !l && !(a != null && a.aborted)))
27
+ return s(a, n, !0);
28
+ throw d;
29
+ } finally {
30
+ --f.active === 0 && f.retired && f.decoder.then(
31
+ (d) => d.terminate(),
32
+ () => {
33
+ }
34
+ );
35
+ }
14
36
  };
15
- if ((o = t.photometric) != null && o.startsWith("MONOCHROME"))
16
- return s.decode(e, { ...n, output: "samples" });
17
- const i = await s.decode(e, n), c = i.rgba;
18
- return createImageBitmap(new ImageData(c, i.width, i.height));
19
- };
20
- function Tt() {
21
- for (const e of ["image/jp2", "image/jls"])
22
- q.tileDecoder(e) || q.registerTileDecoder(e, xt);
23
- q.iccTransform() || q.registerIccTransform(Et);
37
+ return { decodeTile: (a, { level: n, signal: l }) => s(l, async (f) => {
38
+ var u;
39
+ const d = {
40
+ photometric: n.photometric,
41
+ bitsStored: n.bitsStored,
42
+ signal: l
43
+ };
44
+ if ((u = n.photometric) != null && u.startsWith("MONOCHROME"))
45
+ return f.decode(a, { ...d, output: "samples" });
46
+ const m = await f.decode(a, d), h = m.rgba;
47
+ return createImageBitmap(new ImageData(h, m.width, m.height));
48
+ }), bakeProfile: (a, n) => s(n, async (l) => {
49
+ const f = await l.iccLut(a, { size: ct, signal: n });
50
+ return f && { size: ct, data: f };
51
+ }) };
24
52
  }
25
- const et = (e) => e.webkitRelativePath || e.name, _ = 4294967295, lt = 8, K = 64 << 20, Q = 1 << 22, ht = 2 * Q, mt = 4096, Pt = { US: 2, UL: 4, SL: 4 }, d = (e, t) => (e << 16 | t) >>> 0, m = {
26
- transferSyntax: d(2, 16),
27
- imageType: d(8, 8),
28
- sop: d(8, 24),
29
- study: d(32, 13),
30
- series: d(32, 14),
31
- frameOfReference: d(32, 82),
32
- organization: d(32, 37649),
33
- concatNumber: d(32, 37218),
34
- concatOffset: d(32, 37416),
35
- photometric: d(40, 4),
36
- frames: d(40, 8),
37
- rows: d(40, 16),
38
- columns: d(40, 17),
39
- spacing: d(40, 48),
40
- bitsStored: d(40, 257),
41
- iccProfile: d(40, 8192),
42
- colorSpace: d(40, 8194),
43
- container: d(64, 1298),
44
- zOffset: d(64, 1866),
45
- totalColumns: d(72, 6),
46
- totalRows: d(72, 7),
47
- opticalPathSeq: d(72, 261),
48
- opticalPathId: d(72, 262),
49
- columnPosition: d(72, 542),
50
- rowPosition: d(72, 543),
51
- opticalPaths: d(72, 770),
52
- focalPlanes: d(72, 771),
53
- shared: d(20992, 37417),
54
- perFrame: d(20992, 37424),
55
- eot: d(32736, 1),
56
- eotLengths: d(32736, 2),
57
- pixelData: d(32736, 16),
58
- item: d(65534, 57344),
59
- itemEnd: d(65534, 57357),
60
- sequenceEnd: d(65534, 57565)
61
- }, bt = new Set("OB OD OF OL OV OW SQ SV UC UN UR UT UV".split(" ")), Ut = /* @__PURE__ */ new Set([
53
+ function vt() {
54
+ const { decodeTile: e, bakeProfile: t } = Mt();
55
+ for (const r of ["image/jp2", "image/jls"])
56
+ q.tileDecoder(r) || q.registerTileDecoder(r, e);
57
+ q.iccTransform() || q.registerIccTransform(t);
58
+ }
59
+ const tt = (e) => e.webkitRelativePath || e.name, k = 4294967295, ft = 8, J = 64 << 20, K = 1 << 22, lt = 2 * K, ht = 4096, Et = { US: 2, UL: 4, SL: 4 }, p = (e, t) => (e << 16 | t) >>> 0, w = {
60
+ transferSyntax: p(2, 16),
61
+ imageType: p(8, 8),
62
+ sop: p(8, 24),
63
+ study: p(32, 13),
64
+ series: p(32, 14),
65
+ frameOfReference: p(32, 82),
66
+ organization: p(32, 37649),
67
+ concatNumber: p(32, 37218),
68
+ concatOffset: p(32, 37416),
69
+ photometric: p(40, 4),
70
+ frames: p(40, 8),
71
+ rows: p(40, 16),
72
+ columns: p(40, 17),
73
+ spacing: p(40, 48),
74
+ bitsStored: p(40, 257),
75
+ iccProfile: p(40, 8192),
76
+ colorSpace: p(40, 8194),
77
+ container: p(64, 1298),
78
+ zOffset: p(64, 1866),
79
+ totalColumns: p(72, 6),
80
+ totalRows: p(72, 7),
81
+ opticalPathSeq: p(72, 261),
82
+ opticalPathId: p(72, 262),
83
+ columnPosition: p(72, 542),
84
+ rowPosition: p(72, 543),
85
+ opticalPaths: p(72, 770),
86
+ focalPlanes: p(72, 771),
87
+ shared: p(20992, 37417),
88
+ perFrame: p(20992, 37424),
89
+ eot: p(32736, 1),
90
+ eotLengths: p(32736, 2),
91
+ pixelData: p(32736, 16),
92
+ item: p(65534, 57344),
93
+ itemEnd: p(65534, 57357),
94
+ sequenceEnd: p(65534, 57565)
95
+ }, Pt = new Set("OB OD OF OL OV OW SQ SV UC UN UR UT UV".split(" ")), bt = /* @__PURE__ */ new Set([
62
96
  "1.2.840.10008.1.2",
63
97
  "1.2.840.10008.1.2.2",
64
98
  "1.2.840.10008.1.2.1.99"
65
99
  ]);
66
- class x extends Error {
100
+ class P extends Error {
67
101
  constructor(t, r) {
68
102
  super(r), this.reason = t;
69
103
  }
70
104
  }
71
- class W extends Error {
105
+ class z extends Error {
72
106
  }
73
107
  function A(e, t) {
74
- if (t + 2 > e.length) throw new W();
108
+ if (t + 2 > e.length) throw new z();
75
109
  return e[t] | e[t + 1] << 8;
76
110
  }
77
111
  function v(e, t) {
78
- if (t + 4 > e.length) throw new W();
112
+ if (t + 4 > e.length) throw new z();
79
113
  return (e[t] | e[t + 1] << 8 | e[t + 2] << 16 | e[t + 3] << 24) >>> 0;
80
114
  }
81
- function dt(e, t, r, s, n, i) {
115
+ function ut(e, t, r, s, o, c) {
82
116
  for (; t < r; ) {
83
- const c = d(A(e, t), A(e, t + 2));
84
- if (c === m.itemEnd || c === m.sequenceEnd) return t + 8;
85
- if (n.depth === 0 && c === m.pixelData) return t;
86
- const o = String.fromCharCode(e[t + 4], e[t + 5]);
87
- if (!/^[A-Z]{2}$/.test(o))
88
- throw new x("malformed", `no explicit VR at byte ${t}`);
89
- const f = bt.has(o), h = f ? v(e, t + 8) : A(e, t + 6), p = t + (f ? 12 : 8);
90
- if (o === "SQ" || o === "UN" && h === _) {
91
- if (n.depth >= lt)
92
- throw new x("malformed", `sequences nested deeper than ${lt}`);
93
- const u = h === _ ? s : p + h;
94
- let l = p;
95
- for (let w = 0; l < u; w++) {
96
- const E = d(A(e, l), A(e, l + 2));
97
- if (E === m.sequenceEnd) {
98
- l += 8;
117
+ const a = p(A(e, t), A(e, t + 2));
118
+ if (a === w.itemEnd || a === w.sequenceEnd) return t + 8;
119
+ if (o.depth === 0 && a === w.pixelData) return t;
120
+ const n = String.fromCharCode(e[t + 4], e[t + 5]);
121
+ if (!/^[A-Z]{2}$/.test(n))
122
+ throw new P("malformed", `no explicit VR at byte ${t}`);
123
+ const l = Pt.has(n), f = l ? v(e, t + 8) : A(e, t + 6), d = t + (l ? 12 : 8);
124
+ if (n === "SQ" || n === "UN" && f === k) {
125
+ if (o.depth >= ft)
126
+ throw new P("malformed", `sequences nested deeper than ${ft}`);
127
+ const m = f === k ? s : d + f;
128
+ let h = d;
129
+ for (let u = 0; h < m; u++) {
130
+ const E = p(A(e, h), A(e, h + 2));
131
+ if (E === w.sequenceEnd) {
132
+ h += 8;
99
133
  break;
100
134
  }
101
- if (E !== m.item)
102
- throw new x("malformed", `no item at byte ${l}`);
103
- const y = v(e, l + 4), S = y === _ ? s : l + 8 + y, U = n.depth === 0 ? { root: c, item: w, depth: 1 } : { ...n, depth: n.depth + 1 }, I = dt(e, l + 8, S, s, U, i);
104
- l = y === _ ? I : S;
135
+ if (E !== w.item)
136
+ throw new P("malformed", `no item at byte ${h}`);
137
+ const y = v(e, h + 4), S = y === k ? s : h + 8 + y, U = o.depth === 0 ? { root: a, item: u, depth: 1 } : { ...o, depth: o.depth + 1 }, I = ut(e, h + 8, S, s, U, c);
138
+ h = y === k ? I : S;
105
139
  }
106
- t = l;
140
+ t = h;
107
141
  continue;
108
142
  }
109
- if (h === _)
110
- throw new x("malformed", `undefined-length ${o} at byte ${t}`);
111
- if (p + h > s)
112
- throw new x("malformed", `${o} at byte ${t} runs past the file`);
113
- if (p + h > e.length) throw new W();
114
- i({ tag: c, vr: o, offset: p, length: h, ...n }), t = p + h;
143
+ if (f === k)
144
+ throw new P("malformed", `undefined-length ${n} at byte ${t}`);
145
+ if (d + f > s)
146
+ throw new P("malformed", `${n} at byte ${t} runs past the file`);
147
+ if (d + f > e.length) throw new z();
148
+ c({ tag: a, vr: n, offset: d, length: f, ...o }), t = d + f;
115
149
  }
116
150
  return t;
117
151
  }
@@ -119,405 +153,405 @@ const N = (e, t) => String.fromCharCode(...e.subarray(t.offset, t.offset + t.len
119
153
  /[\0 ]+$/,
120
154
  ""
121
155
  );
122
- function z(e, t) {
123
- const r = Pt[t.vr];
156
+ function W(e, t) {
157
+ const r = Et[t.vr];
124
158
  if (!r) return Number(N(e, t).split("\\")[0]);
125
159
  if (t.length < r) return Number.NaN;
126
160
  const s = new DataView(e.buffer, e.byteOffset + t.offset, r);
127
161
  return t.vr === "US" ? s.getUint16(0, !0) : t.vr === "UL" ? s.getUint32(0, !0) : s.getInt32(0, !0);
128
162
  }
129
- function ut(e, t) {
130
- const r = t.find((s) => s.tag === m.transferSyntax);
131
- if (r && Ut.has(N(e, r)))
132
- throw new x(
163
+ function mt(e, t) {
164
+ const r = t.find((s) => s.tag === w.transferSyntax);
165
+ if (r && bt.has(N(e, r)))
166
+ throw new P(
133
167
  "transfer-syntax",
134
168
  `transfer syntax ${N(e, r)} is not explicit VR little endian`
135
169
  );
136
170
  }
137
- async function Ot(e, t) {
171
+ async function xt(e, t) {
138
172
  let r = Math.min(t, 1048576), s = new Uint8Array(0);
139
173
  for (; ; ) {
140
- const n = await e(s.length, r - s.length), i = new Uint8Array(r);
141
- i.set(s), i.set(n, s.length), s = i;
142
- const c = [];
174
+ const o = await e(s.length, r - s.length), c = new Uint8Array(r);
175
+ c.set(s), c.set(o, s.length), s = c;
176
+ const a = [];
143
177
  try {
144
178
  if (String.fromCharCode(...s.subarray(128, 132)) !== "DICM")
145
- throw new x("not-dicom", "no DICM preamble");
146
- const o = dt(
179
+ throw new P("not-dicom", "no DICM preamble");
180
+ const n = ut(
147
181
  s,
148
182
  132,
149
183
  t,
150
184
  t,
151
185
  { root: 0, item: 0, depth: 0 },
152
- (f) => c.push(f)
186
+ (l) => a.push(l)
153
187
  );
154
- if (o + 20 > s.length) throw new W();
155
- return ut(s, c), { b: s, pixel: o, elements: c };
156
- } catch (o) {
157
- if (ut(s, c), !(o instanceof W)) throw o;
188
+ if (n + 20 > s.length) throw new z();
189
+ return mt(s, a), { b: s, pixel: n, elements: a };
190
+ } catch (n) {
191
+ if (mt(s, a), !(n instanceof z)) throw n;
158
192
  if (r >= t)
159
- throw new x("not-tiled", "no Pixel Data (e.g. a DICOMDIR)");
160
- if (r >= K)
161
- throw new x("malformed", `header over ${K >> 20} MiB`);
162
- r = Math.min(t, K, r * 4);
193
+ throw new P("not-tiled", "no Pixel Data (e.g. a DICOMDIR)");
194
+ if (r >= J)
195
+ throw new P("malformed", `header over ${J >> 20} MiB`);
196
+ r = Math.min(t, J, r * 4);
163
197
  }
164
198
  }
165
199
  }
166
- async function At(e) {
167
- const t = e.size, r = async (a, g) => new Uint8Array(await e.slice(a, a + g).arrayBuffer()), { b: s, pixel: n, elements: i } = await Ot(r, t), c = /* @__PURE__ */ new Map(), o = [], f = [], h = [];
168
- let p, u, l, w = 0, E = !1;
169
- for (const a of i)
170
- a.depth === 0 ? c.set(a.tag, a) : a.root === m.opticalPathSeq && a.depth === 1 ? (a.tag === m.opticalPathId && (o[a.item] = N(s, a)), a.tag === m.iccProfile && (f[a.item] = { offset: a.offset, length: a.length }), a.tag === m.colorSpace && (h[a.item] = N(s, a))) : a.root === m.shared ? (a.tag === m.spacing && (p = N(s, a)), a.tag === m.opticalPathId && (u = N(s, a)), a.tag === m.zOffset && (l = z(s, a))) : a.root === m.perFrame && (w = Math.max(w, a.item + 1), a.tag === m.columnPosition && (E = !0));
171
- f.forEach((a, g) => {
172
- a && h[g] && (a.colorSpace = h[g]);
200
+ async function Ut(e) {
201
+ const t = e.size, r = async (i, g) => new Uint8Array(await e.slice(i, i + g).arrayBuffer()), { b: s, pixel: o, elements: c } = await xt(r, t), a = /* @__PURE__ */ new Map(), n = [], l = [], f = [];
202
+ let d, m, h, u = 0, E = !1;
203
+ for (const i of c)
204
+ i.depth === 0 ? a.set(i.tag, i) : i.root === w.opticalPathSeq && i.depth === 1 ? (i.tag === w.opticalPathId && (n[i.item] = N(s, i)), i.tag === w.iccProfile && (l[i.item] = { offset: i.offset, length: i.length }), i.tag === w.colorSpace && (f[i.item] = N(s, i))) : i.root === w.shared ? (i.tag === w.spacing && (d = N(s, i)), i.tag === w.opticalPathId && (m = N(s, i)), i.tag === w.zOffset && (h = W(s, i))) : i.root === w.perFrame && (u = Math.max(u, i.item + 1), i.tag === w.columnPosition && (E = !0));
205
+ l.forEach((i, g) => {
206
+ i && f[g] && (i.colorSpace = f[g]);
173
207
  });
174
- const y = (a) => {
175
- const g = c.get(a);
208
+ const y = (i) => {
209
+ const g = a.get(i);
176
210
  return g ? N(s, g) : "";
177
- }, S = (a, g, R, j) => {
178
- const ct = c.get(a), k = ct ? z(s, ct) : g;
179
- if (!Number.isInteger(k) || k < R || k > j)
180
- throw new x("malformed", `tag ${a.toString(16)} is ${k}`);
181
- return k;
182
- }, U = S(m.columns, 0, 0, 65535), I = S(m.rows, 0, 0, 65535);
183
- if (!U || !I) throw new x("not-tiled", "no Rows/Columns");
184
- const b = S(m.frames, 1, 1, Math.floor((t - n) / 8)), D = S(m.totalColumns, U, 1, 4294967295), $ = S(m.totalRows, I, 1, 4294967295), O = Math.ceil(D / U), M = Math.ceil($ / I), P = O * M;
185
- if (P > Q)
186
- throw new x("malformed", `${P} tiles in one level (over ${Q})`);
187
- const C = S(m.focalPlanes, 1, 1, 65535), nt = S(m.concatOffset, 0, 0, 4294967295), ot = y(m.organization), X = ot === "TILED_SPARSE", G = ot === "TILED_FULL" || !X && !E;
211
+ }, S = (i, g, R, j) => {
212
+ const it = a.get(i), B = it ? W(s, it) : g;
213
+ if (!Number.isInteger(B) || B < R || B > j)
214
+ throw new P("malformed", `tag ${i.toString(16)} is ${B}`);
215
+ return B;
216
+ }, U = S(w.columns, 0, 0, 65535), I = S(w.rows, 0, 0, 65535);
217
+ if (!U || !I) throw new P("not-tiled", "no Rows/Columns");
218
+ const x = S(w.frames, 1, 1, Math.floor((t - o) / 8)), D = S(w.totalColumns, U, 1, 4294967295), $ = S(w.totalRows, I, 1, 4294967295), O = Math.ceil(D / U), M = Math.ceil($ / I), b = O * M;
219
+ if (b > K)
220
+ throw new P("malformed", `${b} tiles in one level (over ${K})`);
221
+ const C = S(w.focalPlanes, 1, 1, 65535), et = S(w.concatOffset, 0, 0, 4294967295), nt = y(w.organization), X = nt === "TILED_SPARSE", G = nt === "TILED_FULL" || !X && !E;
188
222
  if (G) {
189
- const a = S(m.opticalPaths, 1, 1, 65535);
190
- if (nt + b > P * C * a)
191
- throw new x(
223
+ const i = S(w.opticalPaths, 1, 1, 65535);
224
+ if (et + x > b * C * i)
225
+ throw new P(
192
226
  "malformed",
193
- `frames past ${P} tiles x ${C} planes x ${a} paths`
227
+ `frames past ${b} tiles x ${C} planes x ${i} paths`
194
228
  );
195
229
  }
196
230
  const L = {
197
231
  instance: {
198
- id: y(m.sop),
199
- file: et(e),
232
+ id: y(w.sop),
233
+ file: tt(e),
200
234
  size: t,
201
235
  version: String(e.lastModified),
202
- transferSyntax: y(m.transferSyntax),
203
- photometric: y(m.photometric),
204
- bitsStored: S(m.bitsStored, 8, 1, 32)
236
+ transferSyntax: y(w.transferSyntax),
237
+ photometric: y(w.photometric),
238
+ bitsStored: S(w.bitsStored, 8, 1, 32)
205
239
  },
206
- study: y(m.study),
207
- series: y(m.series),
208
- frameOfReference: y(m.frameOfReference),
209
- container: y(m.container) || void 0,
210
- volume: y(m.imageType).includes("VOLUME"),
240
+ study: y(w.study),
241
+ series: y(w.series),
242
+ frameOfReference: y(w.frameOfReference),
243
+ container: y(w.container) || void 0,
244
+ volume: y(w.imageType).includes("VOLUME"),
211
245
  totalColumns: D,
212
246
  totalRows: $,
213
247
  tileWidth: U,
214
248
  tileHeight: I,
215
249
  columns: O,
216
250
  rows: M,
217
- frameCount: b,
251
+ frameCount: x,
218
252
  sparse: X,
219
- concatNumber: S(m.concatNumber, 0, 0, 65535),
220
- pathIds: o,
221
- icc: f,
222
- column: new Int32Array(b),
223
- row: new Int32Array(b),
224
- path: new Uint32Array(b),
225
- z: new Float64Array(b),
253
+ concatNumber: S(w.concatNumber, 0, 0, 65535),
254
+ pathIds: n,
255
+ icc: l,
256
+ column: new Int32Array(x),
257
+ row: new Int32Array(x),
258
+ path: new Uint32Array(x),
259
+ z: new Float64Array(x),
226
260
  zStated: !1,
227
261
  planes: [],
228
- offset: new Float64Array(b),
229
- length: new Uint32Array(b)
262
+ offset: new Float64Array(x),
263
+ length: new Uint32Array(x)
230
264
  };
231
- if (p) {
232
- const [a, g] = p.split("\\").map(Number);
233
- L.pixelSpacingMM = [g, a];
265
+ if (d) {
266
+ const [i, g] = d.split("\\").map(Number);
267
+ L.pixelSpacingMM = [g, i];
234
268
  }
235
- const { column: Z, row: Y, path: F, z: B } = L;
236
- if (X && !w)
237
- throw new x("malformed", "TILED_SPARSE without per-frame positions");
238
- if (w && w !== b)
239
- throw new x(
269
+ const { column: Z, row: Y, path: F, z: _ } = L;
270
+ if (X && !u)
271
+ throw new P("malformed", "TILED_SPARSE without per-frame positions");
272
+ if (u && u !== x)
273
+ throw new P(
240
274
  "malformed",
241
- `${w} per-frame items for ${b} frames`
275
+ `${u} per-frame items for ${x} frames`
242
276
  );
243
277
  if (!G)
244
278
  Z.fill(-1), Y.fill(-1);
245
279
  else
246
- for (let a = 0; a < b; a++) {
247
- const g = nt + a, R = g % P;
248
- Z[a] = R % O, Y[a] = Math.floor(R / O), B[a] = Math.floor(g / P) % C, F[a] = Math.floor(g / (P * C));
280
+ for (let i = 0; i < x; i++) {
281
+ const g = et + i, R = g % b;
282
+ Z[i] = R % O, Y[i] = Math.floor(R / O), _[i] = Math.floor(g / b) % C, F[i] = Math.floor(g / (b * C));
249
283
  }
250
284
  const V = /* @__PURE__ */ new Map();
251
- o.forEach((a, g) => {
252
- V.has(a) || V.set(a, g);
285
+ n.forEach((i, g) => {
286
+ V.has(i) || V.set(i, g);
253
287
  });
254
- const st = (a) => {
255
- const g = V.get(a);
256
- return g !== void 0 ? g : (V.set(a, o.length), o.push(a) - 1);
288
+ const ot = (i) => {
289
+ const g = V.get(i);
290
+ return g !== void 0 ? g : (V.set(i, n.length), n.push(i) - 1);
257
291
  };
258
- u !== void 0 && F.fill(st(u));
259
- const rt = (a, g) => Number.isInteger(a) && a >= 1 && (a - 1) % g === 0 ? (a - 1) / g : -1;
260
- let T = 0, at = -1;
261
- for (const a of i) {
262
- if (a.root !== m.perFrame) continue;
263
- const g = a.item;
264
- a.tag === m.columnPosition ? Z[g] = rt(z(s, a), U) : a.tag === m.rowPosition ? Y[g] = rt(z(s, a), I) : a.tag === m.zOffset ? (B[g] = z(s, a), g !== at && T++, at = g) : a.tag === m.opticalPathId && (F[g] = st(N(s, a)));
292
+ m !== void 0 && F.fill(ot(m));
293
+ const rt = (i, g) => Number.isInteger(i) && i >= 1 && (i - 1) % g === 0 ? (i - 1) / g : -1;
294
+ let T = 0, st = -1;
295
+ for (const i of c) {
296
+ if (i.root !== w.perFrame) continue;
297
+ const g = i.item;
298
+ i.tag === w.columnPosition ? Z[g] = rt(W(s, i), U) : i.tag === w.rowPosition ? Y[g] = rt(W(s, i), I) : i.tag === w.zOffset ? (_[g] = W(s, i), g !== st && T++, st = g) : i.tag === w.opticalPathId && (F[g] = ot(N(s, i)));
265
299
  }
266
- if (T && T !== b)
267
- throw new x("malformed", `ZOffset on ${T} of ${b} frames`);
268
- const it = !T && l !== void 0 && (!G || C === 1);
269
- it && B.fill(l), L.zStated = T > 0 || it;
300
+ if (T && T !== x)
301
+ throw new P("malformed", `ZOffset on ${T} of ${x} frames`);
302
+ const at = !T && h !== void 0 && (!G || C === 1);
303
+ at && _.fill(h), L.zStated = T > 0 || at;
270
304
  const { planes: H } = L;
271
- let Mt = 0;
272
- for (let a = 0; a < b; a++) {
273
- const g = Math.round(B[a] * 1e3) / 1e3;
305
+ let St = 0;
306
+ for (let i = 0; i < x; i++) {
307
+ const g = Math.round(_[i] * 1e3) / 1e3;
274
308
  if (!Number.isFinite(g))
275
- throw new x("malformed", `frame ${a + 1} has no finite focal plane`);
276
- B[a] = g;
277
- const R = F[a];
309
+ throw new P("malformed", `frame ${i + 1} has no finite focal plane`);
310
+ _[i] = g;
311
+ const R = F[i];
278
312
  H[R] ?? (H[R] = /* @__PURE__ */ new Set());
279
313
  const j = H[R];
280
- if (!j.has(g) && (j.add(g), ++Mt > mt))
281
- throw new x(
314
+ if (!j.has(g) && (j.add(g), ++St > ht))
315
+ throw new P(
282
316
  "malformed",
283
- `over ${mt} (optical path, focal plane) pairs`
317
+ `over ${ht} (optical path, focal plane) pairs`
284
318
  );
285
319
  }
286
- return await Dt(L, s, n, c, r), L;
320
+ return await Ot(L, s, o, a, r), L;
287
321
  }
288
- async function Dt(e, t, r, s, n) {
289
- const { frameCount: i, offset: c, length: o } = e, { size: f } = e.instance, h = String.fromCharCode(t[r + 4], t[r + 5]);
290
- if (h !== "OB" && h !== "OW")
291
- throw new x("malformed", `Pixel Data has VR ${h}`);
292
- if (v(t, r + 8) !== _)
293
- throw new x("uncompressed", "native (uncompressed) pixel data");
294
- if (d(A(t, r + 12), A(t, r + 14)) !== m.item)
295
- throw new x("malformed", "no Basic Offset Table item");
296
- const p = v(t, r + 16), u = r + 20 + p;
297
- if (u > f)
298
- throw new x("malformed", "the Basic Offset Table runs past the file");
299
- const l = async ($) => {
300
- const O = await n($, 8);
301
- return { tag: d(A(O, 0), A(O, 2)), length: v(O, 4) };
302
- }, w = s.get(m.eot), E = s.get(m.eotLengths);
303
- if (w && E && w.length >= 8 * i && E.length >= 8 * i) {
304
- const $ = new DataView(t.buffer, t.byteOffset + w.offset, w.length), O = new DataView(
322
+ async function Ot(e, t, r, s, o) {
323
+ const { frameCount: c, offset: a, length: n } = e, { size: l } = e.instance, f = String.fromCharCode(t[r + 4], t[r + 5]);
324
+ if (f !== "OB" && f !== "OW")
325
+ throw new P("malformed", `Pixel Data has VR ${f}`);
326
+ if (v(t, r + 8) !== k)
327
+ throw new P("uncompressed", "native (uncompressed) pixel data");
328
+ if (p(A(t, r + 12), A(t, r + 14)) !== w.item)
329
+ throw new P("malformed", "no Basic Offset Table item");
330
+ const d = v(t, r + 16), m = r + 20 + d;
331
+ if (m > l)
332
+ throw new P("malformed", "the Basic Offset Table runs past the file");
333
+ const h = async ($) => {
334
+ const O = await o($, 8);
335
+ return { tag: p(A(O, 0), A(O, 2)), length: v(O, 4) };
336
+ }, u = s.get(w.eot), E = s.get(w.eotLengths);
337
+ if (u && E && u.length >= 8 * c && E.length >= 8 * c) {
338
+ const $ = new DataView(t.buffer, t.byteOffset + u.offset, u.length), O = new DataView(
305
339
  t.buffer,
306
340
  t.byteOffset + E.offset,
307
341
  E.length
308
342
  );
309
343
  let M = !0;
310
- for (let P = 0; P < i && M; P++) {
311
- c[P] = u + Number($.getBigUint64(8 * P, !0)) + 8;
312
- const C = Number(O.getBigUint64(8 * P, !0));
313
- M = C <= 4294967295 && c[P] + C <= f, o[P] = C;
344
+ for (let b = 0; b < c && M; b++) {
345
+ a[b] = m + Number($.getBigUint64(8 * b, !0)) + 8;
346
+ const C = Number(O.getBigUint64(8 * b, !0));
347
+ M = C <= 4294967295 && a[b] + C <= l, n[b] = C;
314
348
  }
315
349
  if (M) return;
316
- } else if (p === 4 * i && p > 0) {
317
- const $ = await n(r + 20, p);
318
- for (let M = 0; M < i; M++) c[M] = u + v($, 4 * M) + 8;
319
- let O = v($, 0) === 0 && c[i - 1] <= f;
320
- for (let M = 0; M + 1 < i && O; M++) {
321
- const P = c[M + 1] - 8 - c[M];
322
- O = P >= 0, o[M] = P;
350
+ } else if (d === 4 * c && d > 0) {
351
+ const $ = await o(r + 20, d);
352
+ for (let M = 0; M < c; M++) a[M] = m + v($, 4 * M) + 8;
353
+ let O = v($, 0) === 0 && a[c - 1] <= l;
354
+ for (let M = 0; M + 1 < c && O; M++) {
355
+ const b = a[M + 1] - 8 - a[M];
356
+ O = b >= 0, n[M] = b;
323
357
  }
324
358
  if (O) {
325
- let M = c[i - 1] - 8;
326
- for (; M + 8 <= f; ) {
327
- const P = await l(M);
328
- if (P.tag !== m.item || M + 8 + P.length > f) break;
329
- M += 8 + P.length;
359
+ let M = a[c - 1] - 8;
360
+ for (; M + 8 <= l; ) {
361
+ const b = await h(M);
362
+ if (b.tag !== w.item || M + 8 + b.length > l) break;
363
+ M += 8 + b.length;
330
364
  }
331
- if (M > c[i - 1] - 8) {
332
- o[i - 1] = M - c[i - 1];
365
+ if (M > a[c - 1] - 8) {
366
+ n[c - 1] = M - a[c - 1];
333
367
  return;
334
368
  }
335
369
  }
336
370
  }
337
371
  let y = new Uint8Array(0), S = 0, U = 0;
338
- const I = async ($) => (($ < S || $ + 12 > S + y.length) && (S = $, y = await n($, Math.min(U < 4096 ? 65536 : 12, f - $))), y.subarray($ - S, $ - S + 12));
339
- let b = u, D = -1;
372
+ const I = async ($) => (($ < S || $ + 12 > S + y.length) && (S = $, y = await o($, Math.min(U < 4096 ? 65536 : 12, l - $))), y.subarray($ - S, $ - S + 12));
373
+ let x = m, D = -1;
340
374
  for (; ; ) {
341
- const $ = await I(b), O = d(A($, 0), A($, 2));
342
- if (O === m.sequenceEnd) break;
343
- if (O !== m.item)
344
- throw new x("malformed", `no fragment item at byte ${b}`);
375
+ const $ = await I(x), O = p(A($, 0), A($, 2));
376
+ if (O === w.sequenceEnd) break;
377
+ if (O !== w.item)
378
+ throw new P("malformed", `no fragment item at byte ${x}`);
345
379
  const M = v($, 4);
346
- if (U = M, b + 8 + M > f)
347
- throw new x(
380
+ if (U = M, x + 8 + M > l)
381
+ throw new P(
348
382
  "malformed",
349
- `fragment past the end of the file at byte ${b}`
383
+ `fragment past the end of the file at byte ${x}`
350
384
  );
351
385
  if (($[8] === 255 && $[9] === 216 || // JPEG and JPEG-LS SOI
352
386
  $[8] === 255 && $[9] === 79 && $[10] === 255 && $[11] === 81 || // J2K start-of-codestream + image-size markers
353
- $[8] === 0 && $[9] === 0 && $[10] === 0 && $[11] === 12) && D + 1 < i && (c[++D] = b + 8), D < 0)
354
- throw new x(
387
+ $[8] === 0 && $[9] === 0 && $[10] === 0 && $[11] === 12) && D + 1 < c && (a[++D] = x + 8), D < 0)
388
+ throw new P(
355
389
  "malformed",
356
390
  "the first fragment does not start a codestream"
357
391
  );
358
- o[D] = b + 8 + M - c[D], b += 8 + M;
392
+ n[D] = x + 8 + M - a[D], x += 8 + M;
359
393
  }
360
- if (D + 1 !== i)
361
- throw new x("malformed", `${D + 1} codestreams for ${i} frames`);
394
+ if (D + 1 !== c)
395
+ throw new P("malformed", `${D + 1} codestreams for ${c} frames`);
362
396
  }
363
- async function _t(e) {
397
+ async function Lt(e) {
364
398
  const t = [], r = [];
365
399
  for (const s of e)
366
400
  try {
367
- t.push(await At(s));
368
- } catch (n) {
401
+ t.push(await Ut(s));
402
+ } catch (o) {
369
403
  r.push({
370
- file: et(s),
371
- reason: n instanceof x ? n.reason : "malformed",
372
- message: n instanceof Error ? n.message : String(n)
404
+ file: tt(s),
405
+ reason: o instanceof P ? o.reason : "malformed",
406
+ message: o instanceof Error ? o.message : String(o)
373
407
  });
374
408
  }
375
409
  return {
376
410
  version: 1,
377
411
  producer: "nv-ext-tile/indexDicomWsi",
378
- slides: Ct(t, r),
412
+ slides: Dt(t, r),
379
413
  skipped: r
380
414
  };
381
415
  }
382
- const pt = (e, t) => e.pathIds[t] ?? String(t), tt = (e, t) => [
416
+ const wt = (e, t) => e.pathIds[t] ?? String(t), Q = (e, t) => [
383
417
  e.study,
384
418
  e.series,
385
419
  e.frameOfReference,
386
420
  e.container ?? "",
387
- pt(e, t)
421
+ wt(e, t)
388
422
  ].join("|");
389
- function It(e) {
390
- const t = e.filter((n) => n.volume && n.zStated), r = /* @__PURE__ */ new Map();
391
- for (const n of t)
392
- n.planes.forEach((i, c) => {
393
- const o = tt(n, c), f = r.get(o) ?? /* @__PURE__ */ new Set();
394
- for (const h of i) f.add(h);
395
- r.set(o, f);
423
+ function At(e) {
424
+ const t = e.filter((o) => o.volume && o.zStated), r = /* @__PURE__ */ new Map();
425
+ for (const o of t)
426
+ o.planes.forEach((c, a) => {
427
+ const n = Q(o, a), l = r.get(n) ?? /* @__PURE__ */ new Set();
428
+ for (const f of c) l.add(f);
429
+ r.set(n, l);
396
430
  });
397
431
  const s = new Map(
398
- [...r].map(([n, i]) => [
399
- n,
400
- new Map([...i].sort((c, o) => c - o).map((c, o) => [c, o]))
432
+ [...r].map(([o, c]) => [
433
+ o,
434
+ new Map([...c].sort((a, n) => a - n).map((a, n) => [a, n]))
401
435
  ])
402
436
  );
403
- for (const n of t) {
404
- const i = n.planes.map(
405
- (f, h) => s.get(tt(n, h))
406
- ), { z: c, path: o } = n;
407
- for (let f = 0; f < n.frameCount; f++) {
408
- const h = i[o[f]];
409
- c[f] = h.get(c[f]);
437
+ for (const o of t) {
438
+ const c = o.planes.map(
439
+ (l, f) => s.get(Q(o, f))
440
+ ), { z: a, path: n } = o;
441
+ for (let l = 0; l < o.frameCount; l++) {
442
+ const f = c[n[l]];
443
+ a[l] = f.get(a[l]);
410
444
  }
411
445
  }
412
446
  }
413
- function Ct(e, t) {
414
- It(e);
447
+ function Dt(e, t) {
448
+ At(e);
415
449
  const r = /* @__PURE__ */ new Map();
416
- for (const n of e) {
417
- if (!n.volume) continue;
418
- const i = [];
419
- for (let c = 0; c < n.frameCount; c++) {
420
- const o = n.path[c];
421
- i[o] ?? (i[o] = /* @__PURE__ */ new Map());
422
- const f = i[o], h = n.z[c], p = f.get(h);
423
- p ? p.push(c) : f.set(h, [c]);
450
+ for (const o of e) {
451
+ if (!o.volume) continue;
452
+ const c = [];
453
+ for (let a = 0; a < o.frameCount; a++) {
454
+ const n = o.path[a];
455
+ c[n] ?? (c[n] = /* @__PURE__ */ new Map());
456
+ const l = c[n], f = o.z[a], d = l.get(f);
457
+ d ? d.push(a) : l.set(f, [a]);
424
458
  }
425
- i.forEach((c, o) => {
426
- const f = pt(n, o), h = tt(n, o);
427
- for (const [p, u] of c) {
428
- const l = `${h}|${p}`;
429
- let w = r.get(l);
430
- if (!w) {
431
- const { study: E, series: y, frameOfReference: S, container: U } = n;
432
- w = {
459
+ c.forEach((a, n) => {
460
+ const l = wt(o, n), f = Q(o, n);
461
+ for (const [d, m] of a) {
462
+ const h = `${f}|${d}`;
463
+ let u = r.get(h);
464
+ if (!u) {
465
+ const { study: E, series: y, frameOfReference: S, container: U } = o;
466
+ u = {
433
467
  slide: {
434
468
  study: E,
435
469
  series: y,
436
470
  frameOfReference: S,
437
471
  container: U,
438
- opticalPath: f,
439
- focalPlane: p,
472
+ opticalPath: l,
473
+ focalPlane: d,
440
474
  instances: [],
441
475
  levels: []
442
476
  },
443
477
  own: []
444
- }, r.set(l, w);
478
+ }, r.set(h, u);
445
479
  }
446
- w.own.push({ info: n, pathIndex: o, frames: u });
480
+ u.own.push({ info: o, pathIndex: n, frames: m });
447
481
  }
448
482
  });
449
483
  }
450
484
  const s = /* @__PURE__ */ new Set();
451
- return [...r.values()].map(({ slide: n, own: i }) => {
452
- i.sort(
453
- (u, l) => l.info.totalColumns * l.info.totalRows - u.info.totalColumns * u.info.totalRows || u.info.concatNumber - l.info.concatNumber
485
+ return [...r.values()].map(({ slide: o, own: c }) => {
486
+ c.sort(
487
+ (m, h) => h.info.totalColumns * h.info.totalRows - m.info.totalColumns * m.info.totalRows || m.info.concatNumber - h.info.concatNumber
454
488
  );
455
- const c = /* @__PURE__ */ new Map();
456
- let o = 0;
457
- const f = i.filter(({ info: u }) => {
458
- const l = `${u.totalColumns}x${u.totalRows}`;
459
- let w = c.get(l);
460
- return w === void 0 && (o += u.columns * u.rows, w = o <= ht, c.set(l, w)), !w && !s.has(u.instance.file) && (s.add(u.instance.file), t.push({
461
- file: u.instance.file,
489
+ const a = /* @__PURE__ */ new Map();
490
+ let n = 0;
491
+ const l = c.filter(({ info: m }) => {
492
+ const h = `${m.totalColumns}x${m.totalRows}`;
493
+ let u = a.get(h);
494
+ return u === void 0 && (n += m.columns * m.rows, u = n <= lt, a.set(h, u)), !u && !s.has(m.instance.file) && (s.add(m.instance.file), t.push({
495
+ file: m.instance.file,
462
496
  reason: "malformed",
463
- message: `level ${l} takes its slide past ${ht} tiles`
464
- })), w;
497
+ message: `level ${h} takes its slide past ${lt} tiles`
498
+ })), u;
465
499
  });
466
- n.instances = f.map((u) => u.info.instance);
467
- const h = /* @__PURE__ */ new Map();
468
- f.forEach(({ info: u }, l) => {
469
- const w = `${u.totalColumns}x${u.totalRows}`, E = h.get(w);
470
- E ? E.push(l) : h.set(w, [l]);
500
+ o.instances = l.map((m) => m.info.instance);
501
+ const f = /* @__PURE__ */ new Map();
502
+ l.forEach(({ info: m }, h) => {
503
+ const u = `${m.totalColumns}x${m.totalRows}`, E = f.get(u);
504
+ E ? E.push(h) : f.set(u, [h]);
471
505
  });
472
- for (const u of h.values()) n.levels.push(Rt(u, f));
473
- const p = f[0];
474
- return n.pixelSpacingMM = p.info.pixelSpacingMM, n.iccProfile = p.info.icc[p.pathIndex], n;
506
+ for (const m of f.values()) o.levels.push(It(m, l));
507
+ const d = l[0];
508
+ return o.pixelSpacingMM = d.info.pixelSpacingMM, o.iccProfile = d.info.icc[d.pathIndex], o;
475
509
  });
476
510
  }
477
- function Rt(e, t) {
478
- const r = t[e[0]].info, { columns: s, rows: n } = r, i = e.reduce((l, w) => l + t[w].frames.length, 0), c = {
479
- instance: new Uint32Array(i),
480
- frame: new Uint32Array(i),
481
- offset: new Float64Array(i),
482
- length: new Uint32Array(i),
483
- tile: new Int32Array(i)
511
+ function It(e, t) {
512
+ const r = t[e[0]].info, { columns: s, rows: o } = r, c = e.reduce((h, u) => h + t[u].frames.length, 0), a = {
513
+ instance: new Uint32Array(c),
514
+ frame: new Uint32Array(c),
515
+ offset: new Float64Array(c),
516
+ length: new Uint32Array(c),
517
+ tile: new Int32Array(c)
484
518
  };
485
- let o = 0;
486
- for (const l of e) {
487
- const w = t[l], { info: E } = w;
488
- for (const y of w.frames) {
519
+ let n = 0;
520
+ for (const h of e) {
521
+ const u = t[h], { info: E } = u;
522
+ for (const y of u.frames) {
489
523
  const S = E.column[y], U = E.row[y];
490
- c.instance[o] = l, c.frame[o] = y + 1, c.offset[o] = E.offset[y], c.length[o] = E.length[y], c.tile[o] = S < 0 || U < 0 || S >= s || U >= n ? -1 : U * s + S, o++;
524
+ a.instance[n] = h, a.frame[n] = y + 1, a.offset[n] = E.offset[y], a.length[n] = E.length[y], a.tile[n] = S < 0 || U < 0 || S >= s || U >= o ? -1 : U * s + S, n++;
491
525
  }
492
526
  }
493
- const f = c.tile.slice().sort();
494
- let h = 0, p = -1;
495
- for (const l of f)
496
- l > p && (h++, p = l);
497
- const u = s * n - h;
527
+ const l = a.tile.slice().sort();
528
+ let f = 0, d = -1;
529
+ for (const h of l)
530
+ h > d && (f++, d = h);
531
+ const m = s * o - f;
498
532
  return {
499
533
  width: r.totalColumns,
500
534
  height: r.totalRows,
501
535
  tileWidth: r.tileWidth,
502
536
  tileHeight: r.tileHeight,
503
537
  columns: s,
504
- rows: n,
538
+ rows: o,
505
539
  instances: e,
506
- frames: c,
507
- duplicates: i - h,
508
- holes: u,
509
- sparse: u > 0 || e.some((l) => t[l].info.sparse)
540
+ frames: a,
541
+ duplicates: c - f,
542
+ holes: m,
543
+ sparse: m > 0 || e.some((h) => t[h].info.sparse)
510
544
  };
511
545
  }
512
- function gt(e) {
546
+ function dt(e) {
513
547
  const t = new Int32Array(e.columns * e.rows).fill(-1), { tile: r } = e.frames;
514
548
  for (let s = r.length - 1; s >= 0; s--) {
515
- const n = r[s];
516
- n >= 0 && (t[n] = s);
549
+ const o = r[s];
550
+ o >= 0 && (t[o] = s);
517
551
  }
518
552
  return t;
519
553
  }
520
- const Nt = {
554
+ const Ct = {
521
555
  "1.2.840.10008.1.2.4.50": "image/jpeg",
522
556
  // JPEG Baseline (Process 1)
523
557
  "1.2.840.10008.1.2.4.51": "image/jpeg",
@@ -531,34 +565,34 @@ const Nt = {
531
565
  "1.2.840.10008.1.2.4.91": "image/jp2"
532
566
  // JPEG 2000
533
567
  };
534
- function yt(e, t) {
535
- const r = new Map(t.map((s) => [et(s), s]));
568
+ function pt(e, t) {
569
+ const r = new Map(t.map((s) => [tt(s), s]));
536
570
  return e.instances.map((s) => {
537
- const n = r.get(s.file);
538
- if (!n) throw new Error(`${s.file} is not among the files`);
539
- if (n.size !== s.size || String(n.lastModified) !== s.version)
571
+ const o = r.get(s.file);
572
+ if (!o) throw new Error(`${s.file} is not among the files`);
573
+ if (o.size !== s.size || String(o.lastModified) !== s.version)
540
574
  throw new Error(`${s.file} changed since it was indexed`);
541
- return n;
575
+ return o;
542
576
  });
543
577
  }
544
- function St(e, t) {
578
+ function gt(e, t) {
545
579
  const r = new DataView(e.buffer, e.byteOffset, e.byteLength), s = [];
546
- for (let n = 0; n < e.length; ) {
547
- if (n + 8 > e.length || r.getUint16(n, !0) !== 65534 || r.getUint16(n + 2, !0) !== 57344)
580
+ for (let o = 0; o < e.length; ) {
581
+ if (o + 8 > e.length || r.getUint16(o, !0) !== 65534 || r.getUint16(o + 2, !0) !== 57344)
548
582
  throw new Error(
549
- n === 0 ? `${t} is not where the index says` : `${t}: no fragment item at byte ${n}`
583
+ o === 0 ? `${t} is not where the index says` : `${t}: no fragment item at byte ${o}`
550
584
  );
551
- const i = r.getUint32(n + 4, !0);
552
- if (n + 8 + i > e.length)
585
+ const c = r.getUint32(o + 4, !0);
586
+ if (o + 8 + c > e.length)
553
587
  throw new Error(`${t}: a fragment runs past its span`);
554
- s.push({ offset: n + 8, length: i }), n += 8 + i;
588
+ s.push({ offset: o + 8, length: c }), o += 8 + c;
555
589
  }
556
590
  if (!s.length) throw new Error(`${t}: an empty span`);
557
591
  return s;
558
592
  }
559
- class Bt {
593
+ class Tt {
560
594
  constructor(t, r) {
561
- this.slide = t, this.host = null, this.files = yt(t, r), this.maps = t.levels.map(gt), this.manifest = $t(t, this.maps);
595
+ this.slide = t, this.host = null, this.files = pt(t, r), this.maps = t.levels.map(dt), this.manifest = yt(t, this.maps);
562
596
  }
563
597
  bind(t) {
564
598
  this.host = t;
@@ -567,144 +601,144 @@ class Bt {
567
601
  async fetchIccProfile(t) {
568
602
  const r = this.slide.iccProfile, s = this.slide.levels[0];
569
603
  if (!r || !s) return null;
570
- const n = this.files[s.instances[0]], i = new Uint8Array(
571
- await n.slice(r.offset, r.offset + r.length).arrayBuffer()
604
+ const o = this.files[s.instances[0]], c = new Uint8Array(
605
+ await o.slice(r.offset, r.offset + r.length).arrayBuffer()
572
606
  );
573
- return t == null || t.throwIfAborted(), i;
607
+ return t == null || t.throwIfAborted(), c;
574
608
  }
575
- async fetchTileBytes(t, r, s, n) {
609
+ async fetchTileBytes(t, r, s, o) {
576
610
  var y;
577
- const { frames: i } = this.slide.levels[t.index], o = this.maps[t.index][r.y * t.columns + r.x];
578
- if (!(o >= 0)) throw new Error(`no frame for tile ${r.x},${r.y}`);
579
- const f = this.files[i.instance[o]], h = i.offset[o], p = i.length[o], u = new Uint8Array(
580
- await f.slice(h - 8, h + p).arrayBuffer()
611
+ const { frames: c } = this.slide.levels[t.index], n = this.maps[t.index][r.y * t.columns + r.x];
612
+ if (!(n >= 0)) throw new Error(`no frame for tile ${r.x},${r.y}`);
613
+ const l = this.files[c.instance[n]], f = c.offset[n], d = c.length[n], m = new Uint8Array(
614
+ await l.slice(f - 8, f + d).arrayBuffer()
581
615
  );
582
- n == null || n.throwIfAborted(), (y = this.host) == null || y.addWireBytes(u.byteLength);
583
- const l = St(u, `${f.name} frame ${i.frame[o]}`);
584
- if (l.length === 1) return u.subarray(8);
585
- const w = new Uint8Array(l.reduce((S, U) => S + U.length, 0));
616
+ o == null || o.throwIfAborted(), (y = this.host) == null || y.addWireBytes(m.byteLength);
617
+ const h = gt(m, `${l.name} frame ${c.frame[n]}`);
618
+ if (h.length === 1) return m.subarray(8);
619
+ const u = new Uint8Array(h.reduce((S, U) => S + U.length, 0));
586
620
  let E = 0;
587
- for (const S of l)
588
- w.set(u.subarray(S.offset, S.offset + S.length), E), E += S.length;
589
- return w;
621
+ for (const S of h)
622
+ u.set(m.subarray(S.offset, S.offset + S.length), E), E += S.length;
623
+ return u;
590
624
  }
591
625
  }
592
- function $t(e, t, {
626
+ function yt(e, t, {
593
627
  format: r = "dicom-wsi-index-v1",
594
- fileUrl: s = (n) => n.file
628
+ fileUrl: s = (o) => o.file
595
629
  } = {}) {
596
- const n = e.levels[0];
597
- if (!n) throw new Error("the slide has no levels");
598
- const i = e.instances[n.instances[0]], c = e.levels.map((o, f) => {
599
- const h = e.instances[o.instances[0]], p = Nt[h.transferSyntax];
600
- if (!p)
630
+ const o = e.levels[0];
631
+ if (!o) throw new Error("the slide has no levels");
632
+ const c = e.instances[o.instances[0]], a = e.levels.map((n, l) => {
633
+ const f = e.instances[n.instances[0]], d = Ct[f.transferSyntax];
634
+ if (!d)
601
635
  throw new Error(
602
- `transfer syntax ${h.transferSyntax} cannot be decoded (JPEG, JPEG 2000 and JPEG-LS are supported)`
636
+ `transfer syntax ${f.transferSyntax} cannot be decoded (JPEG, JPEG 2000 and JPEG-LS are supported)`
603
637
  );
604
- const u = Array.from(
605
- t[f],
606
- (l, w) => {
607
- if (l < 0) return null;
608
- const E = w % o.columns, y = Math.floor(w / o.columns);
638
+ const m = Array.from(
639
+ t[l],
640
+ (h, u) => {
641
+ if (h < 0) return null;
642
+ const E = u % n.columns, y = Math.floor(u / n.columns);
609
643
  return {
610
644
  x: E,
611
645
  y,
612
- width: Math.min(o.tileWidth, o.width - E * o.tileWidth),
646
+ width: Math.min(n.tileWidth, n.width - E * n.tileWidth),
613
647
  height: Math.min(
614
- o.tileHeight,
615
- o.height - y * o.tileHeight
648
+ n.tileHeight,
649
+ n.height - y * n.tileHeight
616
650
  ),
617
- frame: o.frames.frame[l]
651
+ frame: n.frames.frame[h]
618
652
  };
619
653
  }
620
654
  );
621
655
  return {
622
- index: f,
623
- width: o.width,
624
- height: o.height,
625
- downsample: n.width / o.width,
626
- tileWidth: o.tileWidth,
627
- tileHeight: o.tileHeight,
628
- columns: o.columns,
629
- rows: o.rows,
630
- codec: p,
631
- transferSyntaxUid: h.transferSyntax,
632
- photometric: h.photometric,
633
- bitsStored: h.bitsStored,
634
- frames: o.frames.offset.length,
656
+ index: l,
657
+ width: n.width,
658
+ height: n.height,
659
+ downsample: o.width / n.width,
660
+ tileWidth: n.tileWidth,
661
+ tileHeight: n.tileHeight,
662
+ columns: n.columns,
663
+ rows: n.rows,
664
+ codec: d,
665
+ transferSyntaxUid: f.transferSyntax,
666
+ photometric: f.photometric,
667
+ bitsStored: f.bitsStored,
668
+ frames: n.frames.offset.length,
635
669
  // The index made one slide per optical path and focal plane.
636
670
  opticalPaths: 1,
637
671
  focalPlanes: 1,
638
- ...o.sparse ? { sparse: !0 } : {},
639
- tiles: u
672
+ ...n.sparse ? { sparse: !0 } : {},
673
+ tiles: m
640
674
  };
641
675
  });
642
676
  return {
643
677
  id: `${e.series}/${e.opticalPath}/${e.focalPlane}`,
644
678
  name: e.container ?? e.series,
645
679
  format: r,
646
- width: n.width,
647
- height: n.height,
680
+ width: o.width,
681
+ height: o.height,
648
682
  // Row 0 at the top, as OpenSlide and the TIFF/DZI sources show a slide.
649
683
  displayYAxis: "down",
650
- tileSize: n.tileWidth,
684
+ tileSize: o.tileWidth,
651
685
  dtype: "uint8",
652
686
  channels: "encoded-rgb",
653
687
  ...e.pixelSpacingMM ? { pixelSpacingMM: e.pixelSpacingMM } : {},
654
- ...e.iccProfile ? { iccProfile: { url: s(i), ...e.iccProfile } } : {},
655
- levels: c
688
+ ...e.iccProfile ? { iccProfile: { url: s(c), ...e.iccProfile } } : {},
689
+ levels: a
656
690
  };
657
691
  }
658
692
  async function kt(e, t, r) {
659
- const s = yt(e, t), n = e.levels.map(gt), i = $t(e, n, {
693
+ const s = pt(e, t), o = e.levels.map(dt), c = yt(e, o, {
660
694
  format: "dicom-wsi-range-v1",
661
695
  fileUrl: r
662
696
  });
663
- for (const [c, o] of i.levels.entries()) {
664
- const f = e.levels[c], { frames: h } = f, p = f.instances.length > 1;
665
- p || (o.fileUrl = r(
666
- e.instances[f.instances[0]]
697
+ for (const [a, n] of c.levels.entries()) {
698
+ const l = e.levels[a], { frames: f } = l, d = l.instances.length > 1;
699
+ d || (n.fileUrl = r(
700
+ e.instances[l.instances[0]]
667
701
  ));
668
- const u = [];
669
- for (const [l, w] of n[c].entries()) {
670
- if (w < 0) continue;
671
- const E = o.tiles[l], y = h.instance[w], S = await vt(
702
+ const m = [];
703
+ for (const [h, u] of o[a].entries()) {
704
+ if (u < 0) continue;
705
+ const E = n.tiles[h], y = f.instance[u], S = await Rt(
672
706
  s[y],
673
- h.offset[w],
674
- h.length[w]
707
+ f.offset[u],
708
+ f.length[u]
675
709
  );
676
- u.push({
710
+ m.push({
677
711
  ...E,
678
712
  ...S,
679
- ...p ? { fileUrl: r(e.instances[y]) } : {}
713
+ ...d ? { fileUrl: r(e.instances[y]) } : {}
680
714
  });
681
715
  }
682
- o.tiles = u;
716
+ n.tiles = m;
683
717
  }
684
- return i;
718
+ return c;
685
719
  }
686
- async function vt(e, t, r) {
720
+ async function Rt(e, t, r) {
687
721
  const s = new Uint8Array(
688
722
  await e.slice(t - 8, t).arrayBuffer()
689
- ), n = `${e.name} at byte ${t - 8}`, i = new DataView(s.buffer);
690
- if (i.getUint32(4, !0) === r) {
691
- if (i.getUint32(0, !0) !== 3758161918)
692
- throw new Error(`${n}: no item where the index says`);
723
+ ), o = `${e.name} at byte ${t - 8}`, c = new DataView(s.buffer);
724
+ if (c.getUint32(4, !0) === r) {
725
+ if (c.getUint32(0, !0) !== 3758161918)
726
+ throw new Error(`${o}: no item where the index says`);
693
727
  return { offset: t, length: r };
694
728
  }
695
- const c = new Uint8Array(
729
+ const a = new Uint8Array(
696
730
  await e.slice(t - 8, t + r).arrayBuffer()
697
731
  );
698
732
  return {
699
- fragments: St(c, n).map((f) => ({
700
- offset: t - 8 + f.offset,
701
- length: f.length
733
+ fragments: gt(a, o).map((l) => ({
734
+ offset: t - 8 + l.offset,
735
+ length: l.length
702
736
  }))
703
737
  };
704
738
  }
705
739
  export {
706
- Bt as DicomWsiSource,
707
- _t as indexDicomWsi,
740
+ Tt as DicomWsiSource,
741
+ Lt as indexDicomWsi,
708
742
  kt as manifestFromIndex,
709
- Tt as registerDcm2niixDecoders
743
+ vt as registerDcm2niixDecoders
710
744
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@niivue/nv-ext-tile",
3
- "version": "1.0.0-rc.7",
3
+ "version": "1.0.0-rc.8",
4
4
  "type": "module",
5
5
  "description": "Tiled image formats for NiiVue's NVSlide: pyramidal TIFF/OME-TIFF/SVS by HTTP Range (/tiff) and DICOM-WSI folders and hosted manifests (/dicom)",
6
6
  "exports": {
@@ -19,7 +19,7 @@
19
19
  "comment:niivue-peer": "workspace:^, not workspace:* (which bun publish pins to the exact rc): published as ^<core version at publish>, so one release accepts later niivue rcs and 1.x, and the floor never predates an API this package uses.",
20
20
  "peerDependencies": {
21
21
  "@niivue/dcm2niix": "^1.4.20261105-rc.3",
22
- "@niivue/niivue": "^1.0.0-rc.116"
22
+ "@niivue/niivue": "^1.0.0-rc.117"
23
23
  },
24
24
  "peerDependenciesMeta": {
25
25
  "@niivue/dcm2niix": {
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "devDependencies": {
33
33
  "@niivue/dcm2niix": "^1.4.20261105-rc.3",
34
- "@niivue/niivue": "1.0.0-rc.116",
34
+ "@niivue/niivue": "1.0.0-rc.117",
35
35
  "@types/bun": "^1.3.12",
36
36
  "typescript": "~5.8.3",
37
37
  "vite": "^6.3.2",