ag-psd 19.0.1 → 20.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README_PSD.md +20 -2
  3. package/TODO +7 -0
  4. package/dist/abr.js +4 -4
  5. package/dist/abr.js.map +1 -1
  6. package/dist/additionalInfo.d.ts +3 -3
  7. package/dist/additionalInfo.js +107 -12
  8. package/dist/additionalInfo.js.map +1 -1
  9. package/dist/bundle.js +15305 -7643
  10. package/dist/descriptor.js +21 -16
  11. package/dist/descriptor.js.map +1 -1
  12. package/dist/engineData2.d.ts +1 -0
  13. package/dist/engineData2.js +349 -0
  14. package/dist/engineData2.js.map +1 -0
  15. package/dist/helpers.d.ts +2 -7
  16. package/dist/helpers.js +45 -12
  17. package/dist/helpers.js.map +1 -1
  18. package/dist/imageResources.js +42 -1
  19. package/dist/imageResources.js.map +1 -1
  20. package/dist/psd.d.ts +46 -3
  21. package/dist/psd.js +8 -1
  22. package/dist/psd.js.map +1 -1
  23. package/dist/psdReader.d.ts +11 -5
  24. package/dist/psdReader.js +213 -100
  25. package/dist/psdReader.js.map +1 -1
  26. package/dist/psdWriter.js +24 -4
  27. package/dist/psdWriter.js.map +1 -1
  28. package/dist/utf8.js +10 -4
  29. package/dist/utf8.js.map +1 -1
  30. package/dist-es/abr.js +4 -4
  31. package/dist-es/abr.js.map +1 -1
  32. package/dist-es/additionalInfo.d.ts +3 -3
  33. package/dist-es/additionalInfo.js +110 -15
  34. package/dist-es/additionalInfo.js.map +1 -1
  35. package/dist-es/descriptor.js +21 -16
  36. package/dist-es/descriptor.js.map +1 -1
  37. package/dist-es/engineData2.d.ts +1 -0
  38. package/dist-es/engineData2.js +345 -0
  39. package/dist-es/engineData2.js.map +1 -0
  40. package/dist-es/helpers.d.ts +2 -7
  41. package/dist-es/helpers.js +43 -11
  42. package/dist-es/helpers.js.map +1 -1
  43. package/dist-es/imageResources.js +42 -1
  44. package/dist-es/imageResources.js.map +1 -1
  45. package/dist-es/psd.d.ts +46 -3
  46. package/dist-es/psd.js +7 -0
  47. package/dist-es/psd.js.map +1 -1
  48. package/dist-es/psdReader.d.ts +11 -5
  49. package/dist-es/psdReader.js +212 -102
  50. package/dist-es/psdReader.js.map +1 -1
  51. package/dist-es/psdWriter.js +25 -5
  52. package/dist-es/psdWriter.js.map +1 -1
  53. package/dist-es/utf8.js +10 -4
  54. package/dist-es/utf8.js.map +1 -1
  55. package/package.json +6 -7
  56. package/src/abr.ts +4 -4
  57. package/src/additionalInfo.ts +156 -51
  58. package/src/descriptor.ts +14 -9
  59. package/src/engineData2.ts +367 -0
  60. package/src/helpers.ts +47 -20
  61. package/src/imageResources.ts +59 -2
  62. package/src/psd.ts +41 -5
  63. package/src/psdReader.ts +210 -128
  64. package/src/psdWriter.ts +33 -14
  65. package/src/utf8.ts +12 -4
package/src/psdReader.ts CHANGED
@@ -1,12 +1,6 @@
1
- import { inflate } from 'pako';
2
- import {
3
- Psd, Layer, ColorMode, SectionDividerType, LayerAdditionalInfo, ReadOptions, LayerMaskData, Color,
4
- PatternInfo, GlobalLayerMaskInfo, RGB
5
- } from './psd';
6
- import {
7
- resetImageData, offsetForChannel, decodeBitmap, PixelData, createCanvas, createImageData,
8
- toBlendMode, ChannelID, Compression, LayerMaskFlags, MaskParams, ColorSpace, RAW_IMAGE_DATA, largeAdditionalInfoKeys
9
- } from './helpers';
1
+ import { inflateSync } from 'zlib';
2
+ import { Psd, Layer, ColorMode, SectionDividerType, LayerAdditionalInfo, ReadOptions, LayerMaskData, Color, PatternInfo, GlobalLayerMaskInfo, RGB, PixelData, PixelArray } from './psd';
3
+ import { resetImageData, offsetForChannel, decodeBitmap, createImageData, toBlendMode, ChannelID, Compression, LayerMaskFlags, MaskParams, ColorSpace, RAW_IMAGE_DATA, largeAdditionalInfoKeys, imageDataToCanvas } from './helpers';
10
4
  import { infoHandlersMap } from './additionalInfo';
11
5
  import { resourceHandlersMap } from './imageResources';
12
6
 
@@ -15,8 +9,9 @@ interface ChannelInfo {
15
9
  length: number;
16
10
  }
17
11
 
18
- interface ReadOptionsExt extends ReadOptions {
12
+ export interface ReadOptionsExt extends ReadOptions {
19
13
  large: boolean;
14
+ globalAlpha: boolean;
20
15
  }
21
16
 
22
17
  export const supportedColorModes = [ColorMode.Bitmap, ColorMode.Grayscale, ColorMode.RGB];
@@ -210,7 +205,7 @@ function isValidSignature(sig: string) {
210
205
  return sig === '8BIM' || sig === 'MeSa' || sig === 'AgHg' || sig === 'PHUT' || sig === 'DCSR';
211
206
  }
212
207
 
213
- export function readPsd(reader: PsdReader, options: ReadOptions = {}) {
208
+ export function readPsd(reader: PsdReader, readOptions: ReadOptions = {}) {
214
209
  // header
215
210
  checkSignature(reader, '8BPS');
216
211
  const version = readUint16(reader);
@@ -224,19 +219,27 @@ export function readPsd(reader: PsdReader, options: ReadOptions = {}) {
224
219
  const colorMode = readUint16(reader);
225
220
  const maxSize = version === 1 ? 30000 : 300000;
226
221
 
227
- if (width > maxSize || height > maxSize) throw new Error(`Invalid size`);
228
- if (channels > 16) throw new Error(`Invalid channel count`);
229
- if (bitsPerChannel > 32) throw new Error(`Invalid bitsPerChannel count`);
230
- if (supportedColorModes.indexOf(colorMode) === -1)
231
- throw new Error(`Color mode not supported: ${colorModes[colorMode] ?? colorMode}`);
222
+ if (width > maxSize || height > maxSize) throw new Error(`Invalid size: ${width}x${height}`);
223
+ if (channels > 16) throw new Error(`Invalid channel count: ${channels}`);
224
+ if (![1, 8, 16, 32].includes(bitsPerChannel)) throw new Error(`Invalid bitsPerChannel: ${bitsPerChannel}`);
225
+ if (supportedColorModes.indexOf(colorMode) === -1) throw new Error(`Color mode not supported: ${colorModes[colorMode] ?? colorMode}`);
232
226
 
233
227
  const psd: Psd = { width, height, channels, bitsPerChannel, colorMode };
234
- const opt: ReadOptionsExt = { ...options, large: version === 2 };
228
+ const options: ReadOptionsExt = { ...readOptions, large: version === 2, globalAlpha: false };
235
229
  const fixOffsets = [0, 1, -1, 2, -2, 3, -3, 4, -4];
236
230
 
237
231
  // color mode data
238
232
  readSection(reader, 1, left => {
239
- if (opt.throwForMissingFeatures) throw new Error('Color mode data not supported');
233
+ if (!left()) return;
234
+
235
+ // const numbers: number[] = [];
236
+ // console.log('color mode', left());
237
+ // while (left()) {
238
+ // numbers.push(readUint32(reader));
239
+ // }
240
+ // console.log('color mode', numbers);
241
+
242
+ // if (options.throwForMissingFeatures) throw new Error('Color mode data not supported');
240
243
  skipBytes(reader, left());
241
244
  });
242
245
 
@@ -264,7 +267,7 @@ export function readPsd(reader: PsdReader, options: ReadOptions = {}) {
264
267
 
265
268
  readSection(reader, 2, left => {
266
269
  const handler = resourceHandlersMap[id];
267
- const skip = id === 1036 && !!opt.skipThumbnail;
270
+ const skip = id === 1036 && !!options.skipThumbnail;
268
271
 
269
272
  if (!psd.imageResources) {
270
273
  psd.imageResources = {};
@@ -272,13 +275,13 @@ export function readPsd(reader: PsdReader, options: ReadOptions = {}) {
272
275
 
273
276
  if (handler && !skip) {
274
277
  try {
275
- handler.read(reader, psd.imageResources, left, opt);
278
+ handler.read(reader, psd.imageResources, left, options);
276
279
  } catch (e) {
277
- if (opt.throwForMissingFeatures) throw e;
280
+ if (options.throwForMissingFeatures) throw e;
278
281
  skipBytes(reader, left());
279
282
  }
280
283
  } else {
281
- // options.logMissingFeatures && console.log(`Unhandled image resource: ${id}`);
284
+ // options.logMissingFeatures && console.log(`Unhandled image resource: ${id} (${left()})`);
282
285
  skipBytes(reader, left());
283
286
  }
284
287
  });
@@ -286,10 +289,11 @@ export function readPsd(reader: PsdReader, options: ReadOptions = {}) {
286
289
  });
287
290
 
288
291
  // layer and mask info
289
- let globalAlpha = false;
290
-
291
292
  readSection(reader, 1, left => {
292
- globalAlpha = readLayerInfo(reader, psd, opt);
293
+ readSection(reader, 2, left => {
294
+ readLayerInfo(reader, psd, options);
295
+ skipBytes(reader, left());
296
+ }, undefined, options.large);
293
297
 
294
298
  // SAI does not include this section
295
299
  if (left() > 0) {
@@ -309,19 +313,19 @@ export function readPsd(reader: PsdReader, options: ReadOptions = {}) {
309
313
  }
310
314
 
311
315
  if (left() >= 12) {
312
- readAdditionalLayerInfo(reader, psd, psd, opt);
316
+ readAdditionalLayerInfo(reader, psd, psd, options);
313
317
  } else {
314
318
  // opt.logMissingFeatures && console.log('skipping leftover bytes', left());
315
319
  skipBytes(reader, left());
316
320
  }
317
321
  }
318
- }, undefined, opt.large);
322
+ }, undefined, options.large);
319
323
 
320
324
  const hasChildren = psd.children && psd.children.length;
321
- const skipComposite = opt.skipCompositeImageData && (opt.skipLayerImageData || hasChildren);
325
+ const skipComposite = options.skipCompositeImageData && (options.skipLayerImageData || hasChildren);
322
326
 
323
327
  if (!skipComposite) {
324
- readImageData(reader, psd, globalAlpha, opt);
328
+ readImageData(reader, psd, options);
325
329
  }
326
330
 
327
331
  // TODO: show converted color mode instead of original PSD file color mode
@@ -331,60 +335,52 @@ export function readPsd(reader: PsdReader, options: ReadOptions = {}) {
331
335
  return psd;
332
336
  }
333
337
 
334
- function readLayerInfo(reader: PsdReader, psd: Psd, options: ReadOptionsExt) {
335
- let globalAlpha = false;
338
+ export function readLayerInfo(reader: PsdReader, psd: Psd, options: ReadOptionsExt) {
339
+ let layerCount = readInt16(reader);
336
340
 
337
- readSection(reader, 2, left => {
338
- let layerCount = readInt16(reader);
341
+ if (layerCount < 0) {
342
+ options.globalAlpha = true;
343
+ layerCount = -layerCount;
344
+ }
339
345
 
340
- if (layerCount < 0) {
341
- globalAlpha = true;
342
- layerCount = -layerCount;
343
- }
346
+ const layers: Layer[] = [];
347
+ const layerChannels: ChannelInfo[][] = [];
344
348
 
345
- const layers: Layer[] = [];
346
- const layerChannels: ChannelInfo[][] = [];
349
+ for (let i = 0; i < layerCount; i++) {
350
+ const { layer, channels } = readLayerRecord(reader, psd, options);
351
+ layers.push(layer);
352
+ layerChannels.push(channels);
353
+ }
347
354
 
355
+ if (!options.skipLayerImageData) {
348
356
  for (let i = 0; i < layerCount; i++) {
349
- const { layer, channels } = readLayerRecord(reader, psd, options);
350
- layers.push(layer);
351
- layerChannels.push(channels);
352
- }
353
-
354
- if (!options.skipLayerImageData) {
355
- for (let i = 0; i < layerCount; i++) {
356
- readLayerChannelImageData(reader, psd, layers[i], layerChannels[i], options);
357
- }
357
+ readLayerChannelImageData(reader, psd, layers[i], layerChannels[i], options);
358
358
  }
359
+ }
359
360
 
360
- skipBytes(reader, left());
361
-
362
- if (!psd.children) psd.children = [];
363
-
364
- const stack: (Layer | Psd)[] = [psd];
365
-
366
- for (let i = layers.length - 1; i >= 0; i--) {
367
- const l = layers[i];
368
- const type = l.sectionDivider ? l.sectionDivider.type : SectionDividerType.Other;
369
-
370
- if (type === SectionDividerType.OpenFolder || type === SectionDividerType.ClosedFolder) {
371
- l.opened = type === SectionDividerType.OpenFolder;
372
- l.children = [];
373
- stack[stack.length - 1].children!.unshift(l);
374
- stack.push(l);
375
- } else if (type === SectionDividerType.BoundingSectionDivider) {
376
- stack.pop();
377
- // this was workaround because I didn't know what `lsdk` section was, now it's probably not needed anymore
378
- // } else if (l.name === '</Layer group>' && !l.sectionDivider && !l.top && !l.left && !l.bottom && !l.right) {
379
- // // sometimes layer group terminator doesn't have sectionDivider, so we just guess here (PS bug ?)
380
- // stack.pop();
381
- } else {
382
- stack[stack.length - 1].children!.unshift(l);
383
- }
361
+ if (!psd.children) psd.children = [];
362
+
363
+ const stack: (Layer | Psd)[] = [psd];
364
+
365
+ for (let i = layers.length - 1; i >= 0; i--) {
366
+ const l = layers[i];
367
+ const type = l.sectionDivider ? l.sectionDivider.type : SectionDividerType.Other;
368
+
369
+ if (type === SectionDividerType.OpenFolder || type === SectionDividerType.ClosedFolder) {
370
+ l.opened = type === SectionDividerType.OpenFolder;
371
+ l.children = [];
372
+ stack[stack.length - 1].children!.unshift(l);
373
+ stack.push(l);
374
+ } else if (type === SectionDividerType.BoundingSectionDivider) {
375
+ stack.pop();
376
+ // this was workaround because I didn't know what `lsdk` section was, now it's probably not needed anymore
377
+ // } else if (l.name === '</Layer group>' && !l.sectionDivider && !l.top && !l.left && !l.bottom && !l.right) {
378
+ // // sometimes layer group terminator doesn't have sectionDivider, so we just guess here (PS bug ?)
379
+ // stack.pop();
380
+ } else {
381
+ stack[stack.length - 1].children!.unshift(l);
384
382
  }
385
- }, undefined, options.large);
386
-
387
- return globalAlpha;
383
+ }
388
384
  }
389
385
 
390
386
  function readLayerRecord(reader: PsdReader, psd: Psd, options: ReadOptionsExt) {
@@ -499,21 +495,20 @@ function readLayerBlendingRanges(reader: PsdReader) {
499
495
  });
500
496
  }
501
497
 
502
- function readLayerChannelImageData(
503
- reader: PsdReader, psd: Psd, layer: Layer, channels: ChannelInfo[], options: ReadOptionsExt
504
- ) {
498
+ function readLayerChannelImageData(reader: PsdReader, psd: Psd, layer: Layer, channels: ChannelInfo[], options: ReadOptionsExt) {
505
499
  const layerWidth = (layer.right || 0) - (layer.left || 0);
506
500
  const layerHeight = (layer.bottom || 0) - (layer.top || 0);
507
501
  const cmyk = psd.colorMode === ColorMode.CMYK;
508
502
 
509
- let imageData: ImageData | undefined;
503
+ let imageData: PixelData | undefined;
510
504
 
511
505
  if (layerWidth && layerHeight) {
512
506
  if (cmyk) {
507
+ if (psd.bitsPerChannel !== 8) throw new Error('bitsPerChannel Not supproted');
513
508
  imageData = { width: layerWidth, height: layerHeight, data: new Uint8ClampedArray(layerWidth * layerHeight * 5) } as any as ImageData;
514
509
  for (let p = 4; p < imageData.data.byteLength; p += 5) imageData.data[p] = 255;
515
510
  } else {
516
- imageData = createImageData(layerWidth, layerHeight);
511
+ imageData = createImageDataBitDepth(layerWidth, layerHeight, psd.bitsPerChannel ?? 8);
517
512
  resetImageData(imageData);
518
513
  }
519
514
  }
@@ -551,11 +546,11 @@ function readLayerChannelImageData(
551
546
  const maskHeight = (mask.bottom || 0) - (mask.top || 0);
552
547
 
553
548
  if (maskWidth && maskHeight) {
554
- const maskData = createImageData(maskWidth, maskHeight);
549
+ const maskData = createImageDataBitDepth(maskWidth, maskHeight, psd.bitsPerChannel ?? 8);
555
550
  resetImageData(maskData);
556
551
 
557
552
  const start = reader.offset;
558
- readData(reader, channel.length, maskData, compression, maskWidth, maskHeight, 0, options.large, 4);
553
+ readData(reader, channel.length, maskData, compression, maskWidth, maskHeight, psd.bitsPerChannel ?? 8, 0, options.large, 4);
559
554
 
560
555
  if (RAW_IMAGE_DATA) {
561
556
  (layer as any).maskDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
@@ -566,8 +561,7 @@ function readLayerChannelImageData(
566
561
  if (options.useImageData) {
567
562
  mask.imageData = maskData;
568
563
  } else {
569
- mask.canvas = createCanvas(maskWidth, maskHeight);
570
- mask.canvas.getContext('2d')!.putImageData(maskData, 0, 0);
564
+ mask.canvas = imageDataToCanvas(maskData);
571
565
  }
572
566
  }
573
567
  } else {
@@ -582,7 +576,7 @@ function readLayerChannelImageData(
582
576
  }
583
577
  }
584
578
 
585
- readData(reader, channel.length, targetData, compression, layerWidth, layerHeight, offset, options.large, cmyk ? 5 : 4);
579
+ readData(reader, channel.length, targetData, compression, layerWidth, layerHeight, psd.bitsPerChannel ?? 8, offset, options.large, cmyk ? 5 : 4);
586
580
 
587
581
  if (RAW_IMAGE_DATA) {
588
582
  (layer as any).imageDataRaw[channel.id] = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start + 2, channel.length - 2);
@@ -606,30 +600,26 @@ function readLayerChannelImageData(
606
600
  if (options.useImageData) {
607
601
  layer.imageData = imageData;
608
602
  } else {
609
- layer.canvas = createCanvas(layerWidth, layerHeight);
610
- layer.canvas.getContext('2d')!.putImageData(imageData, 0, 0);
603
+ layer.canvas = imageDataToCanvas(imageData);
611
604
  }
612
605
  }
613
606
  }
614
607
 
615
- function readData(
616
- reader: PsdReader, length: number, data: ImageData | undefined, compression: Compression, width: number, height: number,
617
- offset: number, large: boolean, step: number
618
- ) {
608
+ function readData(reader: PsdReader, length: number, data: PixelData | undefined, compression: Compression, width: number, height: number, bitDepth: number, offset: number, large: boolean, step: number) {
619
609
  if (compression === Compression.RawData) {
620
- readDataRaw(reader, data, width, height, step, offset);
610
+ readDataRaw(reader, data, width, height, bitDepth, step, offset);
621
611
  } else if (compression === Compression.RleCompressed) {
622
- readDataRLE(reader, data, width, height, step, [offset], large);
612
+ readDataRLE(reader, data, width, height, bitDepth, step, [offset], large);
623
613
  } else if (compression === Compression.ZipWithoutPrediction) {
624
- readDataZipWithoutPrediction(reader, length, data, width, height, step, offset);
614
+ readDataZip(reader, length, data, width, height, bitDepth, step, offset, false);
625
615
  } else if (compression === Compression.ZipWithPrediction) {
626
- throw new Error(`Compression type not supported: ${compression}`);
616
+ readDataZip(reader, length, data, width, height, bitDepth, step, offset, true);
627
617
  } else {
628
618
  throw new Error(`Invalid Compression type: ${compression}`);
629
619
  }
630
620
  }
631
621
 
632
- function readGlobalLayerMaskInfo(reader: PsdReader) {
622
+ export function readGlobalLayerMaskInfo(reader: PsdReader) {
633
623
  return readSection<GlobalLayerMaskInfo | undefined>(reader, 1, left => {
634
624
  if (!left()) return undefined;
635
625
 
@@ -645,7 +635,7 @@ function readGlobalLayerMaskInfo(reader: PsdReader) {
645
635
  });
646
636
  }
647
637
 
648
- function readAdditionalLayerInfo(reader: PsdReader, target: LayerAdditionalInfo, psd: Psd, options: ReadOptionsExt) {
638
+ export function readAdditionalLayerInfo(reader: PsdReader, target: LayerAdditionalInfo, psd: Psd, options: ReadOptionsExt) {
649
639
  const sig = readSignature(reader);
650
640
  if (sig !== '8BIM' && sig !== '8B64') throw new Error(`Invalid signature: '${sig}' at 0x${(reader.offset - 4).toString(16)}`);
651
641
  const key = readSignature(reader);
@@ -674,8 +664,21 @@ function readAdditionalLayerInfo(reader: PsdReader, target: LayerAdditionalInfo,
674
664
  }, false, u64);
675
665
  }
676
666
 
677
- function readImageData(reader: PsdReader, psd: Psd, globalAlpha: boolean, options: ReadOptionsExt) {
667
+ function createImageDataBitDepth(width: number, height: number, bitDepth: number): PixelData {
668
+ if (bitDepth === 1 || bitDepth === 8) {
669
+ return createImageData(width, height);
670
+ } else if (bitDepth === 16) {
671
+ return { width, height, data: new Uint16Array(width * height * 4) };
672
+ } else if (bitDepth === 32) {
673
+ return { width, height, data: new Float32Array(width * height * 4) };
674
+ } else {
675
+ throw new Error(`Invalid bitDepth (${bitDepth})`);
676
+ }
677
+ }
678
+
679
+ function readImageData(reader: PsdReader, psd: Psd, options: ReadOptionsExt) {
678
680
  const compression = readUint16(reader) as Compression;
681
+ const bitsPerChannel = psd.bitsPerChannel ?? 8;
679
682
 
680
683
  if (supportedColorModes.indexOf(psd.colorMode!) === -1)
681
684
  throw new Error(`Color mode not supported: ${psd.colorMode}`);
@@ -683,18 +686,20 @@ function readImageData(reader: PsdReader, psd: Psd, globalAlpha: boolean, option
683
686
  if (compression !== Compression.RawData && compression !== Compression.RleCompressed)
684
687
  throw new Error(`Compression type not supported: ${compression}`);
685
688
 
686
- const imageData = createImageData(psd.width, psd.height);
689
+ const imageData = createImageDataBitDepth(psd.width, psd.height, bitsPerChannel);
687
690
  resetImageData(imageData);
688
691
 
689
692
  switch (psd.colorMode) {
690
693
  case ColorMode.Bitmap: {
694
+ if (bitsPerChannel !== 1) throw new Error('Invalid bitsPerChannel for bitmap color mode');
695
+
691
696
  let bytes: Uint8Array;
692
697
 
693
698
  if (compression === Compression.RawData) {
694
699
  bytes = readBytes(reader, Math.ceil(psd.width / 8) * psd.height);
695
700
  } else if (compression === Compression.RleCompressed) {
696
701
  bytes = new Uint8Array(psd.width * psd.height);
697
- readDataRLE(reader, { data: bytes, width: psd.width, height: psd.height }, psd.width, psd.height, 1, [0], options.large);
702
+ readDataRLE(reader, { data: bytes, width: psd.width, height: psd.height }, psd.width, psd.height, 8, 1, [0], options.large);
698
703
  } else {
699
704
  throw new Error(`Bitmap compression not supported: ${compression}`);
700
705
  }
@@ -711,17 +716,17 @@ function readImageData(reader: PsdReader, psd: Psd, globalAlpha: boolean, option
711
716
  // TODO: store these channels in additional image data
712
717
  channels.push(i);
713
718
  }
714
- } else if (globalAlpha) {
719
+ } else if (options.globalAlpha) {
715
720
  channels.push(3);
716
721
  }
717
722
 
718
723
  if (compression === Compression.RawData) {
719
724
  for (let i = 0; i < channels.length; i++) {
720
- readDataRaw(reader, imageData, psd.width, psd.height, 4, channels[i]);
725
+ readDataRaw(reader, imageData, psd.width, psd.height, bitsPerChannel, 4, channels[i]);
721
726
  }
722
727
  } else if (compression === Compression.RleCompressed) {
723
728
  const start = reader.offset;
724
- readDataRLE(reader, imageData, psd.width, psd.height, 4, channels, options.large);
729
+ readDataRLE(reader, imageData, psd.width, psd.height, bitsPerChannel, 4, channels, options.large);
725
730
  if (RAW_IMAGE_DATA) (psd as any).imageDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
726
731
  }
727
732
 
@@ -731,10 +736,11 @@ function readImageData(reader: PsdReader, psd: Psd, globalAlpha: boolean, option
731
736
  break;
732
737
  }
733
738
  case ColorMode.CMYK: {
739
+ if (psd.bitsPerChannel !== 8) throw new Error('bitsPerChannel Not supproted');
734
740
  if (psd.channels !== 4) throw new Error(`Invalid channel count`);
735
741
 
736
742
  const channels = [0, 1, 2, 3];
737
- if (globalAlpha) channels.push(4);
743
+ if (options.globalAlpha) channels.push(4);
738
744
 
739
745
  if (compression === Compression.RawData) {
740
746
  throw new Error(`Not implemented`);
@@ -750,7 +756,7 @@ function readImageData(reader: PsdReader, psd: Psd, globalAlpha: boolean, option
750
756
  };
751
757
 
752
758
  const start = reader.offset;
753
- readDataRLE(reader, cmykImageData, psd.width, psd.height, 5, channels, options.large);
759
+ readDataRLE(reader, cmykImageData, psd.width, psd.height, psd.bitsPerChannel ?? 8, 5, channels, options.large);
754
760
  cmykToRgb(cmykImageData, imageData, true);
755
761
 
756
762
  if (RAW_IMAGE_DATA) (psd as any).imageDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
@@ -762,7 +768,8 @@ function readImageData(reader: PsdReader, psd: Psd, globalAlpha: boolean, option
762
768
  }
763
769
 
764
770
  // remove weird white matte
765
- if (globalAlpha) {
771
+ if (options.globalAlpha) {
772
+ if (psd.bitsPerChannel !== 8) throw new Error('bitsPerChannel Not supproted');
766
773
  const p = imageData.data;
767
774
  const size = imageData.width * imageData.height * 4;
768
775
  for (let i = 0; i < size; i += 4) {
@@ -781,8 +788,7 @@ function readImageData(reader: PsdReader, psd: Psd, globalAlpha: boolean, option
781
788
  if (options.useImageData) {
782
789
  psd.imageData = imageData;
783
790
  } else {
784
- psd.canvas = createCanvas(psd.width, psd.height);
785
- psd.canvas.getContext('2d')!.putImageData(imageData, 0, 0);
791
+ psd.canvas = imageDataToCanvas(imageData);
786
792
  }
787
793
  }
788
794
 
@@ -814,40 +820,114 @@ function cmykToRgb(cmyk: PixelData, rgb: PixelData, reverseAlpha: boolean) {
814
820
  // }
815
821
  }
816
822
 
817
- function readDataRaw(reader: PsdReader, pixelData: PixelData | undefined, width: number, height: number, step: number, offset: number) {
818
- const size = width * height;
819
- const buffer = readBytes(reader, size);
823
+ function verifyCompatible(a: PixelArray, b: PixelArray) {
824
+ if ((a.byteLength / a.length) !== (b.byteLength / b.length)) {
825
+ throw new Error('Invalid array types');
826
+ }
827
+ }
828
+
829
+ function bytesToArray(bytes: Uint8Array, bitDepth: number) {
830
+ if (bitDepth === 8) {
831
+ return bytes;
832
+ } else if (bitDepth === 16) {
833
+ if (bytes.byteOffset % 2) {
834
+ const result = new Uint16Array(bytes.byteLength / 2);
835
+ new Uint8Array(result.buffer, result.byteOffset, result.byteLength).set(bytes);
836
+ return result;
837
+ } else {
838
+ return new Uint16Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 2);
839
+ }
840
+ } else if (bitDepth === 32) {
841
+ if (bytes.byteOffset % 4) {
842
+ const result = new Float32Array(bytes.byteLength / 4);
843
+ new Uint8Array(result.buffer, result.byteOffset, result.byteLength).set(bytes);
844
+ return result;
845
+ } else {
846
+ return new Float32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength / 4);
847
+ }
848
+ } else {
849
+ throw new Error(`Invalid bitDepth (${bitDepth})`)
850
+ }
851
+ }
852
+
853
+ function copyChannelToPixelData(pixelData: PixelData, channel: PixelArray, offset: number, step: number) {
854
+ verifyCompatible(pixelData.data, channel);
855
+ const size = pixelData.width * pixelData.height;
856
+ const data = pixelData.data;
857
+ for (let i = 0, p = offset | 0; i < size; i++, p = (p + step) | 0) {
858
+ data[p] = channel[i];
859
+ }
860
+ }
861
+
862
+ function readDataRaw(reader: PsdReader, pixelData: PixelData | undefined, width: number, height: number, bitDepth: number, step: number, offset: number) {
863
+ const buffer = readBytes(reader, width * height * Math.floor(bitDepth / 8));
864
+
865
+ if (bitDepth == 32) {
866
+ for (let i = 0; i < buffer.byteLength; i += 4) {
867
+ const a = buffer[i + 0];
868
+ const b = buffer[i + 1];
869
+ const c = buffer[i + 2];
870
+ const d = buffer[i + 3];
871
+ buffer[i + 0] = d;
872
+ buffer[i + 1] = c;
873
+ buffer[i + 2] = b;
874
+ buffer[i + 3] = a;
875
+ }
876
+ }
877
+
878
+ const array = bytesToArray(buffer, bitDepth);
820
879
 
821
880
  if (pixelData && offset < step) {
822
- const data = pixelData.data;
881
+ copyChannelToPixelData(pixelData, array, offset, step);
882
+ }
883
+ }
823
884
 
824
- for (let i = 0, p = offset | 0; i < size; i++, p = (p + step) | 0) {
825
- data[p] = buffer[i];
885
+ function decodePredicted(data: Uint8Array | Uint16Array, width: number, height: number, mod: number) {
886
+ for (let y = 0; y < height; y++) {
887
+ const offset = y * width;
888
+
889
+ for (let x = 1, o = offset + 1; x < width; x++, o++) {
890
+ data[o] = (data[o - 1] + data[o]) % mod;
826
891
  }
827
892
  }
828
893
  }
829
894
 
830
- export function readDataZipWithoutPrediction(
831
- reader: PsdReader, length: number, pixelData: PixelData | undefined, width: number, height: number,
832
- step: number, offset: number
833
- ) {
895
+ export function readDataZip(reader: PsdReader, length: number, pixelData: PixelData | undefined, width: number, height: number, bitDepth: number, step: number, offset: number, prediction: boolean) {
834
896
  const compressed = readBytes(reader, length);
835
- const decompressed = inflate(compressed);
836
- const size = width * height;
897
+ const decompressed = inflateSync(compressed);
837
898
 
838
899
  if (pixelData && offset < step) {
839
- const data = pixelData.data;
840
-
841
- for (let i = 0, p = offset | 0; i < size; i++, p = (p + step) | 0) {
842
- data[p] = decompressed[i];
900
+ const array = bytesToArray(decompressed, bitDepth);
901
+
902
+ if (bitDepth === 8) {
903
+ if (prediction) decodePredicted(decompressed, width, height, 0x100);
904
+ copyChannelToPixelData(pixelData, decompressed, offset, step);
905
+ } else if (bitDepth === 16) {
906
+ if (prediction) decodePredicted(array as Uint16Array, width, height, 0x10000);
907
+ copyChannelToPixelData(pixelData, array, offset, step);
908
+ } else if (bitDepth === 32) {
909
+ if (prediction) decodePredicted(decompressed, width * 4, height, 0x100);
910
+
911
+ let di = offset;
912
+ const dst = new Uint32Array(pixelData.data.buffer, pixelData.data.byteOffset, pixelData.data.length);
913
+
914
+ for (let y = 0; y < height; y++) {
915
+ let a = width * 4 * y;
916
+
917
+ for (let x = 0; x < width; x++, a++, di += step) {
918
+ const b = a + width;
919
+ const c = b + width;
920
+ const d = c + width;
921
+ dst[di] = ((decompressed[a] << 24) | (decompressed[b] << 16) | (decompressed[c] << 8) | decompressed[d]) >>> 0;
922
+ }
923
+ }
924
+ } else {
925
+ throw new Error('Invalid bitDepth');
843
926
  }
844
927
  }
845
928
  }
846
929
 
847
- export function readDataRLE(
848
- reader: PsdReader, pixelData: PixelData | undefined, _width: number, height: number, step: number, offsets: number[],
849
- large: boolean
850
- ) {
930
+ export function readDataRLE(reader: PsdReader, pixelData: PixelData | undefined, _width: number, height: number, bitDepth: number, step: number, offsets: number[], large: boolean) {
851
931
  const data = pixelData && pixelData.data;
852
932
  let lengths: Uint16Array | Uint32Array;
853
933
 
@@ -869,6 +949,8 @@ export function readDataRLE(
869
949
  }
870
950
  }
871
951
 
952
+ if (bitDepth !== 1 && bitDepth !== 8) throw new Error(`Invalid bit depth (${bitDepth})`);
953
+
872
954
  const extraLimit = (step - 1) | 0; // 3 for rgb, 4 for cmyk
873
955
 
874
956
  for (let c = 0, li = 0; c < offsets.length; c++) {