@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/abr.d.ts +57 -4
- package/dist/abr.js +95 -18
- package/dist/additionalInfo.js +92 -10
- package/dist/ase.d.ts +32 -0
- package/dist/ase.js +78 -0
- package/dist/descriptor.d.ts +35 -0
- package/dist/helpers.d.ts +3 -18
- package/dist/helpers.js +16 -15
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -1
- package/dist/psd.d.ts +51 -0
- package/dist/psdReader.d.ts +4 -5
- package/dist/psdReader.js +150 -108
- package/package.json +2 -5
- package/dist/initializeCanvas.d.ts +0 -1
- package/dist/initializeCanvas.js +0 -21
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;
|
package/dist/psdReader.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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(
|
|
43
|
-
export declare function readDataRLE(reader: PsdReader, pixelData: PixelData | undefined, width: number, height: number,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
243
|
-
readSection(reader, 2,
|
|
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
|
-
|
|
302
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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 =
|
|
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 (
|
|
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,
|
|
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
|
|
465
|
-
if (
|
|
504
|
+
for (const { id, compression, data } of channels) {
|
|
505
|
+
if (!data)
|
|
466
506
|
continue;
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
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 ${
|
|
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,
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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 =
|
|
522
|
+
layer.maskDataRaw = data;
|
|
501
523
|
}
|
|
502
524
|
else {
|
|
503
|
-
;
|
|
504
525
|
layer.realMaskDataRawCompression = compression;
|
|
505
|
-
layer.realMaskDataRaw =
|
|
526
|
+
layer.realMaskDataRaw = data;
|
|
506
527
|
}
|
|
507
528
|
}
|
|
508
529
|
setupGrayscale(maskData);
|
|
509
|
-
|
|
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(
|
|
540
|
+
const offset = offsetForChannel(id, cmyk);
|
|
519
541
|
let targetData = imageData;
|
|
520
542
|
if (offset < 0) {
|
|
521
543
|
targetData = undefined;
|
|
522
|
-
if (
|
|
523
|
-
throw new Error(`Channel not supported: ${
|
|
544
|
+
if (throwForMissingFeatures) {
|
|
545
|
+
throw new Error(`Channel not supported: ${id}`);
|
|
524
546
|
}
|
|
525
547
|
}
|
|
526
|
-
readData(
|
|
527
|
-
if (RAW_IMAGE_DATA) {
|
|
528
|
-
;
|
|
529
|
-
layer.
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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,
|
|
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 =
|
|
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);
|
|
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
|
-
|
|
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) {
|