@yft-design/psd-lib 1.0.1 → 1.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.
package/dist/psd.d.ts CHANGED
@@ -1482,6 +1482,29 @@ export interface LayerAdditionalInfo {
1482
1482
  }[];
1483
1483
  };
1484
1484
  vowv?: number;
1485
+ pixelSource?: {
1486
+ type: 'vdPS';
1487
+ origin: {
1488
+ x: number;
1489
+ y: number;
1490
+ };
1491
+ interpretation: {
1492
+ interpretAlpha: string;
1493
+ profile: Uint8Array;
1494
+ };
1495
+ frameReader: {
1496
+ type: 'QTFR';
1497
+ link: {
1498
+ name: string;
1499
+ fullPath: string;
1500
+ originalPath: string;
1501
+ relativePath: string;
1502
+ alias: string;
1503
+ };
1504
+ mediaDescriptor: string;
1505
+ };
1506
+ showAlteredVideo: boolean;
1507
+ };
1485
1508
  engineData?: string;
1486
1509
  }
1487
1510
  export declare enum LayerCompCapturedInfo {
@@ -1720,6 +1743,32 @@ export interface Annotation {
1720
1743
  date: string;
1721
1744
  data: string | Uint8Array;
1722
1745
  }
1746
+ export declare const enum ChannelID {
1747
+ Color0 = 0,// red (rgb) / cyan (cmyk)
1748
+ Color1 = 1,// green (rgb) / magenta (cmyk)
1749
+ Color2 = 2,// blue (rgb) / yellow (cmyk)
1750
+ Color3 = 3,// - (rgb) / black (cmyk)
1751
+ Transparency = -1,
1752
+ UserMask = -2,
1753
+ RealUserMask = -3
1754
+ }
1755
+ export declare const enum Compression {
1756
+ RawData = 0,
1757
+ RleCompressed = 1,
1758
+ ZipWithoutPrediction = 2,
1759
+ ZipWithPrediction = 3
1760
+ }
1761
+ export interface LayerRawDataChannel {
1762
+ id: ChannelID;
1763
+ compression: Compression;
1764
+ data: Uint8Array | undefined;
1765
+ }
1766
+ export interface LayerRawData {
1767
+ colorMode: ColorMode;
1768
+ bitsPerChannel: number;
1769
+ channels: LayerRawDataChannel[];
1770
+ large: boolean;
1771
+ }
1723
1772
  export interface Layer extends LayerAdditionalInfo {
1724
1773
  top?: number;
1725
1774
  left?: number;
@@ -1733,6 +1782,7 @@ export interface Layer extends LayerAdditionalInfo {
1733
1782
  clipping?: boolean;
1734
1783
  canvas?: HTMLCanvasElement;
1735
1784
  imageData?: PixelData;
1785
+ rawData?: LayerRawData;
1736
1786
  children?: Layer[];
1737
1787
  /** Applies only for layer groups. */
1738
1788
  opened?: boolean;
@@ -1788,6 +1838,7 @@ export interface ReadOptions {
1788
1838
  * (image data will appear in `imageData` fields instead of `canvas` fields)
1789
1839
  * This avoids issues with canvas premultiplied alpha corrupting image data. */
1790
1840
  useImageData?: boolean;
1841
+ useRawData?: boolean;
1791
1842
  /** Loads thumbnail raw data instead of decoding it's content into canvas.
1792
1843
  * `thumnailRaw` field is used instead. */
1793
1844
  useRawThumbnail?: boolean;
@@ -1,6 +1,5 @@
1
- import { Compression } from './helpers';
1
+ import { Psd, Layer, ColorMode, LayerAdditionalInfo, ReadOptions, Color, PatternInfo, GlobalLayerMaskInfo, PixelData } from './psd';
2
2
  import { InternalImageResources } from './imageResources';
3
- import { Color, ColorMode, GlobalLayerMaskInfo, LayerAdditionalInfo, PatternInfo, PixelData, Psd, ReadOptions } from './psd';
4
3
  export declare const supportedColorModes: ColorMode[];
5
4
  export interface PsdReader extends ReadOptions {
6
5
  offset: number;
@@ -35,12 +34,12 @@ export declare function skipBytes(reader: PsdReader, count: number): void;
35
34
  export declare function checkSignature(reader: PsdReader, a: string, b?: string): void;
36
35
  export declare function readPsd(reader: PsdReader, readOptions?: ReadOptions): Psd;
37
36
  export declare function readLayerInfo(reader: PsdReader, psd: Psd, imageResources: InternalImageResources): void;
38
- export declare function readData(reader: PsdReader, length: number, data: PixelData | undefined, compression: Compression, width: number, height: number, bitDepth: number, offset: number, large: boolean, step: number): void;
37
+ export declare function decodeLayerImageData(layer: Layer, useImageData?: boolean, throwForMissingFeatures?: boolean): void;
39
38
  export declare function readGlobalLayerMaskInfo(reader: PsdReader): GlobalLayerMaskInfo | undefined;
40
39
  export declare function readAdditionalLayerInfo(reader: PsdReader, target: LayerAdditionalInfo, psd: Psd, imageResources: InternalImageResources): void;
41
40
  export declare function createImageDataBitDepth(width: number, height: number, bitDepth: number, channels?: number): PixelData;
42
- export declare function readDataZip(reader: PsdReader, length: number, pixelData: PixelData | undefined, width: number, height: number, bitDepth: number, step: number, offset: number, prediction: boolean): void;
43
- export declare function readDataRLE(reader: PsdReader, pixelData: PixelData | undefined, width: number, height: number, bitDepth: number, step: number, offsets: number[], large: boolean): void;
41
+ export declare function readDataZip(compressed: Uint8Array, pixelData: PixelData | undefined, width: number, height: number, bitDepth: number, step: number, offset: number, prediction: boolean): void;
42
+ export declare function readDataRLE(reader: PsdReader, pixelData: PixelData | undefined, width: number, height: number, _bitDepth: number, step: number, offsets: number[], large: boolean): void;
44
43
  export declare function readSection<T>(reader: PsdReader, round: number, func: (left: () => number) => T, skipEmpty?: boolean, eightBytes?: boolean): T | undefined;
45
44
  export declare function readColor(reader: PsdReader): Color;
46
45
  export declare function readPattern(reader: PsdReader): PatternInfo;
package/dist/psdReader.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { inflate as inflateSync } from 'pako';
2
+ import { resetImageData, offsetForChannel, decodeBitmap, createImageData, toBlendMode, RAW_IMAGE_DATA, largeAdditionalInfoKeys, imageDataToCanvas } from './helpers';
2
3
  import { infoHandlersMap } from './additionalInfo';
3
- import { RAW_IMAGE_DATA, createImageData, decodeBitmap, imageDataToCanvas, largeAdditionalInfoKeys, offsetForChannel, resetImageData, toBlendMode } from './helpers';
4
4
  import { resourceHandlersMap } from './imageResources';
5
5
  export const supportedColorModes = [0 /* ColorMode.Bitmap */, 1 /* ColorMode.Grayscale */, 3 /* ColorMode.RGB */, 2 /* ColorMode.Indexed */, 4 /* ColorMode.CMYK */];
6
6
  const colorModes = ['bitmap', 'grayscale', 'indexed', 'RGB', 'CMYK', 'multichannel', 'duotone', 'lab'];
@@ -72,10 +72,10 @@ export function readFixedPointPath32(reader) {
72
72
  export function readBytes(reader, length) {
73
73
  const start = reader.view.byteOffset + reader.offset;
74
74
  reader.offset += length;
75
- if (start + length > reader.view.buffer.byteLength) {
75
+ if ((start + length) > reader.view.buffer.byteLength) {
76
76
  // fix for broken PSD files that are missing part of file at the end
77
77
  warnOrThrow(reader, 'Reading bytes exceeding buffer length');
78
- if (length > 100 * 1024 * 1024)
78
+ if (length > (100 * 1024 * 1024))
79
79
  throw new Error('Reading past end of file'); // limit to 100MB
80
80
  const result = new Uint8Array(length);
81
81
  const len = Math.min(length, reader.view.byteLength - start);
@@ -91,17 +91,16 @@ export function readSignature(reader) {
91
91
  return readShortString(reader, 4);
92
92
  }
93
93
  export function validSignatureAt(reader, offset) {
94
- const sig = String.fromCharCode(reader.view.getUint8(offset)) +
95
- String.fromCharCode(reader.view.getUint8(offset + 1)) +
96
- String.fromCharCode(reader.view.getUint8(offset + 2)) +
97
- String.fromCharCode(reader.view.getUint8(offset + 3));
94
+ const sig = String.fromCharCode(reader.view.getUint8(offset))
95
+ + String.fromCharCode(reader.view.getUint8(offset + 1))
96
+ + String.fromCharCode(reader.view.getUint8(offset + 2))
97
+ + String.fromCharCode(reader.view.getUint8(offset + 3));
98
98
  return sig == '8BIM' || sig == '8B64';
99
99
  }
100
100
  export function readPascalString(reader, padTo) {
101
101
  let length = readUint8(reader);
102
102
  const text = length ? readShortString(reader, length) : '';
103
- while (++length % padTo) {
104
- // starts with length + 1 so we count the size byte too
103
+ while (++length % padTo) { // starts with length + 1 so we count the size byte too
105
104
  reader.offset++;
106
105
  }
107
106
  return text;
@@ -114,8 +113,7 @@ export function readUnicodeStringWithLength(reader, length) {
114
113
  let text = '';
115
114
  while (length--) {
116
115
  const value = readUint16(reader);
117
- if (value || length > 0) {
118
- // remove trailing \0
116
+ if (value || length > 0) { // remove trailing \0
119
117
  text += String.fromCharCode(value);
120
118
  }
121
119
  }
@@ -125,8 +123,7 @@ export function readUnicodeStringWithLengthLE(reader, length) {
125
123
  let text = '';
126
124
  while (length--) {
127
125
  const value = readUint16LE(reader);
128
- if (value || length > 0) {
129
- // remove trailing \0
126
+ if (value || length > 0) { // remove trailing \0
130
127
  text += String.fromCharCode(value);
131
128
  }
132
129
  }
@@ -186,7 +183,7 @@ export function readPsd(reader, readOptions = {}) {
186
183
  reader.large = version === 2;
187
184
  reader.globalAlpha = false;
188
185
  // color mode data
189
- readSection(reader, 1, (left) => {
186
+ readSection(reader, 1, left => {
190
187
  if (!left())
191
188
  return;
192
189
  if (colorMode === 2 /* ColorMode.Indexed */) {
@@ -209,12 +206,12 @@ export function readPsd(reader, readOptions = {}) {
209
206
  });
210
207
  // image resources
211
208
  const imageResources = {};
212
- readSection(reader, 1, (left) => {
209
+ readSection(reader, 1, left => {
213
210
  while (left() > 0) {
214
211
  realignWithSignature(reader, isValidSignature);
215
212
  const id = readUint16(reader);
216
213
  readPascalString(reader, 2); // name
217
- readSection(reader, 2, (left) => {
214
+ readSection(reader, 2, left => {
218
215
  const handler = resourceHandlersMap[id];
219
216
  const skip = id === 1036 && !!reader.skipThumbnail;
220
217
  if (handler && !skip) {
@@ -239,8 +236,8 @@ export function readPsd(reader, readOptions = {}) {
239
236
  psd.imageResources = rest;
240
237
  }
241
238
  // layer and mask info
242
- readSection(reader, 1, (left) => {
243
- readSection(reader, 2, (left) => {
239
+ readSection(reader, 1, left => {
240
+ readSection(reader, 2, left => {
244
241
  readLayerInfo(reader, psd, imageResources);
245
242
  skipBytes(reader, left());
246
243
  }, undefined, reader.large);
@@ -298,10 +295,8 @@ export function readLayerInfo(reader, psd, imageResources) {
298
295
  layers.push(layer);
299
296
  layerChannels.push(channels);
300
297
  }
301
- if (!reader.skipLayerImageData) {
302
- for (let i = 0; i < layerCount; i++) {
303
- readLayerChannelImageData(reader, psd, layers[i], layerChannels[i]);
304
- }
298
+ for (let i = 0; i < layerCount; i++) {
299
+ readLayerChannelImageData(reader, psd, layers[i], layerChannels[i]);
305
300
  }
306
301
  if (!psd.children)
307
302
  psd.children = [];
@@ -365,7 +360,7 @@ function readLayerRecord(reader, psd, imageResources) {
365
360
  // 0x10 - pixel data irrelevant to appearance of document
366
361
  // 0x20 - effects/filters panel is expanded
367
362
  skipBytes(reader, 1);
368
- readSection(reader, 1, (left) => {
363
+ readSection(reader, 1, left => {
369
364
  readLayerMaskData(reader, layer);
370
365
  const blendingRanges = readLayerBlendingRanges(reader);
371
366
  if (blendingRanges)
@@ -381,7 +376,7 @@ function readLayerRecord(reader, psd, imageResources) {
381
376
  return { layer, channels };
382
377
  }
383
378
  function readLayerMaskData(reader, layer) {
384
- return readSection(reader, 1, (left) => {
379
+ return readSection(reader, 1, left => {
385
380
  if (!left())
386
381
  return undefined;
387
382
  const mask = {};
@@ -426,7 +421,7 @@ function readBlendingRange(reader) {
426
421
  return [readUint8(reader), readUint8(reader), readUint8(reader), readUint8(reader)];
427
422
  }
428
423
  function readLayerBlendingRanges(reader) {
429
- return readSection(reader, 1, (left) => {
424
+ return readSection(reader, 1, left => {
430
425
  const compositeGrayBlendSource = readBlendingRange(reader);
431
426
  const compositeGraphBlendDestinationRange = readBlendingRange(reader);
432
427
  const ranges = [];
@@ -439,74 +434,101 @@ function readLayerBlendingRanges(reader) {
439
434
  });
440
435
  }
441
436
  function readLayerChannelImageData(reader, psd, layer, channels) {
437
+ if (reader.skipLayerImageData)
438
+ return;
439
+ const { colorMode = 3 /* ColorMode.RGB */, bitsPerChannel = 8 } = psd;
440
+ layer.rawData = { colorMode, bitsPerChannel, channels: [], large: reader.large };
441
+ for (const channel of channels) {
442
+ const start = reader.offset;
443
+ let compression = 0 /* Compression.RawData */;
444
+ let data = undefined;
445
+ if (channel.length === 1)
446
+ throw new Error('Invalid channel length');
447
+ if (channel.length) {
448
+ compression = readUint16(reader);
449
+ // try to fix broken files where there's 1 byte shift of channel
450
+ if (compression > 3) {
451
+ reader.offset -= 1;
452
+ compression = readUint16(reader);
453
+ }
454
+ // try to fix broken files where there's 1 byte shift of channel
455
+ if (compression > 3) {
456
+ reader.offset -= 3;
457
+ compression = readUint16(reader);
458
+ }
459
+ if (compression > 3)
460
+ throw new Error(`Invalid compression: ${compression}`);
461
+ if (channel.length > 2) {
462
+ data = readBytes(reader, channel.length - 2);
463
+ }
464
+ }
465
+ reader.offset = start + channel.length;
466
+ layer.rawData.channels.push({ id: channel.id, compression, data });
467
+ }
468
+ if (!reader.useRawData) {
469
+ decodeLayerImageData(layer, !!reader.useImageData, !!reader.throwForMissingFeatures);
470
+ }
471
+ }
472
+ function resetAlpha({ data }, cmyk) {
473
+ const alpha = (data instanceof Float32Array) ? 1.0 : ((data instanceof Uint16Array) ? 0xffff : 0xff);
474
+ const offset = (cmyk ? 4 : 3) | 0;
475
+ const length = data.length | 0;
476
+ const step = (cmyk ? 5 : 4) | 0;
477
+ for (let p = offset; p < length; p = (p + step) | 0) {
478
+ data[p] = alpha;
479
+ }
480
+ }
481
+ export function decodeLayerImageData(layer, useImageData, throwForMissingFeatures) {
482
+ if (!layer.rawData)
483
+ return;
484
+ const { colorMode, bitsPerChannel, channels, large } = layer.rawData;
442
485
  const layerWidth = (layer.right || 0) - (layer.left || 0);
443
486
  const layerHeight = (layer.bottom || 0) - (layer.top || 0);
444
- const cmyk = psd.colorMode === 4 /* ColorMode.CMYK */;
487
+ const cmyk = colorMode === 4 /* ColorMode.CMYK */;
445
488
  let imageData;
489
+ let initializedAlpha = false;
446
490
  if (layerWidth && layerHeight) {
447
491
  if (cmyk) {
448
- if (psd.bitsPerChannel !== 8)
492
+ if (bitsPerChannel !== 8)
449
493
  throw new Error('bitsPerChannel Not supproted');
450
494
  imageData = { width: layerWidth, height: layerHeight, data: new Uint8ClampedArray(layerWidth * layerHeight * 5) };
451
- for (let p = 4; p < imageData.data.byteLength; p += 5)
452
- imageData.data[p] = 255;
453
495
  }
454
496
  else {
455
- imageData = createImageDataBitDepth(layerWidth, layerHeight, psd.bitsPerChannel ?? 8);
456
- resetImageData(imageData);
497
+ imageData = createImageDataBitDepth(layerWidth, layerHeight, bitsPerChannel);
457
498
  }
458
499
  }
459
- if (RAW_IMAGE_DATA) {
460
- ;
500
+ if (RAW_IMAGE_DATA) { // TODO: use layer.rawData instead
461
501
  layer.imageDataRaw = [];
462
502
  layer.imageDataRawCompression = [];
463
503
  }
464
- for (const channel of channels) {
465
- if (channel.length === 0)
504
+ for (const { id, compression, data } of channels) {
505
+ if (!data)
466
506
  continue;
467
- if (channel.length < 2)
468
- throw new Error('Invalid channel length');
469
- const start = reader.offset;
470
- let compression = readUint16(reader);
471
- // try to fix broken files where there's 1 byte shift of channel
472
- if (compression > 3) {
473
- reader.offset -= 1;
474
- compression = readUint16(reader);
475
- }
476
- // try to fix broken files where there's 1 byte shift of channel
477
- if (compression > 3) {
478
- reader.offset -= 3;
479
- compression = readUint16(reader);
480
- }
481
- if (compression > 3)
482
- throw new Error(`Invalid compression: ${compression}`);
483
- if (channel.id === -2 /* ChannelID.UserMask */ || channel.id === -3 /* ChannelID.RealUserMask */) {
484
- const mask = channel.id === -2 /* ChannelID.UserMask */ ? layer.mask : layer.realMask;
507
+ const dataReader = createReader(data.buffer, data.byteOffset, data.byteLength);
508
+ if (id === -2 /* ChannelID.UserMask */ || id === -3 /* ChannelID.RealUserMask */) {
509
+ const mask = id === -2 /* ChannelID.UserMask */ ? layer.mask : layer.realMask;
485
510
  if (!mask)
486
- throw new Error(`Missing layer ${channel.id === -2 /* ChannelID.UserMask */ ? 'mask' : 'real mask'} data`);
511
+ throw new Error(`Missing layer ${id === -2 /* ChannelID.UserMask */ ? 'mask' : 'real mask'} data`);
487
512
  const maskWidth = (mask.right || 0) - (mask.left || 0);
488
513
  const maskHeight = (mask.bottom || 0) - (mask.top || 0);
489
514
  if (maskWidth < 0 || maskHeight < 0 || maskWidth > 30000 || maskHeight > 30000)
490
515
  throw new Error('Invalid mask size');
491
516
  if (maskWidth && maskHeight) {
492
- const maskData = createImageDataBitDepth(maskWidth, maskHeight, psd.bitsPerChannel ?? 8);
493
- resetImageData(maskData);
494
- const start = reader.offset;
495
- readData(reader, channel.length, maskData, compression, maskWidth, maskHeight, psd.bitsPerChannel ?? 8, 0, reader.large, 4);
496
- if (RAW_IMAGE_DATA) {
497
- if (channel.id === -2 /* ChannelID.UserMask */) {
498
- ;
517
+ const maskData = createImageDataBitDepth(maskWidth, maskHeight, bitsPerChannel);
518
+ readData(dataReader, data.byteLength, maskData, compression, maskWidth, maskHeight, bitsPerChannel, 0, large, 4);
519
+ if (RAW_IMAGE_DATA) { // TODO: use layer.rawData instead
520
+ if (id === -2 /* ChannelID.UserMask */) {
499
521
  layer.maskDataRawCompression = compression;
500
- layer.maskDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
522
+ layer.maskDataRaw = data;
501
523
  }
502
524
  else {
503
- ;
504
525
  layer.realMaskDataRawCompression = compression;
505
- layer.realMaskDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
526
+ layer.realMaskDataRaw = data;
506
527
  }
507
528
  }
508
529
  setupGrayscale(maskData);
509
- if (reader.useImageData) {
530
+ resetAlpha(maskData, false);
531
+ if (useImageData) {
510
532
  mask.imageData = maskData;
511
533
  }
512
534
  else {
@@ -515,59 +537,72 @@ function readLayerChannelImageData(reader, psd, layer, channels) {
515
537
  }
516
538
  }
517
539
  else {
518
- const offset = offsetForChannel(channel.id, cmyk);
540
+ const offset = offsetForChannel(id, cmyk);
519
541
  let targetData = imageData;
520
542
  if (offset < 0) {
521
543
  targetData = undefined;
522
- if (reader.throwForMissingFeatures) {
523
- throw new Error(`Channel not supported: ${channel.id}`);
544
+ if (throwForMissingFeatures) {
545
+ throw new Error(`Channel not supported: ${id}`);
524
546
  }
525
547
  }
526
- readData(reader, channel.length, targetData, compression, layerWidth, layerHeight, psd.bitsPerChannel ?? 8, offset, reader.large, cmyk ? 5 : 4);
527
- if (RAW_IMAGE_DATA) {
528
- ;
529
- layer.imageDataRawCompression[channel.id] = compression;
530
- layer.imageDataRaw[channel.id] = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start + 2, channel.length - 2);
548
+ readData(dataReader, data.byteLength, targetData, compression, layerWidth, layerHeight, bitsPerChannel, offset, large, cmyk ? 5 : 4);
549
+ if (RAW_IMAGE_DATA) { // TODO: use layer.rawData instead
550
+ layer.imageDataRawCompression[id] = compression;
551
+ layer.imageDataRaw[id] = data;
531
552
  }
532
- reader.offset = start + channel.length;
533
- if (targetData && psd.colorMode === 1 /* ColorMode.Grayscale */) {
553
+ if (targetData && colorMode === 1 /* ColorMode.Grayscale */) {
534
554
  setupGrayscale(targetData);
535
555
  }
536
556
  }
557
+ if (id === -1 /* ChannelID.Transparency */) {
558
+ initializedAlpha = true;
559
+ }
537
560
  }
538
561
  if (imageData) {
562
+ if (!initializedAlpha)
563
+ resetAlpha(imageData, cmyk);
539
564
  if (cmyk) {
540
565
  const cmykData = imageData;
541
566
  imageData = createImageData(cmykData.width, cmykData.height);
542
567
  cmykToRgb(cmykData, imageData, false);
543
568
  }
544
- if (reader.useImageData) {
569
+ if (useImageData) {
545
570
  layer.imageData = imageData;
546
571
  }
547
572
  else {
548
573
  layer.canvas = imageDataToCanvas(imageData);
549
574
  }
550
575
  }
576
+ delete layer.rawData;
551
577
  }
552
- export function readData(reader, length, data, compression, width, height, bitDepth, offset, large, step) {
578
+ function readData(reader, length, pixels, compression, width, height, bitDepth, offset, large, step) {
579
+ if (!length)
580
+ return;
553
581
  if (compression === 0 /* Compression.RawData */) {
554
- readDataRaw(reader, data, width, height, bitDepth, step, offset);
582
+ if (length !== (width * height * Math.floor(bitDepth / 8))) {
583
+ reader.log(`Invalid length (${length}, ${width * height * Math.floor(bitDepth / 8)})`);
584
+ }
585
+ const data = readBytes(reader, length);
586
+ readDataRaw(data, pixels, bitDepth, step, offset);
555
587
  }
556
588
  else if (compression === 1 /* Compression.RleCompressed */) {
557
- readDataRLE(reader, data, width, height, bitDepth, step, [offset], large);
589
+ // const reader = createReader(data.buffer, data.byteOffset, data.byteLength);
590
+ readDataRLE(reader, pixels, width, height, bitDepth, step, [offset], large);
558
591
  }
559
592
  else if (compression === 2 /* Compression.ZipWithoutPrediction */) {
560
- readDataZip(reader, length, data, width, height, bitDepth, step, offset, false);
593
+ const data = readBytes(reader, length);
594
+ readDataZip(data, pixels, width, height, bitDepth, step, offset, false);
561
595
  }
562
596
  else if (compression === 3 /* Compression.ZipWithPrediction */) {
563
- readDataZip(reader, length, data, width, height, bitDepth, step, offset, true);
597
+ const data = readBytes(reader, length);
598
+ readDataZip(data, pixels, width, height, bitDepth, step, offset, true);
564
599
  }
565
600
  else {
566
601
  throw new Error(`Invalid Compression type: ${compression}`);
567
602
  }
568
603
  }
569
604
  export function readGlobalLayerMaskInfo(reader) {
570
- return readSection(reader, 1, (left) => {
605
+ return readSection(reader, 1, left => {
571
606
  if (!left())
572
607
  return undefined;
573
608
  const overlayColorSpace = readUint16(reader);
@@ -596,7 +631,7 @@ function realignWithSignature(reader, isValid) {
596
631
  break;
597
632
  }
598
633
  if (!isValid(sig)) {
599
- throw new Error(`Invalid signature: '${sig}' at 0x${sigOffset.toString(16)}`);
634
+ throw new Error(`Invalid signature: '${sig}' at 0x${(sigOffset).toString(16)}`);
600
635
  }
601
636
  return sig;
602
637
  }
@@ -608,7 +643,7 @@ export function readAdditionalLayerInfo(reader, target, psd, imageResources) {
608
643
  const key = readSignature(reader);
609
644
  // `largeAdditionalInfoKeys` fallback, because some keys don't have 8B64 signature even when they are 64bit
610
645
  const u64 = sig === '8B64' || (reader.large && largeAdditionalInfoKeys.indexOf(key) !== -1);
611
- readSection(reader, 2, (left) => {
646
+ readSection(reader, 2, left => {
612
647
  const handler = infoHandlersMap[key];
613
648
  if (handler) {
614
649
  try {
@@ -689,7 +724,8 @@ function readImageData(reader, psd) {
689
724
  }
690
725
  if (compression === 0 /* Compression.RawData */) {
691
726
  for (let i = 0; i < channels.length; i++) {
692
- readDataRaw(reader, imageData, psd.width, psd.height, bitsPerChannel, 4, channels[i]);
727
+ const data = readBytes(reader, psd.width * psd.height * Math.floor(bitsPerChannel / 8));
728
+ readDataRaw(data, imageData, bitsPerChannel, 4, channels[i]);
693
729
  }
694
730
  }
695
731
  else if (compression === 1 /* Compression.RleCompressed */) {
@@ -717,7 +753,7 @@ function readImageData(reader, psd) {
717
753
  const indexedImageData = {
718
754
  width: imageData.width,
719
755
  height: imageData.height,
720
- data: new Uint8Array(imageData.width * imageData.height)
756
+ data: new Uint8Array(imageData.width * imageData.height),
721
757
  };
722
758
  readDataRLE(reader, indexedImageData, psd.width, psd.height, bitsPerChannel, 1, [0], reader.large);
723
759
  indexedToRgb(indexedImageData, imageData, psd.palette);
@@ -730,7 +766,7 @@ function readImageData(reader, psd) {
730
766
  case 4 /* ColorMode.CMYK */: {
731
767
  if (bitsPerChannel !== 8)
732
768
  throw new Error('bitsPerChannel Not supproted');
733
- if (psd.channels !== 4 && psd.channels !== 5)
769
+ if (psd.channels !== 4)
734
770
  throw new Error(`Invalid channel count`);
735
771
  const channels = [0, 1, 2, 3];
736
772
  if (reader.globalAlpha)
@@ -746,7 +782,7 @@ function readImageData(reader, psd) {
746
782
  const cmykImageData = {
747
783
  width: imageData.width,
748
784
  height: imageData.height,
749
- data: new Uint8Array(imageData.width * imageData.height * 5)
785
+ data: new Uint8Array(imageData.width * imageData.height * 5),
750
786
  };
751
787
  const start = reader.offset;
752
788
  readDataRLE(reader, cmykImageData, psd.width, psd.height, bitsPerChannel, 5, channels, reader.large);
@@ -759,8 +795,7 @@ function readImageData(reader, psd) {
759
795
  }
760
796
  break;
761
797
  }
762
- default:
763
- throw new Error(`Color mode not supported: ${psd.colorMode}`);
798
+ default: throw new Error(`Color mode not supported: ${psd.colorMode}`);
764
799
  }
765
800
  // remove weird white matte
766
801
  if (reader.globalAlpha) {
@@ -796,9 +831,9 @@ function cmykToRgb(cmyk, rgb, reverseAlpha) {
796
831
  const m = srcData[src + 1];
797
832
  const y = srcData[src + 2];
798
833
  const k = srcData[src + 3];
799
- dstData[dst] = (((c * k) | 0) / 255) | 0;
800
- dstData[dst + 1] = (((m * k) | 0) / 255) | 0;
801
- dstData[dst + 2] = (((y * k) | 0) / 255) | 0;
834
+ dstData[dst] = ((((c * k) | 0) / 255) | 0);
835
+ dstData[dst + 1] = ((((m * k) | 0) / 255) | 0);
836
+ dstData[dst + 2] = ((((y * k) | 0) / 255) | 0);
802
837
  dstData[dst + 3] = reverseAlpha ? 255 - srcData[src + 4] : srcData[src + 4];
803
838
  }
804
839
  // for (let src = 0, dst = 0; dst < size; src += 5, dst += 4) {
@@ -825,7 +860,7 @@ function indexedToRgb(indexed, rgb, palette) {
825
860
  }
826
861
  }
827
862
  function verifyCompatible(a, b) {
828
- if (a.byteLength / a.length !== b.byteLength / b.length) {
863
+ if ((a.byteLength / a.length) !== (b.byteLength / b.length)) {
829
864
  throw new Error('Invalid array types');
830
865
  }
831
866
  }
@@ -834,6 +869,13 @@ function bytesToArray(bytes, bitDepth) {
834
869
  return bytes;
835
870
  }
836
871
  else if (bitDepth === 16) {
872
+ // PSD files store 16-bit channel data in big-endian byte order.
873
+ // Swap each pair of bytes so that Uint16Array (native-endian) reads the correct values.
874
+ for (let i = 0; i < bytes.byteLength; i += 2) {
875
+ const tmp = bytes[i];
876
+ bytes[i] = bytes[i + 1];
877
+ bytes[i + 1] = tmp;
878
+ }
837
879
  if (bytes.byteOffset % 2) {
838
880
  const result = new Uint16Array(bytes.byteLength / 2);
839
881
  new Uint8Array(result.buffer, result.byteOffset, result.byteLength).set(bytes);
@@ -865,8 +907,7 @@ function copyChannelToPixelData(pixelData, channel, offset, step) {
865
907
  data[p] = channel[i];
866
908
  }
867
909
  }
868
- function readDataRaw(reader, pixelData, width, height, bitDepth, step, offset) {
869
- const buffer = readBytes(reader, width * height * Math.floor(bitDepth / 8));
910
+ function readDataRaw(buffer, pixelData, bitDepth, step, offset) {
870
911
  if (bitDepth == 32) {
871
912
  for (let i = 0; i < buffer.byteLength; i += 4) {
872
913
  const a = buffer[i + 0];
@@ -892,8 +933,7 @@ function decodePredicted(data, width, height, mod) {
892
933
  }
893
934
  }
894
935
  }
895
- export function readDataZip(reader, length, pixelData, width, height, bitDepth, step, offset, prediction) {
896
- const compressed = readBytes(reader, length);
936
+ export function readDataZip(compressed, pixelData, width, height, bitDepth, step, offset, prediction) {
897
937
  const decompressed = inflateSync(compressed);
898
938
  if (pixelData && offset < step) {
899
939
  const array = bytesToArray(decompressed, bitDepth);
@@ -927,7 +967,7 @@ export function readDataZip(reader, length, pixelData, width, height, bitDepth,
927
967
  }
928
968
  }
929
969
  }
930
- export function readDataRLE(reader, pixelData, width, height, bitDepth, step, offsets, large) {
970
+ export function readDataRLE(reader, pixelData, width, height, _bitDepth, step, offsets, large) {
931
971
  const data = pixelData && pixelData.data;
932
972
  let lengths;
933
973
  if (large) {
@@ -946,8 +986,7 @@ export function readDataRLE(reader, pixelData, width, height, bitDepth, step, of
946
986
  }
947
987
  }
948
988
  }
949
- if (bitDepth !== 1 && bitDepth !== 8)
950
- throw new Error(`Invalid bit depth (${bitDepth})`);
989
+ // if (bitDepth !== 1 && bitDepth !== 8) throw new Error(`Invalid bit depth (${bitDepth})`);
951
990
  const extraLimit = (step - 1) | 0; // 3 for rgb, 4 for cmyk
952
991
  for (let c = 0, li = 0; c < offsets.length; c++) {
953
992
  const offset = offsets[c] | 0;
@@ -1044,13 +1083,13 @@ export function readColor(reader) {
1044
1083
  const l = readInt16(reader) / 10000;
1045
1084
  const ta = readInt16(reader);
1046
1085
  const tb = readInt16(reader);
1047
- const a = ta < 0 ? ta / 12800 : ta / 12700;
1048
- const b = tb < 0 ? tb / 12800 : tb / 12700;
1086
+ const a = ta < 0 ? (ta / 12800) : (ta / 12700);
1087
+ const b = tb < 0 ? (tb / 12800) : (tb / 12700);
1049
1088
  skipBytes(reader, 2);
1050
1089
  return { l, a, b };
1051
1090
  }
1052
1091
  case 8 /* ColorSpace.Grayscale */: {
1053
- const k = (readUint16(reader) * 255) / 10000;
1092
+ const k = readUint16(reader) * 255 / 10000;
1054
1093
  skipBytes(reader, 6);
1055
1094
  return { k };
1056
1095
  }
@@ -1059,7 +1098,10 @@ export function readColor(reader) {
1059
1098
  }
1060
1099
  }
1061
1100
  export function readPattern(reader) {
1062
- readUint32(reader); // length
1101
+ let length = readUint32(reader);
1102
+ while (length % 4)
1103
+ length++;
1104
+ const end = reader.offset + length;
1063
1105
  const version = readUint32(reader);
1064
1106
  if (version !== 1)
1065
1107
  throw new Error(`Invalid pattern version: ${version}`);
@@ -1078,7 +1120,7 @@ export function readPattern(reader) {
1078
1120
  palette.push({
1079
1121
  r: readUint8(reader),
1080
1122
  g: readUint8(reader),
1081
- b: readUint8(reader)
1123
+ b: readUint8(reader),
1082
1124
  });
1083
1125
  }
1084
1126
  skipBytes(reader, 4); // no idea what this is
@@ -1099,7 +1141,7 @@ export function readPattern(reader) {
1099
1141
  for (let i = 3; i < data.byteLength; i += 4) {
1100
1142
  data[i] = 255;
1101
1143
  }
1102
- for (let i = 0, ch = 0; i < channelsCount + 2; i++) {
1144
+ for (let i = 0, ch = 0; i < (channelsCount + 2); i++) {
1103
1145
  const has = readUint32(reader);
1104
1146
  if (!has)
1105
1147
  continue;
@@ -1170,7 +1212,7 @@ export function readPattern(reader) {
1170
1212
  }
1171
1213
  ch++;
1172
1214
  }
1173
- // TODO: use canvas instead of data ?
1215
+ reader.offset = end;
1174
1216
  return { id, name, x, y, bounds: { x: left, y: top, w: width, h: height }, data };
1175
1217
  }
1176
1218
  function copyChannelToRGBA(srcData, dstData, ox, oy, offset) {