ag-psd 32.0.0 → 33.0.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.
Files changed (103) hide show
  1. package/.v8-cache/v24.7.0-x64-ef5a0af0/03e19709 +0 -0
  2. package/.v8-cache/v24.7.0-x64-ef5a0af0/05135617 +0 -0
  3. package/.v8-cache/v24.7.0-x64-ef5a0af0/1130885f +0 -0
  4. package/.v8-cache/v24.7.0-x64-ef5a0af0/226e2801 +0 -0
  5. package/.v8-cache/v24.7.0-x64-ef5a0af0/2a19133e +0 -0
  6. package/.v8-cache/v24.7.0-x64-ef5a0af0/4511bacf +0 -0
  7. package/.v8-cache/v24.7.0-x64-ef5a0af0/68416cb2 +0 -0
  8. package/.v8-cache/v24.7.0-x64-ef5a0af0/71df9cb3 +0 -0
  9. package/.v8-cache/v24.7.0-x64-ef5a0af0/7ea06cea +0 -0
  10. package/.v8-cache/v24.7.0-x64-ef5a0af0/932f0770 +0 -0
  11. package/.v8-cache/v24.7.0-x64-ef5a0af0/acc36e66 +0 -0
  12. package/.v8-cache/v24.7.0-x64-ef5a0af0/b3c2fab7 +0 -0
  13. package/.v8-cache/v24.7.0-x64-ef5a0af0/c314aece +0 -0
  14. package/.v8-cache/v24.7.0-x64-ef5a0af0/cfc49f4f +0 -0
  15. package/.v8-cache/v24.7.0-x64-ef5a0af0/cff9434a +0 -0
  16. package/.v8-cache/v24.7.0-x64-ef5a0af0/dc86f000 +0 -0
  17. package/.v8-cache/v24.7.0-x64-ef5a0af0/e03e2acd +0 -0
  18. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6745c2b +0 -0
  19. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6b99945 +0 -0
  20. package/.v8-cache/v24.7.0-x64-ef5a0af0/eeeebdfe +0 -0
  21. package/.v8-cache/v24.7.0-x64-ef5a0af0/f6fd8125 +0 -0
  22. package/CHANGELOG.md +78 -0
  23. package/LICENSE +5 -0
  24. package/README.md +3 -6
  25. package/README_PSD.md +242 -81
  26. package/dist/abr.d.ts +11 -4
  27. package/dist/abr.js +357 -230
  28. package/dist/abr.js.map +1 -1
  29. package/dist/additionalInfo.d.ts +54 -2
  30. package/dist/additionalInfo.js +487 -306
  31. package/dist/additionalInfo.js.map +1 -1
  32. package/dist/ase.d.ts +6 -1
  33. package/dist/ase.js +61 -61
  34. package/dist/ase.js.map +1 -1
  35. package/dist/bundle.js +1937 -1115
  36. package/dist/descriptor.d.ts +14 -8
  37. package/dist/descriptor.js +202 -186
  38. package/dist/descriptor.js.map +1 -1
  39. package/dist/helpers.d.ts +1 -1
  40. package/dist/helpers.js +4 -2
  41. package/dist/helpers.js.map +1 -1
  42. package/dist/imageResources.d.ts +15 -3
  43. package/dist/imageResources.js +503 -142
  44. package/dist/imageResources.js.map +1 -1
  45. package/dist/jpeg.js +8 -8
  46. package/dist/jpeg.js.map +1 -1
  47. package/dist/psd.d.ts +148 -17
  48. package/dist/psd.js.map +1 -1
  49. package/dist/psdReader.d.ts +10 -0
  50. package/dist/psdReader.js +278 -166
  51. package/dist/psdReader.js.map +1 -1
  52. package/dist/psdWriter.d.ts +1 -0
  53. package/dist/psdWriter.js +77 -62
  54. package/dist/psdWriter.js.map +1 -1
  55. package/dist/text.js +17 -6
  56. package/dist/text.js.map +1 -1
  57. package/dist/utf8.js +0 -3
  58. package/dist/utf8.js.map +1 -1
  59. package/dist-es/abr.d.ts +11 -4
  60. package/dist-es/abr.js +357 -230
  61. package/dist-es/abr.js.map +1 -1
  62. package/dist-es/additionalInfo.d.ts +54 -2
  63. package/dist-es/additionalInfo.js +487 -306
  64. package/dist-es/additionalInfo.js.map +1 -1
  65. package/dist-es/ase.d.ts +6 -1
  66. package/dist-es/ase.js +61 -61
  67. package/dist-es/ase.js.map +1 -1
  68. package/dist-es/descriptor.d.ts +14 -8
  69. package/dist-es/descriptor.js +202 -186
  70. package/dist-es/descriptor.js.map +1 -1
  71. package/dist-es/helpers.d.ts +1 -1
  72. package/dist-es/helpers.js +4 -2
  73. package/dist-es/helpers.js.map +1 -1
  74. package/dist-es/imageResources.d.ts +15 -3
  75. package/dist-es/imageResources.js +503 -142
  76. package/dist-es/imageResources.js.map +1 -1
  77. package/dist-es/jpeg.js +8 -8
  78. package/dist-es/jpeg.js.map +1 -1
  79. package/dist-es/psd.d.ts +148 -17
  80. package/dist-es/psd.js.map +1 -1
  81. package/dist-es/psdReader.d.ts +10 -0
  82. package/dist-es/psdReader.js +278 -166
  83. package/dist-es/psdReader.js.map +1 -1
  84. package/dist-es/psdWriter.d.ts +1 -0
  85. package/dist-es/psdWriter.js +77 -62
  86. package/dist-es/psdWriter.js.map +1 -1
  87. package/dist-es/text.js +17 -6
  88. package/dist-es/text.js.map +1 -1
  89. package/dist-es/utf8.js +0 -3
  90. package/dist-es/utf8.js.map +1 -1
  91. package/package.json +2 -2
  92. package/src/abr.ts +375 -235
  93. package/src/additionalInfo.ts +663 -420
  94. package/src/ase.ts +66 -61
  95. package/src/descriptor.ts +199 -195
  96. package/src/helpers.ts +4 -1
  97. package/src/imageResources.ts +632 -168
  98. package/src/jpeg.ts +8 -8
  99. package/src/psd.ts +156 -30
  100. package/src/psdReader.ts +273 -172
  101. package/src/psdWriter.ts +95 -58
  102. package/src/text.ts +17 -6
  103. package/src/utf8.ts +0 -4
@@ -12,6 +12,10 @@ var __rest = (this && this.__rest) || function (s, e) {
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.supportedColorModes = void 0;
15
+ exports.getUtf8Decoder = getUtf8Decoder;
16
+ exports.getUtf8StrictDecoder = getUtf8StrictDecoder;
17
+ exports.getGbkStrictDecoder = getGbkStrictDecoder;
18
+ exports.decodeUtf8 = decodeUtf8;
15
19
  exports.createReader = createReader;
16
20
  exports.warnOrThrow = warnOrThrow;
17
21
  exports.readUint8 = readUint8;
@@ -53,12 +57,15 @@ exports.readDataZip = readDataZip;
53
57
  exports.readDataRLE = readDataRLE;
54
58
  exports.readSection = readSection;
55
59
  exports.readColor = readColor;
60
+ exports.readPatternSafe = readPatternSafe;
56
61
  exports.readPattern = readPattern;
62
+ exports.consumeMemory = consumeMemory;
57
63
  const pako_1 = require("pako");
58
64
  const helpers_1 = require("./helpers");
59
65
  const additionalInfo_1 = require("./additionalInfo");
66
+ const utf8_1 = require("./utf8");
60
67
  const imageResources_1 = require("./imageResources");
61
- exports.supportedColorModes = [0 /* ColorMode.Bitmap */, 1 /* ColorMode.Grayscale */, 3 /* ColorMode.RGB */, 2 /* ColorMode.Indexed */];
68
+ exports.supportedColorModes = [0 /* ColorMode.Bitmap */, 1 /* ColorMode.Grayscale */, 3 /* ColorMode.RGB */, 2 /* ColorMode.Indexed */, 4 /* ColorMode.CMYK */];
62
69
  const colorModes = ['bitmap', 'grayscale', 'indexed', 'RGB', 'CMYK', '', '', 'multichannel', 'duotone', 'lab'];
63
70
  function setupGrayscale(data) {
64
71
  const size = data.width * data.height * 4;
@@ -68,6 +75,37 @@ function setupGrayscale(data) {
68
75
  data.data[i + 2] = c;
69
76
  }
70
77
  }
78
+ function createTextDecoder(label, options) {
79
+ try {
80
+ return typeof TextDecoder !== 'undefined' ? new TextDecoder(label, options) : null;
81
+ }
82
+ catch (_a) {
83
+ return null; // encoding not supported
84
+ }
85
+ }
86
+ // non-fatal UTF-8 decoder, malformed byte sequences are replaced with U+FFFD
87
+ function getUtf8Decoder(reader) {
88
+ if (reader.utf8Decoder === undefined)
89
+ reader.utf8Decoder = createTextDecoder('utf-8', { ignoreBOM: true });
90
+ return reader.utf8Decoder;
91
+ }
92
+ // UTF-8 decoder that throws on malformed byte sequences
93
+ function getUtf8StrictDecoder(reader) {
94
+ if (reader.utf8StrictDecoder === undefined)
95
+ reader.utf8StrictDecoder = createTextDecoder('utf-8', { ignoreBOM: true, fatal: true });
96
+ return reader.utf8StrictDecoder;
97
+ }
98
+ // GBK decoder that throws on malformed byte sequences
99
+ function getGbkStrictDecoder(reader) {
100
+ if (reader.gbkDecoder === undefined)
101
+ reader.gbkDecoder = createTextDecoder('gbk', { fatal: true });
102
+ return reader.gbkDecoder;
103
+ }
104
+ function decodeUtf8(reader, buffer) {
105
+ // native decoder is faster only for longer strings
106
+ const decoder = buffer.byteLength > 1000 ? getUtf8Decoder(reader) : null;
107
+ return decoder ? decoder.decode(buffer) : (0, utf8_1.decodeString)(buffer);
108
+ }
71
109
  function createReader(buffer, offset, length) {
72
110
  const view = new DataView(buffer, offset, length);
73
111
  return { view, offset: 0, strict: false, debug: false, large: false, globalAlpha: false, log: console.log };
@@ -247,7 +285,8 @@ function readPsd(reader, readOptions = {}) {
247
285
  const maxSize = version === 1 ? 30000 : 300000;
248
286
  if (width > maxSize || height > maxSize)
249
287
  throw new Error(`Invalid size: ${width}x${height}`);
250
- if (channels < 1 || channels > 56)
288
+ // up to 56 channels plus transparency of composite image (Photoshop CS3 writes 57 channels for 53 alpha channels)
289
+ if (channels < 1 || channels > 57)
251
290
  throw new Error(`Invalid channel count: ${channels}`);
252
291
  if (![1, 8, 16, 32].includes(bitsPerChannel))
253
292
  throw new Error(`Invalid bitsPerChannel: ${bitsPerChannel}`);
@@ -257,6 +296,7 @@ function readPsd(reader, readOptions = {}) {
257
296
  Object.assign(reader, readOptions);
258
297
  reader.large = version === 2;
259
298
  reader.globalAlpha = false;
299
+ reader.layersInGlobalInfo = false;
260
300
  if (!('totalMemoryLimit' in reader)) { // setting totalMemoryLimit to undefined explicitly disables memory limit
261
301
  reader.totalMemoryLimit = 2 * 1024 * 1024 * 1024; // default 2GB memory limit
262
302
  }
@@ -284,17 +324,21 @@ function readPsd(reader, readOptions = {}) {
284
324
  });
285
325
  // image resources
286
326
  const imageResources = {};
327
+ let unreadResourceBytes = 0;
287
328
  readSection(reader, 1, left => {
288
- while (left() > 0) {
289
- realignWithSignature(reader, isValidSignature);
329
+ // stop if there's not enough data left for resource header (some files have few trailing bytes in this section)
330
+ while (left() >= 12) {
331
+ const signature = realignWithSignature(reader, isValidSignature);
290
332
  const id = readUint16(reader);
291
- readPascalString(reader, 2); // name
333
+ const nameLength = readUint8(reader);
334
+ const name = readBytes(reader, nameLength);
335
+ skipBytes(reader, (nameLength + 1) % 2); // pad to even size
292
336
  readSection(reader, 2, left => {
293
337
  const handler = imageResources_1.resourceHandlersMap[id];
294
338
  const skip = id === 1036 && !!reader.skipThumbnail;
295
339
  if (handler && !skip) {
296
340
  try {
297
- handler.read(reader, imageResources, left);
341
+ handler.read(reader, imageResources, left, { signature, id, name, width, height });
298
342
  }
299
343
  catch (e) {
300
344
  if (reader.throwForMissingFeatures)
@@ -303,13 +347,20 @@ function readPsd(reader, readOptions = {}) {
303
347
  }
304
348
  }
305
349
  else {
306
- // options.logMissingFeatures && console.log(`Unhandled image resource: ${id} (${left()})`);
350
+ reader.logMissingFeatures && reader.log(`Unhandled image resource: ${id} (${left()})`);
307
351
  skipBytes(reader, left());
308
352
  }
309
353
  });
310
354
  }
355
+ unreadResourceBytes = left();
356
+ skipBytes(reader, left());
311
357
  });
312
- const { layersGroup, layerGroupsEnabledId } = imageResources, rest = __rest(imageResources, ["layersGroup", "layerGroupsEnabledId"]);
358
+ // some files have image resources section length few bytes larger than the data, layer and mask info section
359
+ // starts right after the last resource in that case
360
+ if (unreadResourceBytes && !sectionFits(reader, reader.offset) && sectionFits(reader, reader.offset - unreadResourceBytes)) {
361
+ reader.offset -= unreadResourceBytes;
362
+ }
363
+ const { layersGroup, layerGroupsEnabledId, savedPathsOrigination } = imageResources, rest = __rest(imageResources, ["layersGroup", "layerGroupsEnabledId", "savedPathsOrigination"]);
313
364
  if (Object.keys(rest).length) {
314
365
  psd.imageResources = rest;
315
366
  }
@@ -353,6 +404,8 @@ function readPsd(reader, readOptions = {}) {
353
404
  const rawCompositeData = new Uint8Array(reader.view.buffer, reader.view.byteOffset + rawOffset, reader.view.byteLength - rawOffset);
354
405
  psd.rawCompositeData = rawCompositeData;
355
406
  psd.rawCompositeDataInfo = { large: reader.large, globalAlpha: reader.globalAlpha };
407
+ if (reader.layersInGlobalInfo)
408
+ psd.rawCompositeDataInfo.layersInGlobalInfo = true;
356
409
  }
357
410
  else {
358
411
  const imageData = readImageData(reader, psd);
@@ -364,9 +417,6 @@ function readPsd(reader, readOptions = {}) {
364
417
  }
365
418
  }
366
419
  }
367
- // TODO: show converted color mode instead of original PSD file color mode
368
- // but add option to preserve file color mode (need to return image data instead of canvas in that case)
369
- // psd.colorMode = ColorMode.RGB; // we convert all color modes to RGB
370
420
  return psd;
371
421
  }
372
422
  function readLayerInfo(reader, psd, imageResources) {
@@ -442,9 +492,12 @@ function readLayerRecord(reader, psd, imageResources) {
442
492
  }
443
493
  checkSignature(reader, '8BIM');
444
494
  const blendMode = readSignature(reader);
445
- if (!helpers_1.toBlendMode[blendMode])
446
- throw new Error(`Invalid blend mode: '${blendMode}'`);
447
- layer.blendMode = helpers_1.toBlendMode[blendMode];
495
+ if (!helpers_1.toBlendMode[blendMode]) {
496
+ if (reader.throwForMissingFeatures)
497
+ throw new Error(`Invalid blend mode: '${blendMode}'`);
498
+ reader.logMissingFeatures && reader.log(`Unknown blend mode: '${blendMode}', using normal blend mode`);
499
+ }
500
+ layer.blendMode = helpers_1.toBlendMode[blendMode] || 'normal';
448
501
  layer.opacity = readUint8(reader) / 0xff;
449
502
  layer.clipping = readUint8(reader) === 1;
450
503
  const flags = readUint8(reader);
@@ -472,11 +525,20 @@ function readLayerRecord(reader, psd, imageResources) {
472
525
  });
473
526
  return { layer, channels };
474
527
  }
528
+ // negative sizes are treated as empty (Photoshop writes them for some empty layers, like 0,0,-1,0 bounds of fill layer)
529
+ // checks if length of section (layer and mask info) at given offset fits in the file
530
+ function sectionFits(reader, offset) {
531
+ const lengthSize = reader.large ? 8 : 4;
532
+ if (offset < 0 || offset + lengthSize > reader.view.byteLength)
533
+ return false;
534
+ const length = reader.large ? reader.view.getUint32(offset) * 0x100000000 + reader.view.getUint32(offset + 4) : reader.view.getUint32(offset);
535
+ return offset + lengthSize + length <= reader.view.byteLength;
536
+ }
475
537
  function isValidBoxSize(box, reader) {
476
538
  const width = (box.right || 0) - (box.left || 0);
477
539
  const height = (box.bottom || 0) - (box.top || 0);
478
540
  const maxSize = reader.large ? 300000 : 30000;
479
- return width >= 0 && height >= 0 && width <= maxSize && height <= maxSize;
541
+ return width <= maxSize && height <= maxSize;
480
542
  }
481
543
  function readLayerMaskData(reader, layer) {
482
544
  return readSection(reader, 1, left => {
@@ -595,9 +657,9 @@ function readLayerChannelImageData(reader, psd, layer, channels) {
595
657
  reader.offset -= 1;
596
658
  compression = readUint16(reader);
597
659
  }
598
- // try to fix broken files where there's 1 byte shift of channel
660
+ // try to fix broken files where there's 1 byte shift of channel (in the other direction)
599
661
  if (compression > 3) {
600
- reader.offset -= 3;
662
+ reader.offset -= 4;
601
663
  compression = readUint16(reader);
602
664
  }
603
665
  if (compression > 3)
@@ -631,6 +693,7 @@ function getCompositeImageData(psd) {
631
693
  if (info) {
632
694
  reader.large = info.large;
633
695
  reader.globalAlpha = info.globalAlpha;
696
+ reader.layersInGlobalInfo = !!info.layersInGlobalInfo;
634
697
  }
635
698
  return readImageData(reader, psd);
636
699
  }
@@ -711,18 +774,14 @@ function getDataFromLayer(layer, read, throwForMissingFeatures, memoryLimit, log
711
774
  let initializedAlpha = false;
712
775
  if (layerWidth && layerHeight && read === LayerDataType.Layer) {
713
776
  if (cmyk) {
714
- if (bitsPerChannel !== 8)
777
+ if (bitsPerChannel !== 8 && bitsPerChannel !== 16)
715
778
  throw new Error('bitsPerChannel not supported');
716
- imageData = { width: layerWidth, height: layerHeight, data: new Uint8ClampedArray(layerWidth * layerHeight * 5) };
779
+ imageData = createImageDataBitDepth(layerWidth, layerHeight, bitsPerChannel, 5, memoryLimit);
717
780
  }
718
781
  else {
719
782
  imageData = createImageDataBitDepth(layerWidth, layerHeight, bitsPerChannel, 4, memoryLimit);
720
783
  }
721
784
  }
722
- if (helpers_1.RAW_IMAGE_DATA) { // TODO: use layer.rawData instead
723
- layer.imageDataRaw = [];
724
- layer.imageDataRawCompression = [];
725
- }
726
785
  for (const { id, compression, data } of channels) {
727
786
  if (!data)
728
787
  continue;
@@ -747,16 +806,6 @@ function getDataFromLayer(layer, read, throwForMissingFeatures, memoryLimit, log
747
806
  if (memoryLimit !== undefined)
748
807
  dataReader.totalMemoryLimit = Math.max(0, dataReader.totalMemoryLimit - maskData.data.byteLength);
749
808
  readData(dataReader, data.byteLength, maskData, compression, maskWidth, maskHeight, bitsPerChannel, 0, large, 4);
750
- if (helpers_1.RAW_IMAGE_DATA) { // TODO: use layer.rawData instead
751
- if (id === -2 /* ChannelID.UserMask */) {
752
- layer.maskDataRawCompression = compression;
753
- layer.maskDataRaw = data;
754
- }
755
- else {
756
- layer.realMaskDataRawCompression = compression;
757
- layer.realMaskDataRaw = data;
758
- }
759
- }
760
809
  setupGrayscale(maskData);
761
810
  resetAlpha(maskData, false);
762
811
  }
@@ -773,10 +822,6 @@ function getDataFromLayer(layer, read, throwForMissingFeatures, memoryLimit, log
773
822
  }
774
823
  }
775
824
  readData(dataReader, data.byteLength, targetData, compression, layerWidth, layerHeight, bitsPerChannel, offset, large, cmyk ? 5 : 4);
776
- if (helpers_1.RAW_IMAGE_DATA) { // TODO: use layer.rawData instead
777
- layer.imageDataRawCompression[id] = compression;
778
- layer.imageDataRaw[id] = data;
779
- }
780
825
  }
781
826
  if (id === -1 /* ChannelID.Transparency */) {
782
827
  initializedAlpha = true;
@@ -789,7 +834,8 @@ function getDataFromLayer(layer, read, throwForMissingFeatures, memoryLimit, log
789
834
  resetAlpha(imageData, cmyk);
790
835
  if (cmyk) {
791
836
  const cmykData = imageData;
792
- imageData = (0, helpers_1.createImageData)(cmykData.width, cmykData.height);
837
+ const rgbMemoryLimit = memoryLimit === undefined ? undefined : Math.max(0, memoryLimit - cmykData.data.byteLength);
838
+ imageData = createImageDataBitDepth(cmykData.width, cmykData.height, bitsPerChannel, 4, rgbMemoryLimit);
793
839
  cmykToRgb(cmykData, imageData, false);
794
840
  }
795
841
  }
@@ -862,7 +908,15 @@ function readAdditionalLayerInfo(reader, target, psd, imageResources) {
862
908
  const sig = realignWithSignature(reader, isValidAdditionalInfoSignature);
863
909
  const key = readSignature(reader);
864
910
  // `largeAdditionalInfoKeys` fallback, because some keys don't have 8B64 signature even when they are 64bit
865
- const u64 = sig === '8B64' || (reader.large && helpers_1.largeAdditionalInfoKeys.indexOf(key) !== -1);
911
+ let u64 = sig === '8B64' || (reader.large && helpers_1.largeAdditionalInfoKeys.indexOf(key) !== -1);
912
+ // these keys were written by older versions of this library with 32bit length and 8BIM signature,
913
+ // 64bit length always has high 32bits set to 0 (lengths over 4GB are not supported)
914
+ if (reader.large && sig === '8BIM' && helpers_1.largeAdditionalInfoKeysObserved.indexOf(key) !== -1) {
915
+ // 32bit length of 0 is followed by signature of the next block, 64bit length would continue with its low 32bits
916
+ const next = reader.offset + 8 <= reader.view.byteLength ? reader.view.getUint32(reader.offset + 4) : 0;
917
+ const nextIsSignature = next === 0x3842494d /* 8BIM */ || next === 0x38423634 /* 8B64 */;
918
+ u64 = reader.view.getUint32(reader.offset) === 0 && !nextIsSignature;
919
+ }
866
920
  readSection(reader, 2, left => {
867
921
  const handler = additionalInfo_1.infoHandlersMap[key];
868
922
  if (handler) {
@@ -875,7 +929,7 @@ function readAdditionalLayerInfo(reader, target, psd, imageResources) {
875
929
  }
876
930
  }
877
931
  else {
878
- reader.logMissingFeatures && reader.log(`Unhandled additional info: ${key}`);
932
+ reader.logMissingFeatures && key !== 'GenI' && key !== 'OCIO' && key !== 'CAI ' && reader.log(`Unhandled additional info: ${key}`);
879
933
  skipBytes(reader, left());
880
934
  }
881
935
  if (left()) {
@@ -907,7 +961,7 @@ function createImageDataBitDepth(width, height, bitDepth, channels, memoryLimit)
907
961
  }
908
962
  }
909
963
  function readImageData(reader, psd) {
910
- var _a;
964
+ var _a, _b, _c;
911
965
  const compression = readUint16(reader);
912
966
  const bitsPerChannel = (_a = psd.bitsPerChannel) !== null && _a !== void 0 ? _a : 8;
913
967
  const { width, height } = psd;
@@ -939,11 +993,7 @@ function readImageData(reader, psd) {
939
993
  case 1 /* ColorMode.Grayscale */: {
940
994
  const colorChannels = psd.colorMode === 1 /* ColorMode.Grayscale */ ? 1 : 3;
941
995
  const offsets = compositeChannelOffsets(reader, psd, colorChannels, 3);
942
- const start = reader.offset;
943
996
  readCompositeChannels(reader, compression, imageData, bitsPerChannel, 4, offsets);
944
- if (helpers_1.RAW_IMAGE_DATA && compression === 1 /* Compression.RleCompressed */) {
945
- psd.imageDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
946
- }
947
997
  if (psd.colorMode === 1 /* ColorMode.Grayscale */) {
948
998
  setupGrayscale(imageData);
949
999
  }
@@ -957,41 +1007,42 @@ function readImageData(reader, psd) {
957
1007
  const offsets = compositeChannelOffsets(reader, psd, 1, -1);
958
1008
  const indexedImageData = { width, height, data: new Uint8Array(width * height) };
959
1009
  readCompositeChannels(reader, compression, indexedImageData, 8, 1, offsets);
960
- indexedToRgb(indexedImageData, imageData, psd.palette);
1010
+ indexedToRgb(indexedImageData, imageData, psd.palette, (_c = (_b = psd.imageResources) === null || _b === void 0 ? void 0 : _b.transparencyIndex) !== null && _c !== void 0 ? _c : -1);
961
1011
  break;
962
1012
  }
963
1013
  case 4 /* ColorMode.CMYK */: {
964
- if (bitsPerChannel !== 8)
1014
+ if (bitsPerChannel !== 8 && bitsPerChannel !== 16)
965
1015
  throw new Error('bitsPerChannel not supported');
966
1016
  const offsets = compositeChannelOffsets(reader, psd, 4, 4);
967
- const cmykImageData = { width, height, data: new Uint8Array(width * height * 5) };
968
- const start = reader.offset;
969
- readCompositeChannels(reader, compression, cmykImageData, 8, 5, offsets);
970
- cmykToRgb(cmykImageData, imageData, true);
971
- if (helpers_1.RAW_IMAGE_DATA && compression === 1 /* Compression.RleCompressed */) {
972
- psd.imageDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
973
- }
1017
+ const cmykImageData = createImageDataBitDepth(width, height, bitsPerChannel, 5, reader.totalMemoryLimit);
1018
+ resetAlpha(cmykImageData, true);
1019
+ readCompositeChannels(reader, compression, cmykImageData, bitsPerChannel, 5, offsets);
1020
+ cmykToRgb(cmykImageData, imageData, !!reader.globalAlpha);
974
1021
  break;
975
1022
  }
976
1023
  default: throw new Error(`Color mode not supported: ${psd.colorMode}`);
977
1024
  }
978
- // remove weird white matte
979
- if (reader.globalAlpha) {
1025
+ // remove weird white matte (already removed when converting from CMYK)
1026
+ if (reader.globalAlpha && psd.colorMode !== 4 /* ColorMode.CMYK */) {
980
1027
  removeWhiteMatte(imageData);
981
1028
  }
982
1029
  return imageData;
983
1030
  }
984
1031
  // returns target offset for each of the composite image channels stored in the file, -1 for channels that are skipped
985
1032
  function compositeChannelOffsets(reader, psd, colorChannels, alphaOffset) {
986
- var _a, _b, _c, _d;
1033
+ var _a, _b, _c;
987
1034
  const channelCount = (_a = psd.channels) !== null && _a !== void 0 ? _a : (colorChannels + (reader.globalAlpha ? 1 : 0));
988
1035
  if (channelCount < colorChannels)
989
1036
  throw new Error(`Invalid channel count: ${channelCount}`);
990
1037
  // First extra channel is transparency if document has global alpha (negative layer count). Some non-Photoshop
991
1038
  // applications write transparency channel without setting global alpha, in that case extra channel is not
992
1039
  // listed in alpha channel names (Photoshop names all user alpha channels / saved selections).
993
- const namedChannels = (_d = (_c = (_b = psd.imageResources) === null || _b === void 0 ? void 0 : _b.alphaChannelNames) === null || _c === void 0 ? void 0 : _c.length) !== null && _d !== void 0 ? _d : 0;
994
- const hasTransparency = reader.globalAlpha || (channelCount - colorChannels) > namedChannels;
1040
+ // Photoshop also lists merged transparency as first alpha channel named "Transparency" (in documents marked with
1041
+ // Mtrn/Mt16/Mt32 additional info, or in some 16bit documents with layers stored in Lr16 without any other mark)
1042
+ const alphaNames = (_c = (_b = psd.imageResources) === null || _b === void 0 ? void 0 : _b.alphaChannelNames) !== null && _c !== void 0 ? _c : [];
1043
+ const namedChannels = alphaNames.length;
1044
+ const namedTransparency = !!reader.layersInGlobalInfo && alphaNames[0] === 'Transparency' && namedChannels === channelCount - colorChannels;
1045
+ const hasTransparency = reader.globalAlpha || namedTransparency || (channelCount - colorChannels) > namedChannels;
995
1046
  const offsets = [];
996
1047
  for (let i = 0; i < channelCount; i++) {
997
1048
  if (i < colorChannels) {
@@ -1070,41 +1121,67 @@ function removeWhiteMatte({ data, width, height }) {
1070
1121
  }
1071
1122
  }
1072
1123
  }
1073
- function cmykToRgb(cmyk, rgb, reverseAlpha) {
1124
+ // converts value in 0-255 range to target range, rounding and clamping it
1125
+ function toRange(value, max) {
1126
+ value = Math.round(value * (max / 255));
1127
+ return value <= 0 ? 0 : (value >= max ? max : value);
1128
+ }
1129
+ // Polynomial approximation of U.S. Web Coated (SWOP) v2 CMYK to RGB conversion (default CMYK profile of Photoshop).
1130
+ // Coefficients are taken from pdf.js (DeviceCmykCS in src/core/colorspace.js, https://github.com/mozilla/pdf.js),
1131
+ // Copyright 2012 Mozilla Foundation, licensed under the Apache License, Version 2.0
1132
+ // (http://www.apache.org/licenses/LICENSE-2.0)
1133
+ // cmyk - 8 or 16 bit data with 5 channels (cmyka), rgb - 8 or 16 bit data with 4 channels (rgba)
1134
+ // removeMatte - colors are blended with white (no ink), used for composite image of documents with global alpha
1135
+ function cmykToRgb(cmyk, rgb, removeMatte) {
1074
1136
  const size = rgb.width * rgb.height * 4;
1075
1137
  const srcData = cmyk.data;
1076
1138
  const dstData = rgb.data;
1139
+ const srcMax = srcData instanceof Uint16Array ? 0xffff : 0xff;
1140
+ const dstMax = dstData instanceof Uint16Array ? 0xffff : 0xff;
1077
1141
  for (let src = 0, dst = 0; dst < size; src += 5, dst += 4) {
1078
- const c = srcData[src];
1079
- const m = srcData[src + 1];
1080
- const y = srcData[src + 2];
1081
- const k = srcData[src + 3];
1082
- dstData[dst] = ((((c * k) | 0) / 255) | 0);
1083
- dstData[dst + 1] = ((((m * k) | 0) / 255) | 0);
1084
- dstData[dst + 2] = ((((y * k) | 0) / 255) | 0);
1085
- dstData[dst + 3] = reverseAlpha ? 255 - srcData[src + 4] : srcData[src + 4];
1086
- }
1087
- // for (let src = 0, dst = 0; dst < size; src += 5, dst += 4) {
1088
- // const c = 1 - (srcData[src + 0] / 255);
1089
- // const m = 1 - (srcData[src + 1] / 255);
1090
- // const y = 1 - (srcData[src + 2] / 255);
1091
- // // const k = srcData[src + 3] / 255;
1092
- // dstData[dst + 0] = ((1 - c * 0.8) * 255) | 0;
1093
- // dstData[dst + 1] = ((1 - m * 0.8) * 255) | 0;
1094
- // dstData[dst + 2] = ((1 - y * 0.8) * 255) | 0;
1095
- // dstData[dst + 3] = reverseAlpha ? 255 - srcData[src + 4] : srcData[src + 4];
1096
- // }
1097
- }
1098
- function indexedToRgb(indexed, rgb, palette) {
1142
+ const a = srcData[src + 4];
1143
+ // image data stores inverted values (0 = 100% ink)
1144
+ let c = 1 - srcData[src] / srcMax;
1145
+ let m = 1 - srcData[src + 1] / srcMax;
1146
+ let y = 1 - srcData[src + 2] / srcMax;
1147
+ let k = 1 - srcData[src + 3] / srcMax;
1148
+ if (removeMatte && a > 0 && a < srcMax) {
1149
+ const ra = srcMax / a;
1150
+ c = Math.min(1, c * ra);
1151
+ m = Math.min(1, m * ra);
1152
+ y = Math.min(1, y * ra);
1153
+ k = Math.min(1, k * ra);
1154
+ }
1155
+ dstData[dst] = toRange(255 +
1156
+ c * (-4.387332384609988 * c + 54.48615194189176 * m + 18.82290502165302 * y + 212.25662451639585 * k - 285.2331026137004) +
1157
+ m * (1.7149763477362134 * m - 5.6096736904047315 * y - 17.873870861415444 * k - 5.497006427196366) +
1158
+ y * (-2.5217340131683033 * y - 21.248923337353073 * k + 17.5119270841813) +
1159
+ k * (-21.86122147463605 * k - 189.48180835922747), dstMax);
1160
+ dstData[dst + 1] = toRange(255 +
1161
+ c * (8.841041422036149 * c + 60.118027045597366 * m + 6.871425592049007 * y + 31.159100130055922 * k - 79.2970844816548) +
1162
+ m * (-15.310361306967817 * m + 17.575251261109482 * y + 131.35250912493976 * k - 190.9453302588951) +
1163
+ y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) +
1164
+ k * (-20.737325471181034 * k - 187.80453709719578), dstMax);
1165
+ dstData[dst + 2] = toRange(255 +
1166
+ c * (0.8842522430003296 * c + 8.078677503112928 * m + 30.89978309703729 * y - 0.23883238689178934 * k - 14.183576799673286) +
1167
+ m * (10.49593273432072 * m + 63.02378494754052 * y + 50.606957656360734 * k - 112.23884253719248) +
1168
+ y * (0.03296041114873217 * y + 115.60384449646641 * k - 193.58209356861505) +
1169
+ k * (-22.33816807309886 * k - 180.12613974708367), dstMax);
1170
+ dstData[dst + 3] = srcMax === dstMax ? a : Math.round(a * dstMax / srcMax);
1171
+ }
1172
+ }
1173
+ // transparencyIndex - palette index of transparent color (-1 if there's none)
1174
+ function indexedToRgb(indexed, rgb, palette, transparencyIndex) {
1099
1175
  const size = indexed.width * indexed.height;
1100
1176
  const srcData = indexed.data;
1101
1177
  const dstData = rgb.data;
1102
1178
  for (let src = 0, dst = 0; src < size; src++, dst += 4) {
1103
- const c = palette[srcData[src]];
1179
+ const index = srcData[src];
1180
+ const c = palette[index];
1104
1181
  dstData[dst + 0] = c.r;
1105
1182
  dstData[dst + 1] = c.g;
1106
1183
  dstData[dst + 2] = c.b;
1107
- dstData[dst + 3] = 255;
1184
+ dstData[dst + 3] = index === transparencyIndex ? 0 : 255;
1108
1185
  }
1109
1186
  }
1110
1187
  // number of bytes used to store single channel value, 1-bit data is handled as bytes
@@ -1415,11 +1492,11 @@ function readColor(reader) {
1415
1492
  skipBytes(reader, 2);
1416
1493
  return { h, s, b };
1417
1494
  }
1418
- case 2 /* ColorSpace.CMYK */: {
1419
- const c = readUint16(reader) / 257;
1420
- const m = readUint16(reader) / 257;
1421
- const y = readUint16(reader) / 257;
1422
- const k = readUint16(reader) / 257;
1495
+ case 2 /* ColorSpace.CMYK */: { // 0 = 100% ink
1496
+ const c = (0xffff - readUint16(reader)) / 257;
1497
+ const m = (0xffff - readUint16(reader)) / 257;
1498
+ const y = (0xffff - readUint16(reader)) / 257;
1499
+ const k = (0xffff - readUint16(reader)) / 257;
1423
1500
  return { c, m, y, k };
1424
1501
  }
1425
1502
  case 7 /* ColorSpace.Lab */: {
@@ -1440,6 +1517,24 @@ function readColor(reader) {
1440
1517
  throw new Error('Invalid color space');
1441
1518
  }
1442
1519
  }
1520
+ const MAX_PATTERN_SIZE = 30000;
1521
+ // reads pattern, unsupported or invalid patterns are skipped (returns undefined) unless throwForMissingFeatures is set
1522
+ function readPatternSafe(reader) {
1523
+ let length = reader.view.getUint32(reader.offset);
1524
+ while (length % 4)
1525
+ length++;
1526
+ const end = reader.offset + 4 + length;
1527
+ try {
1528
+ return readPattern(reader);
1529
+ }
1530
+ catch (e) {
1531
+ if (reader.throwForMissingFeatures)
1532
+ throw e;
1533
+ reader.logMissingFeatures && reader.log(`Skipped pattern: ${e.message}`);
1534
+ reader.offset = end;
1535
+ return undefined;
1536
+ }
1537
+ }
1443
1538
  function readPattern(reader) {
1444
1539
  let length = readUint32(reader);
1445
1540
  while (length % 4)
@@ -1451,8 +1546,8 @@ function readPattern(reader) {
1451
1546
  const colorMode = readUint32(reader);
1452
1547
  const x = readInt16(reader);
1453
1548
  const y = readInt16(reader);
1454
- // we only support RGB and grayscale for now
1455
- if (colorMode !== 3 /* ColorMode.RGB */ && colorMode !== 1 /* ColorMode.Grayscale */ && colorMode !== 2 /* ColorMode.Indexed */) {
1549
+ // we only support RGB, grayscale, indexed and CMYK for now
1550
+ if (colorMode !== 3 /* ColorMode.RGB */ && colorMode !== 1 /* ColorMode.Grayscale */ && colorMode !== 2 /* ColorMode.Indexed */ && colorMode !== 4 /* ColorMode.CMYK */) {
1456
1551
  throw new Error(`Unsupported pattern color mode: ${colorMode}`);
1457
1552
  }
1458
1553
  let name = readUnicodeString(reader);
@@ -1466,7 +1561,9 @@ function readPattern(reader) {
1466
1561
  b: readUint8(reader),
1467
1562
  });
1468
1563
  }
1469
- skipBytes(reader, 4); // no idea what this is
1564
+ // some writers follow the palette with 4 extra bytes (unknown)
1565
+ if (reader.view.getUint32(reader.offset) !== 3)
1566
+ skipBytes(reader, 4);
1470
1567
  }
1471
1568
  // virtual memory array list
1472
1569
  const version2 = readUint32(reader);
@@ -1480,17 +1577,37 @@ function readPattern(reader) {
1480
1577
  const channelsCount = readUint32(reader);
1481
1578
  const width = right - left;
1482
1579
  const height = bottom - top;
1580
+ if (width < 0 || height < 0)
1581
+ throw new Error('Invalid pattern bounds');
1582
+ if (width > MAX_PATTERN_SIZE || height > MAX_PATTERN_SIZE)
1583
+ throw new Error(`Pattern too large (${width}x${height})`);
1584
+ if (channelsCount > 64)
1585
+ throw new Error(`Invalid pattern channel count: ${channelsCount}`);
1586
+ // channels cover whole pattern, each 2 bytes of RLE data can produce at most 128 bytes
1587
+ if (width * height > (Math.min(end, reader.view.byteLength) - reader.offset) * 64)
1588
+ throw new Error('Pattern data out of bounds');
1483
1589
  const size = width * height * 4;
1484
1590
  consumeMemory(reader, size);
1485
1591
  const data = new Uint8Array(size);
1486
1592
  for (let i = 3; i < data.byteLength; i += 4) {
1487
1593
  data[i] = 255;
1488
1594
  }
1489
- for (let i = 0, ch = 0; i < (channelsCount + 2); i++) {
1595
+ // first written arrays are color channels, next one is transparency (Photoshop stores it in the last, sheet mask, array)
1596
+ const colorChannels = colorMode === 3 /* ColorMode.RGB */ ? 3 : (colorMode === 4 /* ColorMode.CMYK */ ? 4 : 1);
1597
+ // CMYK channels are decoded into separate buffer (5 values per pixel, inverted, 0 = 100% ink) and converted to RGB at the end
1598
+ const cmyk = colorMode === 4 /* ColorMode.CMYK */ ? new Uint8Array(width * height * 5).fill(255) : undefined;
1599
+ if (cmyk)
1600
+ consumeMemory(reader, cmyk.byteLength);
1601
+ const alphaTarget = cmyk ? 4 : 3;
1602
+ for (let i = 0, ch = 0; i < (channelsCount + 2) && reader.offset + 4 <= end; i++) {
1490
1603
  const has = readUint32(reader);
1491
1604
  if (!has)
1492
1605
  continue;
1493
1606
  const length = readUint32(reader);
1607
+ if (!length)
1608
+ continue;
1609
+ if (length < 23)
1610
+ throw new Error('Invalid pattern channel length');
1494
1611
  const pixelDepth = readUint32(reader);
1495
1612
  const ctop = readUint32(reader);
1496
1613
  const cleft = readUint32(reader);
@@ -1498,97 +1615,92 @@ function readPattern(reader) {
1498
1615
  const cright = readUint32(reader);
1499
1616
  const pixelDepth2 = readUint16(reader);
1500
1617
  const compressionMode = readUint8(reader); // 0 - raw, 1 - rle
1501
- const dataLength = length - (4 + 16 + 2 + 1);
1502
- const cdata = readBytes(reader, dataLength);
1503
- if (pixelDepth !== 8 || pixelDepth2 !== 8) {
1504
- throw new Error('16bit pixel depth not supported for patterns');
1505
- }
1618
+ const cdata = readBytes(reader, length - 23);
1619
+ const depth = (pixelDepth2 === 8 || pixelDepth2 === 16 || pixelDepth2 === 32) ? pixelDepth2 : pixelDepth;
1620
+ if (depth !== 8 && depth !== 16 && depth !== 32)
1621
+ throw new Error(`Unsupported pattern pixel depth: ${depth}`);
1622
+ if (ctop < top || cleft < left || cbottom > bottom || cright > right || cbottom < ctop || cright < cleft) {
1623
+ throw new Error('Pattern channel bounds outside of pattern bounds');
1624
+ }
1625
+ const target = ch < colorChannels ? ch : (ch === colorChannels ? alphaTarget : -1);
1626
+ ch++;
1627
+ if (target === -1)
1628
+ continue;
1506
1629
  const w = cright - cleft;
1507
1630
  const h = cbottom - ctop;
1508
- const ox = cleft - left;
1509
- const oy = ctop - top;
1631
+ const rowBytes = w * (depth / 8);
1632
+ const channelSize = rowBytes * h;
1633
+ let bytes;
1510
1634
  if (compressionMode === 0) {
1511
- if (colorMode === 3 /* ColorMode.RGB */ && ch < 3) {
1512
- for (let y = 0; y < h; y++) {
1513
- for (let x = 0; x < w; x++) {
1514
- const src = x + y * w;
1515
- const dst = (ox + x + (y + oy) * width) * 4;
1516
- data[dst + ch] = cdata[src];
1517
- }
1518
- }
1519
- }
1520
- else if (colorMode === 1 /* ColorMode.Grayscale */ && ch < 1) {
1521
- for (let y = 0; y < h; y++) {
1522
- for (let x = 0; x < w; x++) {
1523
- const src = x + y * w;
1524
- const dst = (ox + x + (y + oy) * width) * 4;
1525
- const value = cdata[src];
1526
- data[dst + 0] = value;
1527
- data[dst + 1] = value;
1528
- data[dst + 2] = value;
1529
- }
1530
- }
1531
- }
1532
- else if (colorMode === 2 /* ColorMode.Indexed */) {
1533
- for (let y = 0; y < h; y++) {
1534
- for (let x = 0; x < w; x++) {
1535
- const src = x + y * w;
1536
- const dst = (ox + x + (y + oy) * width) * 4;
1537
- const index = cdata[src];
1538
- const color = palette[index];
1539
- data[dst + 0] = color.r;
1540
- data[dst + 1] = color.g;
1541
- data[dst + 2] = color.b;
1542
- }
1543
- }
1544
- }
1545
- else {
1546
- if (reader.throwForMissingFeatures)
1547
- throw new Error('Invalid color pattern');
1548
- }
1635
+ if (cdata.byteLength < channelSize)
1636
+ throw new Error('Pattern channel data out of bounds');
1637
+ bytes = cdata;
1549
1638
  }
1550
1639
  else if (compressionMode === 1) {
1551
- consumeMemory(reader, w * h);
1552
- const pixelData = { data, width, height };
1553
- const tempData = { data: new Uint8Array(w * h), width: w, height: h };
1640
+ // each 2 bytes of RLE data can produce at most 128 bytes
1641
+ if (channelSize > cdata.byteLength * 64)
1642
+ throw new Error('Pattern channel data out of bounds');
1643
+ consumeMemory(reader, channelSize);
1644
+ bytes = new Uint8Array(channelSize);
1554
1645
  const cdataReader = createReader(cdata.buffer, cdata.byteOffset, cdata.byteLength);
1555
- if (colorMode === 3 /* ColorMode.RGB */ && ch < 3) {
1556
- readDataRLE(cdataReader, tempData, w, h, 8, 1, [0], false);
1557
- copyChannelToRGBA(tempData, pixelData, ox, oy, ch);
1558
- }
1559
- if (colorMode === 1 /* ColorMode.Grayscale */ && ch < 1) {
1560
- readDataRLE(cdataReader, tempData, w, h, 8, 1, [0], false);
1561
- copyChannelToRGBA(tempData, pixelData, ox, oy, 0);
1562
- setupGrayscale(pixelData);
1563
- }
1564
- if (colorMode === 2 /* ColorMode.Indexed */) {
1565
- // TODO:
1566
- throw new Error('Indexed pattern color mode not implemented');
1567
- }
1568
- recoverMemory(reader, w * h);
1646
+ readDataRLE(cdataReader, { data: bytes, width: rowBytes, height: h }, rowBytes, h, 8, 1, [0], false);
1569
1647
  }
1570
1648
  else {
1571
1649
  throw new Error('Invalid pattern compression mode');
1572
1650
  }
1573
- ch++;
1651
+ const ox = cleft - left;
1652
+ const oy = ctop - top;
1653
+ for (let y = 0; y < h; y++) {
1654
+ for (let x = 0; x < w; x++) {
1655
+ const src = x + y * w;
1656
+ const dst = (ox + x + (y + oy) * width) * 4;
1657
+ const value = depth === 8 ? bytes[src] : patternValueTo8bit(bytes, src, depth);
1658
+ if (cmyk) {
1659
+ cmyk[(ox + x + (y + oy) * width) * 5 + target] = value;
1660
+ }
1661
+ else if (target === 3) {
1662
+ data[dst + 3] = value;
1663
+ }
1664
+ else if (colorMode === 2 /* ColorMode.Indexed */) {
1665
+ const color = palette[value];
1666
+ data[dst + 0] = color.r;
1667
+ data[dst + 1] = color.g;
1668
+ data[dst + 2] = color.b;
1669
+ }
1670
+ else if (colorMode === 1 /* ColorMode.Grayscale */) {
1671
+ data[dst + 0] = value;
1672
+ data[dst + 1] = value;
1673
+ data[dst + 2] = value;
1674
+ }
1675
+ else {
1676
+ data[dst + target] = value;
1677
+ }
1678
+ }
1679
+ }
1680
+ if (compressionMode === 1)
1681
+ recoverMemory(reader, channelSize);
1682
+ }
1683
+ if (cmyk) {
1684
+ cmykToRgb({ width, height, data: cmyk }, { width, height, data }, false);
1685
+ recoverMemory(reader, cmyk.byteLength);
1574
1686
  }
1575
1687
  reader.offset = end;
1576
1688
  return { id, name, x, y, bounds: { x: left, y: top, w: width, h: height }, data };
1577
1689
  }
1578
- function copyChannelToRGBA(srcData, dstData, ox, oy, offset) {
1579
- const w = srcData.width;
1580
- const h = srcData.height;
1581
- const width = dstData.width;
1582
- for (let y = 0; y < h; y++) {
1583
- for (let x = 0; x < w; x++) {
1584
- const src = x + y * w;
1585
- const dst = (ox + x + (y + oy) * width) * 4;
1586
- const value = srcData.data[src];
1587
- dstData.data[dst + offset] = value;
1588
- }
1690
+ // converts big-endian 16bit or 32bit (float) value at given index to 8bit value
1691
+ function patternValueTo8bit(bytes, index, depth) {
1692
+ if (depth === 16) {
1693
+ return Math.round(((bytes[index * 2] << 8) | bytes[index * 2 + 1]) / 257);
1694
+ }
1695
+ else {
1696
+ const view = new DataView(bytes.buffer, bytes.byteOffset + index * 4, 4);
1697
+ const value = view.getFloat32(0, false);
1698
+ return value > 0 ? (value < 1 ? Math.round(value * 255) : 255) : 0;
1589
1699
  }
1590
1700
  }
1591
1701
  function consumeMemory(reader, size) {
1702
+ if (!(size >= 0))
1703
+ throw new Error('Invalid size');
1592
1704
  if (reader.totalMemoryLimit !== undefined) {
1593
1705
  if (reader.totalMemoryLimit < size)
1594
1706
  throw new Error('Exceeded memory limit');