dcmjs 0.48.0 → 0.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/dcmjs.es.js CHANGED
@@ -7951,14 +7951,14 @@ var Inflate_1$1 = Inflate$1;
7951
7951
  var inflate_2 = inflate$1;
7952
7952
  var inflateRaw_1$1 = inflateRaw$1;
7953
7953
  var ungzip$1 = inflate$1;
7954
- var constants = constants$2;
7954
+ var constants$3 = constants$2;
7955
7955
 
7956
7956
  var inflate_1$1 = {
7957
7957
  Inflate: Inflate_1$1,
7958
7958
  inflate: inflate_2,
7959
7959
  inflateRaw: inflateRaw_1$1,
7960
7960
  ungzip: ungzip$1,
7961
- constants: constants
7961
+ constants: constants$3
7962
7962
  };
7963
7963
 
7964
7964
  const { Deflate, deflate, deflateRaw, gzip } = deflate_1$1;
@@ -8024,26 +8024,28 @@ var SplitDataView = /*#__PURE__*/function () {
8024
8024
  _createClass(SplitDataView, [{
8025
8025
  key: "consume",
8026
8026
  value: function consume(offset) {
8027
- var _this$consumeListener;
8028
8027
  this.consumeOffset = Math.max(offset, this.consumeOffset);
8029
8028
  if (!this.consumed || !this.offsets.length) {
8030
8029
  return;
8031
8030
  }
8032
- var nextOffset = this.offsets[this.consumed.length];
8033
- var nextLength = this.lengths[this.consumed.length];
8034
- if (nextOffset === undefined || nextLength === undefined) {
8035
- return;
8036
- }
8037
- var currentEnd = nextOffset + nextLength;
8038
- if (this.consumeOffset < currentEnd) {
8039
- // Haven't finished consuming all the data in the current block
8040
- return;
8031
+ while (true) {
8032
+ var _this$consumeListener;
8033
+ var nextOffset = this.offsets[this.consumed.length];
8034
+ var nextLength = this.lengths[this.consumed.length];
8035
+ if (nextOffset === undefined || nextLength === undefined) {
8036
+ return;
8037
+ }
8038
+ var currentEnd = nextOffset + nextLength;
8039
+ if (this.consumeOffset < currentEnd) {
8040
+ // Haven't finished consuming all the data in the current block
8041
+ return;
8042
+ }
8043
+ // Consume the entire buffer for now
8044
+ this.consumed.push((_this$consumeListener = this.consumeListener) === null || _this$consumeListener === void 0 ? void 0 : _this$consumeListener.call(this, this.buffers, 0, Math.min(this.buffers.length, nextOffset - this.consumeOffset)));
8045
+ this.buffers[this.consumed.length - 1] = null;
8046
+ this.views[this.consumed.length - 1] = null;
8047
+ // Continue loop to check if there are more buffers to consume
8041
8048
  }
8042
- // Consume the entire buffer for now
8043
- this.consumed.push((_this$consumeListener = this.consumeListener) === null || _this$consumeListener === void 0 ? void 0 : _this$consumeListener.call(this, this.buffers, 0, Math.min(this.buffers.length, nextOffset - this.consumeOffset)));
8044
- this.buffers[this.consumed.length - 1] = null;
8045
- this.views[this.consumed.length - 1] = null;
8046
- this.consume(offset);
8047
8049
  }
8048
8050
 
8049
8051
  /**
@@ -8386,6 +8388,52 @@ var SplitDataView = /*#__PURE__*/function () {
8386
8388
  this.writeCommit(view, offset);
8387
8389
  }
8388
8390
  }
8391
+
8392
+ /**
8393
+ * Reports on the amount of memory held by the buffers in the view.
8394
+ * @param {number} consumeOffset - The current consume offset (typically from BufferStream.offset)
8395
+ * @returns {Object} An object containing:
8396
+ * - bufferCount: Number of buffers still held (not null)
8397
+ * - totalSize: Total size of all buffers in bytes
8398
+ * - consumeOffset: The current consume offset
8399
+ * - buffersBeforeOffset: Number of buffers before the consume offset
8400
+ * - bytesBeforeOffset: Total bytes before the consume offset
8401
+ */
8402
+ }, {
8403
+ key: "getBufferMemoryInfo",
8404
+ value: function getBufferMemoryInfo(consumeOffset) {
8405
+ var bufferCount = 0;
8406
+ var totalSize = 0;
8407
+ var buffersBeforeOffset = 0;
8408
+ var bytesBeforeOffset = 0;
8409
+ var currentConsumeOffset = consumeOffset !== null && consumeOffset !== void 0 ? consumeOffset : this.consumeOffset;
8410
+ for (var i = 0; i < this.buffers.length; i++) {
8411
+ var buffer = this.buffers[i];
8412
+ if (buffer !== null && buffer !== undefined) {
8413
+ bufferCount++;
8414
+ totalSize += buffer.byteLength;
8415
+
8416
+ // Count buffers and bytes that are before the consume offset
8417
+ var bufferStart = this.offsets[i];
8418
+ var bufferEnd = bufferStart + this.lengths[i];
8419
+ if (bufferEnd <= currentConsumeOffset) {
8420
+ // Buffer is completely before the offset
8421
+ buffersBeforeOffset++;
8422
+ bytesBeforeOffset += buffer.byteLength;
8423
+ } else if (bufferStart < currentConsumeOffset) {
8424
+ // Buffer spans the offset, count the portion before it
8425
+ bytesBeforeOffset += currentConsumeOffset - bufferStart;
8426
+ }
8427
+ }
8428
+ }
8429
+ return {
8430
+ bufferCount: bufferCount,
8431
+ totalSize: totalSize,
8432
+ consumeOffset: currentConsumeOffset,
8433
+ buffersBeforeOffset: buffersBeforeOffset,
8434
+ bytesBeforeOffset: bytesBeforeOffset
8435
+ };
8436
+ }
8389
8437
  }]);
8390
8438
  return SplitDataView;
8391
8439
  }();
@@ -8948,6 +8996,20 @@ var BufferStream = /*#__PURE__*/function () {
8948
8996
  value: function toEnd() {
8949
8997
  this.offset = this.view.byteLength;
8950
8998
  }
8999
+
9000
+ /**
9001
+ * Reports on the amount of memory held by the buffers in the view.
9002
+ * @returns {Object} An object containing:
9003
+ * - bufferCount: Number of buffers still held (not null)
9004
+ * - totalSize: Total size of all buffers in bytes
9005
+ * - consumeOffset: The current consume offset
9006
+ * - buffersBeforeOffset: Number of buffers before the consume offset
9007
+ */
9008
+ }, {
9009
+ key: "getBufferMemoryInfo",
9010
+ value: function getBufferMemoryInfo() {
9011
+ return this.view.getBufferMemoryInfo(this.offset);
9012
+ }
8951
9013
  }]);
8952
9014
  return BufferStream;
8953
9015
  }();
@@ -9110,6 +9172,15 @@ var EXPLICIT_BIG_ENDIAN = "1.2.840.10008.1.2.2";
9110
9172
  * canonical UNDEFINED_LENGTH_FIX value of -1 instead
9111
9173
  */
9112
9174
  var UNDEFINED_LENGTH = 0xffffffff;
9175
+ /**
9176
+ * Use a -1 value for the undefined length being fixed as this is consistent
9177
+ * with other usage and can't be confused with a real length.
9178
+ */
9179
+ var UNDEFINED_LENGTH_FIX = -1;
9180
+ var ITEM_DELIMITATION_LENGTH = 0x00000000;
9181
+
9182
+ // Delimitation Value
9183
+ var SEQUENCE_DELIMITATION_VALUE = 0x00000000;
9113
9184
 
9114
9185
  // Value multiplicity and padding
9115
9186
  var VM_DELIMITER = 0x5c;
@@ -9130,6 +9201,53 @@ var unencapsulatedTransferSyntaxes = {
9130
9201
  EXPLICIT_LITTLE_ENDIAN: true
9131
9202
  };
9132
9203
 
9204
+ /**
9205
+ * Video transfer syntax UIDs (MPEG2, H.264, H.265)
9206
+ * These transfer syntaxes treat the entire pixel data stream as a single frame
9207
+ * regardless of the number of fragments.
9208
+ */
9209
+ var videoTransferSyntaxUIDs = new Set(["1.2.840.10008.1.2.4.100",
9210
+ // MPEG2 Main Profile @ Main Level
9211
+ "1.2.840.10008.1.2.4.100.1",
9212
+ // MPEG2 Main Profile @ Main Level (retired)
9213
+ "1.2.840.10008.1.2.4.101",
9214
+ // MPEG2 Main Profile @ High Level
9215
+ "1.2.840.10008.1.2.4.101.1",
9216
+ // MPEG2 Main Profile @ High Level (retired)
9217
+ "1.2.840.10008.1.2.4.102",
9218
+ // MPEG-4 AVC/H.264 High Profile / Level 4.1
9219
+ "1.2.840.10008.1.2.4.102.1",
9220
+ // MPEG-4 AVC/H.264 High Profile / Level 4.1 (retired)
9221
+ "1.2.840.10008.1.2.4.103",
9222
+ // MPEG-4 AVC/H.264 BD-compatible High Profile / Level 4.1
9223
+ "1.2.840.10008.1.2.4.103.1",
9224
+ // MPEG-4 AVC/H.264 BD-compatible High Profile / Level 4.1 (retired)
9225
+ "1.2.840.10008.1.2.4.104",
9226
+ // MPEG-4 AVC/H.264 High Profile / Level 4.2 For 2D Video
9227
+ "1.2.840.10008.1.2.4.104.1",
9228
+ // MPEG-4 AVC/H.264 High Profile / Level 4.2 For 2D Video (retired)
9229
+ "1.2.840.10008.1.2.4.105",
9230
+ // MPEG-4 AVC/H.264 High Profile / Level 4.2 For 3D Video
9231
+ "1.2.840.10008.1.2.4.105.1",
9232
+ // MPEG-4 AVC/H.264 High Profile / Level 4.2 For 3D Video (retired)
9233
+ "1.2.840.10008.1.2.4.106",
9234
+ // MPEG-4 AVC/H.264 Stereo High Profile / Level 4.2
9235
+ "1.2.840.10008.1.2.4.106.1",
9236
+ // MPEG-4 AVC/H.264 Stereo High Profile / Level 4.2 (retired)
9237
+ "1.2.840.10008.1.2.4.107",
9238
+ // HEVC/H.265 Main Profile / Level 5.1
9239
+ "1.2.840.10008.1.2.4.108" // HEVC/H.265 Main 10 Profile / Level 5.1
9240
+ ]);
9241
+
9242
+ /**
9243
+ * Checks if a transfer syntax UID is a video transfer syntax
9244
+ * @param {string} uid - Transfer syntax UID to check
9245
+ * @returns {boolean} - True if the UID is a video transfer syntax
9246
+ */
9247
+ function isVideoTransferSyntax(uid) {
9248
+ return uid && videoTransferSyntaxUIDs.has(uid);
9249
+ }
9250
+
9133
9251
  /**
9134
9252
  * This is an enumeration of some HEX values for the tag strings, used to replace
9135
9253
  * constants in a few places.
@@ -9148,7 +9266,13 @@ var TagHex = {
9148
9266
  NumberOfFrames: "00280008",
9149
9267
  SpecificCharacterSet: "00080005",
9150
9268
  PixelRepresentation: "00280103",
9151
- DataSetTrailingPadding: "FFFCFFFC"
9269
+ DataSetTrailingPadding: "FFFCFFFC",
9270
+ StudyInstanceUID: "0020000D",
9271
+ SeriesInstanceUID: "0020000E",
9272
+ SOPInstanceUID: "00080018",
9273
+ TimezoneOffsetFromUTC: "00080201",
9274
+ AvailableTransferSyntaxUID: "00083002",
9275
+ MediaStorageSOPInstanceUID: "00020003"
9152
9276
  };
9153
9277
  var encodingMapping = {
9154
9278
  "": "iso-8859-1",
@@ -9187,6 +9311,202 @@ var encodingMapping = {
9187
9311
  gbk: "gbk"
9188
9312
  };
9189
9313
 
9314
+ /**
9315
+ * Maps DICOM tag hex strings to their normalized lower camelCase names
9316
+ * for use in listener.information tracking
9317
+ */
9318
+ var TAG_NAME_MAP = {
9319
+ "0020000D": "studyInstanceUid",
9320
+ "0020000E": "seriesInstanceUid",
9321
+ "00080018": "sopInstanceUid",
9322
+ "00020010": "transferSyntaxUid",
9323
+ "00083002": "availableTransferSyntaxUid",
9324
+ "00080201": "timezoneOffsetFromUtc",
9325
+ "00080005": "specificCharacterSet",
9326
+ "00280008": "numberOfFrames",
9327
+ "00280010": "rows",
9328
+ "00280011": "columns",
9329
+ "00280002": "samplesPerPixel",
9330
+ "00280100": "bitsAllocated",
9331
+ "00280103": "pixelRepresentation"
9332
+ };
9333
+
9334
+ /**
9335
+ * Default tags to track in listener.information
9336
+ */
9337
+ var DEFAULT_INFORMATION_TAGS = new Set(["0020000D",
9338
+ // StudyInstanceUID
9339
+ "0020000E",
9340
+ // SeriesInstanceUID
9341
+ "00080018",
9342
+ // SOPInstanceUID
9343
+ "00020010",
9344
+ // TransferSyntaxUID
9345
+ "00083002",
9346
+ // AvailableTransferSyntaxUID
9347
+ "00080201",
9348
+ // TimezoneOffsetFromUTC
9349
+ "00080005",
9350
+ // SpecificCharacterSet
9351
+ "00280008",
9352
+ // NumberOfFrames
9353
+ "00280010",
9354
+ // Rows
9355
+ "00280011",
9356
+ // Columns
9357
+ "00280002",
9358
+ // SamplesPerPixel
9359
+ "00280100",
9360
+ // BitsAllocated
9361
+ "00280103" // PixelRepresentation
9362
+ ]);
9363
+
9364
+ /**
9365
+ * All valid DICOM VR (Value Representation) codes
9366
+ */
9367
+ var VALID_VRS = new Set(["AE",
9368
+ // Application Entity
9369
+ "AS",
9370
+ // Age String
9371
+ "AT",
9372
+ // Attribute Tag
9373
+ "CS",
9374
+ // Code String
9375
+ "DA",
9376
+ // Date
9377
+ "DS",
9378
+ // Decimal String
9379
+ "DT",
9380
+ // Date Time
9381
+ "FL",
9382
+ // Floating Point Single
9383
+ "FD",
9384
+ // Floating Point Double
9385
+ "IS",
9386
+ // Integer String
9387
+ "LO",
9388
+ // Long String
9389
+ "LT",
9390
+ // Long Text
9391
+ "OB",
9392
+ // Other Byte
9393
+ "OD",
9394
+ // Other Double
9395
+ "OF",
9396
+ // Other Float
9397
+ "OL",
9398
+ // Other Long
9399
+ "OV",
9400
+ // Other 64-bit Very Long
9401
+ "OW",
9402
+ // Other Word
9403
+ "PN",
9404
+ // Person Name
9405
+ "SH",
9406
+ // Short String
9407
+ "SL",
9408
+ // Signed Long
9409
+ "SQ",
9410
+ // Sequence of Items
9411
+ "SS",
9412
+ // Signed Short
9413
+ "ST",
9414
+ // Short Text
9415
+ "SV",
9416
+ // Signed 64-bit Very Long
9417
+ "TM",
9418
+ // Time
9419
+ "UC",
9420
+ // Unlimited Characters
9421
+ "UI",
9422
+ // Unique Identifier
9423
+ "UL",
9424
+ // Unsigned Long
9425
+ "UN",
9426
+ // Unknown
9427
+ "UR",
9428
+ // Universal Resource
9429
+ "US",
9430
+ // Unsigned Short
9431
+ "UT",
9432
+ // Unlimited Text
9433
+ "UV" // Unsigned 64-bit Very Long
9434
+ ]);
9435
+
9436
+ /**
9437
+ * DICOM VR (Value Representation) types that are allowed for bulkdata encoding
9438
+ * According to DICOMweb specification
9439
+ */
9440
+ var BULKDATA_VRS = new Set(["DS",
9441
+ // Decimal String
9442
+ "FL",
9443
+ // Floating Point Single
9444
+ "FD",
9445
+ // Floating Point Double
9446
+ "IS",
9447
+ // Integer String
9448
+ "LT",
9449
+ // Long Text
9450
+ "OB",
9451
+ // Other Byte
9452
+ "OD",
9453
+ // Other Double
9454
+ "OF",
9455
+ // Other Float
9456
+ "OL",
9457
+ // Other Long
9458
+ "OV",
9459
+ // Other 64-bit Very Long
9460
+ "OW",
9461
+ // Other Word
9462
+ "SL",
9463
+ // Signed Long
9464
+ "SS",
9465
+ // Signed Short
9466
+ "ST",
9467
+ // Short Text
9468
+ "SV",
9469
+ // Signed 64-bit Very Long
9470
+ "UC",
9471
+ // Unlimited Characters
9472
+ "UL",
9473
+ // Unsigned Long
9474
+ "UN",
9475
+ // Unknown
9476
+ "US",
9477
+ // Unsigned Short
9478
+ "UT",
9479
+ // Unlimited Text
9480
+ "UV" // Unsigned 64-bit Very Long
9481
+ ]);
9482
+
9483
+ var constants = /*#__PURE__*/Object.freeze({
9484
+ __proto__: null,
9485
+ BULKDATA_VRS: BULKDATA_VRS,
9486
+ DEFAULT_INFORMATION_TAGS: DEFAULT_INFORMATION_TAGS,
9487
+ DEFLATED_EXPLICIT_LITTLE_ENDIAN: DEFLATED_EXPLICIT_LITTLE_ENDIAN,
9488
+ EXPLICIT_BIG_ENDIAN: EXPLICIT_BIG_ENDIAN,
9489
+ EXPLICIT_LITTLE_ENDIAN: EXPLICIT_LITTLE_ENDIAN$1,
9490
+ IMPLICIT_LITTLE_ENDIAN: IMPLICIT_LITTLE_ENDIAN,
9491
+ ITEM_DELIMITATION_LENGTH: ITEM_DELIMITATION_LENGTH,
9492
+ PADDING_NULL: PADDING_NULL,
9493
+ PADDING_SPACE: PADDING_SPACE,
9494
+ PN_COMPONENT_DELIMITER: PN_COMPONENT_DELIMITER,
9495
+ SEQUENCE_DELIMITATION_VALUE: SEQUENCE_DELIMITATION_VALUE,
9496
+ SEQUENCE_DELIMITER_TAG: SEQUENCE_DELIMITER_TAG,
9497
+ SEQUENCE_ITEM_TAG: SEQUENCE_ITEM_TAG,
9498
+ TAG_NAME_MAP: TAG_NAME_MAP,
9499
+ TagHex: TagHex,
9500
+ UNDEFINED_LENGTH: UNDEFINED_LENGTH,
9501
+ UNDEFINED_LENGTH_FIX: UNDEFINED_LENGTH_FIX,
9502
+ VALID_VRS: VALID_VRS,
9503
+ VM_DELIMITER: VM_DELIMITER,
9504
+ encodingMapping: encodingMapping,
9505
+ isVideoTransferSyntax: isVideoTransferSyntax,
9506
+ unencapsulatedTransferSyntaxes: unencapsulatedTransferSyntaxes,
9507
+ videoTransferSyntaxUIDs: videoTransferSyntaxUIDs
9508
+ });
9509
+
9190
9510
  /**
9191
9511
  * Converts a PN string to the dicom+json equivalent, or returns the
9192
9512
  * original object
@@ -11156,7 +11476,8 @@ var DicomMetaDictionary = /*#__PURE__*/function () {
11156
11476
  return namedDataset;
11157
11477
  }
11158
11478
 
11159
- /** converts from DICOM JSON Model dataset to a natural dataset
11479
+ /**
11480
+ * converts from DICOM JSON Model dataset to a natural dataset
11160
11481
  * - sequences become lists
11161
11482
  * - single element lists are replaced by their first element,
11162
11483
  * with single element lists remaining lists, but being a
@@ -12166,80 +12487,1426 @@ var Colors = /*#__PURE__*/function () {
12166
12487
  } else {
12167
12488
  return Math.pow((n + 0.055) / 1.055, 2.4);
12168
12489
  }
12169
- }
12170
- }, {
12171
- key: "rgb2XYZ",
12172
- value: function rgb2XYZ(rgb) {
12173
- var R = Colors.invGammaCorrection(rgb[0]);
12174
- var G = Colors.invGammaCorrection(rgb[1]);
12175
- var B = Colors.invGammaCorrection(rgb[2]);
12176
- return [0.4123955889674142161 * R + 0.3575834307637148171 * G + 0.1804926473817015735 * B, 0.2125862307855955516 * R + 0.7151703037034108499 * G + 0.07220049864333622685 * B, 0.01929721549174694484 * R + 0.1191838645808485318 * G + 0.950497125131579766 * B];
12177
- }
12490
+ }
12491
+ }, {
12492
+ key: "rgb2XYZ",
12493
+ value: function rgb2XYZ(rgb) {
12494
+ var R = Colors.invGammaCorrection(rgb[0]);
12495
+ var G = Colors.invGammaCorrection(rgb[1]);
12496
+ var B = Colors.invGammaCorrection(rgb[2]);
12497
+ return [0.4123955889674142161 * R + 0.3575834307637148171 * G + 0.1804926473817015735 * B, 0.2125862307855955516 * R + 0.7151703037034108499 * G + 0.07220049864333622685 * B, 0.01929721549174694484 * R + 0.1191838645808485318 * G + 0.950497125131579766 * B];
12498
+ }
12499
+ }, {
12500
+ key: "xyz2LAB",
12501
+ value: function xyz2LAB(xyz) {
12502
+ var whitePoint = Colors.d65WhitePointXYZ();
12503
+ var X = xyz[0] / whitePoint[0];
12504
+ var Y = xyz[1] / whitePoint[1];
12505
+ var Z = xyz[2] / whitePoint[2];
12506
+ X = Colors.labf(X);
12507
+ Y = Colors.labf(Y);
12508
+ Z = Colors.labf(Z);
12509
+ return [116 * Y - 16, 500 * (X - Y), 200 * (Y - Z)];
12510
+ }
12511
+ }, {
12512
+ key: "lab2RGB",
12513
+ value: function lab2RGB(lab) {
12514
+ return Colors.xyz2RGB(Colors.lab2XYZ(lab));
12515
+ }
12516
+ }, {
12517
+ key: "lab2XYZ",
12518
+ value: function lab2XYZ(lab) {
12519
+ var L = (lab[0] + 16) / 116;
12520
+ var a = L + lab[1] / 500;
12521
+ var b = L - lab[2] / 200;
12522
+ var whitePoint = Colors.d65WhitePointXYZ();
12523
+ return [whitePoint[0] * Colors.labfInv(a), whitePoint[1] * Colors.labfInv(L), whitePoint[2] * Colors.labfInv(b)];
12524
+ }
12525
+ }, {
12526
+ key: "xyz2RGB",
12527
+ value: function xyz2RGB(xyz) {
12528
+ var R1 = 3.2406 * xyz[0] - 1.5372 * xyz[1] - 0.4986 * xyz[2];
12529
+ var G1 = -0.9689 * xyz[0] + 1.8758 * xyz[1] + 0.0415 * xyz[2];
12530
+ var B1 = 0.0557 * xyz[0] - 0.204 * xyz[1] + 1.057 * xyz[2];
12531
+
12532
+ /* Force nonnegative values so that gamma correction is well-defined. */
12533
+ var minimumComponent = Math.min(R1, G1);
12534
+ minimumComponent = Math.min(minimumComponent, B1);
12535
+ if (minimumComponent < 0) {
12536
+ R1 -= minimumComponent;
12537
+ G1 -= minimumComponent;
12538
+ B1 -= minimumComponent;
12539
+ }
12540
+
12541
+ /* Transform from RGB to R'G'B' */
12542
+ return [Colors.gammaCorrection(R1), Colors.gammaCorrection(G1), Colors.gammaCorrection(B1)];
12543
+ }
12544
+ }, {
12545
+ key: "labf",
12546
+ value: function labf(n) {
12547
+ if (n >= 8.85645167903563082e-3) {
12548
+ return Math.pow(n, 0.333333333333333);
12549
+ } else {
12550
+ return 841.0 / 108.0 * n + 4.0 / 29.0;
12551
+ }
12552
+ }
12553
+ }, {
12554
+ key: "labfInv",
12555
+ value: function labfInv(n) {
12556
+ if (n >= 0.206896551724137931) {
12557
+ return n * n * n;
12558
+ } else {
12559
+ return 108.0 / 841.0 * (n - 4.0 / 29.0);
12560
+ }
12561
+ }
12562
+ }]);
12563
+ return Colors;
12564
+ }();
12565
+
12566
+ /**
12567
+ * Creates an information filter that tracks top-level DICOM attributes.
12568
+ *
12569
+ * @param {Set<string>} tags - Optional set of tag hex strings to track.
12570
+ * If not provided, uses DEFAULT_INFORMATION_TAGS.
12571
+ * @returns {Object} A filter object that adds listener.information attribute
12572
+ */
12573
+ function createInformationFilter() {
12574
+ var tags = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : DEFAULT_INFORMATION_TAGS;
12575
+ var filter = {
12576
+ information: null,
12577
+ /**
12578
+ * Initializes the filter, synchronizing the information object with the parent listener
12579
+ */
12580
+ _init: function _init(options) {
12581
+ filter.information = (options === null || options === void 0 ? void 0 : options.information) || {};
12582
+ this.information = filter.information;
12583
+ },
12584
+ /**
12585
+ * Intercepts addTag calls to track top-level attributes in listener.information
12586
+ */
12587
+ addTag: function addTag(next, tag, tagInfo) {
12588
+ var _this$current;
12589
+ // Check if this is a top-level tag (level 0) and is in our tracked set
12590
+ if (((_this$current = this.current) === null || _this$current === void 0 ? void 0 : _this$current.level) === 0 && tags.has(tag)) {
12591
+ // Store a reference to track this tag for value updates
12592
+ var normalizedName = TAG_NAME_MAP[tag] || tag;
12593
+ this.information[normalizedName] = null;
12594
+
12595
+ // Mark this tag for tracking
12596
+ var result = next(tag, tagInfo);
12597
+ this.current._trackInformation = normalizedName;
12598
+ return result;
12599
+ }
12600
+ return next(tag, tagInfo);
12601
+ },
12602
+ /**
12603
+ * Intercepts value calls to populate information values
12604
+ */
12605
+ value: function value(next, v) {
12606
+ var _this$current2;
12607
+ // If current context is tracking information, store the first value
12608
+ if ((_this$current2 = this.current) !== null && _this$current2 !== void 0 && _this$current2._trackInformation) {
12609
+ var name = this.current._trackInformation;
12610
+ this.information[name] = v;
12611
+ this.current._trackInformation = null;
12612
+ }
12613
+ return next(v);
12614
+ }
12615
+ };
12616
+ return filter;
12617
+ }
12618
+
12619
+ /**
12620
+ * A DICOM Metadata listener implements the basic listener for creating a dicom
12621
+ * metadata instance from a stream of notification events.
12622
+ *
12623
+ * There are additional listeners defined in @cornerstonejs/metadata as well as
12624
+ * other event sources in that package.
12625
+ *
12626
+ * **Important behaviors:**
12627
+ * - Each frame is ALWAYS delivered as an array (ArrayBuffer[]), even for single frames
12628
+ * - Video transfer syntaxes are handled as though they were a single frame
12629
+ * - Binary data can be delivered fragmented - a single frame may be split across multiple ArrayBuffer fragments
12630
+ * - Multiple fragments are combined into one array per frame
12631
+ *
12632
+ * **WARNING** This class is still under development, do not count on the API
12633
+ * not changing a bit over the next few dcmjs releases.
12634
+ */
12635
+ var DicomMetadataListener = /*#__PURE__*/function () {
12636
+ /**
12637
+ * Creates a new DicomMetadataListener instance.
12638
+ *
12639
+ * @param {Object} options - Configuration options
12640
+ * @param {Object} options.informationFilter - Optional information filter to use.
12641
+ * If not provided, creates one automatically.
12642
+ * @param {Set<string>} options.informationTags - Optional set of tag hex strings
12643
+ * to track in listener.information. If not provided, uses default tags.
12644
+ * @param {...Object} filters - Optional filter objects that can intercept
12645
+ * method calls. Each filter can have methods like addTag, startObject,
12646
+ * pop, or value. Each filter method receives a 'next'
12647
+ * function as the first argument, followed by the same arguments as
12648
+ * the original method.
12649
+ *
12650
+ * @example
12651
+ * const listener = new DicomMetadataListener();
12652
+ *
12653
+ * @example
12654
+ * const listener = new DicomMetadataListener(
12655
+ * { informationTags: new Set(['0020000D', '0020000E']) }
12656
+ * );
12657
+ *
12658
+ * @example
12659
+ * const listener = new DicomMetadataListener(
12660
+ * {},
12661
+ * {
12662
+ * addTag(next, tag, tagInfo) {
12663
+ * console.log('Adding tag:', tag);
12664
+ * return next(tag, tagInfo);
12665
+ * }
12666
+ * }
12667
+ * );
12668
+ */
12669
+ function DicomMetadataListener() {
12670
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
12671
+ for (var _len = arguments.length, filters = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
12672
+ filters[_key - 1] = arguments[_key];
12673
+ }
12674
+ _classCallCheck(this, DicomMetadataListener);
12675
+ _defineProperty(this, "current", null);
12676
+ _defineProperty(this, "fmi", null);
12677
+ _defineProperty(this, "dict", null);
12678
+ _defineProperty(this, "filters", []);
12679
+ _defineProperty(this, "information", null);
12680
+ // Handle legacy constructor format where first arg might be a filter
12681
+ if (typeof options.addTag === "function" || typeof options.startObject === "function" || typeof options.pop === "function" || typeof options.value === "function") {
12682
+ // Legacy format: all arguments are filters
12683
+ filters = [options].concat(_toConsumableArray(filters));
12684
+ options = {};
12685
+ }
12686
+
12687
+ // Information filter should always be first so it can track tags
12688
+ // Use the provided informationFilter or create a new one
12689
+ var informationFilter = options.informationFilter || createInformationFilter(options.informationTags);
12690
+ this.filters = [informationFilter].concat(_toConsumableArray(filters));
12691
+ this._createMethodChains();
12692
+
12693
+ // Initialize filters to synchronize state
12694
+ this.init(options);
12695
+ }
12696
+
12697
+ /**
12698
+ * Initializes state, allowing it to be re-used.
12699
+ * @param {Object} options - Optional options to pass to the filters to re-initialize them
12700
+ * @param {Object} options.information - Optional information to pass to the filters
12701
+ * @returns {void}
12702
+ */
12703
+ _createClass(DicomMetadataListener, [{
12704
+ key: "init",
12705
+ value: function init() {
12706
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
12707
+ this.current = null;
12708
+ this.fmi = null;
12709
+ this.dict = null;
12710
+ this.information = null;
12711
+ var _iterator = _createForOfIteratorHelper(this.filters),
12712
+ _step;
12713
+ try {
12714
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
12715
+ var _filter$_init;
12716
+ var filter = _step.value;
12717
+ (_filter$_init = filter._init) === null || _filter$_init === void 0 || _filter$_init.call(this, options);
12718
+ }
12719
+ } catch (err) {
12720
+ _iterator.e(err);
12721
+ } finally {
12722
+ _iterator.f();
12723
+ }
12724
+ }
12725
+
12726
+ /**
12727
+ * Creates method chains for each method that can be filtered.
12728
+ * @private
12729
+ */
12730
+ }, {
12731
+ key: "_createMethodChains",
12732
+ value: function _createMethodChains() {
12733
+ var _this = this;
12734
+ var methods = ["addTag", "startObject", "pop", "value"];
12735
+ for (var _i = 0, _methods = methods; _i < _methods.length; _i++) {
12736
+ var methodName = _methods[_i];
12737
+ var baseMethod = this["_base".concat(methodName.charAt(0).toUpperCase() + methodName.slice(1))].bind(this);
12738
+
12739
+ // Build the chain by wrapping each filter
12740
+ // Start with the base implementation
12741
+ var chain = baseMethod;
12742
+
12743
+ // Apply filters in reverse order so they execute in forward order
12744
+ var _loop = function _loop() {
12745
+ var filter = _this.filters[i];
12746
+ if (filter && typeof filter[methodName] === "function") {
12747
+ var filterFn = filter[methodName];
12748
+ var next = chain;
12749
+ chain = function chain() {
12750
+ for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
12751
+ args[_key2] = arguments[_key2];
12752
+ }
12753
+ return filterFn.call.apply(filterFn, [_this, next].concat(args));
12754
+ };
12755
+ }
12756
+ };
12757
+ for (var i = this.filters.length - 1; i >= 0; i--) {
12758
+ _loop();
12759
+ }
12760
+
12761
+ // Replace the method with the chained version
12762
+ this[methodName] = chain;
12763
+ }
12764
+ }
12765
+
12766
+ /**
12767
+ * Base implementation: Adds a new tag value
12768
+ * @private
12769
+ */
12770
+ }, {
12771
+ key: "_baseAddTag",
12772
+ value: function _baseAddTag(tag, tagInfo) {
12773
+ var _this$current$level;
12774
+ var dest = {
12775
+ vr: tagInfo === null || tagInfo === void 0 ? void 0 : tagInfo.vr,
12776
+ Value: null
12777
+ };
12778
+ if (this.current && this.current.dest) {
12779
+ this.current.dest[tag] = dest;
12780
+ }
12781
+ // Tags are at the same level as their parent (they're properties, not nested structures)
12782
+ var level = this.current ? (_this$current$level = this.current.level) !== null && _this$current$level !== void 0 ? _this$current$level : 0 : 0;
12783
+ this.current = {
12784
+ parent: this.current,
12785
+ dest: dest,
12786
+ type: tag,
12787
+ tag: tag,
12788
+ vr: tagInfo === null || tagInfo === void 0 ? void 0 : tagInfo.vr,
12789
+ level: level,
12790
+ length: tagInfo === null || tagInfo === void 0 ? void 0 : tagInfo.length
12791
+ };
12792
+ }
12793
+
12794
+ /**
12795
+ * Base implementation: Starts a new object, using the provided value
12796
+ * @private
12797
+ */
12798
+ }, {
12799
+ key: "_baseStartObject",
12800
+ value: function _baseStartObject() {
12801
+ var _this$current$level2;
12802
+ var dest = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
12803
+ // Objects/sequences are nested structures, so they increment the level
12804
+ // Root object is at level 0, nested objects are one level deeper
12805
+ var level = this.current ? ((_this$current$level2 = this.current.level) !== null && _this$current$level2 !== void 0 ? _this$current$level2 : 0) + 1 : 0;
12806
+ if (this.current) {
12807
+ this.value(dest);
12808
+ }
12809
+ this.current = {
12810
+ parent: this.current,
12811
+ dest: dest,
12812
+ type: "object",
12813
+ level: level
12814
+ };
12815
+ }
12816
+
12817
+ /**
12818
+ * Base implementation: Pops the current value being created off the stack.
12819
+ * @private
12820
+ */
12821
+ }, {
12822
+ key: "_basePop",
12823
+ value: function _basePop() {
12824
+ var _this$current$pop, _this$current$pop2, _this$current3, _result$Value;
12825
+ var result = (_this$current$pop = (_this$current$pop2 = (_this$current3 = this.current).pop) === null || _this$current$pop2 === void 0 ? void 0 : _this$current$pop2.call(_this$current3)) !== null && _this$current$pop !== void 0 ? _this$current$pop : this.current.dest;
12826
+ if (result.InlineBinary) {
12827
+ console.log("********* InlineBinary already set", result, this.current);
12828
+ }
12829
+ if (result.Value === null) {
12830
+ result.Value = [];
12831
+ } else if (((_result$Value = result.Value) === null || _result$Value === void 0 ? void 0 : _result$Value.length) === 1 && (result.Value[0] === null || result.Value[0] === undefined)) {
12832
+ result.Value = [];
12833
+ }
12834
+ this.current = this.current.parent;
12835
+ return result;
12836
+ }
12837
+
12838
+ /**
12839
+ * Base implementation: Registers a new value for the current destination being created.
12840
+ *
12841
+ * Note: Binary data (ArrayBuffer) can be delivered fragmented across multiple calls.
12842
+ * Multiple fragments are combined into arrays. Each frame is always delivered as an array.
12843
+ *
12844
+ * @private
12845
+ */
12846
+ }, {
12847
+ key: "_baseValue",
12848
+ value: function _baseValue(v) {
12849
+ var _this$current$dest;
12850
+ if (Array.isArray(this.current.dest)) {
12851
+ this.current.dest.push(v);
12852
+ return;
12853
+ }
12854
+ (_this$current$dest = this.current.dest).Value || (_this$current$dest.Value = []);
12855
+ this.current.dest.Value.push(v);
12856
+ }
12857
+
12858
+ /**
12859
+ * Gets the Transfer Syntax UID from the File Meta Information (FMI)
12860
+ * @returns {string|undefined} - Transfer syntax UID or undefined if not available
12861
+ */
12862
+ }, {
12863
+ key: "getTransferSyntaxUID",
12864
+ value: function getTransferSyntaxUID() {
12865
+ var _this$fmi$transferSyn;
12866
+ if (!this.fmi) {
12867
+ return undefined;
12868
+ }
12869
+ var transferSyntaxTag = "00020010"; // TransferSyntaxUID tag
12870
+ if ((_this$fmi$transferSyn = this.fmi[transferSyntaxTag]) !== null && _this$fmi$transferSyn !== void 0 && _this$fmi$transferSyn.Value && Array.isArray(this.fmi[transferSyntaxTag].Value) && this.fmi[transferSyntaxTag].Value.length > 0) {
12871
+ return this.fmi[transferSyntaxTag].Value[0];
12872
+ }
12873
+ return undefined;
12874
+ }
12875
+ }]);
12876
+ return DicomMetadataListener;
12877
+ }();
12878
+
12879
+ /**
12880
+ * This is an asynchronous binary DICOM reader.
12881
+ *
12882
+ * Assume this is still preliminary as to the exact interface as it is
12883
+ * initially being released for testing purposes only.
12884
+ *
12885
+ * There is no support for compressed streams.
12886
+ */
12887
+ var AsyncDicomReader = /*#__PURE__*/function () {
12888
+ function AsyncDicomReader() {
12889
+ var _options$maxFragmentS;
12890
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
12891
+ _classCallCheck(this, AsyncDicomReader);
12892
+ _defineProperty(this, "syntax", EXPLICIT_LITTLE_ENDIAN$1);
12893
+ this.isLittleEndian = options === null || options === void 0 ? void 0 : options.isLittleEndian;
12894
+ // Default maxFragmentSize is 128 MB (128 * 1024 * 1024 bytes)
12895
+ this.maxFragmentSize = (_options$maxFragmentS = options === null || options === void 0 ? void 0 : options.maxFragmentSize) !== null && _options$maxFragmentS !== void 0 ? _options$maxFragmentS : 128 * 1024 * 1024;
12896
+ this.stream = new ReadBufferStream(null, this.isLittleEndian, _objectSpread2({
12897
+ clearBuffers: true
12898
+ }, options));
12899
+ }
12900
+
12901
+ /**
12902
+ * Reads the preamble and checks for the DICM marker.
12903
+ * Returns true if found/read, leaving the stream past the
12904
+ * marker, or false, having not found the marker.
12905
+ *
12906
+ * If no preamble is found, attempts to detect raw LEI/LEE encoding
12907
+ * by examining the first tag structure.
12908
+ */
12909
+ _createClass(AsyncDicomReader, [{
12910
+ key: "readPreamble",
12911
+ value: (function () {
12912
+ var _readPreamble = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee() {
12913
+ var stream;
12914
+ return _regeneratorRuntime().wrap(function _callee$(_context) {
12915
+ while (1) switch (_context.prev = _context.next) {
12916
+ case 0:
12917
+ stream = this.stream;
12918
+ _context.next = 3;
12919
+ return stream.ensureAvailable();
12920
+ case 3:
12921
+ stream.reset();
12922
+ stream.increment(128);
12923
+ if (!(stream.readAsciiString(4) !== "DICM")) {
12924
+ _context.next = 10;
12925
+ break;
12926
+ }
12927
+ stream.reset();
12928
+ // No preamble found - try to detect raw dataset encoding
12929
+ _context.next = 9;
12930
+ return this.detectRawEncoding();
12931
+ case 9:
12932
+ return _context.abrupt("return", false);
12933
+ case 10:
12934
+ return _context.abrupt("return", true);
12935
+ case 11:
12936
+ case "end":
12937
+ return _context.stop();
12938
+ }
12939
+ }, _callee, this);
12940
+ }));
12941
+ function readPreamble() {
12942
+ return _readPreamble.apply(this, arguments);
12943
+ }
12944
+ return readPreamble;
12945
+ }()
12946
+ /**
12947
+ * Detects whether a raw dataset (no Part 10 preamble) is LEI or LEE encoded.
12948
+ * This is done by examining the first tag structure:
12949
+ * - LEI: Tag (4 bytes) + Length (4 bytes) - no VR
12950
+ * - LEE: Tag (4 bytes) + VR (2 bytes ASCII) + Length (2 or 4 bytes)
12951
+ *
12952
+ * We check if bytes 4-5 look like a valid DICOM VR (2 ASCII letters that
12953
+ * match known VR codes). If a valid VR is detected, we use LEE, otherwise LEI.
12954
+ */
12955
+ )
12956
+ }, {
12957
+ key: "detectRawEncoding",
12958
+ value: (function () {
12959
+ var _detectRawEncoding = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee2() {
12960
+ var stream, group, byte4, byte5, potentialLength, isASCIILetter, vrCandidate;
12961
+ return _regeneratorRuntime().wrap(function _callee2$(_context2) {
12962
+ while (1) switch (_context2.prev = _context2.next) {
12963
+ case 0:
12964
+ stream = this.stream;
12965
+ _context2.next = 3;
12966
+ return stream.ensureAvailable(8);
12967
+ case 3:
12968
+ // Need at least 8 bytes (tag + length) to detect
12969
+
12970
+ stream.reset();
12971
+ stream.setEndian(true); // Little endian for both LEI and LEE
12972
+
12973
+ // Read the tag (first 4 bytes)
12974
+ group = stream.readUint16();
12975
+ stream.readUint16();
12976
+
12977
+ // Verify this looks like a DICOM file - first tag should be in group 0008
12978
+ if (!(group !== 0x0008)) {
12979
+ _context2.next = 9;
12980
+ break;
12981
+ }
12982
+ throw new Error("Invalid DICOM file: expected first tag group to be 0x0008, found 0x".concat(group.toString(16).padStart(4, "0")));
12983
+ case 9:
12984
+ // Check if bytes 4-5 (after tag) look like a valid VR (2 ASCII letters)
12985
+ byte4 = stream.peekUint8(0);
12986
+ byte5 = stream.peekUint8(1); // If we're going to assume LEI, check the length is even (DICOM requirement)
12987
+ // Read the length value (4 bytes after tag in LEI format) as little-endian uint32
12988
+ _context2.next = 13;
12989
+ return stream.ensureAvailable(8);
12990
+ case 13:
12991
+ // Need at least 8 bytes (tag + length)
12992
+ potentialLength = stream.view.getUint32(stream.offset, true); // true = little endian
12993
+ isASCIILetter = function isASCIILetter(byte) {
12994
+ return byte >= 0x41 && byte <= 0x5a ||
12995
+ // A-Z
12996
+ byte >= 0x61 && byte <= 0x7a; // a-z
12997
+ };
12998
+ if (!(isASCIILetter(byte4) && isASCIILetter(byte5))) {
12999
+ _context2.next = 26;
13000
+ break;
13001
+ }
13002
+ // Check if it's a valid VR code
13003
+ vrCandidate = String.fromCharCode(byte4, byte5).toUpperCase();
13004
+ if (!VALID_VRS.has(vrCandidate)) {
13005
+ _context2.next = 21;
13006
+ break;
13007
+ }
13008
+ // Valid VR detected - this is LEE (Explicit Little Endian)
13009
+ this.syntax = EXPLICIT_LITTLE_ENDIAN$1;
13010
+ _context2.next = 24;
13011
+ break;
13012
+ case 21:
13013
+ if (!(potentialLength % 2 !== 0)) {
13014
+ _context2.next = 23;
13015
+ break;
13016
+ }
13017
+ throw new Error("Invalid DICOM file: detected LEI encoding but length is odd (".concat(potentialLength, "), which is not allowed in DICOM"));
13018
+ case 23:
13019
+ this.syntax = IMPLICIT_LITTLE_ENDIAN;
13020
+ case 24:
13021
+ _context2.next = 29;
13022
+ break;
13023
+ case 26:
13024
+ if (!(potentialLength % 2 !== 0)) {
13025
+ _context2.next = 28;
13026
+ break;
13027
+ }
13028
+ throw new Error("Invalid DICOM file: detected LEI encoding but length is odd (".concat(potentialLength, "), which is not allowed in DICOM"));
13029
+ case 28:
13030
+ this.syntax = IMPLICIT_LITTLE_ENDIAN;
13031
+ case 29:
13032
+ // Reset stream to beginning for reading
13033
+ stream.reset();
13034
+ case 30:
13035
+ case "end":
13036
+ return _context2.stop();
13037
+ }
13038
+ }, _callee2, this);
13039
+ }));
13040
+ function detectRawEncoding() {
13041
+ return _detectRawEncoding.apply(this, arguments);
13042
+ }
13043
+ return detectRawEncoding;
13044
+ }())
13045
+ }, {
13046
+ key: "readFile",
13047
+ value: function () {
13048
+ var _readFile = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee3() {
13049
+ var options,
13050
+ hasPreamble,
13051
+ _listener,
13052
+ information,
13053
+ listener,
13054
+ _this$meta,
13055
+ _information,
13056
+ _args3 = arguments;
13057
+ return _regeneratorRuntime().wrap(function _callee3$(_context3) {
13058
+ while (1) switch (_context3.prev = _context3.next) {
13059
+ case 0:
13060
+ options = _args3.length > 0 && _args3[0] !== undefined ? _args3[0] : undefined;
13061
+ _context3.next = 3;
13062
+ return this.readPreamble();
13063
+ case 3:
13064
+ hasPreamble = _context3.sent;
13065
+ if (hasPreamble) {
13066
+ _context3.next = 14;
13067
+ break;
13068
+ }
13069
+ // Handle raw dataset (no Part 10 preamble)
13070
+ // Encoding should already be detected in readPreamble()
13071
+ this.meta = {}; // No meta header for raw datasets
13072
+ _listener = (options === null || options === void 0 ? void 0 : options.listener) || new DicomMetadataListener();
13073
+ if (_listener.information) {
13074
+ information = _listener.information;
13075
+ information.transferSyntaxUid = this.syntax;
13076
+ }
13077
+ this.dict || (this.dict = {});
13078
+ _listener.startObject(this.dict);
13079
+ _context3.next = 12;
13080
+ return this.read(_listener, options);
13081
+ case 12:
13082
+ this.dict = _context3.sent;
13083
+ return _context3.abrupt("return", this);
13084
+ case 14:
13085
+ _context3.next = 16;
13086
+ return this.readMeta(options);
13087
+ case 16:
13088
+ this.meta = _context3.sent;
13089
+ listener = (options === null || options === void 0 ? void 0 : options.listener) || new DicomMetadataListener();
13090
+ if (listener.information) {
13091
+ _information = listener.information;
13092
+ _information.transferSyntaxUid = this.syntax;
13093
+ _information.sopInstanceUid = (_this$meta = this.meta) === null || _this$meta === void 0 || (_this$meta = _this$meta[TagHex.MediaStoreSOPInstanceUID]) === null || _this$meta === void 0 ? void 0 : _this$meta.Value[0];
13094
+ }
13095
+ this.dict || (this.dict = {});
13096
+ listener.startObject(this.dict);
13097
+ _context3.next = 23;
13098
+ return this.read(listener, options);
13099
+ case 23:
13100
+ this.dict = _context3.sent;
13101
+ return _context3.abrupt("return", this);
13102
+ case 25:
13103
+ case "end":
13104
+ return _context3.stop();
13105
+ }
13106
+ }, _callee3, this);
13107
+ }));
13108
+ function readFile() {
13109
+ return _readFile.apply(this, arguments);
13110
+ }
13111
+ return readFile;
13112
+ }()
13113
+ /**
13114
+ * Reads the file meta information.
13115
+ *
13116
+ * @param options.maxSizeMeta - maximum number of bytes for reading the meta
13117
+ * header when it isn't in a group length section.
13118
+ * @param options.ignoreErrors - allow reading past some errors.
13119
+ * In this case, reading a meta section not inside a group length.
13120
+ */
13121
+ }, {
13122
+ key: "readMeta",
13123
+ value: (function () {
13124
+ var _readMeta = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee4() {
13125
+ var options,
13126
+ stream,
13127
+ metaStartPos,
13128
+ el,
13129
+ metaLength,
13130
+ metaStream,
13131
+ _args4 = arguments;
13132
+ return _regeneratorRuntime().wrap(function _callee4$(_context4) {
13133
+ while (1) switch (_context4.prev = _context4.next) {
13134
+ case 0:
13135
+ options = _args4.length > 0 && _args4[0] !== undefined ? _args4[0] : undefined;
13136
+ stream = this.stream;
13137
+ _context4.next = 4;
13138
+ return stream.ensureAvailable();
13139
+ case 4:
13140
+ metaStartPos = stream.offset;
13141
+ el = this.readTagHeader();
13142
+ if (!(el.tag !== TagHex.FileMetaInformationGroupLength)) {
13143
+ _context4.next = 15;
13144
+ break;
13145
+ }
13146
+ if (options !== null && options !== void 0 && options.ignoreErrors) {
13147
+ _context4.next = 9;
13148
+ break;
13149
+ }
13150
+ throw new Error("Invalid DICOM file, meta length tag is malformed or not present.");
13151
+ case 9:
13152
+ // reset stream to the position where we started reading tags
13153
+ stream.offset = metaStartPos;
13154
+ // Wait for at least 10k to be available to make sure there is enough
13155
+ // data to read the entire Meta header
13156
+ _context4.next = 12;
13157
+ return stream.ensureAvailable((options === null || options === void 0 ? void 0 : options.maxSizeMeta) || 1024 * 10);
13158
+ case 12:
13159
+ // read meta header elements sequentially
13160
+ this.meta = DicomMessage._read(stream, EXPLICIT_LITTLE_ENDIAN$1, {
13161
+ untilTag: "00030000",
13162
+ stopOnGreaterTag: true,
13163
+ ignoreErrors: true
13164
+ });
13165
+ _context4.next = 20;
13166
+ break;
13167
+ case 15:
13168
+ // meta length tag is present
13169
+ metaLength = el.vrObj.readBytes(stream);
13170
+ _context4.next = 18;
13171
+ return stream.ensureAvailable(metaLength);
13172
+ case 18:
13173
+ // read header buffer using the specified meta length
13174
+ metaStream = stream.more(metaLength);
13175
+ this.meta = DicomMessage._read(metaStream, EXPLICIT_LITTLE_ENDIAN$1, {});
13176
+ case 20:
13177
+ this.syntax = this.meta[TagHex.TransferSyntaxUID].Value[0];
13178
+ return _context4.abrupt("return", this.meta);
13179
+ case 22:
13180
+ case "end":
13181
+ return _context4.stop();
13182
+ }
13183
+ }, _callee4, this);
13184
+ }));
13185
+ function readMeta() {
13186
+ return _readMeta.apply(this, arguments);
13187
+ }
13188
+ return readMeta;
13189
+ }())
13190
+ }, {
13191
+ key: "read",
13192
+ value: function () {
13193
+ var _read = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee5(listener, options) {
13194
+ var untilOffset, stream, tagInfo, tag, tagObj, length, addTagResult;
13195
+ return _regeneratorRuntime().wrap(function _callee5$(_context5) {
13196
+ while (1) switch (_context5.prev = _context5.next) {
13197
+ case 0:
13198
+ untilOffset = (options === null || options === void 0 ? void 0 : options.untilOffset) || Number.MAX_SAFE_INTEGER;
13199
+ this.listener = listener;
13200
+ stream = this.stream;
13201
+ _context5.next = 5;
13202
+ return stream.ensureAvailable();
13203
+ case 5:
13204
+ if (!(stream.offset < untilOffset && stream.isAvailable(1, false))) {
13205
+ _context5.next = 43;
13206
+ break;
13207
+ }
13208
+ // Consume before reading the tag so that data before the
13209
+ // current tag can be cleared.
13210
+ stream.consume();
13211
+ tagInfo = this.readTagHeader(options);
13212
+ tag = tagInfo.tag, tagObj = tagInfo.tagObj, length = tagInfo.length;
13213
+ if (!(tag === TagHex.ItemDelimitationEnd)) {
13214
+ _context5.next = 11;
13215
+ break;
13216
+ }
13217
+ return _context5.abrupt("return", listener.pop());
13218
+ case 11:
13219
+ if (!tagObj.isInstruction()) {
13220
+ _context5.next = 13;
13221
+ break;
13222
+ }
13223
+ return _context5.abrupt("continue", 5);
13224
+ case 13:
13225
+ if (!(tagObj.group() === 0 || tag === TagHex.DataSetTrailingPadding)) {
13226
+ _context5.next = 16;
13227
+ break;
13228
+ }
13229
+ // Group length
13230
+ stream.increment(tagObj.length);
13231
+ return _context5.abrupt("continue", 5);
13232
+ case 16:
13233
+ addTagResult = listener.addTag(tag, tagInfo);
13234
+ if (!this.isSequence(tagInfo)) {
13235
+ _context5.next = 22;
13236
+ break;
13237
+ }
13238
+ _context5.next = 20;
13239
+ return this.readSequence(listener, tagInfo, options);
13240
+ case 20:
13241
+ _context5.next = 38;
13242
+ break;
13243
+ case 22:
13244
+ if (!tagObj.isPixelDataTag()) {
13245
+ _context5.next = 27;
13246
+ break;
13247
+ }
13248
+ _context5.next = 25;
13249
+ return this.readPixelData(tagInfo);
13250
+ case 25:
13251
+ _context5.next = 38;
13252
+ break;
13253
+ case 27:
13254
+ if (!(length === UNDEFINED_LENGTH_FIX)) {
13255
+ _context5.next = 31;
13256
+ break;
13257
+ }
13258
+ throw new Error("Can't handle tag ".concat(tagInfo.tag, " with -1 length and not sequence"));
13259
+ case 31:
13260
+ if (!((addTagResult === null || addTagResult === void 0 ? void 0 : addTagResult.expectsRaw) === true && length > 0)) {
13261
+ _context5.next = 36;
13262
+ break;
13263
+ }
13264
+ _context5.next = 34;
13265
+ return this.readRawBinary(tagInfo);
13266
+ case 34:
13267
+ _context5.next = 38;
13268
+ break;
13269
+ case 36:
13270
+ _context5.next = 38;
13271
+ return this.readSingle(tagInfo, listener, options);
13272
+ case 38:
13273
+ listener.pop();
13274
+ _context5.next = 41;
13275
+ return this.stream.ensureAvailable();
13276
+ case 41:
13277
+ _context5.next = 5;
13278
+ break;
13279
+ case 43:
13280
+ return _context5.abrupt("return", listener.pop());
13281
+ case 44:
13282
+ case "end":
13283
+ return _context5.stop();
13284
+ }
13285
+ }, _callee5, this);
13286
+ }));
13287
+ function read(_x, _x2) {
13288
+ return _read.apply(this, arguments);
13289
+ }
13290
+ return read;
13291
+ }()
13292
+ }, {
13293
+ key: "readSequence",
13294
+ value: function () {
13295
+ var _readSequence = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee6(listener, sqTagInfo, options) {
13296
+ var length, stream, syntax, endOffset, tagInfo, tag;
13297
+ return _regeneratorRuntime().wrap(function _callee6$(_context6) {
13298
+ while (1) switch (_context6.prev = _context6.next) {
13299
+ case 0:
13300
+ length = sqTagInfo.length;
13301
+ stream = this.stream, syntax = this.syntax;
13302
+ endOffset = length === UNDEFINED_LENGTH_FIX ? Number.MAX_SAFE_INTEGER : stream.offset + length;
13303
+ case 3:
13304
+ _context6.t0 = stream.offset < endOffset;
13305
+ if (!_context6.t0) {
13306
+ _context6.next = 8;
13307
+ break;
13308
+ }
13309
+ _context6.next = 7;
13310
+ return stream.ensureAvailable();
13311
+ case 7:
13312
+ _context6.t0 = _context6.sent;
13313
+ case 8:
13314
+ if (!_context6.t0) {
13315
+ _context6.next = 25;
13316
+ break;
13317
+ }
13318
+ tagInfo = this.readTagHeader(syntax, options);
13319
+ tag = tagInfo.tag;
13320
+ if (!(tag === TagHex.Item)) {
13321
+ _context6.next = 17;
13322
+ break;
13323
+ }
13324
+ listener.startObject();
13325
+ _context6.next = 15;
13326
+ return this.read(listener, _objectSpread2(_objectSpread2({}, options), {}, {
13327
+ untilOffset: endOffset
13328
+ }));
13329
+ case 15:
13330
+ _context6.next = 23;
13331
+ break;
13332
+ case 17:
13333
+ if (!(tag === TagHex.SequenceDelimitationEnd)) {
13334
+ _context6.next = 21;
13335
+ break;
13336
+ }
13337
+ return _context6.abrupt("return");
13338
+ case 21:
13339
+ console.warn("Unknown tag info", length, tagInfo);
13340
+ throw new Error();
13341
+ case 23:
13342
+ _context6.next = 3;
13343
+ break;
13344
+ case 25:
13345
+ case "end":
13346
+ return _context6.stop();
13347
+ }
13348
+ }, _callee6, this);
13349
+ }));
13350
+ function readSequence(_x3, _x4, _x5) {
13351
+ return _readSequence.apply(this, arguments);
13352
+ }
13353
+ return readSequence;
13354
+ }()
13355
+ }, {
13356
+ key: "readPixelData",
13357
+ value: function readPixelData(tagInfo) {
13358
+ if (tagInfo.length === -1) {
13359
+ return this.readCompressed(tagInfo);
13360
+ }
13361
+ return this.readUncompressed(tagInfo);
13362
+ }
13363
+
13364
+ /**
13365
+ * Emits one or more listener.value() calls for a fragment, splitting if needed.
13366
+ * This does NOT start/pop any array contexts; callers control the surrounding structure.
13367
+ */
13368
+ }, {
13369
+ key: "_emitSplitValues",
13370
+ value: (function () {
13371
+ var _emitSplitValues2 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee7(length) {
13372
+ var stream, listener, maxFragmentSize, offset, chunkSize, buffer;
13373
+ return _regeneratorRuntime().wrap(function _callee7$(_context7) {
13374
+ while (1) switch (_context7.prev = _context7.next) {
13375
+ case 0:
13376
+ stream = this.stream, listener = this.listener;
13377
+ maxFragmentSize = this.maxFragmentSize;
13378
+ offset = 0;
13379
+ case 3:
13380
+ if (!(offset < length)) {
13381
+ _context7.next = 13;
13382
+ break;
13383
+ }
13384
+ chunkSize = Math.min(maxFragmentSize, length - offset);
13385
+ _context7.next = 7;
13386
+ return stream.ensureAvailable(chunkSize);
13387
+ case 7:
13388
+ buffer = stream.readArrayBuffer(chunkSize);
13389
+ listener.value(buffer);
13390
+ offset += chunkSize;
13391
+ stream.consume();
13392
+ _context7.next = 3;
13393
+ break;
13394
+ case 13:
13395
+ case "end":
13396
+ return _context7.stop();
13397
+ }
13398
+ }, _callee7, this);
13399
+ }));
13400
+ function _emitSplitValues(_x6) {
13401
+ return _emitSplitValues2.apply(this, arguments);
13402
+ }
13403
+ return _emitSplitValues;
13404
+ }()
13405
+ /**
13406
+ * Reads compressed streams.
13407
+ */
13408
+ )
13409
+ }, {
13410
+ key: "readCompressed",
13411
+ value: (function () {
13412
+ var _readCompressed = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee8(_tagInfo) {
13413
+ var _listener$information;
13414
+ var stream, listener, transferSyntaxUid, isVideo, numberOfFrames, isSingleFrame, offsets, singleArray, startOffset, frameNumber, lastFrame, frameTag, length;
13415
+ return _regeneratorRuntime().wrap(function _callee8$(_context8) {
13416
+ while (1) switch (_context8.prev = _context8.next) {
13417
+ case 0:
13418
+ stream = this.stream, listener = this.listener;
13419
+ transferSyntaxUid = this.syntax;
13420
+ _context8.next = 4;
13421
+ return stream.ensureAvailable();
13422
+ case 4:
13423
+ // Check if this is a video transfer syntax
13424
+ isVideo = isVideoTransferSyntax(transferSyntaxUid); // Check number of frames - only use video logic for single frame (or undefined)
13425
+ numberOfFrames = (_listener$information = listener.information) === null || _listener$information === void 0 ? void 0 : _listener$information.numberOfFrames;
13426
+ isSingleFrame = !numberOfFrames || parseInt(numberOfFrames) <= 1;
13427
+ _context8.next = 9;
13428
+ return this.readOffsets();
13429
+ case 9:
13430
+ offsets = _context8.sent;
13431
+ singleArray = isVideo || isSingleFrame;
13432
+ if (singleArray && !offsets) {
13433
+ offsets = [0];
13434
+ }
13435
+ if (offsets) {
13436
+ // Last frame ends when the sequence ends, so merge the frame data
13437
+ offsets.push(Number.MAX_SAFE_INTEGER / 2);
13438
+ }
13439
+ startOffset = stream.offset;
13440
+ frameNumber = 0;
13441
+ lastFrame = null;
13442
+ case 16:
13443
+ stream.consume();
13444
+ _context8.next = 20;
13445
+ return stream.ensureAvailable();
13446
+ case 20:
13447
+ frameTag = this.readTagHeader();
13448
+ if (!(frameTag.tag === TagHex.SequenceDelimitationEnd)) {
13449
+ _context8.next = 24;
13450
+ break;
13451
+ }
13452
+ if (lastFrame) {
13453
+ // Always deliver frames as arrays, using streaming splitFrame
13454
+ listener.pop();
13455
+ }
13456
+ return _context8.abrupt("return");
13457
+ case 24:
13458
+ if (!(frameTag.tag !== TagHex.Item)) {
13459
+ _context8.next = 26;
13460
+ break;
13461
+ }
13462
+ throw new Error("frame tag isn't item: ".concat(frameTag.tag));
13463
+ case 26:
13464
+ length = frameTag.length;
13465
+ if (!lastFrame) {
13466
+ lastFrame = [];
13467
+ listener.startObject(lastFrame);
13468
+ }
13469
+ _context8.next = 30;
13470
+ return this._emitSplitValues(length);
13471
+ case 30:
13472
+ if (!offsets || stream.offset >= offsets[frameNumber + 1] + startOffset) {
13473
+ lastFrame = null;
13474
+ listener.pop();
13475
+ frameNumber++;
13476
+ }
13477
+ _context8.next = 16;
13478
+ break;
13479
+ case 33:
13480
+ case "end":
13481
+ return _context8.stop();
13482
+ }
13483
+ }, _callee8, this);
13484
+ }));
13485
+ function readCompressed(_x7) {
13486
+ return _readCompressed.apply(this, arguments);
13487
+ }
13488
+ return readCompressed;
13489
+ }())
13490
+ }, {
13491
+ key: "readOffsets",
13492
+ value: function () {
13493
+ var _readOffsets = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee9() {
13494
+ var tagInfo, offsets, numOfFrames, i;
13495
+ return _regeneratorRuntime().wrap(function _callee9$(_context9) {
13496
+ while (1) switch (_context9.prev = _context9.next) {
13497
+ case 0:
13498
+ tagInfo = this.readTagHeader();
13499
+ if (!(tagInfo.tag !== TagHex.Item)) {
13500
+ _context9.next = 3;
13501
+ break;
13502
+ }
13503
+ throw new Error("Offsets tag is missing: ".concat(tagInfo.tag));
13504
+ case 3:
13505
+ if (!(tagInfo.length === 0)) {
13506
+ _context9.next = 5;
13507
+ break;
13508
+ }
13509
+ return _context9.abrupt("return");
13510
+ case 5:
13511
+ offsets = [];
13512
+ numOfFrames = tagInfo.length / 4;
13513
+ _context9.next = 9;
13514
+ return this.stream.ensureAvailable(tagInfo.length);
13515
+ case 9:
13516
+ for (i = 0; i < numOfFrames; i++) {
13517
+ offsets.push(this.stream.readUint32());
13518
+ }
13519
+ return _context9.abrupt("return", offsets);
13520
+ case 11:
13521
+ case "end":
13522
+ return _context9.stop();
13523
+ }
13524
+ }, _callee9, this);
13525
+ }));
13526
+ function readOffsets() {
13527
+ return _readOffsets.apply(this, arguments);
13528
+ }
13529
+ return readOffsets;
13530
+ }()
13531
+ /**
13532
+ * Reads uncompressed pixel data, delivering it to the listener streaming it.
13533
+ */
13534
+ }, {
13535
+ key: "readUncompressed",
13536
+ value: (function () {
13537
+ var _readUncompressed = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee0(tagInfo) {
13538
+ var _listener$information2;
13539
+ var length, listener, numberOfFrames, frameLength, _listener$information3, _listener$information4, _listener$information5, _listener$information6, rows, cols, samplesPerPixel, bitsAllocated, bitsPerFrame, frameNumber;
13540
+ return _regeneratorRuntime().wrap(function _callee0$(_context0) {
13541
+ while (1) switch (_context0.prev = _context0.next) {
13542
+ case 0:
13543
+ length = tagInfo.length;
13544
+ listener = this.listener;
13545
+ numberOfFrames = parseInt(((_listener$information2 = listener.information) === null || _listener$information2 === void 0 ? void 0 : _listener$information2.numberOfFrames) || 1);
13546
+ frameLength = length;
13547
+ if (!(numberOfFrames > 1)) {
13548
+ _context0.next = 17;
13549
+ break;
13550
+ }
13551
+ rows = (_listener$information3 = listener.information) === null || _listener$information3 === void 0 ? void 0 : _listener$information3.rows;
13552
+ cols = (_listener$information4 = listener.information) === null || _listener$information4 === void 0 ? void 0 : _listener$information4.columns;
13553
+ samplesPerPixel = (_listener$information5 = listener.information) === null || _listener$information5 === void 0 ? void 0 : _listener$information5.samplesPerPixel;
13554
+ bitsAllocated = (_listener$information6 = listener.information) === null || _listener$information6 === void 0 ? void 0 : _listener$information6.bitsAllocated;
13555
+ bitsPerFrame = rows * cols * samplesPerPixel * bitsAllocated;
13556
+ if (!(bitsPerFrame % 8 !== 0)) {
13557
+ _context0.next = 16;
13558
+ break;
13559
+ }
13560
+ if (!(bitsAllocated === 1)) {
13561
+ _context0.next = 15;
13562
+ break;
13563
+ }
13564
+ _context0.next = 14;
13565
+ return this.readUncompressedBitFrame(tagInfo);
13566
+ case 14:
13567
+ return _context0.abrupt("return", _context0.sent);
13568
+ case 15:
13569
+ throw new Error("Odd frame length must be single bit: ".concat(rows, ",").concat(cols, " ").concat(samplesPerPixel, " ").concat(bitsAllocated));
13570
+ case 16:
13571
+ frameLength = bitsPerFrame / 8;
13572
+ case 17:
13573
+ frameNumber = 0;
13574
+ case 18:
13575
+ if (!(frameNumber < numberOfFrames)) {
13576
+ _context0.next = 26;
13577
+ break;
13578
+ }
13579
+ listener.startObject([]);
13580
+ _context0.next = 22;
13581
+ return this._emitSplitValues(frameLength);
13582
+ case 22:
13583
+ listener.pop();
13584
+ // console.log(stream.getBufferMemoryInfo());
13585
+ case 23:
13586
+ frameNumber++;
13587
+ _context0.next = 18;
13588
+ break;
13589
+ case 26:
13590
+ case "end":
13591
+ return _context0.stop();
13592
+ }
13593
+ }, _callee0, this);
13594
+ }));
13595
+ function readUncompressed(_x8) {
13596
+ return _readUncompressed.apply(this, arguments);
13597
+ }
13598
+ return readUncompressed;
13599
+ }()
13600
+ /**
13601
+ * Reads uncompressed pixel data with support for odd frame lengths (in bits).
13602
+ * This method handles cases where frames are packed sequentially bit-by-bit,
13603
+ * without restarting at byte boundaries. Each frame is unpacked starting at byte 0.
13604
+ *
13605
+ * For odd-length bit frames:
13606
+ * - bitsAllocated is always 1 (single bit per pixel)
13607
+ * - rows * cols * samplesPerPixel is not a multiple of 8
13608
+ * - Frames are packed sequentially into bits without byte alignment
13609
+ * - Each frame is unpacked to start at byte 0
13610
+ *
13611
+ * @param {Object} tagInfo - Tag information containing length and other metadata
13612
+ */
13613
+ )
12178
13614
  }, {
12179
- key: "xyz2LAB",
12180
- value: function xyz2LAB(xyz) {
12181
- var whitePoint = Colors.d65WhitePointXYZ();
12182
- var X = xyz[0] / whitePoint[0];
12183
- var Y = xyz[1] / whitePoint[1];
12184
- var Z = xyz[2] / whitePoint[2];
12185
- X = Colors.labf(X);
12186
- Y = Colors.labf(Y);
12187
- Z = Colors.labf(Z);
12188
- return [116 * Y - 16, 500 * (X - Y), 200 * (Y - Z)];
12189
- }
13615
+ key: "readUncompressedBitFrame",
13616
+ value: (function () {
13617
+ var _readUncompressedBitFrame = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee1(tagInfo) {
13618
+ var _listener$information7, _listener$information8, _listener$information9, _listener$information0, _listener$information1;
13619
+ var length, listener, stream, numberOfFrames, rows, cols, samplesPerPixel, bitsAllocated, bitsPerFrame, bytesPerFrame, totalBits, totalBytes, allPixelData, frameNumber, bitOffset, frameBuffer, frameView, sourceView, bitIndex, globalBitIndex, sourceByteIndex, sourceBitIndex, targetByteIndex, targetBitIndex, sourceByte, bitValue;
13620
+ return _regeneratorRuntime().wrap(function _callee1$(_context1) {
13621
+ while (1) switch (_context1.prev = _context1.next) {
13622
+ case 0:
13623
+ length = tagInfo.length;
13624
+ listener = this.listener, stream = this.stream;
13625
+ numberOfFrames = parseInt(((_listener$information7 = listener.information) === null || _listener$information7 === void 0 ? void 0 : _listener$information7.numberOfFrames) || 1);
13626
+ rows = (_listener$information8 = listener.information) === null || _listener$information8 === void 0 ? void 0 : _listener$information8.rows;
13627
+ cols = (_listener$information9 = listener.information) === null || _listener$information9 === void 0 ? void 0 : _listener$information9.columns;
13628
+ samplesPerPixel = ((_listener$information0 = listener.information) === null || _listener$information0 === void 0 ? void 0 : _listener$information0.samplesPerPixel) || 1;
13629
+ bitsAllocated = (_listener$information1 = listener.information) === null || _listener$information1 === void 0 ? void 0 : _listener$information1.bitsAllocated;
13630
+ if (!(!rows || !cols || !bitsAllocated)) {
13631
+ _context1.next = 9;
13632
+ break;
13633
+ }
13634
+ throw new Error("Missing required pixel data information: rows, columns, or bitsAllocated");
13635
+ case 9:
13636
+ if (!(bitsAllocated !== 1)) {
13637
+ _context1.next = 11;
13638
+ break;
13639
+ }
13640
+ throw new Error("Odd-length bit frames require bitsAllocated=1, got ".concat(bitsAllocated));
13641
+ case 11:
13642
+ bitsPerFrame = rows * cols * samplesPerPixel * bitsAllocated;
13643
+ bytesPerFrame = Math.ceil(bitsPerFrame / 8);
13644
+ totalBits = bitsPerFrame * numberOfFrames;
13645
+ totalBytes = Math.ceil(totalBits / 8);
13646
+ if (!(totalBytes !== length)) {
13647
+ _context1.next = 17;
13648
+ break;
13649
+ }
13650
+ throw new Error("The calculated length ".concat(totalBytes, " does not match the actual length ").concat(length));
13651
+ case 17:
13652
+ _context1.next = 19;
13653
+ return stream.ensureAvailable(length);
13654
+ case 19:
13655
+ allPixelData = stream.readArrayBuffer(totalBytes);
13656
+ stream.consume();
13657
+
13658
+ // Extract each frame, unpacking bits so each frame starts at byte 0
13659
+ for (frameNumber = 0; frameNumber < numberOfFrames; frameNumber++) {
13660
+ listener.startObject([]);
13661
+
13662
+ // Calculate the bit offset for this frame in the packed data
13663
+ bitOffset = frameNumber * bitsPerFrame; // Create a buffer for this frame (starting at byte 0)
13664
+ frameBuffer = new ArrayBuffer(bytesPerFrame);
13665
+ frameView = new Uint8Array(frameBuffer);
13666
+ sourceView = new Uint8Array(allPixelData); // Extract bits for this frame
13667
+ for (bitIndex = 0; bitIndex < bitsPerFrame; bitIndex++) {
13668
+ globalBitIndex = bitOffset + bitIndex;
13669
+ sourceByteIndex = Math.floor(globalBitIndex / 8);
13670
+ sourceBitIndex = globalBitIndex % 8;
13671
+ targetByteIndex = Math.floor(bitIndex / 8);
13672
+ targetBitIndex = bitIndex % 8; // Read the bit from source
13673
+ sourceByte = sourceView[sourceByteIndex];
13674
+ bitValue = sourceByte >> 7 - sourceBitIndex & 1; // Write the bit to target (starting at byte 0)
13675
+ if (bitValue) {
13676
+ frameView[targetByteIndex] |= 1 << 7 - targetBitIndex;
13677
+ }
13678
+ }
13679
+
13680
+ // Deliver the frame buffer
13681
+ listener.value(frameBuffer);
13682
+ listener.pop();
13683
+ }
13684
+ case 22:
13685
+ case "end":
13686
+ return _context1.stop();
13687
+ }
13688
+ }, _callee1, this);
13689
+ }));
13690
+ function readUncompressedBitFrame(_x9) {
13691
+ return _readUncompressedBitFrame.apply(this, arguments);
13692
+ }
13693
+ return readUncompressedBitFrame;
13694
+ }()
13695
+ /**
13696
+ * Reads raw binary data in chunks and delivers it to the listener.
13697
+ * This method reads data in 64KB chunks to manage memory efficiently.
13698
+ */
13699
+ )
12190
13700
  }, {
12191
- key: "lab2RGB",
12192
- value: function lab2RGB(lab) {
12193
- return Colors.xyz2RGB(Colors.lab2XYZ(lab));
12194
- }
13701
+ key: "readRawBinary",
13702
+ value: (function () {
13703
+ var _readRawBinary = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee10(tagInfo) {
13704
+ var length;
13705
+ return _regeneratorRuntime().wrap(function _callee10$(_context10) {
13706
+ while (1) switch (_context10.prev = _context10.next) {
13707
+ case 0:
13708
+ length = tagInfo.length;
13709
+ this._emitSplitValues(length);
13710
+ case 2:
13711
+ case "end":
13712
+ return _context10.stop();
13713
+ }
13714
+ }, _callee10, this);
13715
+ }));
13716
+ function readRawBinary(_x0) {
13717
+ return _readRawBinary.apply(this, arguments);
13718
+ }
13719
+ return readRawBinary;
13720
+ }())
12195
13721
  }, {
12196
- key: "lab2XYZ",
12197
- value: function lab2XYZ(lab) {
12198
- var L = (lab[0] + 16) / 116;
12199
- var a = L + lab[1] / 500;
12200
- var b = L - lab[2] / 200;
12201
- var whitePoint = Colors.d65WhitePointXYZ();
12202
- return [whitePoint[0] * Colors.labfInv(a), whitePoint[1] * Colors.labfInv(L), whitePoint[2] * Colors.labfInv(b)];
13722
+ key: "isSequence",
13723
+ value: function isSequence(tagInfo) {
13724
+ var vr = tagInfo.vr,
13725
+ length = tagInfo.length;
13726
+ return vr === "SQ" || vr === "UN" && length === UNDEFINED_LENGTH_FIX;
12203
13727
  }
12204
- }, {
12205
- key: "xyz2RGB",
12206
- value: function xyz2RGB(xyz) {
12207
- var R1 = 3.2406 * xyz[0] - 1.5372 * xyz[1] - 0.4986 * xyz[2];
12208
- var G1 = -0.9689 * xyz[0] + 1.8758 * xyz[1] + 0.0415 * xyz[2];
12209
- var B1 = 0.0557 * xyz[0] - 0.204 * xyz[1] + 1.057 * xyz[2];
12210
-
12211
- /* Force nonnegative values so that gamma correction is well-defined. */
12212
- var minimumComponent = Math.min(R1, G1);
12213
- minimumComponent = Math.min(minimumComponent, B1);
12214
- if (minimumComponent < 0) {
12215
- R1 -= minimumComponent;
12216
- G1 -= minimumComponent;
12217
- B1 -= minimumComponent;
12218
- }
12219
13728
 
12220
- /* Transform from RGB to R'G'B' */
12221
- return [Colors.gammaCorrection(R1), Colors.gammaCorrection(G1), Colors.gammaCorrection(B1)];
12222
- }
13729
+ /**
13730
+ * Reads a tag header.
13731
+ */
12223
13732
  }, {
12224
- key: "labf",
12225
- value: function labf(n) {
12226
- if (n >= 8.85645167903563082e-3) {
12227
- return Math.pow(n, 0.333333333333333);
13733
+ key: "readTagHeader",
13734
+ value: function readTagHeader() {
13735
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
13736
+ untilTag: null,
13737
+ includeUntilTagValue: false
13738
+ };
13739
+ var stream = this.stream,
13740
+ syntax = this.syntax;
13741
+ var untilTag = options.untilTag,
13742
+ includeUntilTagValue = options.includeUntilTagValue;
13743
+ var implicit = syntax == IMPLICIT_LITTLE_ENDIAN;
13744
+ var isLittleEndian = syntax !== EXPLICIT_BIG_ENDIAN;
13745
+ stream.setEndian(isLittleEndian);
13746
+ var tagObj = Tag.readTag(stream);
13747
+ var tag = tagObj.cleanString;
13748
+ if (untilTag && untilTag === tag) {
13749
+ if (!includeUntilTagValue) {
13750
+ return {
13751
+ tag: tag,
13752
+ tagObj: tagObj,
13753
+ vr: 0,
13754
+ values: 0,
13755
+ untilTag: true
13756
+ };
13757
+ }
13758
+ }
13759
+ var length = null;
13760
+ var vr = null;
13761
+ var vrType;
13762
+ var isCommand = tagObj.group() === 0;
13763
+ if (tagObj.isInstruction()) {
13764
+ length = stream.readUint32();
13765
+ vr = ValueRepresentation.createByTypeString("UN");
13766
+ } else if (implicit && !isCommand) {
13767
+ length = stream.readUint32();
13768
+ var elementData = DicomMessage.lookupTag(tagObj);
13769
+ if (elementData) {
13770
+ vrType = elementData.vr;
13771
+ } else {
13772
+ //unknown tag
13773
+ if (length == UNDEFINED_LENGTH) {
13774
+ vrType = "SQ";
13775
+ } else if (tagObj.isPixelDataTag()) {
13776
+ vrType = "OW";
13777
+ } else if (vrType == "xs") {
13778
+ var _this$listener$inform;
13779
+ // This should work for any tag after PixelRepresentation,
13780
+ // which is all but 2 of the xs code values.
13781
+ var signed = ((_this$listener$inform = this.listener.information) === null || _this$listener$inform === void 0 ? void 0 : _this$listener$inform.pixelRepresentation) === 0;
13782
+ vrType = signed ? "SS" : "US";
13783
+ } else if (tagObj.isPrivateCreator()) {
13784
+ vrType = "LO";
13785
+ } else {
13786
+ vrType = "UN";
13787
+ }
13788
+ }
13789
+ vr = ValueRepresentation.createByTypeString(vrType);
12228
13790
  } else {
12229
- return 841.0 / 108.0 * n + 4.0 / 29.0;
13791
+ var _DicomMessage$lookupT;
13792
+ vrType = stream.readVR();
13793
+ if (vrType === "UN" && (_DicomMessage$lookupT = DicomMessage.lookupTag(tagObj)) !== null && _DicomMessage$lookupT !== void 0 && _DicomMessage$lookupT.vr) {
13794
+ vrType = DicomMessage.lookupTag(tagObj).vr;
13795
+ vr = ValueRepresentation.parseUnknownVr(vrType);
13796
+ } else {
13797
+ vr = ValueRepresentation.createByTypeString(vrType);
13798
+ }
13799
+ if (vr.isLength32()) {
13800
+ stream.increment(2);
13801
+ length = stream.readUint32();
13802
+ } else {
13803
+ length = stream.readUint16();
13804
+ }
12230
13805
  }
13806
+ var punctuatedTag = DicomMetaDictionary.punctuateTag(tag);
13807
+ var entry = DicomMetaDictionary.dictionary[punctuatedTag];
13808
+
13809
+ // Include both the string and object values for vr and tag
13810
+ var header = {
13811
+ vrObj: vr,
13812
+ vr: vr.type,
13813
+ tag: tag,
13814
+ tagObj: tagObj,
13815
+ vm: entry === null || entry === void 0 ? void 0 : entry.vm,
13816
+ name: entry === null || entry === void 0 ? void 0 : entry.name,
13817
+ length: length === UNDEFINED_LENGTH ? -1 : length
13818
+ };
13819
+ return header;
12231
13820
  }
13821
+
13822
+ /**
13823
+ * Reads a single tag values and delivers them to the listener.
13824
+ *
13825
+ * If the tag is specific character set, will assign the decoder to that
13826
+ * character set.
13827
+ */
12232
13828
  }, {
12233
- key: "labfInv",
12234
- value: function labfInv(n) {
12235
- if (n >= 0.206896551724137931) {
12236
- return n * n * n;
12237
- } else {
12238
- return 108.0 / 841.0 * (n - 4.0 / 29.0);
13829
+ key: "readSingle",
13830
+ value: (function () {
13831
+ var _readSingle = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee11(tagInfo, listener, options) {
13832
+ var length, stream, syntax, vr, values, times, i, _vr$read, value, _vr$read2, _value, delimiterChar, _values, _values2, coding;
13833
+ return _regeneratorRuntime().wrap(function _callee11$(_context11) {
13834
+ while (1) switch (_context11.prev = _context11.next) {
13835
+ case 0:
13836
+ length = tagInfo.length;
13837
+ stream = this.stream, syntax = this.syntax;
13838
+ _context11.next = 4;
13839
+ return this.stream.ensureAvailable(length);
13840
+ case 4:
13841
+ vr = ValueRepresentation.createByTypeString(tagInfo.vr);
13842
+ values = [];
13843
+ if (vr.isBinary() && length > vr.maxLength && !vr.noMultiple) {
13844
+ times = length / vr.maxLength;
13845
+ i = 0;
13846
+ while (i++ < times) {
13847
+ _vr$read = vr.read(stream, vr.maxLength, syntax), value = _vr$read.value;
13848
+ values.push(value);
13849
+ }
13850
+ } else {
13851
+ _value = (_vr$read2 = vr.read(stream, length, syntax)) === null || _vr$read2 === void 0 ? void 0 : _vr$read2.value;
13852
+ if (!vr.isBinary() && singleVRs.indexOf(vr.type) == -1) {
13853
+ values = _value;
13854
+ if (typeof _value === "string") {
13855
+ delimiterChar = String.fromCharCode(VM_DELIMITER);
13856
+ values = vr.dropPadByte(_value.split(delimiterChar));
13857
+ }
13858
+ } else if (vr.type == "OW" || vr.type == "OB") {
13859
+ values = _value;
13860
+ } else {
13861
+ Array.isArray(_value) ? values = _value : values.push(_value);
13862
+ }
13863
+ }
13864
+ if (!(tagInfo.tag === TagHex.SpecificCharacterSet)) {
13865
+ _context11.next = 21;
13866
+ break;
13867
+ }
13868
+ if (!(values.length > 0)) {
13869
+ _context11.next = 21;
13870
+ break;
13871
+ }
13872
+ _values = values, _values2 = _slicedToArray(_values, 1), coding = _values2[0];
13873
+ coding = coding.replace(/[_ ]/g, "-").toLowerCase();
13874
+ if (!(coding in encodingMapping)) {
13875
+ _context11.next = 16;
13876
+ break;
13877
+ }
13878
+ coding = encodingMapping[coding];
13879
+ this.stream.setDecoder(new TextDecoder(coding));
13880
+ _context11.next = 21;
13881
+ break;
13882
+ case 16:
13883
+ if (!(options !== null && options !== void 0 && options.ignoreErrors)) {
13884
+ _context11.next = 20;
13885
+ break;
13886
+ }
13887
+ log.warn("Unsupported character set: ".concat(coding, ", using default character set"));
13888
+ _context11.next = 21;
13889
+ break;
13890
+ case 20:
13891
+ throw Error("Unsupported character set: ".concat(coding));
13892
+ case 21:
13893
+ values.forEach(function (value) {
13894
+ return listener.value(value);
13895
+ });
13896
+ return _context11.abrupt("return", values);
13897
+ case 23:
13898
+ case "end":
13899
+ return _context11.stop();
13900
+ }
13901
+ }, _callee11, this);
13902
+ }));
13903
+ function readSingle(_x1, _x10, _x11) {
13904
+ return _readSingle.apply(this, arguments);
12239
13905
  }
12240
- }
13906
+ return readSingle;
13907
+ }())
12241
13908
  }]);
12242
- return Colors;
13909
+ return AsyncDicomReader;
12243
13910
  }();
12244
13911
 
12245
13912
  function datasetToDict(dataset) {
@@ -22629,106 +24296,86 @@ var message = {
22629
24296
  };
22630
24297
 
22631
24298
  /**
22632
- * A DICOM Metadata listener implements the basic listener for creating a dicom
22633
- * metadata instance from a stream of notification events.
24299
+ * A filter for DicomMetadataListener that converts ArrayBuffer[] child values
24300
+ * into expanded listener.startObject([]) and value(fragment) calls.
22634
24301
  *
22635
- * There are additional listeners defined in @cornerstonejs/metadata as well as
22636
- * other event sources in that package.
24302
+ * This is useful when you have compressed or fragmented pixel data delivered
24303
+ * as an array of ArrayBuffers and want to process each fragment individually
24304
+ * through the standard listener sequence pattern.
22637
24305
  *
22638
- * **WARNING** This class is still under development, do not count on the API
22639
- * not changing a bit over the next few dcmjs releases.
24306
+ * @example
24307
+ * // Use as a filter in DicomMetadataListener constructor
24308
+ * const listener = new DicomMetadataListener(ArrayBufferExpanderFilter);
24309
+ * await reader.readFile({ listener });
24310
+ *
24311
+ * @example
24312
+ * // Combine with other filters
24313
+ * const listener = new DicomMetadataListener(
24314
+ * ArrayBufferExpanderFilter,
24315
+ * myCustomFilter
24316
+ * );
22640
24317
  */
22641
- var DicomMetadataListener = /*#__PURE__*/function () {
22642
- function DicomMetadataListener() {
22643
- _classCallCheck(this, DicomMetadataListener);
22644
- _defineProperty(this, "current", null);
22645
- _defineProperty(this, "fmi", null);
22646
- _defineProperty(this, "dict", null);
22647
- }
22648
- _createClass(DicomMetadataListener, [{
22649
- key: "addTag",
22650
- value:
22651
- /**
22652
- * Adds a new tag value
22653
- */
22654
- function addTag(tag, tagInfo) {
22655
- var dest = {
22656
- vr: tagInfo === null || tagInfo === void 0 ? void 0 : tagInfo.vr,
22657
- Value: null
22658
- };
22659
- if (this.current.dest) {
22660
- this.current.dest[tag] = dest;
22661
- }
22662
- this.current = {
22663
- parent: this.current,
22664
- dest: dest,
22665
- type: tag
22666
- };
22667
- }
22668
-
22669
- /**
22670
- * Starts a new object, using the provided value
22671
- */
22672
- }, {
22673
- key: "startObject",
22674
- value: function startObject() {
22675
- var dest = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
22676
- this.current = {
22677
- parent: this.current,
22678
- dest: dest,
22679
- type: "object"
22680
- };
22681
- }
22682
-
22683
- /**
22684
- * Starts a new array, using the provided value
22685
- */
22686
- }, {
22687
- key: "startArray",
22688
- value: function startArray() {
22689
- var dest = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
22690
- this.current = {
22691
- parent: this.current,
22692
- dest: dest,
22693
- type: "array"
22694
- };
22695
- }
22696
-
22697
- /**
22698
- * Pops the current value being created off the stack.
22699
- */
22700
- }, {
22701
- key: "pop",
22702
- value: function pop() {
22703
- var _this$current$pop, _this$current$pop2, _this$current;
22704
- var result = (_this$current$pop = (_this$current$pop2 = (_this$current = this.current).pop) === null || _this$current$pop2 === void 0 ? void 0 : _this$current$pop2.call(_this$current)) !== null && _this$current$pop !== void 0 ? _this$current$pop : this.current.dest;
22705
- this.current = this.current.parent;
22706
- return result;
22707
- }
24318
+ var ArrayBufferExpanderFilter = {
24319
+ /**
24320
+ * Filter method that intercepts value calls to expand ArrayBuffer[] into
24321
+ * individual fragments.
24322
+ *
24323
+ * When the value is an array where every element is an ArrayBuffer:
24324
+ * 1. Saves the current tag context
24325
+ * 2. Calls this.startObject([]) to create a new array context
24326
+ * 3. Calls this.value(fragment) for each ArrayBuffer in the array
24327
+ * 4. Calls this.pop() to get the resulting array
24328
+ * 5. Assigns that array to the tag's Value field
24329
+ *
24330
+ * This is necessary because when AsyncDicomReader delivers ArrayBuffer[]
24331
+ * directly to value(), it does not call startObject/pop around them, so we
24332
+ * must make those calls to properly represent the array structure.
24333
+ *
24334
+ * Otherwise, it passes the value through unchanged to the next filter.
24335
+ *
24336
+ * @param {Function} next - The next function in the filter chain
24337
+ * @param {*} v - The value to process
24338
+ */
24339
+ value: function value(next, v) {
24340
+ // Check if the value is an array of ArrayBuffers
24341
+ if (Array.isArray(v) && v.length > 0 && v.every(function (item) {
24342
+ return item instanceof ArrayBuffer || ArrayBuffer.isView(item);
24343
+ })) {
24344
+ // Expand the ArrayBuffer[] into the proper sequence
24345
+ // The reader hasn't called startObject for this, so we must
24346
+
24347
+ // Save the current tag context
24348
+ var tagContext = this.current;
24349
+
24350
+ // Call startObject with an array to create a new array context
24351
+ this.startObject([]);
24352
+
24353
+ // Add each fragment
24354
+ var _iterator = _createForOfIteratorHelper(v),
24355
+ _step;
24356
+ try {
24357
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
24358
+ var fragment = _step.value;
24359
+ this.value(fragment);
24360
+ }
22708
24361
 
22709
- /**
22710
- * Registers a new value for the current destination being created
22711
- */
22712
- }, {
22713
- key: "value",
22714
- value: function value(v) {
22715
- var _this$current$dest;
22716
- (_this$current$dest = this.current.dest).Value || (_this$current$dest.Value = []);
22717
- this.current.dest.Value.push(v);
22718
- }
24362
+ // Pop to get the resulting array structure
24363
+ } catch (err) {
24364
+ _iterator.e(err);
24365
+ } finally {
24366
+ _iterator.f();
24367
+ }
24368
+ var arrayResult = this.pop();
22719
24369
 
22720
- /**
22721
- * Reads the Value[0] instance of the given tag for pixel data parsing.
22722
- */
22723
- }, {
22724
- key: "getValue",
22725
- value: function getValue(tag) {
22726
- var _this$current$parent$;
22727
- return (_this$current$parent$ = this.current.parent[tag]) === null || _this$current$parent$ === void 0 || (_this$current$parent$ = _this$current$parent$.Value) === null || _this$current$parent$ === void 0 ? void 0 : _this$current$parent$[0];
24370
+ // Now we're back in the tag context - assign the array to Value
24371
+ // The arrayResult will have a Value property with the fragments
24372
+ tagContext.dest.Value = arrayResult.Value || arrayResult;
24373
+ } else {
24374
+ // Pass through non-ArrayBuffer[] values unchanged
24375
+ next(v);
22728
24376
  }
22729
- }]);
22730
- return DicomMetadataListener;
22731
- }();
24377
+ }
24378
+ };
22732
24379
 
22733
24380
  var utilities = {
22734
24381
  TID1500: TID1500,
@@ -22738,7 +24385,11 @@ var utilities = {
22738
24385
  orientation: orientation,
22739
24386
  compression: compression,
22740
24387
  dicomJson: dicomJson,
22741
- DicomMetadataListener: DicomMetadataListener
24388
+ DicomMetadataListener: DicomMetadataListener,
24389
+ ArrayBufferExpanderFilter: ArrayBufferExpanderFilter,
24390
+ toFloat: toFloat,
24391
+ toInt: toInt,
24392
+ createInformationFilter: createInformationFilter
22742
24393
  };
22743
24394
 
22744
24395
  var _value = Symbol("value");
@@ -25382,6 +27033,9 @@ var data = {
25382
27033
  datasetToBuffer: datasetToBuffer,
25383
27034
  datasetToBlob: datasetToBlob
25384
27035
  };
27036
+ var async = {
27037
+ AsyncDicomReader: AsyncDicomReader
27038
+ };
25385
27039
  var derivations = {
25386
27040
  DerivedDataset: DerivedDataset,
25387
27041
  DerivedPixels: DerivedPixels,
@@ -25408,18 +27062,20 @@ var anonymizer = {
25408
27062
  var dcmjs = {
25409
27063
  DICOMWEB: DICOMWEB,
25410
27064
  adapters: adapters,
27065
+ constants: constants,
25411
27066
  data: data,
25412
27067
  derivations: derivations,
25413
27068
  normalizers: normalizers,
25414
27069
  sr: sr,
25415
27070
  utilities: utilities,
25416
27071
  log: log,
25417
- anonymizer: anonymizer
27072
+ anonymizer: anonymizer,
27073
+ async: async
25418
27074
  };
25419
27075
  DicomDict.setDicomMessageClass(DicomMessage);
25420
27076
  ValueRepresentation.setDicomMessageClass(DicomMessage);
25421
27077
  ValueRepresentation.setTagClass(Tag);
25422
27078
  Tag.setDicomMessageClass(DicomMessage);
25423
27079
 
25424
- export { DICOMWEB, adapters, anonymizer, data, dcmjs as default, derivations, log, normalizers, sr, utilities };
27080
+ export { DICOMWEB, adapters, anonymizer, async, constants, data, dcmjs as default, derivations, log, normalizers, sr, utilities };
25425
27081
  //# sourceMappingURL=dcmjs.es.js.map