ag-psd 31.0.3 → 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/00d0e0b4 +0 -0
- 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/0cc76730 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/1130885f +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/20729831 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/214a593b +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/226e2801 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/25f8b84a +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/27518289 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/2a19133e +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/330ef2d9 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/37ff16b8 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/3f72a97c +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/75efccd4 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/762f9305 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/7bcfc50a +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/7ea06cea +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/825e9a05 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/87372817 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/87cf2209 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/891a7c73 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/8b68a3a2 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/932f0770 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/a1d12a93 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/a7e80865 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/ac317c9c +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/b8569d6b +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/b8b073c4 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/bb489c89 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/c0b789d2 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/c314aece +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/c46725e1 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/c794cb34 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/cd4ae980 +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/d02dd6f7 +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/e6735171 +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/f5417276 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/f6fd8125 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/f74d3518 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/f886deac +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/fd0a2987 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/fe349423 +0 -0
- package/CHANGELOG.md +107 -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 +363 -229
- package/dist/abr.js.map +1 -1
- package/dist/additionalInfo.d.ts +56 -2
- package/dist/additionalInfo.js +600 -364
- 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 +3096 -1541
- package/dist/descriptor.d.ts +25 -6
- package/dist/descriptor.js +380 -135
- package/dist/descriptor.js.map +1 -1
- package/dist/effectsHelpers.js +12 -12
- package/dist/effectsHelpers.js.map +1 -1
- package/dist/engineData.d.ts +3 -1
- package/dist/engineData.js +34 -13
- package/dist/engineData.js.map +1 -1
- package/dist/engineData2.js +50 -21
- package/dist/engineData2.js.map +1 -1
- package/dist/helpers.d.ts +6 -3
- package/dist/helpers.js +22 -6
- package/dist/helpers.js.map +1 -1
- package/dist/imageResources.d.ts +15 -3
- package/dist/imageResources.js +529 -144
- package/dist/imageResources.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/jpeg.js +30 -10
- package/dist/jpeg.js.map +1 -1
- package/dist/psd.d.ts +154 -18
- package/dist/psd.js.map +1 -1
- package/dist/psdReader.d.ts +12 -2
- package/dist/psdReader.js +720 -421
- package/dist/psdReader.js.map +1 -1
- package/dist/psdWriter.d.ts +3 -0
- package/dist/psdWriter.js +167 -130
- package/dist/psdWriter.js.map +1 -1
- package/dist/text.js +143 -29
- package/dist/text.js.map +1 -1
- package/dist/utf8.js +36 -18
- package/dist/utf8.js.map +1 -1
- package/dist-es/abr.d.ts +11 -4
- package/dist-es/abr.js +363 -229
- package/dist-es/abr.js.map +1 -1
- package/dist-es/additionalInfo.d.ts +56 -2
- package/dist-es/additionalInfo.js +600 -364
- 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 +25 -6
- package/dist-es/descriptor.js +380 -135
- package/dist-es/descriptor.js.map +1 -1
- package/dist-es/effectsHelpers.js +12 -12
- package/dist-es/effectsHelpers.js.map +1 -1
- package/dist-es/engineData.d.ts +3 -1
- package/dist-es/engineData.js +34 -13
- package/dist-es/engineData.js.map +1 -1
- package/dist-es/engineData2.js +50 -21
- package/dist-es/engineData2.js.map +1 -1
- package/dist-es/helpers.d.ts +6 -3
- package/dist-es/helpers.js +22 -6
- package/dist-es/helpers.js.map +1 -1
- package/dist-es/imageResources.d.ts +15 -3
- package/dist-es/imageResources.js +529 -144
- package/dist-es/imageResources.js.map +1 -1
- package/dist-es/index.js +1 -0
- package/dist-es/index.js.map +1 -1
- package/dist-es/jpeg.js +30 -10
- package/dist-es/jpeg.js.map +1 -1
- package/dist-es/psd.d.ts +154 -18
- package/dist-es/psd.js.map +1 -1
- package/dist-es/psdReader.d.ts +12 -2
- package/dist-es/psdReader.js +720 -421
- package/dist-es/psdReader.js.map +1 -1
- package/dist-es/psdWriter.d.ts +3 -0
- package/dist-es/psdWriter.js +167 -130
- package/dist-es/psdWriter.js.map +1 -1
- package/dist-es/text.js +143 -29
- package/dist-es/text.js.map +1 -1
- package/dist-es/utf8.js +36 -18
- package/dist-es/utf8.js.map +1 -1
- package/package.json +2 -2
- package/src/abr.ts +380 -232
- package/src/additionalInfo.ts +781 -477
- package/src/ase.ts +66 -61
- package/src/descriptor.ts +386 -142
- package/src/effectsHelpers.ts +9 -9
- package/src/engineData.ts +36 -13
- package/src/engineData2.ts +468 -432
- package/src/helpers.ts +23 -5
- package/src/imageResources.ts +658 -167
- package/src/index.ts +1 -0
- package/src/jpeg.ts +27 -12
- package/src/psd.ts +158 -30
- package/src/psdReader.ts +732 -414
- package/src/psdWriter.ts +186 -132
- package/src/text.ts +139 -18
- package/src/utf8.ts +35 -21
package/dist-es/psdWriter.js
CHANGED
|
@@ -23,6 +23,8 @@ exports.writeUnicodeStringWithoutLength = writeUnicodeStringWithoutLength;
|
|
|
23
23
|
exports.writeUnicodeStringWithoutLengthLE = writeUnicodeStringWithoutLengthLE;
|
|
24
24
|
exports.writeUnicodeString = writeUnicodeString;
|
|
25
25
|
exports.writeUnicodeStringWithPadding = writeUnicodeStringWithPadding;
|
|
26
|
+
exports.getRLEChannelBound = getRLEChannelBound;
|
|
27
|
+
exports.writeUint64 = writeUint64;
|
|
26
28
|
exports.writeSection = writeSection;
|
|
27
29
|
exports.writePsd = writePsd;
|
|
28
30
|
exports.writeColor = writeColor;
|
|
@@ -30,11 +32,12 @@ exports.writePattern = writePattern;
|
|
|
30
32
|
const helpers_1 = require("./helpers");
|
|
31
33
|
const additionalInfo_1 = require("./additionalInfo");
|
|
32
34
|
const imageResources_1 = require("./imageResources");
|
|
35
|
+
const psdReader_1 = require("./psdReader");
|
|
33
36
|
function createWriter(size = 4096) {
|
|
34
37
|
const buffer = new ArrayBuffer(size);
|
|
35
38
|
const view = new DataView(buffer);
|
|
36
39
|
const offset = 0;
|
|
37
|
-
return { buffer, view, offset, tempBuffer: undefined };
|
|
40
|
+
return { buffer, view, offset, tempBuffer: undefined, log: console.log };
|
|
38
41
|
}
|
|
39
42
|
function getWriterBuffer(writer) {
|
|
40
43
|
return writer.buffer.slice(0, writer.offset);
|
|
@@ -146,59 +149,71 @@ function writeUnicodeStringWithPadding(writer, text) {
|
|
|
146
149
|
}
|
|
147
150
|
writeUint16(writer, 0);
|
|
148
151
|
}
|
|
149
|
-
|
|
152
|
+
// Worst-case size of writeDataRLE output for a single channel: per row the byte count (2 bytes, 4 for PSB)
|
|
153
|
+
// plus PackBits data, which in the worst case is all literal runs of up to 128 bytes, each with 1 header byte.
|
|
154
|
+
function getRLEChannelBound(width, height, large) {
|
|
155
|
+
return height * ((large ? 4 : 2) + width + Math.ceil(width / 128));
|
|
156
|
+
}
|
|
157
|
+
function getLargestLayerSize(layers = [], large) {
|
|
150
158
|
let max = 0;
|
|
151
159
|
for (const layer of layers) {
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
if (layer.realMask) {
|
|
159
|
-
const { width, height } = getLayerDimentions(layer.realMask);
|
|
160
|
-
max = Math.max(max, 2 * height + 2 * width * height);
|
|
160
|
+
for (const target of [layer, layer.mask, layer.realMask]) {
|
|
161
|
+
if (target) {
|
|
162
|
+
const { width, height } = getLayerDimentions(target);
|
|
163
|
+
max = Math.max(max, getRLEChannelBound(width, height, large));
|
|
164
|
+
}
|
|
161
165
|
}
|
|
162
166
|
if (layer.children) {
|
|
163
|
-
max = Math.max(max, getLargestLayerSize(layer.children));
|
|
167
|
+
max = Math.max(max, getLargestLayerSize(layer.children, large));
|
|
164
168
|
}
|
|
165
169
|
}
|
|
166
170
|
return max;
|
|
167
171
|
}
|
|
172
|
+
function writeUint64(writer, value) {
|
|
173
|
+
writeUint32(writer, Math.floor(value / 0x100000000));
|
|
174
|
+
writeUint32(writer, value >>> 0);
|
|
175
|
+
}
|
|
168
176
|
function writeSection(writer, round, func, writeTotalLength = false, large = false) {
|
|
177
|
+
const offset = writer.offset;
|
|
169
178
|
if (large)
|
|
170
179
|
writeUint32(writer, 0);
|
|
171
|
-
const offset = writer.offset;
|
|
172
180
|
writeUint32(writer, 0);
|
|
181
|
+
const start = writer.offset;
|
|
173
182
|
func();
|
|
174
|
-
let length = writer.offset -
|
|
183
|
+
let length = writer.offset - start;
|
|
175
184
|
let len = length;
|
|
176
185
|
while (len % round) {
|
|
177
186
|
writeUint8(writer, 0);
|
|
178
187
|
len++;
|
|
179
188
|
}
|
|
180
|
-
// while (writer.offset % round) {
|
|
181
|
-
// writeUint8(writer, 0);
|
|
182
|
-
// len++;
|
|
183
|
-
// }
|
|
184
189
|
if (writeTotalLength) {
|
|
185
190
|
length = len;
|
|
186
191
|
}
|
|
187
|
-
|
|
192
|
+
if (large) {
|
|
193
|
+
writer.view.setUint32(offset, Math.floor(length / 0x100000000), false);
|
|
194
|
+
writer.view.setUint32(offset + 4, length >>> 0, false);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
if (length > 0xffffffff)
|
|
198
|
+
throw new Error('Section too large, use PSB format instead');
|
|
199
|
+
writer.view.setUint32(offset, length, false);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
function verifyPixelData(data, name) {
|
|
203
|
+
// check element size instead of instanceof, so arrays from other realms (iframes, workers, vm) are accepted
|
|
204
|
+
const bytesPerElement = data && data.data.BYTES_PER_ELEMENT;
|
|
205
|
+
if (bytesPerElement !== undefined && bytesPerElement !== 1) {
|
|
206
|
+
throw new Error(`${name} has incorrect bitDepth (only 8-bit Uint8Array or Uint8ClampedArray data is supported for writing)`);
|
|
207
|
+
}
|
|
188
208
|
}
|
|
189
209
|
function verifyBitCount(target) {
|
|
190
|
-
var _a;
|
|
210
|
+
var _a, _b, _c;
|
|
191
211
|
(_a = target.children) === null || _a === void 0 ? void 0 : _a.forEach(verifyBitCount);
|
|
192
|
-
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const data = target.mask.imageData;
|
|
198
|
-
if (data && (data.data instanceof Uint32Array || data.data instanceof Uint16Array)) {
|
|
199
|
-
throw new Error('mask imageData has incorrect bitDepth');
|
|
200
|
-
}
|
|
201
|
-
}
|
|
212
|
+
verifyPixelData(target.imageData, 'imageData');
|
|
213
|
+
if ('mask' in target)
|
|
214
|
+
verifyPixelData((_b = target.mask) === null || _b === void 0 ? void 0 : _b.imageData, 'mask imageData');
|
|
215
|
+
if ('realMask' in target)
|
|
216
|
+
verifyPixelData((_c = target.realMask) === null || _c === void 0 ? void 0 : _c.imageData, 'realMask imageData');
|
|
202
217
|
}
|
|
203
218
|
function writePsd(writer, psd, options = {}) {
|
|
204
219
|
var _a;
|
|
@@ -209,9 +224,26 @@ function writePsd(writer, psd, options = {}) {
|
|
|
209
224
|
const bitsPerChannel = (_a = psd.bitsPerChannel) !== null && _a !== void 0 ? _a : 8;
|
|
210
225
|
if (bitsPerChannel !== 8)
|
|
211
226
|
throw new Error('bitsPerChannel other than 8 are not supported for writing');
|
|
227
|
+
if (options.log)
|
|
228
|
+
writer.log = options.log;
|
|
229
|
+
const layers = [];
|
|
230
|
+
addChildren(layers, psd.children);
|
|
231
|
+
if (!layers.length)
|
|
232
|
+
layers.push({});
|
|
212
233
|
verifyBitCount(psd);
|
|
213
234
|
const imageResources = Object.assign({}, psd.imageResources);
|
|
214
235
|
const opt = Object.assign(Object.assign({}, options), { layerIds: new Set(), layerToId: new Map() });
|
|
236
|
+
// document is always written in RGB color mode, skip resources specific to original color mode
|
|
237
|
+
if (psd.colorMode !== undefined && psd.colorMode !== 3 /* ColorMode.RGB */) {
|
|
238
|
+
delete imageResources.iccProfile;
|
|
239
|
+
delete imageResources.indexedColorCount;
|
|
240
|
+
delete imageResources.transparencyIndex;
|
|
241
|
+
delete imageResources.alphaChannelDisplayInfo;
|
|
242
|
+
delete imageResources.colorHalftoneScreens;
|
|
243
|
+
delete imageResources.grayscaleHalftoneScreens;
|
|
244
|
+
delete imageResources.colorTransferFunctions;
|
|
245
|
+
delete imageResources.grayscaleTransferFunctions;
|
|
246
|
+
}
|
|
215
247
|
if (opt.generateThumbnail) {
|
|
216
248
|
imageResources.thumbnail = createThumbnail(psd);
|
|
217
249
|
}
|
|
@@ -219,11 +251,14 @@ function writePsd(writer, psd, options = {}) {
|
|
|
219
251
|
if (!imageData && psd.canvas) {
|
|
220
252
|
imageData = psd.canvas.getContext('2d').getImageData(0, 0, psd.canvas.width, psd.canvas.height);
|
|
221
253
|
}
|
|
254
|
+
if (!imageData && psd.rawCompositeData) { // read with `useRawData` option
|
|
255
|
+
imageData = (0, psdReader_1.getCompositeImageData)(psd);
|
|
256
|
+
}
|
|
222
257
|
if (imageData && (psd.width !== imageData.width || psd.height !== imageData.height))
|
|
223
258
|
throw new Error('Document canvas must have the same size as document');
|
|
224
259
|
const globalAlpha = !!imageData && (0, helpers_1.hasAlpha)(imageData);
|
|
225
|
-
|
|
226
|
-
writer.tempBuffer = new Uint8Array(
|
|
260
|
+
// only used for encoding a single layer channel at a time, composite image is encoded directly into the writer buffer
|
|
261
|
+
writer.tempBuffer = new Uint8Array(getLargestLayerSize(psd.children, !!options.psb));
|
|
227
262
|
// header
|
|
228
263
|
writeSignature(writer, '8BPS');
|
|
229
264
|
writeUint16(writer, options.psb ? 2 : 1); // version
|
|
@@ -233,23 +268,8 @@ function writePsd(writer, psd, options = {}) {
|
|
|
233
268
|
writeUint32(writer, psd.width);
|
|
234
269
|
writeUint16(writer, bitsPerChannel); // bits per channel
|
|
235
270
|
writeUint16(writer, 3 /* ColorMode.RGB */); // we only support saving RGB right now
|
|
236
|
-
// color mode data
|
|
237
|
-
|
|
238
|
-
var _a, _b, _c;
|
|
239
|
-
if (psd.palette) {
|
|
240
|
-
for (let i = 0; i < 256; i++)
|
|
241
|
-
writeUint8(writer, ((_a = psd.palette[i]) === null || _a === void 0 ? void 0 : _a.r) || 0);
|
|
242
|
-
for (let i = 0; i < 256; i++)
|
|
243
|
-
writeUint8(writer, ((_b = psd.palette[i]) === null || _b === void 0 ? void 0 : _b.g) || 0);
|
|
244
|
-
for (let i = 0; i < 256; i++)
|
|
245
|
-
writeUint8(writer, ((_c = psd.palette[i]) === null || _c === void 0 ? void 0 : _c.b) || 0);
|
|
246
|
-
}
|
|
247
|
-
// TODO: other data?
|
|
248
|
-
});
|
|
249
|
-
const layers = [];
|
|
250
|
-
addChildren(layers, psd.children);
|
|
251
|
-
if (!layers.length)
|
|
252
|
-
layers.push({});
|
|
271
|
+
// color mode data (empty for RGB color mode)
|
|
272
|
+
writeUint32(writer, 0);
|
|
253
273
|
// image resources
|
|
254
274
|
imageResources.layersGroup = layers.map(l => l.linkGroup || 0);
|
|
255
275
|
imageResources.layerGroupsEnabledId = layers.map(l => l.linkGroupEnabled == false ? 0 : 1);
|
|
@@ -258,10 +278,12 @@ function writePsd(writer, psd, options = {}) {
|
|
|
258
278
|
const has = handler.has(imageResources);
|
|
259
279
|
const count = has === false ? 0 : (has === true ? 1 : has);
|
|
260
280
|
for (let i = 0; i < count; i++) {
|
|
281
|
+
const id = handler.keyCount > 1 ? handler.key + i : handler.key;
|
|
282
|
+
const name = handler.name ? handler.name(imageResources, i) : '';
|
|
261
283
|
writeSignature(writer, '8BIM');
|
|
262
|
-
writeUint16(writer,
|
|
263
|
-
writePascalString(writer,
|
|
264
|
-
writeSection(writer, 2, () => handler.write(writer, imageResources, i));
|
|
284
|
+
writeUint16(writer, id);
|
|
285
|
+
writePascalString(writer, name, 2);
|
|
286
|
+
writeSection(writer, 2, () => handler.write(writer, imageResources, i, psd));
|
|
265
287
|
}
|
|
266
288
|
}
|
|
267
289
|
});
|
|
@@ -271,42 +293,45 @@ function writePsd(writer, psd, options = {}) {
|
|
|
271
293
|
writeGlobalLayerMaskInfo(writer, psd.globalLayerMaskInfo);
|
|
272
294
|
writeAdditionalLayerInfo(writer, psd, psd, opt);
|
|
273
295
|
}, undefined, !!opt.psb);
|
|
296
|
+
writer.tempBuffer = undefined;
|
|
274
297
|
// image data
|
|
275
|
-
const channels = globalAlpha ? [0, 1, 2, 3] : [0, 1, 2];
|
|
276
|
-
const width = imageData ? imageData.width : psd.width;
|
|
277
|
-
const height = imageData ? imageData.height : psd.height;
|
|
278
|
-
const data = { data: new Uint8Array(width * height * 4), width, height };
|
|
279
298
|
writeUint16(writer, 1 /* Compression.RleCompressed */); // Photoshop doesn't support zip compression of composite image data
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
299
|
+
const channels = globalAlpha ? [0, 1, 2, 3] : [0, 1, 2];
|
|
300
|
+
const data = getCompositeData(imageData, psd.width, psd.height, globalAlpha);
|
|
301
|
+
// encode directly into the writer buffer
|
|
302
|
+
const maxSize = channels.length * getRLEChannelBound(data.width, data.height, !!options.psb);
|
|
303
|
+
ensureSize(writer, writer.offset + maxSize);
|
|
304
|
+
const buffer = new Uint8Array(writer.buffer, writer.offset, maxSize);
|
|
305
|
+
writer.offset += (0, helpers_1.writeDataRLE)(buffer, data, channels, !!options.psb).length;
|
|
306
|
+
}
|
|
307
|
+
function getCompositeData(imageData, width, height, globalAlpha) {
|
|
308
|
+
if (!imageData)
|
|
309
|
+
return { data: new Uint8Array(width * height * 4), width, height };
|
|
310
|
+
const size = width * height * 4;
|
|
311
|
+
const src = imageData.data;
|
|
312
|
+
let dst = undefined;
|
|
313
|
+
// add weird white matte (copy the data only if there are semi-transparent pixels)
|
|
314
|
+
if (globalAlpha) {
|
|
315
|
+
for (let i = 0; i < size; i += 4) {
|
|
316
|
+
const pa = src[i + 3];
|
|
317
|
+
if (pa != 0 && pa != 255) {
|
|
318
|
+
if (!dst)
|
|
319
|
+
dst = new Uint8Array(src.buffer, src.byteOffset, size).slice();
|
|
320
|
+
const a = pa / 255;
|
|
321
|
+
const ra = 255 * (1 - a);
|
|
322
|
+
dst[i + 0] = src[i + 0] * a + ra;
|
|
323
|
+
dst[i + 1] = src[i + 1] * a + ra;
|
|
324
|
+
dst[i + 2] = src[i + 2] * a + ra;
|
|
300
325
|
}
|
|
301
326
|
}
|
|
302
|
-
writeBytes(writer, (0, helpers_1.writeDataRLE)(writer.tempBuffer, data, channels, !!options.psb));
|
|
303
327
|
}
|
|
328
|
+
return dst ? { data: dst, width, height } : imageData;
|
|
304
329
|
}
|
|
305
330
|
function writeLayerInfo(writer, layers, psd, globalAlpha, options) {
|
|
306
331
|
writeSection(writer, 4, () => {
|
|
307
332
|
var _a;
|
|
308
333
|
writeInt16(writer, globalAlpha ? -layers.length : layers.length);
|
|
309
|
-
const layersData = layers.map((l, i) => getChannels(writer
|
|
334
|
+
const layersData = layers.map((l, i) => getChannels(writer, l, i === 0, options));
|
|
310
335
|
// layer records
|
|
311
336
|
for (const layerData of layersData) {
|
|
312
337
|
const { layer, top, left, bottom, right, channels } = layerData;
|
|
@@ -317,9 +342,12 @@ function writeLayerInfo(writer, layers, psd, globalAlpha, options) {
|
|
|
317
342
|
writeUint16(writer, channels.length);
|
|
318
343
|
for (const c of channels) {
|
|
319
344
|
writeInt16(writer, c.id);
|
|
320
|
-
if (options.psb)
|
|
321
|
-
|
|
322
|
-
|
|
345
|
+
if (options.psb) {
|
|
346
|
+
writeUint64(writer, c.length);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
writeUint32(writer, c.length);
|
|
350
|
+
}
|
|
323
351
|
}
|
|
324
352
|
writeSignature(writer, '8BIM');
|
|
325
353
|
writeSignature(writer, helpers_1.fromBlendMode[layer.blendMode] || 'norm');
|
|
@@ -355,8 +383,13 @@ function writeLayerInfo(writer, layers, psd, globalAlpha, options) {
|
|
|
355
383
|
}
|
|
356
384
|
}, true, options.psb);
|
|
357
385
|
}
|
|
358
|
-
|
|
359
|
-
|
|
386
|
+
// real mask is only stored separately if layer has both vector and pixel masks, otherwise it's stored as the main mask
|
|
387
|
+
function getMasks(layer) {
|
|
388
|
+
return layer.mask ? { mask: layer.mask, realMask: layer.realMask } : { mask: layer.realMask, realMask: undefined };
|
|
389
|
+
}
|
|
390
|
+
function writeLayerMaskData(writer, layer, layerData) {
|
|
391
|
+
const { mask, realMask } = getMasks(layer);
|
|
392
|
+
writeSection(writer, 4, () => {
|
|
360
393
|
if (!mask && !realMask)
|
|
361
394
|
return;
|
|
362
395
|
let params = 0, flags = 0, realFlags = 0;
|
|
@@ -411,8 +444,7 @@ function writeLayerMaskData(writer, { mask, realMask }, layerData) {
|
|
|
411
444
|
if (mask.vectorMaskFeather !== undefined)
|
|
412
445
|
writeFloat64(writer, mask.vectorMaskFeather);
|
|
413
446
|
}
|
|
414
|
-
|
|
415
|
-
});
|
|
447
|
+
}, true); // photoshop pads mask data to multiple of 4 bytes and includes padding in the length
|
|
416
448
|
}
|
|
417
449
|
function writerBlendingRange(writer, range) {
|
|
418
450
|
writeUint8(writer, range[0]);
|
|
@@ -441,7 +473,7 @@ function writeGlobalLayerMaskInfo(writer, info) {
|
|
|
441
473
|
writeUint16(writer, info.colorSpace2);
|
|
442
474
|
writeUint16(writer, info.colorSpace3);
|
|
443
475
|
writeUint16(writer, info.colorSpace4);
|
|
444
|
-
writeUint16(writer, Math.round(info.opacity *
|
|
476
|
+
writeUint16(writer, Math.round((0, helpers_1.clamp)(info.opacity, 0, 1) * 100)); // 0-100
|
|
445
477
|
writeUint8(writer, info.kind);
|
|
446
478
|
writeZeros(writer, 3);
|
|
447
479
|
}
|
|
@@ -452,13 +484,13 @@ function writeAdditionalLayerInfo(writer, target, psd, options) {
|
|
|
452
484
|
let key = handler.key;
|
|
453
485
|
if (key === 'Txt2' && options.invalidateTextLayers)
|
|
454
486
|
continue;
|
|
455
|
-
if (key === 'vmsk' &&
|
|
456
|
-
key = 'vsms';
|
|
487
|
+
if (key === 'vmsk' && additionalInfo_1.infoHandlersMap['vscg'].has(target))
|
|
488
|
+
key = 'vsms'; // Photoshop uses 'vsms' for layers with vector stroke
|
|
457
489
|
if (handler.has(target)) {
|
|
458
490
|
const large = options.psb && helpers_1.largeAdditionalInfoKeys.indexOf(key) !== -1;
|
|
459
491
|
const writeTotalLength = key !== 'Txt2' && key !== 'cinf' && key !== 'extn' && key !== 'CAI ' && key !== 'OCIO';
|
|
460
492
|
const fourBytes = key === 'levl' || key === 'Txt2' || key === 'luni' || key === 'vmsk' || key === 'artb' || key === 'artd' ||
|
|
461
|
-
key === 'vogk' || key === 'SoLd' || key === 'lnk2' || key === 'vscg' || key === 'vsms' || key === 'GdFl' ||
|
|
493
|
+
key === 'vogk' || key === 'SoLd' || key === 'SoLE' || key === 'lnk2' || key === 'vscg' || key === 'vsms' || key === 'GdFl' ||
|
|
462
494
|
key === 'lmfx' || key === 'lrFX' || key === 'cinf' || key === 'PlLd' || key === 'Anno' || key === 'CAI ' || key === 'OCIO' || key === 'GenI' || key === 'FEid' || key === 'curv' || key === 'CgEd' || key === 'vibA' || key === 'blwh' || key === 'grdm';
|
|
463
495
|
writeSignature(writer, large ? '8B64' : '8BIM');
|
|
464
496
|
writeSignature(writer, key);
|
|
@@ -509,10 +541,8 @@ function addChildren(layers, children) {
|
|
|
509
541
|
}
|
|
510
542
|
}
|
|
511
543
|
function resizeBuffer(writer, size) {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
newLength *= 2;
|
|
515
|
-
} while (size > newLength);
|
|
544
|
+
// grow at least 2x to keep small writes fast, but don't overshoot large reservations (like composite image data)
|
|
545
|
+
const newLength = Math.max(writer.buffer.byteLength * 2, size);
|
|
516
546
|
const newBuffer = new ArrayBuffer(newLength);
|
|
517
547
|
const newBytes = new Uint8Array(newBuffer);
|
|
518
548
|
const oldBytes = new Uint8Array(writer.buffer);
|
|
@@ -535,12 +565,12 @@ function createThumbnail(psd) {
|
|
|
535
565
|
let scale = 1;
|
|
536
566
|
if (psd.width > psd.height) {
|
|
537
567
|
canvas.width = 160;
|
|
538
|
-
canvas.height = Math.floor(psd.height * (canvas.width / psd.width));
|
|
568
|
+
canvas.height = Math.max(1, Math.floor(psd.height * (canvas.width / psd.width)));
|
|
539
569
|
scale = canvas.width / psd.width;
|
|
540
570
|
}
|
|
541
571
|
else {
|
|
542
572
|
canvas.height = 160;
|
|
543
|
-
canvas.width = Math.floor(psd.width * (canvas.height / psd.height));
|
|
573
|
+
canvas.width = Math.max(1, Math.floor(psd.width * (canvas.height / psd.height)));
|
|
544
574
|
scale = canvas.height / psd.height;
|
|
545
575
|
}
|
|
546
576
|
const context = canvas.getContext('2d');
|
|
@@ -553,7 +583,7 @@ function createThumbnail(psd) {
|
|
|
553
583
|
}
|
|
554
584
|
return canvas;
|
|
555
585
|
}
|
|
556
|
-
function getMaskChannels(tempBuffer, layerData,
|
|
586
|
+
function getMaskChannels(tempBuffer, layerData, mask, options, realMask) {
|
|
557
587
|
let top = mask.top | 0;
|
|
558
588
|
let left = mask.left | 0;
|
|
559
589
|
let right = mask.right | 0;
|
|
@@ -570,11 +600,7 @@ function getMaskChannels(tempBuffer, layerData, layer, mask, options, realMask)
|
|
|
570
600
|
bottom = top + height;
|
|
571
601
|
let buffer;
|
|
572
602
|
let compression;
|
|
573
|
-
if (
|
|
574
|
-
buffer = layer[realMask ? 'realMaskDataRaw' : 'maskDataRaw'];
|
|
575
|
-
compression = layer[realMask ? 'realMaskDataRawCompression' : 'maskDataRawCompression'];
|
|
576
|
-
}
|
|
577
|
-
else if (!imageData) {
|
|
603
|
+
if (!imageData) {
|
|
578
604
|
buffer = new Uint8Array(0);
|
|
579
605
|
compression = 1 /* Compression.RleCompressed */;
|
|
580
606
|
}
|
|
@@ -583,7 +609,7 @@ function getMaskChannels(tempBuffer, layerData, layer, mask, options, realMask)
|
|
|
583
609
|
compression = 2 /* Compression.ZipWithoutPrediction */;
|
|
584
610
|
}
|
|
585
611
|
else {
|
|
586
|
-
buffer = (0, helpers_1.writeDataRLE)(tempBuffer, imageData, [0], !!options.psb);
|
|
612
|
+
buffer = (0, helpers_1.writeDataRLE)(tempBuffer, imageData, [0], !!options.psb).slice(); // copy, temp buffer is reused
|
|
587
613
|
compression = 1 /* Compression.RleCompressed */;
|
|
588
614
|
}
|
|
589
615
|
layerData.channels.push({ id: realMask ? -3 /* ChannelID.RealUserMask */ : -2 /* ChannelID.UserMask */, compression, data: buffer, length: 2 + buffer.length });
|
|
@@ -597,15 +623,32 @@ function bounds(obj) {
|
|
|
597
623
|
bottom: obj.bottom || 0,
|
|
598
624
|
} : undefined;
|
|
599
625
|
}
|
|
600
|
-
function getChannels(
|
|
626
|
+
function getChannels(writer, layer, background, options) {
|
|
627
|
+
let source = layer;
|
|
601
628
|
if (layer.rawData) {
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
629
|
+
const { colorMode, bitsPerChannel, large } = layer.rawData;
|
|
630
|
+
if (large === !!options.psb && colorMode === 3 /* ColorMode.RGB */ && bitsPerChannel === 8) {
|
|
631
|
+
// same mask mapping as in writeLayerMaskData
|
|
632
|
+
const { mask, realMask } = getMasks(layer);
|
|
633
|
+
return Object.assign(Object.assign({ layer, channels: layer.rawData.channels.map(c => { var _a, _b; return (Object.assign(Object.assign({}, c), { length: 2 + ((_b = (_a = c.data) === null || _a === void 0 ? void 0 : _a.byteLength) !== null && _b !== void 0 ? _b : 0) })); }) }, bounds(layer)), { mask: bounds(mask), realMask: bounds(realMask) });
|
|
634
|
+
}
|
|
635
|
+
if (bitsPerChannel !== 8)
|
|
636
|
+
throw new Error('Layer raw data with bitsPerChannel other than 8 is not supported for writing');
|
|
637
|
+
// raw data is in different format than the one we're writing, decode it so it can be re-encoded
|
|
638
|
+
source = Object.assign(Object.assign({}, layer), { rawData: undefined, canvas: undefined, imageData: (0, psdReader_1.getLayerImageData)(layer), mask: layer.mask && Object.assign(Object.assign({}, layer.mask), { canvas: undefined, imageData: (0, psdReader_1.getLayerMaskImageData)(layer) }), realMask: layer.realMask && Object.assign(Object.assign({}, layer.realMask), { canvas: undefined, imageData: (0, psdReader_1.getLayerRealMaskImageData)(layer) }) });
|
|
639
|
+
// temp buffer is sized only for layers with image data, grow it and keep it for following layers
|
|
640
|
+
const size = getLargestLayerSize([Object.assign(Object.assign({}, source), { children: undefined })], !!options.psb);
|
|
641
|
+
if (writer.tempBuffer.byteLength < size)
|
|
642
|
+
writer.tempBuffer = new Uint8Array(size);
|
|
643
|
+
}
|
|
644
|
+
const tempBuffer = writer.tempBuffer;
|
|
645
|
+
const { mask, realMask } = getMasks(source);
|
|
646
|
+
const layerData = getLayerChannels(tempBuffer, source, background, options);
|
|
647
|
+
if (mask)
|
|
648
|
+
getMaskChannels(tempBuffer, layerData, mask, options, false);
|
|
649
|
+
if (realMask)
|
|
650
|
+
getMaskChannels(tempBuffer, layerData, realMask, options, true);
|
|
651
|
+
layerData.layer = layer;
|
|
609
652
|
return layerData;
|
|
610
653
|
}
|
|
611
654
|
function getLayerDimentions({ canvas, imageData }) {
|
|
@@ -635,7 +678,6 @@ function cropImageData(data, left, top, width, height) {
|
|
|
635
678
|
return croppedData;
|
|
636
679
|
}
|
|
637
680
|
function getLayerChannels(tempBuffer, layer, background, options) {
|
|
638
|
-
var _a;
|
|
639
681
|
let top = layer.top | 0;
|
|
640
682
|
let left = layer.left | 0;
|
|
641
683
|
let right = layer.right | 0;
|
|
@@ -674,24 +716,19 @@ function getLayerChannels(tempBuffer, layer, background, options) {
|
|
|
674
716
|
1 /* ChannelID.Color1 */,
|
|
675
717
|
2 /* ChannelID.Color2 */,
|
|
676
718
|
];
|
|
677
|
-
if (!background || options.noBackground || layer.mask || (0, helpers_1.hasAlpha)(imageData)
|
|
719
|
+
if (!background || options.noBackground || layer.mask || (0, helpers_1.hasAlpha)(imageData)) {
|
|
678
720
|
channelIds.unshift(-1 /* ChannelID.Transparency */);
|
|
679
721
|
}
|
|
680
722
|
channels = channelIds.map(id => {
|
|
681
|
-
const offset = (0, helpers_1.offsetForChannel)(id, false); //
|
|
723
|
+
const offset = (0, helpers_1.offsetForChannel)(id, false); // image data is always RGBA
|
|
682
724
|
let data;
|
|
683
725
|
let compression;
|
|
684
|
-
if (
|
|
685
|
-
// console.log('written raw layer image data');
|
|
686
|
-
data = layer.imageDataRaw[id];
|
|
687
|
-
compression = layer.imageDataRawCompression[id];
|
|
688
|
-
}
|
|
689
|
-
else if (options.compress) {
|
|
726
|
+
if (options.compress) {
|
|
690
727
|
data = (0, helpers_1.writeDataZipWithoutPrediction)(imageData, [offset]);
|
|
691
728
|
compression = 2 /* Compression.ZipWithoutPrediction */;
|
|
692
729
|
}
|
|
693
730
|
else {
|
|
694
|
-
data = (0, helpers_1.writeDataRLE)(tempBuffer, imageData, [offset], !!options.psb);
|
|
731
|
+
data = (0, helpers_1.writeDataRLE)(tempBuffer, imageData, [offset], !!options.psb).slice(); // copy, temp buffer is reused
|
|
695
732
|
compression = 1 /* Compression.RleCompressed */;
|
|
696
733
|
}
|
|
697
734
|
return { id, compression, data, length: 2 + data.length };
|
|
@@ -755,8 +792,8 @@ function writeColor(writer, color) {
|
|
|
755
792
|
else if ('l' in color) {
|
|
756
793
|
writeUint16(writer, 7 /* ColorSpace.Lab */);
|
|
757
794
|
writeInt16(writer, Math.round(color.l * 10000));
|
|
758
|
-
writeInt16(writer, Math.round(
|
|
759
|
-
writeInt16(writer, Math.round(
|
|
795
|
+
writeInt16(writer, Math.round((0, helpers_1.denormalizeLabAB)(color.a, 100)));
|
|
796
|
+
writeInt16(writer, Math.round((0, helpers_1.denormalizeLabAB)(color.b, 100)));
|
|
760
797
|
writeUint16(writer, 0);
|
|
761
798
|
}
|
|
762
799
|
else if ('h' in color) {
|
|
@@ -767,11 +804,12 @@ function writeColor(writer, color) {
|
|
|
767
804
|
writeUint16(writer, 0);
|
|
768
805
|
}
|
|
769
806
|
else if ('c' in color) {
|
|
807
|
+
// 0 = 100% ink
|
|
770
808
|
writeUint16(writer, 2 /* ColorSpace.CMYK */);
|
|
771
|
-
writeUint16(writer, Math.round(color.c * 257));
|
|
772
|
-
writeUint16(writer, Math.round(color.m * 257));
|
|
773
|
-
writeUint16(writer, Math.round(color.y * 257));
|
|
774
|
-
writeUint16(writer, Math.round(color.k * 257));
|
|
809
|
+
writeUint16(writer, 0xffff - Math.round((0, helpers_1.clamp)(color.c, 0, 255) * 257));
|
|
810
|
+
writeUint16(writer, 0xffff - Math.round((0, helpers_1.clamp)(color.m, 0, 255) * 257));
|
|
811
|
+
writeUint16(writer, 0xffff - Math.round((0, helpers_1.clamp)(color.y, 0, 255) * 257));
|
|
812
|
+
writeUint16(writer, 0xffff - Math.round((0, helpers_1.clamp)(color.k, 0, 255) * 257));
|
|
775
813
|
}
|
|
776
814
|
else {
|
|
777
815
|
writeUint16(writer, 8 /* ColorSpace.Grayscale */);
|
|
@@ -806,6 +844,9 @@ function writePattern(writer, pattern) {
|
|
|
806
844
|
writeUint32(writer, bottom);
|
|
807
845
|
writeUint32(writer, right);
|
|
808
846
|
writeUint32(writer, 24); // channels count
|
|
847
|
+
// ponytail: pattern channels always use small (2-byte) RLE scanline headers, matching
|
|
848
|
+
// readPattern which reads them as non-large
|
|
849
|
+
const buffer = new Uint8Array(getRLEChannelBound(width, height, false));
|
|
809
850
|
// channels: RGB at indices 0,1,2 and alpha at index 25
|
|
810
851
|
for (let i = 0; i < 24 + 2; i++) {
|
|
811
852
|
const offset = i < 3 ? i : (i === 25 ? 3 : -1);
|
|
@@ -813,10 +854,6 @@ function writePattern(writer, pattern) {
|
|
|
813
854
|
writeUint32(writer, 0); // has
|
|
814
855
|
continue;
|
|
815
856
|
}
|
|
816
|
-
// worst-case RLE size for a single channel: width + 1 per scanline, plus scanline length headers
|
|
817
|
-
const buffer = new Uint8Array(width * height + 2 * height + 2 * width + 16);
|
|
818
|
-
// ponytail: pattern channels always use small (2-byte) RLE scanline headers, matching
|
|
819
|
-
// readPattern which reads them as non-large; `large` here only affects the header width.
|
|
820
857
|
const data = (0, helpers_1.writeDataRLE)(buffer, pixelData, [offset], false);
|
|
821
858
|
writeUint32(writer, 1); // has
|
|
822
859
|
writeUint32(writer, data.length + 4 + 16 + 2 + 1); // length
|