ag-psd 30.1.1 → 30.2.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.13.1-x64-cf738c9d/017325ac +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/043c7b90 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/05135617 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/059f8558 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/0706ecb8 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/0db754a7 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/1130885f +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/15f0e479 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/20477c23 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/20dfa422 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/2a19133e +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/2b069ee0 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/2de09439 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/2e44f5f0 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/2fb229a9 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/3144b401 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/37481985 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/4511bacf +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/49d22b79 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/4f5702bd +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/514cc428 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/57d3380b +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/6024ce3f +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/61735b23 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/63dbdb75 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/6cbfc0ec +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/6f08372c +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/7100ee08 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/71df9cb3 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/75e41e43 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/7ea06cea +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/8568576b +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/87372817 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/87cf2209 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/932f0770 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/a79a40bb +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/a7e80865 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/acc36e66 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/b3c2fab7 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/b69ed3a3 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/bdff1628 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/c0b789d2 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/c314aece +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/cfc49f4f +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/cff9434a +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/d02dd6f7 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/dc86f000 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/de74e659 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/e03e2acd +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/e158d332 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/e6735171 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/e6745c2b +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/e6b99945 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/eeeebdfe +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/fbca6276 +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/fc97d72d +0 -0
- package/.v8-cache/v24.13.1-x64-cf738c9d/fd0a2987 +0 -0
- package/CHANGELOG.md +3 -0
- package/README.md +1 -1
- package/README_PSD.md +1 -1
- package/dist/abr.js +43 -56
- package/dist/abr.js.map +1 -1
- package/dist/additionalInfo.js +770 -808
- package/dist/additionalInfo.js.map +1 -1
- package/dist/ase.js +21 -21
- package/dist/ase.js.map +1 -1
- package/dist/bundle.js +2592 -2778
- package/dist/csh.js +18 -29
- package/dist/csh.js.map +1 -1
- package/dist/descriptor.js +234 -269
- package/dist/descriptor.js.map +1 -1
- package/dist/effectsHelpers.js +91 -112
- package/dist/effectsHelpers.js.map +1 -1
- package/dist/engineData.js +45 -51
- package/dist/engineData.js.map +1 -1
- package/dist/engineData2.js +8 -9
- package/dist/engineData2.js.map +1 -1
- package/dist/helpers.js +75 -81
- package/dist/helpers.js.map +1 -1
- package/dist/imageResources.js +342 -381
- package/dist/imageResources.js.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/initializeCanvas.js +2 -2
- package/dist/initializeCanvas.js.map +1 -1
- package/dist/jpeg.js +279 -279
- package/dist/jpeg.js.map +1 -1
- package/dist/psdReader.js +336 -347
- package/dist/psdReader.js.map +1 -1
- package/dist/psdWriter.d.ts +2 -1
- package/dist/psdWriter.js +225 -206
- package/dist/psdWriter.js.map +1 -1
- package/dist/text.js +98 -122
- package/dist/text.js.map +1 -1
- package/dist/utf8.js +22 -22
- package/dist/utf8.js.map +1 -1
- package/dist-es/abr.js +41 -54
- package/dist-es/abr.js.map +1 -1
- package/dist-es/additionalInfo.js +765 -803
- package/dist-es/additionalInfo.js.map +1 -1
- package/dist-es/ase.js +20 -20
- package/dist-es/ase.js.map +1 -1
- package/dist-es/csh.js +16 -27
- package/dist-es/csh.js.map +1 -1
- package/dist-es/descriptor.js +284 -319
- package/dist-es/descriptor.js.map +1 -1
- package/dist-es/effectsHelpers.js +88 -109
- package/dist-es/effectsHelpers.js.map +1 -1
- package/dist-es/engineData.js +45 -51
- package/dist-es/engineData.js.map +1 -1
- package/dist-es/engineData2.js +8 -9
- package/dist-es/engineData2.js.map +1 -1
- package/dist-es/helpers.js +79 -85
- package/dist-es/helpers.js.map +1 -1
- package/dist-es/imageResources.js +338 -377
- package/dist-es/imageResources.js.map +1 -1
- package/dist-es/index.js +4 -4
- package/dist-es/index.js.map +1 -1
- package/dist-es/jpeg.js +279 -279
- package/dist-es/jpeg.js.map +1 -1
- package/dist-es/psdReader.js +333 -344
- package/dist-es/psdReader.js.map +1 -1
- package/dist-es/psdWriter.d.ts +2 -1
- package/dist-es/psdWriter.js +220 -202
- package/dist-es/psdWriter.js.map +1 -1
- package/dist-es/text.js +98 -122
- package/dist-es/text.js.map +1 -1
- package/dist-es/utf8.js +22 -22
- package/dist-es/utf8.js.map +1 -1
- package/package.json +1 -1
- package/publish.js +24 -0
- package/src/additionalInfo.ts +15 -11
- package/src/psdWriter.ts +76 -1
- package/tsconfig.json +4 -3
package/dist-es/psdReader.js
CHANGED
|
@@ -13,19 +13,19 @@ import { inflate as inflateSync } from 'pako';
|
|
|
13
13
|
import { resetImageData, offsetForChannel, decodeBitmap, createImageData, toBlendMode, RAW_IMAGE_DATA, largeAdditionalInfoKeys, imageDataToCanvas } from './helpers';
|
|
14
14
|
import { infoHandlersMap } from './additionalInfo';
|
|
15
15
|
import { resourceHandlersMap } from './imageResources';
|
|
16
|
-
export
|
|
17
|
-
|
|
16
|
+
export const supportedColorModes = [0 /* ColorMode.Bitmap */, 1 /* ColorMode.Grayscale */, 3 /* ColorMode.RGB */, 2 /* ColorMode.Indexed */];
|
|
17
|
+
const colorModes = ['bitmap', 'grayscale', 'indexed', 'RGB', 'CMYK', 'multichannel', 'duotone', 'lab'];
|
|
18
18
|
function setupGrayscale(data) {
|
|
19
|
-
|
|
20
|
-
for (
|
|
21
|
-
|
|
19
|
+
const size = data.width * data.height * 4;
|
|
20
|
+
for (let i = 0; i < size; i += 4) {
|
|
21
|
+
const c = data.data[i];
|
|
22
22
|
data.data[i + 1] = c;
|
|
23
23
|
data.data[i + 2] = c;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
export function createReader(buffer, offset, length) {
|
|
27
|
-
|
|
28
|
-
return { view
|
|
27
|
+
const view = new DataView(buffer, offset, length);
|
|
28
|
+
return { view, offset: 0, strict: false, debug: false, large: false, globalAlpha: false, log: console.log };
|
|
29
29
|
}
|
|
30
30
|
export function warnOrThrow(reader, message) {
|
|
31
31
|
if (reader.strict)
|
|
@@ -81,15 +81,15 @@ export function readFixedPointPath32(reader) {
|
|
|
81
81
|
return readInt32(reader) / (1 << 24);
|
|
82
82
|
}
|
|
83
83
|
export function readBytes(reader, length) {
|
|
84
|
-
|
|
84
|
+
const start = reader.view.byteOffset + reader.offset;
|
|
85
85
|
reader.offset += length;
|
|
86
86
|
if ((start + length) > reader.view.buffer.byteLength) {
|
|
87
87
|
// fix for broken PSD files that are missing part of file at the end
|
|
88
88
|
warnOrThrow(reader, 'Reading bytes exceeding buffer length');
|
|
89
89
|
if (length > (100 * 1024 * 1024))
|
|
90
90
|
throw new Error('Reading past end of file'); // limit to 100MB
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
const result = new Uint8Array(length);
|
|
92
|
+
const len = Math.min(length, reader.view.byteLength - start);
|
|
93
93
|
if (len > 0)
|
|
94
94
|
result.set(new Uint8Array(reader.view.buffer, start, len));
|
|
95
95
|
return result;
|
|
@@ -102,28 +102,28 @@ export function readSignature(reader) {
|
|
|
102
102
|
return readShortString(reader, 4);
|
|
103
103
|
}
|
|
104
104
|
export function validSignatureAt(reader, offset) {
|
|
105
|
-
|
|
105
|
+
const sig = String.fromCharCode(reader.view.getUint8(offset))
|
|
106
106
|
+ String.fromCharCode(reader.view.getUint8(offset + 1))
|
|
107
107
|
+ String.fromCharCode(reader.view.getUint8(offset + 2))
|
|
108
108
|
+ String.fromCharCode(reader.view.getUint8(offset + 3));
|
|
109
109
|
return sig == '8BIM' || sig == '8B64';
|
|
110
110
|
}
|
|
111
111
|
export function readPascalString(reader, padTo) {
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
let length = readUint8(reader);
|
|
113
|
+
const text = length ? readShortString(reader, length) : '';
|
|
114
114
|
while (++length % padTo) { // starts with length + 1 so we count the size byte too
|
|
115
115
|
reader.offset++;
|
|
116
116
|
}
|
|
117
117
|
return text;
|
|
118
118
|
}
|
|
119
119
|
export function readUnicodeString(reader) {
|
|
120
|
-
|
|
120
|
+
const length = readUint32(reader);
|
|
121
121
|
return readUnicodeStringWithLength(reader, length);
|
|
122
122
|
}
|
|
123
123
|
export function readUnicodeStringWithLength(reader, length) {
|
|
124
|
-
|
|
124
|
+
let text = '';
|
|
125
125
|
while (length--) {
|
|
126
|
-
|
|
126
|
+
const value = readUint16(reader);
|
|
127
127
|
if (value || length > 0) { // remove trailing \0
|
|
128
128
|
text += String.fromCharCode(value);
|
|
129
129
|
}
|
|
@@ -131,9 +131,9 @@ export function readUnicodeStringWithLength(reader, length) {
|
|
|
131
131
|
return text;
|
|
132
132
|
}
|
|
133
133
|
export function readUnicodeStringWithLengthLE(reader, length) {
|
|
134
|
-
|
|
134
|
+
let text = '';
|
|
135
135
|
while (length--) {
|
|
136
|
-
|
|
136
|
+
const value = readUint16LE(reader);
|
|
137
137
|
if (value || length > 0) { // remove trailing \0
|
|
138
138
|
text += String.fromCharCode(value);
|
|
139
139
|
}
|
|
@@ -141,7 +141,7 @@ export function readUnicodeStringWithLengthLE(reader, length) {
|
|
|
141
141
|
return text;
|
|
142
142
|
}
|
|
143
143
|
export function readAsciiString(reader, length) {
|
|
144
|
-
|
|
144
|
+
let text = '';
|
|
145
145
|
while (length--) {
|
|
146
146
|
text += String.fromCharCode(readUint8(reader));
|
|
147
147
|
}
|
|
@@ -151,16 +151,16 @@ export function skipBytes(reader, count) {
|
|
|
151
151
|
reader.offset += count;
|
|
152
152
|
}
|
|
153
153
|
export function checkSignature(reader, a, b) {
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
const offset = reader.offset;
|
|
155
|
+
const signature = readSignature(reader);
|
|
156
156
|
if (signature !== a && signature !== b) {
|
|
157
|
-
throw new Error(
|
|
157
|
+
throw new Error(`Invalid signature: '${signature}' at 0x${offset.toString(16)}`);
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
function readShortString(reader, length) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
for (
|
|
161
|
+
const buffer = readBytes(reader, length);
|
|
162
|
+
let result = '';
|
|
163
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
164
164
|
result += String.fromCharCode(buffer[i]);
|
|
165
165
|
}
|
|
166
166
|
return result;
|
|
@@ -168,35 +168,34 @@ function readShortString(reader, length) {
|
|
|
168
168
|
function isValidSignature(sig) {
|
|
169
169
|
return sig === '8BIM' || sig === 'MeSa' || sig === 'AgHg' || sig === 'PHUT' || sig === 'DCSR';
|
|
170
170
|
}
|
|
171
|
-
export function readPsd(reader, readOptions) {
|
|
171
|
+
export function readPsd(reader, readOptions = {}) {
|
|
172
172
|
var _a;
|
|
173
|
-
if (readOptions === void 0) { readOptions = {}; }
|
|
174
173
|
// header
|
|
175
174
|
checkSignature(reader, '8BPS');
|
|
176
|
-
|
|
175
|
+
const version = readUint16(reader);
|
|
177
176
|
if (version !== 1 && version !== 2)
|
|
178
|
-
throw new Error(
|
|
177
|
+
throw new Error(`Invalid PSD file version: ${version}`);
|
|
179
178
|
skipBytes(reader, 6);
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
179
|
+
const channels = readUint16(reader);
|
|
180
|
+
const height = readUint32(reader);
|
|
181
|
+
const width = readUint32(reader);
|
|
182
|
+
const bitsPerChannel = readUint16(reader);
|
|
183
|
+
const colorMode = readUint16(reader);
|
|
184
|
+
const maxSize = version === 1 ? 30000 : 300000;
|
|
186
185
|
if (width > maxSize || height > maxSize)
|
|
187
|
-
throw new Error(
|
|
186
|
+
throw new Error(`Invalid size: ${width}x${height}`);
|
|
188
187
|
if (channels > 16)
|
|
189
|
-
throw new Error(
|
|
188
|
+
throw new Error(`Invalid channel count: ${channels}`);
|
|
190
189
|
if (![1, 8, 16, 32].includes(bitsPerChannel))
|
|
191
|
-
throw new Error(
|
|
190
|
+
throw new Error(`Invalid bitsPerChannel: ${bitsPerChannel}`);
|
|
192
191
|
if (supportedColorModes.indexOf(colorMode) === -1)
|
|
193
|
-
throw new Error(
|
|
194
|
-
|
|
192
|
+
throw new Error(`Color mode not supported: ${(_a = colorModes[colorMode]) !== null && _a !== void 0 ? _a : colorMode}`);
|
|
193
|
+
const psd = { width, height, channels, bitsPerChannel, colorMode };
|
|
195
194
|
Object.assign(reader, readOptions);
|
|
196
195
|
reader.large = version === 2;
|
|
197
196
|
reader.globalAlpha = false;
|
|
198
197
|
// color mode data
|
|
199
|
-
readSection(reader, 1,
|
|
198
|
+
readSection(reader, 1, left => {
|
|
200
199
|
if (!left())
|
|
201
200
|
return;
|
|
202
201
|
if (colorMode === 2 /* ColorMode.Indexed */) {
|
|
@@ -204,11 +203,11 @@ export function readPsd(reader, readOptions) {
|
|
|
204
203
|
if (left() != 768)
|
|
205
204
|
throw new Error('Invalid color palette size');
|
|
206
205
|
psd.palette = [];
|
|
207
|
-
for (
|
|
206
|
+
for (let i = 0; i < 256; i++)
|
|
208
207
|
psd.palette.push({ r: readUint8(reader), g: 0, b: 0 });
|
|
209
|
-
for (
|
|
208
|
+
for (let i = 0; i < 256; i++)
|
|
210
209
|
psd.palette[i].g = readUint8(reader);
|
|
211
|
-
for (
|
|
210
|
+
for (let i = 0; i < 256; i++)
|
|
212
211
|
psd.palette[i].b = readUint8(reader);
|
|
213
212
|
}
|
|
214
213
|
else {
|
|
@@ -218,15 +217,15 @@ export function readPsd(reader, readOptions) {
|
|
|
218
217
|
skipBytes(reader, left());
|
|
219
218
|
});
|
|
220
219
|
// image resources
|
|
221
|
-
|
|
222
|
-
readSection(reader, 1,
|
|
223
|
-
|
|
220
|
+
const imageResources = {};
|
|
221
|
+
readSection(reader, 1, left => {
|
|
222
|
+
while (left() > 0) {
|
|
224
223
|
realignWithSignature(reader, isValidSignature);
|
|
225
|
-
|
|
224
|
+
const id = readUint16(reader);
|
|
226
225
|
readPascalString(reader, 2); // name
|
|
227
|
-
readSection(reader, 2,
|
|
228
|
-
|
|
229
|
-
|
|
226
|
+
readSection(reader, 2, left => {
|
|
227
|
+
const handler = resourceHandlersMap[id];
|
|
228
|
+
const skip = id === 1036 && !!reader.skipThumbnail;
|
|
230
229
|
if (handler && !skip) {
|
|
231
230
|
try {
|
|
232
231
|
handler.read(reader, imageResources, left);
|
|
@@ -242,24 +241,21 @@ export function readPsd(reader, readOptions) {
|
|
|
242
241
|
skipBytes(reader, left());
|
|
243
242
|
}
|
|
244
243
|
});
|
|
245
|
-
};
|
|
246
|
-
while (left() > 0) {
|
|
247
|
-
_loop_1();
|
|
248
244
|
}
|
|
249
245
|
});
|
|
250
|
-
|
|
246
|
+
const { layersGroup, layerGroupsEnabledId } = imageResources, rest = __rest(imageResources, ["layersGroup", "layerGroupsEnabledId"]);
|
|
251
247
|
if (Object.keys(rest)) {
|
|
252
248
|
psd.imageResources = rest;
|
|
253
249
|
}
|
|
254
250
|
// layer and mask info
|
|
255
|
-
readSection(reader, 1,
|
|
256
|
-
readSection(reader, 2,
|
|
251
|
+
readSection(reader, 1, left => {
|
|
252
|
+
readSection(reader, 2, left => {
|
|
257
253
|
readLayerInfo(reader, psd, imageResources);
|
|
258
254
|
skipBytes(reader, left());
|
|
259
255
|
}, undefined, reader.large);
|
|
260
256
|
// SAI does not include this section
|
|
261
257
|
if (left() > 0) {
|
|
262
|
-
|
|
258
|
+
const globalLayerMaskInfo = readGlobalLayerMaskInfo(reader);
|
|
263
259
|
if (globalLayerMaskInfo)
|
|
264
260
|
psd.globalLayerMaskInfo = globalLayerMaskInfo;
|
|
265
261
|
}
|
|
@@ -283,8 +279,8 @@ export function readPsd(reader, readOptions) {
|
|
|
283
279
|
}
|
|
284
280
|
}
|
|
285
281
|
}, undefined, reader.large);
|
|
286
|
-
|
|
287
|
-
|
|
282
|
+
const hasChildren = psd.children && psd.children.length;
|
|
283
|
+
const skipComposite = reader.skipCompositeImageData && (reader.skipLayerImageData || hasChildren);
|
|
288
284
|
if (!skipComposite) {
|
|
289
285
|
readImageData(reader, psd);
|
|
290
286
|
}
|
|
@@ -295,16 +291,16 @@ export function readPsd(reader, readOptions) {
|
|
|
295
291
|
}
|
|
296
292
|
export function readLayerInfo(reader, psd, imageResources) {
|
|
297
293
|
var _a, _b;
|
|
298
|
-
|
|
299
|
-
|
|
294
|
+
const { layersGroup = [], layerGroupsEnabledId = [] } = imageResources;
|
|
295
|
+
let layerCount = readInt16(reader);
|
|
300
296
|
if (layerCount < 0) {
|
|
301
297
|
reader.globalAlpha = true;
|
|
302
298
|
layerCount = -layerCount;
|
|
303
299
|
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
for (
|
|
307
|
-
|
|
300
|
+
const layers = [];
|
|
301
|
+
const layerChannels = [];
|
|
302
|
+
for (let i = 0; i < layerCount; i++) {
|
|
303
|
+
const { layer, channels } = readLayerRecord(reader, psd, imageResources);
|
|
308
304
|
if (layersGroup[i] !== undefined)
|
|
309
305
|
layer.linkGroup = layersGroup[i];
|
|
310
306
|
if (layerGroupsEnabledId[i] !== undefined)
|
|
@@ -312,15 +308,15 @@ export function readLayerInfo(reader, psd, imageResources) {
|
|
|
312
308
|
layers.push(layer);
|
|
313
309
|
layerChannels.push(channels);
|
|
314
310
|
}
|
|
315
|
-
for (
|
|
311
|
+
for (let i = 0; i < layerCount; i++) {
|
|
316
312
|
readLayerChannelImageData(reader, psd, layers[i], layerChannels[i]);
|
|
317
313
|
}
|
|
318
314
|
if (!psd.children)
|
|
319
315
|
psd.children = [];
|
|
320
|
-
|
|
321
|
-
for (
|
|
322
|
-
|
|
323
|
-
|
|
316
|
+
const stack = [psd];
|
|
317
|
+
for (let i = layers.length - 1; i >= 0; i--) {
|
|
318
|
+
const l = layers[i];
|
|
319
|
+
const type = l.sectionDivider ? l.sectionDivider.type : 0 /* SectionDividerType.Other */;
|
|
324
320
|
if (type === 1 /* SectionDividerType.OpenFolder */ || type === 2 /* SectionDividerType.ClosedFolder */) {
|
|
325
321
|
l.opened = type === 1 /* SectionDividerType.OpenFolder */;
|
|
326
322
|
l.children = [];
|
|
@@ -343,31 +339,31 @@ export function readLayerInfo(reader, psd, imageResources) {
|
|
|
343
339
|
}
|
|
344
340
|
}
|
|
345
341
|
function readLayerRecord(reader, psd, imageResources) {
|
|
346
|
-
|
|
342
|
+
const layer = {};
|
|
347
343
|
layer.top = readInt32(reader);
|
|
348
344
|
layer.left = readInt32(reader);
|
|
349
345
|
layer.bottom = readInt32(reader);
|
|
350
346
|
layer.right = readInt32(reader);
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
for (
|
|
354
|
-
|
|
355
|
-
|
|
347
|
+
const channelCount = readUint16(reader);
|
|
348
|
+
const channels = [];
|
|
349
|
+
for (let i = 0; i < channelCount; i++) {
|
|
350
|
+
let id = readInt16(reader);
|
|
351
|
+
let length = readUint32(reader);
|
|
356
352
|
if (reader.large) {
|
|
357
|
-
if (
|
|
353
|
+
if (length !== 0)
|
|
358
354
|
throw new Error('Sizes larger than 4GB are not supported');
|
|
359
|
-
|
|
355
|
+
length = readUint32(reader);
|
|
360
356
|
}
|
|
361
|
-
channels.push({ id
|
|
357
|
+
channels.push({ id, length });
|
|
362
358
|
}
|
|
363
359
|
checkSignature(reader, '8BIM');
|
|
364
|
-
|
|
360
|
+
const blendMode = readSignature(reader);
|
|
365
361
|
if (!toBlendMode[blendMode])
|
|
366
|
-
throw new Error(
|
|
362
|
+
throw new Error(`Invalid blend mode: '${blendMode}'`);
|
|
367
363
|
layer.blendMode = toBlendMode[blendMode];
|
|
368
364
|
layer.opacity = readUint8(reader) / 0xff;
|
|
369
365
|
layer.clipping = readUint8(reader) === 1;
|
|
370
|
-
|
|
366
|
+
const flags = readUint8(reader);
|
|
371
367
|
layer.transparencyProtected = (flags & 0x01) !== 0;
|
|
372
368
|
layer.hidden = (flags & 0x02) !== 0;
|
|
373
369
|
if (flags & 0x20)
|
|
@@ -377,9 +373,9 @@ function readLayerRecord(reader, psd, imageResources) {
|
|
|
377
373
|
// 0x10 - pixel data irrelevant to appearance of document
|
|
378
374
|
// 0x20 - effects/filters panel is expanded
|
|
379
375
|
skipBytes(reader, 1);
|
|
380
|
-
readSection(reader, 1,
|
|
376
|
+
readSection(reader, 1, left => {
|
|
381
377
|
readLayerMaskData(reader, layer);
|
|
382
|
-
|
|
378
|
+
const blendingRanges = readLayerBlendingRanges(reader);
|
|
383
379
|
if (blendingRanges)
|
|
384
380
|
layer.blendingRanges = blendingRanges;
|
|
385
381
|
layer.name = readPascalString(reader, 1); // should be padded to 4, but is not sometimes
|
|
@@ -390,27 +386,27 @@ function readLayerRecord(reader, psd, imageResources) {
|
|
|
390
386
|
readAdditionalLayerInfo(reader, layer, psd, imageResources);
|
|
391
387
|
skipBytes(reader, left());
|
|
392
388
|
});
|
|
393
|
-
return { layer
|
|
389
|
+
return { layer, channels };
|
|
394
390
|
}
|
|
395
391
|
function readLayerMaskData(reader, layer) {
|
|
396
|
-
return readSection(reader, 1,
|
|
392
|
+
return readSection(reader, 1, left => {
|
|
397
393
|
if (!left())
|
|
398
394
|
return undefined;
|
|
399
|
-
|
|
395
|
+
const mask = {};
|
|
400
396
|
layer.mask = mask;
|
|
401
397
|
mask.top = readInt32(reader);
|
|
402
398
|
mask.left = readInt32(reader);
|
|
403
399
|
mask.bottom = readInt32(reader);
|
|
404
400
|
mask.right = readInt32(reader);
|
|
405
401
|
mask.defaultColor = readUint8(reader);
|
|
406
|
-
|
|
402
|
+
const flags = readUint8(reader);
|
|
407
403
|
mask.positionRelativeToLayer = (flags & 1 /* LayerMaskFlags.PositionRelativeToLayer */) !== 0;
|
|
408
404
|
mask.disabled = (flags & 2 /* LayerMaskFlags.LayerMaskDisabled */) !== 0;
|
|
409
405
|
mask.fromVectorData = (flags & 8 /* LayerMaskFlags.LayerMaskFromRenderingOtherData */) !== 0;
|
|
410
406
|
if (left() >= 18) {
|
|
411
|
-
|
|
407
|
+
const realMask = {};
|
|
412
408
|
layer.realMask = realMask;
|
|
413
|
-
|
|
409
|
+
const realFlags = readUint8(reader);
|
|
414
410
|
realMask.positionRelativeToLayer = (realFlags & 1 /* LayerMaskFlags.PositionRelativeToLayer */) !== 0;
|
|
415
411
|
realMask.disabled = (realFlags & 2 /* LayerMaskFlags.LayerMaskDisabled */) !== 0;
|
|
416
412
|
realMask.fromVectorData = (realFlags & 8 /* LayerMaskFlags.LayerMaskFromRenderingOtherData */) !== 0;
|
|
@@ -421,7 +417,7 @@ function readLayerMaskData(reader, layer) {
|
|
|
421
417
|
realMask.right = readInt32(reader);
|
|
422
418
|
}
|
|
423
419
|
if (flags & 16 /* LayerMaskFlags.MaskHasParametersAppliedToIt */) {
|
|
424
|
-
|
|
420
|
+
const params = readUint8(reader);
|
|
425
421
|
if (params & 1 /* MaskParams.UserMaskDensity */)
|
|
426
422
|
mask.userMaskDensity = readUint8(reader) / 0xff;
|
|
427
423
|
if (params & 2 /* MaskParams.UserMaskFeather */)
|
|
@@ -438,28 +434,27 @@ function readBlendingRange(reader) {
|
|
|
438
434
|
return [readUint8(reader), readUint8(reader), readUint8(reader), readUint8(reader)];
|
|
439
435
|
}
|
|
440
436
|
function readLayerBlendingRanges(reader) {
|
|
441
|
-
return readSection(reader, 1,
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
437
|
+
return readSection(reader, 1, left => {
|
|
438
|
+
const compositeGrayBlendSource = readBlendingRange(reader);
|
|
439
|
+
const compositeGraphBlendDestinationRange = readBlendingRange(reader);
|
|
440
|
+
const ranges = [];
|
|
445
441
|
while (left() > 0) {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
ranges.push({ sourceRange
|
|
442
|
+
const sourceRange = readBlendingRange(reader);
|
|
443
|
+
const destRange = readBlendingRange(reader);
|
|
444
|
+
ranges.push({ sourceRange, destRange });
|
|
449
445
|
}
|
|
450
|
-
return { compositeGrayBlendSource
|
|
446
|
+
return { compositeGrayBlendSource, compositeGraphBlendDestinationRange, ranges };
|
|
451
447
|
});
|
|
452
448
|
}
|
|
453
449
|
function readLayerChannelImageData(reader, psd, layer, channels) {
|
|
454
450
|
if (reader.skipLayerImageData)
|
|
455
451
|
return;
|
|
456
|
-
|
|
457
|
-
layer.rawData = { colorMode
|
|
458
|
-
for (
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
var data = undefined;
|
|
452
|
+
const { colorMode = 3 /* ColorMode.RGB */, bitsPerChannel = 8 } = psd;
|
|
453
|
+
layer.rawData = { colorMode, bitsPerChannel, channels: [], large: reader.large };
|
|
454
|
+
for (const channel of channels) {
|
|
455
|
+
const start = reader.offset;
|
|
456
|
+
let compression = 0 /* Compression.RawData */;
|
|
457
|
+
let data = undefined;
|
|
463
458
|
if (channel.length === 1)
|
|
464
459
|
throw new Error('Invalid channel length');
|
|
465
460
|
if (channel.length) {
|
|
@@ -475,37 +470,36 @@ function readLayerChannelImageData(reader, psd, layer, channels) {
|
|
|
475
470
|
compression = readUint16(reader);
|
|
476
471
|
}
|
|
477
472
|
if (compression > 3)
|
|
478
|
-
throw new Error(
|
|
473
|
+
throw new Error(`Invalid compression: ${compression}`);
|
|
479
474
|
if (channel.length > 2) {
|
|
480
475
|
data = readBytes(reader, channel.length - 2);
|
|
481
476
|
}
|
|
482
477
|
}
|
|
483
478
|
reader.offset = start + channel.length;
|
|
484
|
-
layer.rawData.channels.push({ id: channel.id, compression
|
|
479
|
+
layer.rawData.channels.push({ id: channel.id, compression, data });
|
|
485
480
|
}
|
|
486
481
|
if (!reader.useRawData) {
|
|
487
482
|
decodeLayerImageData(layer, !!reader.useImageData, !!reader.throwForMissingFeatures);
|
|
488
483
|
}
|
|
489
484
|
}
|
|
490
|
-
function resetAlpha(
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
for (var p = offset; p < length; p = (p + step) | 0) {
|
|
485
|
+
function resetAlpha({ data }, cmyk) {
|
|
486
|
+
const alpha = (data instanceof Float32Array) ? 1.0 : ((data instanceof Uint16Array) ? 0xffff : 0xff);
|
|
487
|
+
const offset = (cmyk ? 4 : 3) | 0;
|
|
488
|
+
const length = data.length | 0;
|
|
489
|
+
const step = (cmyk ? 5 : 4) | 0;
|
|
490
|
+
for (let p = offset; p < length; p = (p + step) | 0) {
|
|
497
491
|
data[p] = alpha;
|
|
498
492
|
}
|
|
499
493
|
}
|
|
500
494
|
export function decodeLayerImageData(layer, useImageData, throwForMissingFeatures) {
|
|
501
495
|
if (!layer.rawData)
|
|
502
496
|
return;
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
497
|
+
const { colorMode, bitsPerChannel, channels, large } = layer.rawData;
|
|
498
|
+
const layerWidth = (layer.right || 0) - (layer.left || 0);
|
|
499
|
+
const layerHeight = (layer.bottom || 0) - (layer.top || 0);
|
|
500
|
+
const cmyk = colorMode === 4 /* ColorMode.CMYK */;
|
|
501
|
+
let imageData;
|
|
502
|
+
let initializedAlpha = false;
|
|
509
503
|
if (layerWidth && layerHeight) {
|
|
510
504
|
if (cmyk) {
|
|
511
505
|
if (bitsPerChannel !== 8)
|
|
@@ -520,21 +514,20 @@ export function decodeLayerImageData(layer, useImageData, throwForMissingFeature
|
|
|
520
514
|
layer.imageDataRaw = [];
|
|
521
515
|
layer.imageDataRawCompression = [];
|
|
522
516
|
}
|
|
523
|
-
for (
|
|
524
|
-
var _b = channels_2[_i], id = _b.id, compression = _b.compression, data = _b.data;
|
|
517
|
+
for (const { id, compression, data } of channels) {
|
|
525
518
|
if (!data)
|
|
526
519
|
continue;
|
|
527
|
-
|
|
520
|
+
const dataReader = createReader(data.buffer, data.byteOffset, data.byteLength);
|
|
528
521
|
if (id === -2 /* ChannelID.UserMask */ || id === -3 /* ChannelID.RealUserMask */) {
|
|
529
|
-
|
|
522
|
+
const mask = id === -2 /* ChannelID.UserMask */ ? layer.mask : layer.realMask;
|
|
530
523
|
if (!mask)
|
|
531
|
-
throw new Error(
|
|
532
|
-
|
|
533
|
-
|
|
524
|
+
throw new Error(`Missing layer ${id === -2 /* ChannelID.UserMask */ ? 'mask' : 'real mask'} data`);
|
|
525
|
+
const maskWidth = (mask.right || 0) - (mask.left || 0);
|
|
526
|
+
const maskHeight = (mask.bottom || 0) - (mask.top || 0);
|
|
534
527
|
if (maskWidth < 0 || maskHeight < 0 || maskWidth > 30000 || maskHeight > 30000)
|
|
535
528
|
throw new Error('Invalid mask size');
|
|
536
529
|
if (maskWidth && maskHeight) {
|
|
537
|
-
|
|
530
|
+
const maskData = createImageDataBitDepth(maskWidth, maskHeight, bitsPerChannel);
|
|
538
531
|
readData(dataReader, data.byteLength, maskData, compression, maskWidth, maskHeight, bitsPerChannel, 0, large, 4);
|
|
539
532
|
if (RAW_IMAGE_DATA) { // TODO: use layer.rawData instead
|
|
540
533
|
if (id === -2 /* ChannelID.UserMask */) {
|
|
@@ -557,12 +550,12 @@ export function decodeLayerImageData(layer, useImageData, throwForMissingFeature
|
|
|
557
550
|
}
|
|
558
551
|
}
|
|
559
552
|
else {
|
|
560
|
-
|
|
561
|
-
|
|
553
|
+
const offset = offsetForChannel(id, cmyk);
|
|
554
|
+
let targetData = imageData;
|
|
562
555
|
if (offset < 0) {
|
|
563
556
|
targetData = undefined;
|
|
564
557
|
if (throwForMissingFeatures) {
|
|
565
|
-
throw new Error(
|
|
558
|
+
throw new Error(`Channel not supported: ${id}`);
|
|
566
559
|
}
|
|
567
560
|
}
|
|
568
561
|
readData(dataReader, data.byteLength, targetData, compression, layerWidth, layerHeight, bitsPerChannel, offset, large, cmyk ? 5 : 4);
|
|
@@ -582,7 +575,7 @@ export function decodeLayerImageData(layer, useImageData, throwForMissingFeature
|
|
|
582
575
|
if (!initializedAlpha)
|
|
583
576
|
resetAlpha(imageData, cmyk);
|
|
584
577
|
if (cmyk) {
|
|
585
|
-
|
|
578
|
+
const cmykData = imageData;
|
|
586
579
|
imageData = createImageData(cmykData.width, cmykData.height);
|
|
587
580
|
cmykToRgb(cmykData, imageData, false);
|
|
588
581
|
}
|
|
@@ -600,9 +593,9 @@ function readData(reader, length, pixels, compression, width, height, bitDepth,
|
|
|
600
593
|
return;
|
|
601
594
|
if (compression === 0 /* Compression.RawData */) {
|
|
602
595
|
if (length !== (width * height * Math.floor(bitDepth / 8))) {
|
|
603
|
-
reader.log(
|
|
596
|
+
reader.log(`Invalid length (${length}, ${width * height * Math.floor(bitDepth / 8)})`);
|
|
604
597
|
}
|
|
605
|
-
|
|
598
|
+
const data = readBytes(reader, length);
|
|
606
599
|
readDataRaw(data, pixels, bitDepth, step, offset);
|
|
607
600
|
}
|
|
608
601
|
else if (compression === 1 /* Compression.RleCompressed */) {
|
|
@@ -610,39 +603,38 @@ function readData(reader, length, pixels, compression, width, height, bitDepth,
|
|
|
610
603
|
readDataRLE(reader, pixels, width, height, bitDepth, step, [offset], large);
|
|
611
604
|
}
|
|
612
605
|
else if (compression === 2 /* Compression.ZipWithoutPrediction */) {
|
|
613
|
-
|
|
606
|
+
const data = readBytes(reader, length);
|
|
614
607
|
readDataZip(data, pixels, width, height, bitDepth, step, offset, false);
|
|
615
608
|
}
|
|
616
609
|
else if (compression === 3 /* Compression.ZipWithPrediction */) {
|
|
617
|
-
|
|
610
|
+
const data = readBytes(reader, length);
|
|
618
611
|
readDataZip(data, pixels, width, height, bitDepth, step, offset, true);
|
|
619
612
|
}
|
|
620
613
|
else {
|
|
621
|
-
throw new Error(
|
|
614
|
+
throw new Error(`Invalid Compression type: ${compression}`);
|
|
622
615
|
}
|
|
623
616
|
}
|
|
624
617
|
export function readGlobalLayerMaskInfo(reader) {
|
|
625
|
-
return readSection(reader, 1,
|
|
618
|
+
return readSection(reader, 1, left => {
|
|
626
619
|
if (!left())
|
|
627
620
|
return undefined;
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
621
|
+
const overlayColorSpace = readUint16(reader);
|
|
622
|
+
const colorSpace1 = readUint16(reader);
|
|
623
|
+
const colorSpace2 = readUint16(reader);
|
|
624
|
+
const colorSpace3 = readUint16(reader);
|
|
625
|
+
const colorSpace4 = readUint16(reader);
|
|
626
|
+
const opacity = readUint16(reader) / 0xff;
|
|
627
|
+
const kind = readUint8(reader);
|
|
635
628
|
skipBytes(reader, left()); // 3 bytes of padding ?
|
|
636
|
-
return { overlayColorSpace
|
|
629
|
+
return { overlayColorSpace, colorSpace1, colorSpace2, colorSpace3, colorSpace4, opacity, kind };
|
|
637
630
|
});
|
|
638
631
|
}
|
|
639
|
-
|
|
632
|
+
const fixOffsets = [0, 1, -1, 2, -2, 3, -3, 4, -4];
|
|
640
633
|
function realignWithSignature(reader, isValid) {
|
|
641
|
-
|
|
642
|
-
|
|
634
|
+
const sigOffset = reader.offset;
|
|
635
|
+
let sig = '';
|
|
643
636
|
// attempt to fix broken document by realigning with the signature
|
|
644
|
-
for (
|
|
645
|
-
var offset = fixOffsets_1[_i];
|
|
637
|
+
for (const offset of fixOffsets) {
|
|
646
638
|
try {
|
|
647
639
|
reader.offset = sigOffset + offset;
|
|
648
640
|
sig = readSignature(reader);
|
|
@@ -652,7 +644,7 @@ function realignWithSignature(reader, isValid) {
|
|
|
652
644
|
break;
|
|
653
645
|
}
|
|
654
646
|
if (!isValid(sig)) {
|
|
655
|
-
throw new Error(
|
|
647
|
+
throw new Error(`Invalid signature: '${sig}' at 0x${(sigOffset).toString(16)}`);
|
|
656
648
|
}
|
|
657
649
|
return sig;
|
|
658
650
|
}
|
|
@@ -660,12 +652,12 @@ function isValidAdditionalInfoSignature(sig) {
|
|
|
660
652
|
return sig === '8BIM' || sig === '8B64';
|
|
661
653
|
}
|
|
662
654
|
export function readAdditionalLayerInfo(reader, target, psd, imageResources) {
|
|
663
|
-
|
|
664
|
-
|
|
655
|
+
const sig = realignWithSignature(reader, isValidAdditionalInfoSignature);
|
|
656
|
+
const key = readSignature(reader);
|
|
665
657
|
// `largeAdditionalInfoKeys` fallback, because some keys don't have 8B64 signature even when they are 64bit
|
|
666
|
-
|
|
667
|
-
readSection(reader, 2,
|
|
668
|
-
|
|
658
|
+
const u64 = sig === '8B64' || (reader.large && largeAdditionalInfoKeys.indexOf(key) !== -1);
|
|
659
|
+
readSection(reader, 2, left => {
|
|
660
|
+
const handler = infoHandlersMap[key];
|
|
669
661
|
if (handler) {
|
|
670
662
|
try {
|
|
671
663
|
handler.read(reader, target, left, psd, imageResources);
|
|
@@ -676,50 +668,49 @@ export function readAdditionalLayerInfo(reader, target, psd, imageResources) {
|
|
|
676
668
|
}
|
|
677
669
|
}
|
|
678
670
|
else {
|
|
679
|
-
reader.logMissingFeatures && reader.log(
|
|
671
|
+
reader.logMissingFeatures && reader.log(`Unhandled additional info: ${key}`);
|
|
680
672
|
skipBytes(reader, left());
|
|
681
673
|
}
|
|
682
674
|
if (left()) {
|
|
683
|
-
reader.logMissingFeatures && reader.log(
|
|
675
|
+
reader.logMissingFeatures && reader.log(`Unread ${left()} bytes left for additional info: ${key}`);
|
|
684
676
|
skipBytes(reader, left());
|
|
685
677
|
}
|
|
686
678
|
}, false, u64);
|
|
687
679
|
}
|
|
688
|
-
export function createImageDataBitDepth(width, height, bitDepth, channels) {
|
|
689
|
-
if (channels === void 0) { channels = 4; }
|
|
680
|
+
export function createImageDataBitDepth(width, height, bitDepth, channels = 4) {
|
|
690
681
|
if (bitDepth === 1 || bitDepth === 8) {
|
|
691
682
|
if (channels === 4) {
|
|
692
683
|
return createImageData(width, height);
|
|
693
684
|
}
|
|
694
685
|
else {
|
|
695
|
-
return { width
|
|
686
|
+
return { width, height, data: new Uint8ClampedArray(width * height * channels) };
|
|
696
687
|
}
|
|
697
688
|
}
|
|
698
689
|
else if (bitDepth === 16) {
|
|
699
|
-
return { width
|
|
690
|
+
return { width, height, data: new Uint16Array(width * height * channels) };
|
|
700
691
|
}
|
|
701
692
|
else if (bitDepth === 32) {
|
|
702
|
-
return { width
|
|
693
|
+
return { width, height, data: new Float32Array(width * height * channels) };
|
|
703
694
|
}
|
|
704
695
|
else {
|
|
705
|
-
throw new Error(
|
|
696
|
+
throw new Error(`Invalid bitDepth (${bitDepth})`);
|
|
706
697
|
}
|
|
707
698
|
}
|
|
708
699
|
function readImageData(reader, psd) {
|
|
709
700
|
var _a;
|
|
710
|
-
|
|
711
|
-
|
|
701
|
+
const compression = readUint16(reader);
|
|
702
|
+
const bitsPerChannel = (_a = psd.bitsPerChannel) !== null && _a !== void 0 ? _a : 8;
|
|
712
703
|
if (supportedColorModes.indexOf(psd.colorMode) === -1)
|
|
713
|
-
throw new Error(
|
|
704
|
+
throw new Error(`Color mode not supported: ${psd.colorMode}`);
|
|
714
705
|
if (compression !== 0 /* Compression.RawData */ && compression !== 1 /* Compression.RleCompressed */)
|
|
715
|
-
throw new Error(
|
|
716
|
-
|
|
706
|
+
throw new Error(`Compression type not supported: ${compression}`);
|
|
707
|
+
const imageData = createImageDataBitDepth(psd.width, psd.height, bitsPerChannel);
|
|
717
708
|
resetImageData(imageData);
|
|
718
709
|
switch (psd.colorMode) {
|
|
719
710
|
case 0 /* ColorMode.Bitmap */: {
|
|
720
711
|
if (bitsPerChannel !== 1)
|
|
721
712
|
throw new Error('Invalid bitsPerChannel for bitmap color mode');
|
|
722
|
-
|
|
713
|
+
let bytes;
|
|
723
714
|
if (compression === 0 /* Compression.RawData */) {
|
|
724
715
|
bytes = readBytes(reader, Math.ceil(psd.width / 8) * psd.height);
|
|
725
716
|
}
|
|
@@ -728,16 +719,16 @@ function readImageData(reader, psd) {
|
|
|
728
719
|
readDataRLE(reader, { data: bytes, width: psd.width, height: psd.height }, psd.width, psd.height, 8, 1, [0], reader.large);
|
|
729
720
|
}
|
|
730
721
|
else {
|
|
731
|
-
throw new Error(
|
|
722
|
+
throw new Error(`Bitmap compression not supported: ${compression}`);
|
|
732
723
|
}
|
|
733
724
|
decodeBitmap(bytes, imageData.data, psd.width, psd.height);
|
|
734
725
|
break;
|
|
735
726
|
}
|
|
736
727
|
case 3 /* ColorMode.RGB */:
|
|
737
728
|
case 1 /* ColorMode.Grayscale */: {
|
|
738
|
-
|
|
729
|
+
const channels = psd.colorMode === 1 /* ColorMode.Grayscale */ ? [0] : [0, 1, 2];
|
|
739
730
|
if (psd.channels && psd.channels > 3) {
|
|
740
|
-
for (
|
|
731
|
+
for (let i = 3; i < psd.channels; i++) {
|
|
741
732
|
// TODO: store these channels in additional image data
|
|
742
733
|
channels.push(i);
|
|
743
734
|
}
|
|
@@ -746,13 +737,13 @@ function readImageData(reader, psd) {
|
|
|
746
737
|
channels.push(3);
|
|
747
738
|
}
|
|
748
739
|
if (compression === 0 /* Compression.RawData */) {
|
|
749
|
-
for (
|
|
750
|
-
|
|
740
|
+
for (let i = 0; i < channels.length; i++) {
|
|
741
|
+
const data = readBytes(reader, psd.width * psd.height * Math.floor(bitsPerChannel / 8));
|
|
751
742
|
readDataRaw(data, imageData, bitsPerChannel, 4, channels[i]);
|
|
752
743
|
}
|
|
753
744
|
}
|
|
754
745
|
else if (compression === 1 /* Compression.RleCompressed */) {
|
|
755
|
-
|
|
746
|
+
const start = reader.offset;
|
|
756
747
|
readDataRLE(reader, imageData, psd.width, psd.height, bitsPerChannel, 4, channels, reader.large);
|
|
757
748
|
if (RAW_IMAGE_DATA)
|
|
758
749
|
psd.imageDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
|
|
@@ -770,10 +761,10 @@ function readImageData(reader, psd) {
|
|
|
770
761
|
if (!psd.palette)
|
|
771
762
|
throw new Error('Missing color palette');
|
|
772
763
|
if (compression === 0 /* Compression.RawData */) {
|
|
773
|
-
throw new Error(
|
|
764
|
+
throw new Error(`Not implemented`);
|
|
774
765
|
}
|
|
775
766
|
else if (compression === 1 /* Compression.RleCompressed */) {
|
|
776
|
-
|
|
767
|
+
const indexedImageData = {
|
|
777
768
|
width: imageData.width,
|
|
778
769
|
height: imageData.height,
|
|
779
770
|
data: new Uint8Array(imageData.width * imageData.height),
|
|
@@ -782,7 +773,7 @@ function readImageData(reader, psd) {
|
|
|
782
773
|
indexedToRgb(indexedImageData, imageData, psd.palette);
|
|
783
774
|
}
|
|
784
775
|
else {
|
|
785
|
-
throw new Error(
|
|
776
|
+
throw new Error(`Not implemented`);
|
|
786
777
|
}
|
|
787
778
|
break;
|
|
788
779
|
}
|
|
@@ -790,48 +781,48 @@ function readImageData(reader, psd) {
|
|
|
790
781
|
if (bitsPerChannel !== 8)
|
|
791
782
|
throw new Error('bitsPerChannel Not supproted');
|
|
792
783
|
if (psd.channels !== 4)
|
|
793
|
-
throw new Error(
|
|
794
|
-
|
|
784
|
+
throw new Error(`Invalid channel count`);
|
|
785
|
+
const channels = [0, 1, 2, 3];
|
|
795
786
|
if (reader.globalAlpha)
|
|
796
787
|
channels.push(4);
|
|
797
788
|
if (compression === 0 /* Compression.RawData */) {
|
|
798
|
-
throw new Error(
|
|
789
|
+
throw new Error(`Not implemented`);
|
|
799
790
|
// TODO: ...
|
|
800
791
|
// for (let i = 0; i < channels.length; i++) {
|
|
801
792
|
// readDataRaw(reader, imageData, channels[i], psd.width, psd.height);
|
|
802
793
|
// }
|
|
803
794
|
}
|
|
804
795
|
else if (compression === 1 /* Compression.RleCompressed */) {
|
|
805
|
-
|
|
796
|
+
const cmykImageData = {
|
|
806
797
|
width: imageData.width,
|
|
807
798
|
height: imageData.height,
|
|
808
799
|
data: new Uint8Array(imageData.width * imageData.height * 5),
|
|
809
800
|
};
|
|
810
|
-
|
|
801
|
+
const start = reader.offset;
|
|
811
802
|
readDataRLE(reader, cmykImageData, psd.width, psd.height, bitsPerChannel, 5, channels, reader.large);
|
|
812
803
|
cmykToRgb(cmykImageData, imageData, true);
|
|
813
804
|
if (RAW_IMAGE_DATA)
|
|
814
805
|
psd.imageDataRaw = new Uint8Array(reader.view.buffer, reader.view.byteOffset + start, reader.offset - start);
|
|
815
806
|
}
|
|
816
807
|
else {
|
|
817
|
-
throw new Error(
|
|
808
|
+
throw new Error(`Not implemented`);
|
|
818
809
|
}
|
|
819
810
|
break;
|
|
820
811
|
}
|
|
821
|
-
default: throw new Error(
|
|
812
|
+
default: throw new Error(`Color mode not supported: ${psd.colorMode}`);
|
|
822
813
|
}
|
|
823
814
|
// remove weird white matte
|
|
824
815
|
if (reader.globalAlpha) {
|
|
825
816
|
if (psd.bitsPerChannel !== 8)
|
|
826
817
|
throw new Error('bitsPerChannel Not supproted');
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
for (
|
|
830
|
-
|
|
818
|
+
const p = imageData.data;
|
|
819
|
+
const size = imageData.width * imageData.height * 4;
|
|
820
|
+
for (let i = 0; i < size; i += 4) {
|
|
821
|
+
const pa = p[i + 3];
|
|
831
822
|
if (pa != 0 && pa != 255) {
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
823
|
+
const a = pa / 255;
|
|
824
|
+
const ra = 1 / a;
|
|
825
|
+
const invA = 255 * (1 - ra);
|
|
835
826
|
p[i + 0] = p[i + 0] * ra + invA;
|
|
836
827
|
p[i + 1] = p[i + 1] * ra + invA;
|
|
837
828
|
p[i + 2] = p[i + 2] * ra + invA;
|
|
@@ -846,14 +837,14 @@ function readImageData(reader, psd) {
|
|
|
846
837
|
}
|
|
847
838
|
}
|
|
848
839
|
function cmykToRgb(cmyk, rgb, reverseAlpha) {
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
for (
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
840
|
+
const size = rgb.width * rgb.height * 4;
|
|
841
|
+
const srcData = cmyk.data;
|
|
842
|
+
const dstData = rgb.data;
|
|
843
|
+
for (let src = 0, dst = 0; dst < size; src += 5, dst += 4) {
|
|
844
|
+
const c = srcData[src];
|
|
845
|
+
const m = srcData[src + 1];
|
|
846
|
+
const y = srcData[src + 2];
|
|
847
|
+
const k = srcData[src + 3];
|
|
857
848
|
dstData[dst] = ((((c * k) | 0) / 255) | 0);
|
|
858
849
|
dstData[dst + 1] = ((((m * k) | 0) / 255) | 0);
|
|
859
850
|
dstData[dst + 2] = ((((y * k) | 0) / 255) | 0);
|
|
@@ -871,11 +862,11 @@ function cmykToRgb(cmyk, rgb, reverseAlpha) {
|
|
|
871
862
|
// }
|
|
872
863
|
}
|
|
873
864
|
function indexedToRgb(indexed, rgb, palette) {
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
for (
|
|
878
|
-
|
|
865
|
+
const size = indexed.width * indexed.height;
|
|
866
|
+
const srcData = indexed.data;
|
|
867
|
+
const dstData = rgb.data;
|
|
868
|
+
for (let src = 0, dst = 0; src < size; src++, dst += 4) {
|
|
869
|
+
const c = palette[srcData[src]];
|
|
879
870
|
dstData[dst + 0] = c.r;
|
|
880
871
|
dstData[dst + 1] = c.g;
|
|
881
872
|
dstData[dst + 2] = c.b;
|
|
@@ -894,13 +885,13 @@ function bytesToArray(bytes, bitDepth) {
|
|
|
894
885
|
else if (bitDepth === 16) {
|
|
895
886
|
// PSD files store 16-bit channel data in big-endian byte order.
|
|
896
887
|
// Swap each pair of bytes so that Uint16Array (native-endian) reads the correct values.
|
|
897
|
-
for (
|
|
898
|
-
|
|
888
|
+
for (let i = 0; i < bytes.byteLength; i += 2) {
|
|
889
|
+
const tmp = bytes[i];
|
|
899
890
|
bytes[i] = bytes[i + 1];
|
|
900
891
|
bytes[i + 1] = tmp;
|
|
901
892
|
}
|
|
902
893
|
if (bytes.byteOffset % 2) {
|
|
903
|
-
|
|
894
|
+
const result = new Uint16Array(bytes.byteLength / 2);
|
|
904
895
|
new Uint8Array(result.buffer, result.byteOffset, result.byteLength).set(bytes);
|
|
905
896
|
return result;
|
|
906
897
|
}
|
|
@@ -910,7 +901,7 @@ function bytesToArray(bytes, bitDepth) {
|
|
|
910
901
|
}
|
|
911
902
|
else if (bitDepth === 32) {
|
|
912
903
|
if (bytes.byteOffset % 4) {
|
|
913
|
-
|
|
904
|
+
const result = new Float32Array(bytes.byteLength / 4);
|
|
914
905
|
new Uint8Array(result.buffer, result.byteOffset, result.byteLength).set(bytes);
|
|
915
906
|
return result;
|
|
916
907
|
}
|
|
@@ -919,47 +910,47 @@ function bytesToArray(bytes, bitDepth) {
|
|
|
919
910
|
}
|
|
920
911
|
}
|
|
921
912
|
else {
|
|
922
|
-
throw new Error(
|
|
913
|
+
throw new Error(`Invalid bitDepth (${bitDepth})`);
|
|
923
914
|
}
|
|
924
915
|
}
|
|
925
916
|
function copyChannelToPixelData(pixelData, channel, offset, step) {
|
|
926
917
|
verifyCompatible(pixelData.data, channel);
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
for (
|
|
918
|
+
const size = pixelData.width * pixelData.height;
|
|
919
|
+
const data = pixelData.data;
|
|
920
|
+
for (let i = 0, p = offset | 0; i < size; i++, p = (p + step) | 0) {
|
|
930
921
|
data[p] = channel[i];
|
|
931
922
|
}
|
|
932
923
|
}
|
|
933
924
|
function readDataRaw(buffer, pixelData, bitDepth, step, offset) {
|
|
934
925
|
if (bitDepth == 32) {
|
|
935
|
-
for (
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
926
|
+
for (let i = 0; i < buffer.byteLength; i += 4) {
|
|
927
|
+
const a = buffer[i + 0];
|
|
928
|
+
const b = buffer[i + 1];
|
|
929
|
+
const c = buffer[i + 2];
|
|
930
|
+
const d = buffer[i + 3];
|
|
940
931
|
buffer[i + 0] = d;
|
|
941
932
|
buffer[i + 1] = c;
|
|
942
933
|
buffer[i + 2] = b;
|
|
943
934
|
buffer[i + 3] = a;
|
|
944
935
|
}
|
|
945
936
|
}
|
|
946
|
-
|
|
937
|
+
const array = bytesToArray(buffer, bitDepth);
|
|
947
938
|
if (pixelData && offset < step) {
|
|
948
939
|
copyChannelToPixelData(pixelData, array, offset, step);
|
|
949
940
|
}
|
|
950
941
|
}
|
|
951
942
|
function decodePredicted(data, width, height, mod) {
|
|
952
|
-
for (
|
|
953
|
-
|
|
954
|
-
for (
|
|
943
|
+
for (let y = 0; y < height; y++) {
|
|
944
|
+
const offset = y * width;
|
|
945
|
+
for (let x = 1, o = offset + 1; x < width; x++, o++) {
|
|
955
946
|
data[o] = (data[o - 1] + data[o]) % mod;
|
|
956
947
|
}
|
|
957
948
|
}
|
|
958
949
|
}
|
|
959
950
|
export function readDataZip(compressed, pixelData, width, height, bitDepth, step, offset, prediction) {
|
|
960
|
-
|
|
951
|
+
const decompressed = inflateSync(compressed);
|
|
961
952
|
if (pixelData && offset < step) {
|
|
962
|
-
|
|
953
|
+
const array = bytesToArray(decompressed, bitDepth);
|
|
963
954
|
if (bitDepth === 8) {
|
|
964
955
|
if (prediction)
|
|
965
956
|
decodePredicted(decompressed, width, height, 0x100);
|
|
@@ -973,14 +964,14 @@ export function readDataZip(compressed, pixelData, width, height, bitDepth, step
|
|
|
973
964
|
else if (bitDepth === 32) {
|
|
974
965
|
if (prediction)
|
|
975
966
|
decodePredicted(decompressed, width * 4, height, 0x100);
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
for (
|
|
979
|
-
|
|
980
|
-
for (
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
967
|
+
let di = offset;
|
|
968
|
+
const dst = new Uint32Array(pixelData.data.buffer, pixelData.data.byteOffset, pixelData.data.length);
|
|
969
|
+
for (let y = 0; y < height; y++) {
|
|
970
|
+
let a = width * 4 * y;
|
|
971
|
+
for (let x = 0; x < width; x++, a++, di += step) {
|
|
972
|
+
const b = a + width;
|
|
973
|
+
const c = b + width;
|
|
974
|
+
const d = c + width;
|
|
984
975
|
dst[di] = ((decompressed[a] << 24) | (decompressed[b] << 16) | (decompressed[c] << 8) | decompressed[d]) >>> 0;
|
|
985
976
|
}
|
|
986
977
|
}
|
|
@@ -991,50 +982,50 @@ export function readDataZip(compressed, pixelData, width, height, bitDepth, step
|
|
|
991
982
|
}
|
|
992
983
|
}
|
|
993
984
|
export function readDataRLE(reader, pixelData, width, height, _bitDepth, step, offsets, large) {
|
|
994
|
-
|
|
995
|
-
|
|
985
|
+
const data = pixelData && pixelData.data;
|
|
986
|
+
let lengths;
|
|
996
987
|
if (large) {
|
|
997
988
|
lengths = new Uint32Array(offsets.length * height);
|
|
998
|
-
for (
|
|
999
|
-
for (
|
|
989
|
+
for (let o = 0, li = 0; o < offsets.length; o++) {
|
|
990
|
+
for (let y = 0; y < height; y++, li++) {
|
|
1000
991
|
lengths[li] = readUint32(reader);
|
|
1001
992
|
}
|
|
1002
993
|
}
|
|
1003
994
|
}
|
|
1004
995
|
else {
|
|
1005
996
|
lengths = new Uint16Array(offsets.length * height);
|
|
1006
|
-
for (
|
|
1007
|
-
for (
|
|
997
|
+
for (let o = 0, li = 0; o < offsets.length; o++) {
|
|
998
|
+
for (let y = 0; y < height; y++, li++) {
|
|
1008
999
|
lengths[li] = readUint16(reader);
|
|
1009
1000
|
}
|
|
1010
1001
|
}
|
|
1011
1002
|
}
|
|
1012
1003
|
// if (bitDepth !== 1 && bitDepth !== 8) throw new Error(`Invalid bit depth (${bitDepth})`);
|
|
1013
|
-
|
|
1014
|
-
for (
|
|
1015
|
-
|
|
1016
|
-
|
|
1004
|
+
const extraLimit = (step - 1) | 0; // 3 for rgb, 4 for cmyk
|
|
1005
|
+
for (let c = 0, li = 0; c < offsets.length; c++) {
|
|
1006
|
+
const offset = offsets[c] | 0;
|
|
1007
|
+
const extra = c > extraLimit || offset > extraLimit;
|
|
1017
1008
|
if (!data || extra) {
|
|
1018
|
-
for (
|
|
1009
|
+
for (let y = 0; y < height; y++, li++) {
|
|
1019
1010
|
skipBytes(reader, lengths[li]);
|
|
1020
1011
|
}
|
|
1021
1012
|
}
|
|
1022
1013
|
else {
|
|
1023
|
-
for (
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
for (
|
|
1027
|
-
|
|
1014
|
+
for (let y = 0, p = offset | 0; y < height; y++, li++) {
|
|
1015
|
+
const length = lengths[li];
|
|
1016
|
+
const buffer = readBytes(reader, length);
|
|
1017
|
+
for (let i = 0, x = 0; i < length; i++) {
|
|
1018
|
+
let header = buffer[i];
|
|
1028
1019
|
if (header > 128) {
|
|
1029
|
-
|
|
1020
|
+
const value = buffer[++i];
|
|
1030
1021
|
header = (256 - header) | 0;
|
|
1031
|
-
for (
|
|
1022
|
+
for (let j = 0; j <= header && x < width; j = (j + 1) | 0, x = (x + 1) | 0) {
|
|
1032
1023
|
data[p] = value;
|
|
1033
1024
|
p = (p + step) | 0;
|
|
1034
1025
|
}
|
|
1035
1026
|
}
|
|
1036
1027
|
else if (header < 128) {
|
|
1037
|
-
for (
|
|
1028
|
+
for (let j = 0; j <= header && x < width; j = (j + 1) | 0, x = (x + 1) | 0) {
|
|
1038
1029
|
data[p] = buffer[++i];
|
|
1039
1030
|
p = (p + step) | 0;
|
|
1040
1031
|
}
|
|
@@ -1049,10 +1040,8 @@ export function readDataRLE(reader, pixelData, width, height, _bitDepth, step, o
|
|
|
1049
1040
|
}
|
|
1050
1041
|
}
|
|
1051
1042
|
}
|
|
1052
|
-
export function readSection(reader, round, func, skipEmpty, eightBytes) {
|
|
1053
|
-
|
|
1054
|
-
if (eightBytes === void 0) { eightBytes = false; }
|
|
1055
|
-
var length = readUint32(reader);
|
|
1043
|
+
export function readSection(reader, round, func, skipEmpty = true, eightBytes = false) {
|
|
1044
|
+
let length = readUint32(reader);
|
|
1056
1045
|
if (eightBytes) {
|
|
1057
1046
|
if (length !== 0)
|
|
1058
1047
|
throw new Error('Sizes larger than 4GB are not supported');
|
|
@@ -1060,16 +1049,16 @@ export function readSection(reader, round, func, skipEmpty, eightBytes) {
|
|
|
1060
1049
|
}
|
|
1061
1050
|
if (length <= 0 && skipEmpty)
|
|
1062
1051
|
return undefined;
|
|
1063
|
-
|
|
1052
|
+
let end = reader.offset + length;
|
|
1064
1053
|
if (end > reader.view.byteLength)
|
|
1065
1054
|
throw new Error('Section exceeds file size');
|
|
1066
|
-
|
|
1055
|
+
const result = func(() => end - reader.offset);
|
|
1067
1056
|
if (reader.offset !== end) {
|
|
1068
1057
|
if (reader.offset > end) {
|
|
1069
1058
|
warnOrThrow(reader, 'Exceeded section limits');
|
|
1070
1059
|
}
|
|
1071
1060
|
else {
|
|
1072
|
-
warnOrThrow(reader,
|
|
1061
|
+
warnOrThrow(reader, `Unread section data`); // : ${end - reader.offset} bytes at 0x${reader.offset.toString(16)}`);
|
|
1073
1062
|
}
|
|
1074
1063
|
}
|
|
1075
1064
|
while (length % round) {
|
|
@@ -1081,67 +1070,67 @@ export function readSection(reader, round, func, skipEmpty, eightBytes) {
|
|
|
1081
1070
|
return result;
|
|
1082
1071
|
}
|
|
1083
1072
|
export function readColor(reader) {
|
|
1084
|
-
|
|
1073
|
+
const colorSpace = readUint16(reader);
|
|
1085
1074
|
switch (colorSpace) {
|
|
1086
1075
|
case 0 /* ColorSpace.RGB */: {
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1076
|
+
const r = readUint16(reader) / 257;
|
|
1077
|
+
const g = readUint16(reader) / 257;
|
|
1078
|
+
const b = readUint16(reader) / 257;
|
|
1090
1079
|
skipBytes(reader, 2);
|
|
1091
|
-
return { r
|
|
1080
|
+
return { r, g, b };
|
|
1092
1081
|
}
|
|
1093
1082
|
case 1 /* ColorSpace.HSB */: {
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1083
|
+
const h = readUint16(reader) / 0xffff;
|
|
1084
|
+
const s = readUint16(reader) / 0xffff;
|
|
1085
|
+
const b = readUint16(reader) / 0xffff;
|
|
1097
1086
|
skipBytes(reader, 2);
|
|
1098
|
-
return { h
|
|
1087
|
+
return { h, s, b };
|
|
1099
1088
|
}
|
|
1100
1089
|
case 2 /* ColorSpace.CMYK */: {
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
return { c
|
|
1090
|
+
const c = readUint16(reader) / 257;
|
|
1091
|
+
const m = readUint16(reader) / 257;
|
|
1092
|
+
const y = readUint16(reader) / 257;
|
|
1093
|
+
const k = readUint16(reader) / 257;
|
|
1094
|
+
return { c, m, y, k };
|
|
1106
1095
|
}
|
|
1107
1096
|
case 7 /* ColorSpace.Lab */: {
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1097
|
+
const l = readInt16(reader) / 10000;
|
|
1098
|
+
const ta = readInt16(reader);
|
|
1099
|
+
const tb = readInt16(reader);
|
|
1100
|
+
const a = ta < 0 ? (ta / 12800) : (ta / 12700);
|
|
1101
|
+
const b = tb < 0 ? (tb / 12800) : (tb / 12700);
|
|
1113
1102
|
skipBytes(reader, 2);
|
|
1114
|
-
return { l
|
|
1103
|
+
return { l, a, b };
|
|
1115
1104
|
}
|
|
1116
1105
|
case 8 /* ColorSpace.Grayscale */: {
|
|
1117
|
-
|
|
1106
|
+
const k = readUint16(reader) * 255 / 10000;
|
|
1118
1107
|
skipBytes(reader, 6);
|
|
1119
|
-
return { k
|
|
1108
|
+
return { k };
|
|
1120
1109
|
}
|
|
1121
1110
|
default:
|
|
1122
1111
|
throw new Error('Invalid color space');
|
|
1123
1112
|
}
|
|
1124
1113
|
}
|
|
1125
1114
|
export function readPattern(reader) {
|
|
1126
|
-
|
|
1115
|
+
let length = readUint32(reader);
|
|
1127
1116
|
while (length % 4)
|
|
1128
1117
|
length++;
|
|
1129
|
-
|
|
1130
|
-
|
|
1118
|
+
const end = reader.offset + length;
|
|
1119
|
+
const version = readUint32(reader);
|
|
1131
1120
|
if (version !== 1)
|
|
1132
|
-
throw new Error(
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1121
|
+
throw new Error(`Invalid pattern version: ${version}`);
|
|
1122
|
+
const colorMode = readUint32(reader);
|
|
1123
|
+
const x = readInt16(reader);
|
|
1124
|
+
const y = readInt16(reader);
|
|
1136
1125
|
// we only support RGB and grayscale for now
|
|
1137
1126
|
if (colorMode !== 3 /* ColorMode.RGB */ && colorMode !== 1 /* ColorMode.Grayscale */ && colorMode !== 2 /* ColorMode.Indexed */) {
|
|
1138
|
-
throw new Error(
|
|
1127
|
+
throw new Error(`Unsupported pattern color mode: ${colorMode}`);
|
|
1139
1128
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1129
|
+
let name = readUnicodeString(reader);
|
|
1130
|
+
const id = readPascalString(reader, 1);
|
|
1131
|
+
const palette = [];
|
|
1143
1132
|
if (colorMode === 2 /* ColorMode.Indexed */) {
|
|
1144
|
-
for (
|
|
1133
|
+
for (let i = 0; i < 256; i++) {
|
|
1145
1134
|
palette.push({
|
|
1146
1135
|
r: readUint8(reader),
|
|
1147
1136
|
g: readUint8(reader),
|
|
@@ -1151,58 +1140,58 @@ export function readPattern(reader) {
|
|
|
1151
1140
|
skipBytes(reader, 4); // no idea what this is
|
|
1152
1141
|
}
|
|
1153
1142
|
// virtual memory array list
|
|
1154
|
-
|
|
1143
|
+
const version2 = readUint32(reader);
|
|
1155
1144
|
if (version2 !== 3)
|
|
1156
|
-
throw new Error(
|
|
1145
|
+
throw new Error(`Invalid pattern VMAL version: ${version2}`);
|
|
1157
1146
|
readUint32(reader); // length
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
for (
|
|
1147
|
+
const top = readUint32(reader);
|
|
1148
|
+
const left = readUint32(reader);
|
|
1149
|
+
const bottom = readUint32(reader);
|
|
1150
|
+
const right = readUint32(reader);
|
|
1151
|
+
const channelsCount = readUint32(reader);
|
|
1152
|
+
const width = right - left;
|
|
1153
|
+
const height = bottom - top;
|
|
1154
|
+
const data = new Uint8Array(width * height * 4);
|
|
1155
|
+
for (let i = 3; i < data.byteLength; i += 4) {
|
|
1167
1156
|
data[i] = 255;
|
|
1168
1157
|
}
|
|
1169
|
-
for (
|
|
1170
|
-
|
|
1158
|
+
for (let i = 0, ch = 0; i < (channelsCount + 2); i++) {
|
|
1159
|
+
const has = readUint32(reader);
|
|
1171
1160
|
if (!has)
|
|
1172
1161
|
continue;
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1162
|
+
const length = readUint32(reader);
|
|
1163
|
+
const pixelDepth = readUint32(reader);
|
|
1164
|
+
const ctop = readUint32(reader);
|
|
1165
|
+
const cleft = readUint32(reader);
|
|
1166
|
+
const cbottom = readUint32(reader);
|
|
1167
|
+
const cright = readUint32(reader);
|
|
1168
|
+
const pixelDepth2 = readUint16(reader);
|
|
1169
|
+
const compressionMode = readUint8(reader); // 0 - raw, 1 - rle
|
|
1170
|
+
const dataLength = length - (4 + 16 + 2 + 1);
|
|
1171
|
+
const cdata = readBytes(reader, dataLength);
|
|
1183
1172
|
if (pixelDepth !== 8 || pixelDepth2 !== 8) {
|
|
1184
1173
|
throw new Error('16bit pixel depth not supported for patterns');
|
|
1185
1174
|
}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1175
|
+
const w = cright - cleft;
|
|
1176
|
+
const h = cbottom - ctop;
|
|
1177
|
+
const ox = cleft - left;
|
|
1178
|
+
const oy = ctop - top;
|
|
1190
1179
|
if (compressionMode === 0) {
|
|
1191
1180
|
if (colorMode === 3 /* ColorMode.RGB */ && ch < 3) {
|
|
1192
|
-
for (
|
|
1193
|
-
for (
|
|
1194
|
-
|
|
1195
|
-
|
|
1181
|
+
for (let y = 0; y < h; y++) {
|
|
1182
|
+
for (let x = 0; x < w; x++) {
|
|
1183
|
+
const src = x + y * w;
|
|
1184
|
+
const dst = (ox + x + (y + oy) * width) * 4;
|
|
1196
1185
|
data[dst + ch] = cdata[src];
|
|
1197
1186
|
}
|
|
1198
1187
|
}
|
|
1199
1188
|
}
|
|
1200
1189
|
if (colorMode === 1 /* ColorMode.Grayscale */ && ch < 1) {
|
|
1201
|
-
for (
|
|
1202
|
-
for (
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1190
|
+
for (let y = 0; y < h; y++) {
|
|
1191
|
+
for (let x = 0; x < w; x++) {
|
|
1192
|
+
const src = x + y * w;
|
|
1193
|
+
const dst = (ox + x + (y + oy) * width) * 4;
|
|
1194
|
+
const value = cdata[src];
|
|
1206
1195
|
data[dst + 0] = value;
|
|
1207
1196
|
data[dst + 1] = value;
|
|
1208
1197
|
data[dst + 2] = value;
|
|
@@ -1215,9 +1204,9 @@ export function readPattern(reader) {
|
|
|
1215
1204
|
}
|
|
1216
1205
|
}
|
|
1217
1206
|
else if (compressionMode === 1) {
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1207
|
+
const pixelData = { data, width, height };
|
|
1208
|
+
const tempData = { data: new Uint8Array(w * h), width: w, height: h };
|
|
1209
|
+
const cdataReader = createReader(cdata.buffer, cdata.byteOffset, cdata.byteLength);
|
|
1221
1210
|
if (colorMode === 3 /* ColorMode.RGB */ && ch < 3) {
|
|
1222
1211
|
readDataRLE(cdataReader, tempData, w, h, 8, 1, [0], false);
|
|
1223
1212
|
copyChannelToRGBA(tempData, pixelData, ox, oy, ch);
|
|
@@ -1238,17 +1227,17 @@ export function readPattern(reader) {
|
|
|
1238
1227
|
ch++;
|
|
1239
1228
|
}
|
|
1240
1229
|
reader.offset = end;
|
|
1241
|
-
return { id
|
|
1230
|
+
return { id, name, x, y, bounds: { x: left, y: top, w: width, h: height }, data };
|
|
1242
1231
|
}
|
|
1243
1232
|
function copyChannelToRGBA(srcData, dstData, ox, oy, offset) {
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
for (
|
|
1248
|
-
for (
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1233
|
+
const w = srcData.width;
|
|
1234
|
+
const h = srcData.height;
|
|
1235
|
+
const width = dstData.width;
|
|
1236
|
+
for (let y = 0; y < h; y++) {
|
|
1237
|
+
for (let x = 0; x < w; x++) {
|
|
1238
|
+
const src = x + y * w;
|
|
1239
|
+
const dst = (ox + x + (y + oy) * width) * 4;
|
|
1240
|
+
const value = srcData.data[src];
|
|
1252
1241
|
dstData.data[dst + offset] = value;
|
|
1253
1242
|
}
|
|
1254
1243
|
}
|