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.
- package/CHANGELOG.md +10 -0
- package/README_PSD.md +20 -2
- package/TODO +7 -0
- package/dist/abr.js +4 -4
- package/dist/abr.js.map +1 -1
- package/dist/additionalInfo.d.ts +3 -3
- package/dist/additionalInfo.js +107 -12
- package/dist/additionalInfo.js.map +1 -1
- package/dist/bundle.js +15305 -7643
- package/dist/descriptor.js +21 -16
- package/dist/descriptor.js.map +1 -1
- package/dist/engineData2.d.ts +1 -0
- package/dist/engineData2.js +349 -0
- package/dist/engineData2.js.map +1 -0
- package/dist/helpers.d.ts +2 -7
- package/dist/helpers.js +45 -12
- package/dist/helpers.js.map +1 -1
- package/dist/imageResources.js +42 -1
- package/dist/imageResources.js.map +1 -1
- package/dist/psd.d.ts +46 -3
- package/dist/psd.js +8 -1
- package/dist/psd.js.map +1 -1
- package/dist/psdReader.d.ts +11 -5
- package/dist/psdReader.js +213 -100
- package/dist/psdReader.js.map +1 -1
- package/dist/psdWriter.js +24 -4
- package/dist/psdWriter.js.map +1 -1
- package/dist/utf8.js +10 -4
- package/dist/utf8.js.map +1 -1
- package/dist-es/abr.js +4 -4
- package/dist-es/abr.js.map +1 -1
- package/dist-es/additionalInfo.d.ts +3 -3
- package/dist-es/additionalInfo.js +110 -15
- package/dist-es/additionalInfo.js.map +1 -1
- package/dist-es/descriptor.js +21 -16
- package/dist-es/descriptor.js.map +1 -1
- package/dist-es/engineData2.d.ts +1 -0
- package/dist-es/engineData2.js +345 -0
- package/dist-es/engineData2.js.map +1 -0
- package/dist-es/helpers.d.ts +2 -7
- package/dist-es/helpers.js +43 -11
- package/dist-es/helpers.js.map +1 -1
- package/dist-es/imageResources.js +42 -1
- package/dist-es/imageResources.js.map +1 -1
- package/dist-es/psd.d.ts +46 -3
- package/dist-es/psd.js +7 -0
- package/dist-es/psd.js.map +1 -1
- package/dist-es/psdReader.d.ts +11 -5
- package/dist-es/psdReader.js +212 -102
- package/dist-es/psdReader.js.map +1 -1
- package/dist-es/psdWriter.js +25 -5
- package/dist-es/psdWriter.js.map +1 -1
- package/dist-es/utf8.js +10 -4
- package/dist-es/utf8.js.map +1 -1
- package/package.json +6 -7
- package/src/abr.ts +4 -4
- package/src/additionalInfo.ts +156 -51
- package/src/descriptor.ts +14 -9
- package/src/engineData2.ts +367 -0
- package/src/helpers.ts +47 -20
- package/src/imageResources.ts +59 -2
- package/src/psd.ts +41 -5
- package/src/psdReader.ts +210 -128
- package/src/psdWriter.ts +33 -14
- package/src/utf8.ts +12 -4
package/src/psdReader.ts
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
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,
|
|
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 (
|
|
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
|
|
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 (
|
|
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 && !!
|
|
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,
|
|
278
|
+
handler.read(reader, psd.imageResources, left, options);
|
|
276
279
|
} catch (e) {
|
|
277
|
-
if (
|
|
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
|
-
|
|
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,
|
|
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,
|
|
322
|
+
}, undefined, options.large);
|
|
319
323
|
|
|
320
324
|
const hasChildren = psd.children && psd.children.length;
|
|
321
|
-
const skipComposite =
|
|
325
|
+
const skipComposite = options.skipCompositeImageData && (options.skipLayerImageData || hasChildren);
|
|
322
326
|
|
|
323
327
|
if (!skipComposite) {
|
|
324
|
-
readImageData(reader, psd,
|
|
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
|
|
338
|
+
export function readLayerInfo(reader: PsdReader, psd: Psd, options: ReadOptionsExt) {
|
|
339
|
+
let layerCount = readInt16(reader);
|
|
336
340
|
|
|
337
|
-
|
|
338
|
-
|
|
341
|
+
if (layerCount < 0) {
|
|
342
|
+
options.globalAlpha = true;
|
|
343
|
+
layerCount = -layerCount;
|
|
344
|
+
}
|
|
339
345
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
layerCount = -layerCount;
|
|
343
|
-
}
|
|
346
|
+
const layers: Layer[] = [];
|
|
347
|
+
const layerChannels: ChannelInfo[][] = [];
|
|
344
348
|
|
|
345
|
-
|
|
346
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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
|
-
}
|
|
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:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
614
|
+
readDataZip(reader, length, data, width, height, bitDepth, step, offset, false);
|
|
625
615
|
} else if (compression === Compression.ZipWithPrediction) {
|
|
626
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
818
|
-
|
|
819
|
-
|
|
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
|
-
|
|
881
|
+
copyChannelToPixelData(pixelData, array, offset, step);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
823
884
|
|
|
824
|
-
|
|
825
|
-
|
|
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
|
|
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 =
|
|
836
|
-
const size = width * height;
|
|
897
|
+
const decompressed = inflateSync(compressed);
|
|
837
898
|
|
|
838
899
|
if (pixelData && offset < step) {
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
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++) {
|