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