ag-psd 33.0.0 → 34.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/05135617 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/226e2801 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/4511bacf +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/68416cb2 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/71df9cb3 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/7ea06cea +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/87cf2209 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/932f0770 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/acc36e66 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/b3c2fab7 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/c0b789d2 +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/c314aece +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/cfc49f4f +0 -0
- package/.v8-cache/v24.7.0-x64-ef5a0af0/d13d7610 +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/e6b99945 +0 -0
- package/CHANGELOG.md +22 -0
- package/README.md +1 -1
- package/README_PSD.md +25 -9
- package/TODO.md +13 -84
- package/dist/abr.js +12 -7
- package/dist/abr.js.map +1 -1
- package/dist/additionalInfo.d.ts +2 -0
- package/dist/additionalInfo.js +361 -75
- package/dist/additionalInfo.js.map +1 -1
- package/dist/bundle.js +1524 -389
- package/dist/descriptor.d.ts +14 -5
- package/dist/descriptor.js +102 -64
- package/dist/descriptor.js.map +1 -1
- package/dist/helpers.d.ts +6 -0
- package/dist/helpers.js +242 -2
- package/dist/helpers.js.map +1 -1
- package/dist/imageResources.js +84 -3
- package/dist/imageResources.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/psd.d.ts +144 -2
- package/dist/psd.js.map +1 -1
- package/dist/psdReader.js +206 -110
- package/dist/psdReader.js.map +1 -1
- package/dist/psdWriter.d.ts +1 -0
- package/dist/psdWriter.js +336 -91
- package/dist/psdWriter.js.map +1 -1
- package/dist/text.d.ts +32 -1
- package/dist/text.js +58 -0
- package/dist/text.js.map +1 -1
- package/dist-es/abr.js +12 -7
- package/dist-es/abr.js.map +1 -1
- package/dist-es/additionalInfo.d.ts +2 -0
- package/dist-es/additionalInfo.js +361 -75
- package/dist-es/additionalInfo.js.map +1 -1
- package/dist-es/descriptor.d.ts +14 -5
- package/dist-es/descriptor.js +102 -64
- package/dist-es/descriptor.js.map +1 -1
- package/dist-es/helpers.d.ts +6 -0
- package/dist-es/helpers.js +242 -2
- package/dist-es/helpers.js.map +1 -1
- package/dist-es/imageResources.js +84 -3
- package/dist-es/imageResources.js.map +1 -1
- package/dist-es/index.d.ts +1 -0
- package/dist-es/index.js +1 -0
- package/dist-es/index.js.map +1 -1
- package/dist-es/psd.d.ts +144 -2
- package/dist-es/psd.js.map +1 -1
- package/dist-es/psdReader.js +206 -110
- package/dist-es/psdReader.js.map +1 -1
- package/dist-es/psdWriter.d.ts +1 -0
- package/dist-es/psdWriter.js +336 -91
- package/dist-es/psdWriter.js.map +1 -1
- package/dist-es/text.d.ts +32 -1
- package/dist-es/text.js +58 -0
- package/dist-es/text.js.map +1 -1
- package/package.json +1 -1
- package/perf.js +312 -9
- package/src/abr.ts +14 -7
- package/src/additionalInfo.ts +433 -87
- package/src/descriptor.ts +103 -58
- package/src/helpers.ts +262 -2
- package/src/imageResources.ts +107 -4
- package/src/index.ts +1 -0
- package/src/psd.ts +115 -2
- package/src/psdReader.ts +226 -108
- package/src/psdWriter.ts +345 -90
- package/src/text.ts +76 -1
package/dist-es/psdWriter.js
CHANGED
|
@@ -30,6 +30,7 @@ exports.writePsd = writePsd;
|
|
|
30
30
|
exports.writeColor = writeColor;
|
|
31
31
|
exports.writePattern = writePattern;
|
|
32
32
|
const helpers_1 = require("./helpers");
|
|
33
|
+
const pako_1 = require("pako");
|
|
33
34
|
const additionalInfo_1 = require("./additionalInfo");
|
|
34
35
|
const imageResources_1 = require("./imageResources");
|
|
35
36
|
const psdReader_1 = require("./psdReader");
|
|
@@ -202,10 +203,33 @@ function writeSection(writer, round, func, writeTotalLength = false, large = fal
|
|
|
202
203
|
function verifyPixelData(data, name) {
|
|
203
204
|
// check element size instead of instanceof, so arrays from other realms (iframes, workers, vm) are accepted
|
|
204
205
|
const bytesPerElement = data && data.data.BYTES_PER_ELEMENT;
|
|
205
|
-
if (bytesPerElement !== undefined && bytesPerElement !== 1) {
|
|
206
|
-
throw new Error(`${name} has incorrect bitDepth (only
|
|
206
|
+
if (bytesPerElement !== undefined && bytesPerElement !== 1 && bytesPerElement !== 2 && !(data.data instanceof Float32Array)) {
|
|
207
|
+
throw new Error(`${name} has incorrect bitDepth (only Uint8Array, Uint8ClampedArray, Uint16Array and Float32Array data is supported for writing)`);
|
|
207
208
|
}
|
|
208
209
|
}
|
|
210
|
+
// supported bit depths for each color mode
|
|
211
|
+
const writeBitDepths = {
|
|
212
|
+
[0 /* ColorMode.Bitmap */]: [1],
|
|
213
|
+
[1 /* ColorMode.Grayscale */]: [8, 16, 32],
|
|
214
|
+
[2 /* ColorMode.Indexed */]: [8],
|
|
215
|
+
[3 /* ColorMode.RGB */]: [8, 16, 32],
|
|
216
|
+
[4 /* ColorMode.CMYK */]: [8, 16],
|
|
217
|
+
};
|
|
218
|
+
const colorModeNames = {
|
|
219
|
+
[0 /* ColorMode.Bitmap */]: 'Bitmap', [1 /* ColorMode.Grayscale */]: 'Grayscale', [2 /* ColorMode.Indexed */]: 'Indexed', [3 /* ColorMode.RGB */]: 'RGB',
|
|
220
|
+
[4 /* ColorMode.CMYK */]: 'CMYK', [7 /* ColorMode.Multichannel */]: 'Multichannel', [8 /* ColorMode.Duotone */]: 'Duotone', [9 /* ColorMode.Lab */]: 'Lab',
|
|
221
|
+
};
|
|
222
|
+
function colorChannelsCount(colorMode) {
|
|
223
|
+
return colorMode === 3 /* ColorMode.RGB */ ? 3 : (colorMode === 4 /* ColorMode.CMYK */ ? 4 : 1);
|
|
224
|
+
}
|
|
225
|
+
// HDR toning settings that Photoshop stores in color mode data of 32bit documents (default values of new document),
|
|
226
|
+
// Photoshop refuses to open 32bit documents without them
|
|
227
|
+
const hdrToningData = [
|
|
228
|
+
104, 100, 114, 116, 0, 0, 0, 3, 62, 107, 133, 31, 0, 0, 0, 2, 0, 0, 0, 7, 0, 76, 0, 105, 0, 110, 0, 101, 0, 97, 0, 114, 0, 0,
|
|
229
|
+
0, 2, 0, 2, 0, 0, 0, 0, 0, 255, 0, 255, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 65, 128, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 63, 128, 0, 0,
|
|
230
|
+
104, 100, 114, 97, 0, 0, 0, 6, 0, 0, 0, 0, 65, 160, 0, 0, 65, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 128, 0, 0, 0, 0, 0, 0,
|
|
231
|
+
0, 0,
|
|
232
|
+
];
|
|
209
233
|
function verifyBitCount(target) {
|
|
210
234
|
var _a, _b, _c;
|
|
211
235
|
(_a = target.children) === null || _a === void 0 ? void 0 : _a.forEach(verifyBitCount);
|
|
@@ -216,34 +240,37 @@ function verifyBitCount(target) {
|
|
|
216
240
|
verifyPixelData((_c = target.realMask) === null || _c === void 0 ? void 0 : _c.imageData, 'realMask imageData');
|
|
217
241
|
}
|
|
218
242
|
function writePsd(writer, psd, options = {}) {
|
|
219
|
-
var _a;
|
|
243
|
+
var _a, _b, _c, _d;
|
|
220
244
|
if (!(+psd.width > 0 && +psd.height > 0))
|
|
221
245
|
throw new Error('Invalid document size');
|
|
222
246
|
if ((psd.width > 30000 || psd.height > 30000) && !options.psb)
|
|
223
247
|
throw new Error('Document size is too large (max is 30000x30000, use PSB format instead)');
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
248
|
+
const colorMode = (_a = psd.colorMode) !== null && _a !== void 0 ? _a : 3 /* ColorMode.RGB */;
|
|
249
|
+
const bitsPerChannel = (_b = psd.bitsPerChannel) !== null && _b !== void 0 ? _b : (colorMode === 0 /* ColorMode.Bitmap */ ? 1 : 8);
|
|
250
|
+
const bitDepths = writeBitDepths[colorMode];
|
|
251
|
+
if (!bitDepths)
|
|
252
|
+
throw new Error(`${(_c = colorModeNames[colorMode]) !== null && _c !== void 0 ? _c : colorMode} color mode is not supported for writing`);
|
|
253
|
+
if (bitDepths.indexOf(bitsPerChannel) === -1)
|
|
254
|
+
throw new Error(`bitsPerChannel ${bitsPerChannel} is not supported for writing in ${colorModeNames[colorMode]} color mode`);
|
|
255
|
+
// Photoshop doesn't support layers in bitmap and indexed color modes
|
|
256
|
+
const layersAllowed = colorMode !== 0 /* ColorMode.Bitmap */ && colorMode !== 2 /* ColorMode.Indexed */;
|
|
257
|
+
if (!layersAllowed && ((_d = psd.children) === null || _d === void 0 ? void 0 : _d.length))
|
|
258
|
+
throw new Error(`Layers are not supported in ${colorModeNames[colorMode]} color mode`);
|
|
259
|
+
if (colorMode === 2 /* ColorMode.Indexed */ && !psd.palette)
|
|
260
|
+
throw new Error('Missing color palette for Indexed color mode');
|
|
227
261
|
if (options.log)
|
|
228
262
|
writer.log = options.log;
|
|
263
|
+
if (options.logMissingFeatures)
|
|
264
|
+
writer.logMissingFeatures = true;
|
|
229
265
|
const layers = [];
|
|
230
266
|
addChildren(layers, psd.children);
|
|
231
|
-
if (!layers.length)
|
|
267
|
+
if (!layers.length && layersAllowed)
|
|
232
268
|
layers.push({});
|
|
233
269
|
verifyBitCount(psd);
|
|
234
270
|
const imageResources = Object.assign({}, psd.imageResources);
|
|
235
|
-
const opt = Object.assign(Object.assign({}, options), { layerIds: new Set(), layerToId: new Map() });
|
|
236
|
-
//
|
|
237
|
-
|
|
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
|
-
}
|
|
271
|
+
const opt = Object.assign(Object.assign({}, options), { layerIds: new Set(), layerToId: new Map(), colorMode, bitsPerChannel });
|
|
272
|
+
// 16 and 32 bit documents store layers in Lr16 / Lr32 section of global additional info
|
|
273
|
+
const layersInGlobalInfo = bitsPerChannel === 16 || bitsPerChannel === 32;
|
|
247
274
|
if (opt.generateThumbnail) {
|
|
248
275
|
imageResources.thumbnail = createThumbnail(psd);
|
|
249
276
|
}
|
|
@@ -256,20 +283,34 @@ function writePsd(writer, psd, options = {}) {
|
|
|
256
283
|
}
|
|
257
284
|
if (imageData && (psd.width !== imageData.width || psd.height !== imageData.height))
|
|
258
285
|
throw new Error('Document canvas must have the same size as document');
|
|
259
|
-
const globalAlpha = !!imageData && (0, helpers_1.hasAlpha)(imageData);
|
|
286
|
+
const globalAlpha = !!imageData && layersAllowed && (0, helpers_1.hasAlpha)(imageData);
|
|
260
287
|
// only used for encoding a single layer channel at a time, composite image is encoded directly into the writer buffer
|
|
261
288
|
writer.tempBuffer = new Uint8Array(getLargestLayerSize(psd.children, !!options.psb));
|
|
262
289
|
// header
|
|
263
290
|
writeSignature(writer, '8BPS');
|
|
264
291
|
writeUint16(writer, options.psb ? 2 : 1); // version
|
|
265
292
|
writeZeros(writer, 6);
|
|
266
|
-
writeUint16(writer, globalAlpha ?
|
|
293
|
+
writeUint16(writer, colorChannelsCount(colorMode) + (globalAlpha ? 1 : 0)); // channels
|
|
267
294
|
writeUint32(writer, psd.height);
|
|
268
295
|
writeUint32(writer, psd.width);
|
|
269
296
|
writeUint16(writer, bitsPerChannel); // bits per channel
|
|
270
|
-
writeUint16(writer,
|
|
271
|
-
// color mode data
|
|
272
|
-
|
|
297
|
+
writeUint16(writer, colorMode);
|
|
298
|
+
// color mode data
|
|
299
|
+
writeSection(writer, 1, () => {
|
|
300
|
+
var _a, _b, _c, _d, _e, _f;
|
|
301
|
+
if (colorMode === 2 /* ColorMode.Indexed */) {
|
|
302
|
+
const palette = psd.palette;
|
|
303
|
+
for (let i = 0; i < 256; i++)
|
|
304
|
+
writeUint8(writer, (_b = (_a = palette[i]) === null || _a === void 0 ? void 0 : _a.r) !== null && _b !== void 0 ? _b : 0);
|
|
305
|
+
for (let i = 0; i < 256; i++)
|
|
306
|
+
writeUint8(writer, (_d = (_c = palette[i]) === null || _c === void 0 ? void 0 : _c.g) !== null && _d !== void 0 ? _d : 0);
|
|
307
|
+
for (let i = 0; i < 256; i++)
|
|
308
|
+
writeUint8(writer, (_f = (_e = palette[i]) === null || _e === void 0 ? void 0 : _e.b) !== null && _f !== void 0 ? _f : 0);
|
|
309
|
+
}
|
|
310
|
+
else if (bitsPerChannel === 32) {
|
|
311
|
+
writeBytes(writer, new Uint8Array(hdrToningData));
|
|
312
|
+
}
|
|
313
|
+
});
|
|
273
314
|
// image resources
|
|
274
315
|
imageResources.layersGroup = layers.map(l => l.linkGroup || 0);
|
|
275
316
|
imageResources.layerGroupsEnabledId = layers.map(l => l.linkGroupEnabled == false ? 0 : 1);
|
|
@@ -289,20 +330,202 @@ function writePsd(writer, psd, options = {}) {
|
|
|
289
330
|
});
|
|
290
331
|
// layer and mask info
|
|
291
332
|
writeSection(writer, 2, () => {
|
|
292
|
-
|
|
333
|
+
if (layersInGlobalInfo || !layersAllowed) {
|
|
334
|
+
writeSection(writer, 4, () => { }, true, opt.psb); // empty layer info
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
writeLayerInfo(writer, layers, psd, globalAlpha, opt);
|
|
338
|
+
}
|
|
293
339
|
writeGlobalLayerMaskInfo(writer, psd.globalLayerMaskInfo);
|
|
340
|
+
if (layersInGlobalInfo) {
|
|
341
|
+
if (globalAlpha)
|
|
342
|
+
writeGlobalInfoSection(writer, bitsPerChannel === 16 ? 'Mt16' : 'Mt32', !!opt.psb, () => { });
|
|
343
|
+
writeGlobalInfoSection(writer, bitsPerChannel === 16 ? 'Lr16' : 'Lr32', !!opt.psb, () => {
|
|
344
|
+
writeLayerInfoData(writer, layers, psd, globalAlpha, opt);
|
|
345
|
+
});
|
|
346
|
+
}
|
|
294
347
|
writeAdditionalLayerInfo(writer, psd, psd, opt);
|
|
295
348
|
}, undefined, !!opt.psb);
|
|
296
349
|
writer.tempBuffer = undefined;
|
|
297
350
|
// image data
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
351
|
+
if (colorMode === 3 /* ColorMode.RGB */ && bitsPerChannel === 8 && (!imageData || is8bit(imageData))) {
|
|
352
|
+
writeUint16(writer, 1 /* Compression.RleCompressed */); // Photoshop doesn't support zip compression of composite image data
|
|
353
|
+
const channels = globalAlpha ? [0, 1, 2, 3] : [0, 1, 2];
|
|
354
|
+
const data = getCompositeData(imageData, psd.width, psd.height, globalAlpha);
|
|
355
|
+
// encode directly into the writer buffer
|
|
356
|
+
const maxSize = channels.length * getRLEChannelBound(data.width, data.height, !!options.psb);
|
|
357
|
+
ensureSize(writer, writer.offset + maxSize);
|
|
358
|
+
const buffer = new Uint8Array(writer.buffer, writer.offset, maxSize);
|
|
359
|
+
writer.offset += (0, helpers_1.writeDataRLE)(buffer, data, channels, !!options.psb).length;
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
writeCompositeImageData(writer, psd, imageData, colorMode, bitsPerChannel, globalAlpha, !!options.psb);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
function writeGlobalInfoSection(writer, key, psb, func) {
|
|
366
|
+
writeSignature(writer, psb ? '8B64' : '8BIM');
|
|
367
|
+
writeSignature(writer, key);
|
|
368
|
+
writeSection(writer, 4, func, false, psb);
|
|
369
|
+
}
|
|
370
|
+
function is8bit(data) {
|
|
371
|
+
return data.data.BYTES_PER_ELEMENT === 1;
|
|
372
|
+
}
|
|
373
|
+
// converts image data (8, 16 or 32 bit RGBA data) to channels of the document color mode and bit depth
|
|
374
|
+
function getPlanarChannels(imageData, colorMode, bitsPerChannel, matte) {
|
|
375
|
+
const { data, width, height } = imageData;
|
|
376
|
+
const size = width * height;
|
|
377
|
+
const srcFloat = data instanceof Float32Array;
|
|
378
|
+
const srcMax = srcFloat ? 1 : (data instanceof Uint16Array ? 0xffff : 0xff);
|
|
379
|
+
const dstFloat = bitsPerChannel === 32;
|
|
380
|
+
const dstMax = dstFloat ? 1 : (bitsPerChannel === 16 ? 0xffff : 0xff);
|
|
381
|
+
const colorCount = colorChannelsCount(colorMode);
|
|
382
|
+
const create = () => dstFloat ? new Float32Array(size) : (bitsPerChannel === 16 ? new Uint16Array(size) : new Uint8Array(size));
|
|
383
|
+
const colors = [];
|
|
384
|
+
for (let i = 0; i < colorCount; i++)
|
|
385
|
+
colors.push(create());
|
|
386
|
+
const alpha = create();
|
|
387
|
+
// color values are gamma corrected when converting between float (linear) and integer values (same as imageDataToCanvas)
|
|
388
|
+
const toNormalized = (v) => srcFloat ? Math.pow((0, helpers_1.clamp)(v, 0, 1), 1 / 2.2) : v / srcMax;
|
|
389
|
+
const fromNormalized = (v) => dstFloat ? Math.pow(v, 2.2) : Math.round((0, helpers_1.clamp)(v, 0, 1) * dstMax);
|
|
390
|
+
const sameType = srcFloat === dstFloat && srcMax === dstMax;
|
|
391
|
+
const convertColor = (v) => sameType ? (dstFloat ? v : Math.round(v)) : fromNormalized(toNormalized(v));
|
|
392
|
+
const convertAlpha = (v) => sameType ? v : (dstFloat ? v / srcMax : Math.round((0, helpers_1.clamp)(v / srcMax, 0, 1) * dstMax));
|
|
393
|
+
const cmyk = [0, 0, 0, 0];
|
|
394
|
+
for (let i = 0, p = 0; i < size; i++, p += 4) {
|
|
395
|
+
const a = data[p + 3];
|
|
396
|
+
const alphaValue = convertAlpha(a);
|
|
397
|
+
alpha[i] = alphaValue;
|
|
398
|
+
// composite image colors are blended with white (or no ink) for documents with transparency
|
|
399
|
+
const blend = matte && a !== srcMax && a > 0 ? (0, helpers_1.clamp)(a / srcMax, 0, 1) : 1;
|
|
400
|
+
if (colorMode === 4 /* ColorMode.CMYK */) {
|
|
401
|
+
(0, helpers_1.rgbToCmykValues)(toNormalized(data[p]), toNormalized(data[p + 1]), toNormalized(data[p + 2]), cmyk);
|
|
402
|
+
for (let j = 0; j < 4; j++) {
|
|
403
|
+
colors[j][i] = Math.round((1 - (0, helpers_1.clamp)(cmyk[j] * blend, 0, 1)) * dstMax); // stored inverted (0 = 100% ink)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
for (let j = 0; j < colorCount; j++) {
|
|
408
|
+
let value;
|
|
409
|
+
if (colorMode === 3 /* ColorMode.RGB */) {
|
|
410
|
+
value = convertColor(data[p + j]);
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
const r = data[p], g = data[p + 1], b = data[p + 2];
|
|
414
|
+
value = convertColor((r === g && g === b) ? r : (0.299 * r + 0.587 * g + 0.114 * b));
|
|
415
|
+
}
|
|
416
|
+
if (blend !== 1)
|
|
417
|
+
value = dstFloat ? value * blend + 1 - blend : Math.round(value * blend + dstMax * (1 - blend));
|
|
418
|
+
colors[j][i] = value;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return { colors, alpha };
|
|
423
|
+
}
|
|
424
|
+
function encodeChannel(values, width, height, options) {
|
|
425
|
+
if (!(values instanceof Uint8Array)) {
|
|
426
|
+
return { data: (0, helpers_1.writeDataZipWithPrediction)(values, width, height), compression: 3 /* Compression.ZipWithPrediction */ };
|
|
427
|
+
}
|
|
428
|
+
else if (options.compress) {
|
|
429
|
+
return { data: (0, pako_1.deflate)(values), compression: 2 /* Compression.ZipWithoutPrediction */ };
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
return { data: (0, helpers_1.writeDataRLEBytes)(values, width, height, !!options.psb), compression: 1 /* Compression.RleCompressed */ };
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function writeCompositeImageData(writer, psd, imageData, colorMode, bitsPerChannel, globalAlpha, psb) {
|
|
436
|
+
var _a;
|
|
437
|
+
const { width, height } = psd;
|
|
438
|
+
const data = imageData !== null && imageData !== void 0 ? imageData : { data: new Uint8Array(width * height * 4), width, height };
|
|
439
|
+
const parts = [];
|
|
440
|
+
if (colorMode === 0 /* ColorMode.Bitmap */ || colorMode === 2 /* ColorMode.Indexed */) {
|
|
441
|
+
const values = colorMode === 0 /* ColorMode.Bitmap */ ?
|
|
442
|
+
getBitmapData(data) :
|
|
443
|
+
getIndexedData(data, psd.palette, (_a = psd.imageResources) === null || _a === void 0 ? void 0 : _a.transparencyIndex);
|
|
444
|
+
const rowBytes = colorMode === 0 /* ColorMode.Bitmap */ ? Math.ceil(width / 8) : width;
|
|
445
|
+
if (colorMode === 0 /* ColorMode.Bitmap */) {
|
|
446
|
+
writeUint16(writer, 0 /* Compression.RawData */);
|
|
447
|
+
parts.push(values);
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
writeUint16(writer, 1 /* Compression.RleCompressed */);
|
|
451
|
+
parts.push((0, helpers_1.writeDataRLEBytes)(values, rowBytes, height, psb));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
const { colors, alpha } = getPlanarChannels(data, colorMode, bitsPerChannel, globalAlpha);
|
|
456
|
+
const channels = globalAlpha ? [...colors, alpha] : colors;
|
|
457
|
+
if (bitsPerChannel === 8) {
|
|
458
|
+
// row byte counts for all channels are stored before data of all channels
|
|
459
|
+
const encoded = channels.map(c => (0, helpers_1.writeDataRLEBytes)(c, width, height, psb));
|
|
460
|
+
const countsSize = height * (psb ? 4 : 2);
|
|
461
|
+
writeUint16(writer, 1 /* Compression.RleCompressed */);
|
|
462
|
+
for (const e of encoded)
|
|
463
|
+
parts.push(e.subarray(0, countsSize));
|
|
464
|
+
for (const e of encoded)
|
|
465
|
+
parts.push(e.subarray(countsSize));
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
writeUint16(writer, 0 /* Compression.RawData */); // Photoshop writes 16 and 32 bit composite image without compression
|
|
469
|
+
for (const c of channels)
|
|
470
|
+
parts.push((0, helpers_1.planarToBytes)(c));
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
for (const part of parts)
|
|
474
|
+
writeBytes(writer, part);
|
|
475
|
+
}
|
|
476
|
+
function getBitmapData({ data, width, height }) {
|
|
477
|
+
const rowBytes = Math.ceil(width / 8);
|
|
478
|
+
const result = new Uint8Array(rowBytes * height);
|
|
479
|
+
const max = data instanceof Float32Array ? 1 : (data instanceof Uint16Array ? 0xffff : 0xff);
|
|
480
|
+
for (let y = 0, p = 0; y < height; y++) {
|
|
481
|
+
for (let x = 0; x < width; x++, p += 4) {
|
|
482
|
+
const a = data[p + 3] / max;
|
|
483
|
+
const value = ((0.299 * data[p] + 0.587 * data[p + 1] + 0.114 * data[p + 2]) / max) * a + (1 - a); // blend with white
|
|
484
|
+
if (value < 0.5)
|
|
485
|
+
result[y * rowBytes + (x >> 3)] |= 0x80 >> (x & 7); // 1 = black
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return result;
|
|
489
|
+
}
|
|
490
|
+
function getIndexedData({ data, width, height }, palette, transparencyIndex) {
|
|
491
|
+
const size = width * height;
|
|
492
|
+
const result = new Uint8Array(size);
|
|
493
|
+
const shift = data instanceof Uint16Array ? 8 : 0;
|
|
494
|
+
const cache = new Map();
|
|
495
|
+
for (let i = 0; i < palette.length && i < 256; i++) {
|
|
496
|
+
const key = (palette[i].r << 16) | (palette[i].g << 8) | palette[i].b;
|
|
497
|
+
if (i !== transparencyIndex && !cache.has(key))
|
|
498
|
+
cache.set(key, i);
|
|
499
|
+
}
|
|
500
|
+
for (let i = 0, p = 0; i < size; i++, p += 4) {
|
|
501
|
+
const read = (v) => data instanceof Float32Array ? Math.round(Math.pow((0, helpers_1.clamp)(v, 0, 1), 1 / 2.2) * 255) : (v >>> shift);
|
|
502
|
+
const a = data instanceof Float32Array ? data[p + 3] * 255 : (data[p + 3] >>> shift);
|
|
503
|
+
if (transparencyIndex !== undefined && transparencyIndex >= 0 && a < 128) {
|
|
504
|
+
result[i] = transparencyIndex;
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
const r = read(data[p]), g = read(data[p + 1]), b = read(data[p + 2]);
|
|
508
|
+
const key = (r << 16) | (g << 8) | b;
|
|
509
|
+
let index = cache.get(key);
|
|
510
|
+
if (index === undefined) {
|
|
511
|
+
// use closest color from the palette
|
|
512
|
+
let best = Infinity;
|
|
513
|
+
index = 0;
|
|
514
|
+
for (let j = 0; j < palette.length && j < 256; j++) {
|
|
515
|
+
if (j === transparencyIndex)
|
|
516
|
+
continue;
|
|
517
|
+
const dr = palette[j].r - r, dg = palette[j].g - g, db = palette[j].b - b;
|
|
518
|
+
const distance = dr * dr + dg * dg + db * db;
|
|
519
|
+
if (distance < best) {
|
|
520
|
+
best = distance;
|
|
521
|
+
index = j;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
cache.set(key, index);
|
|
525
|
+
}
|
|
526
|
+
result[i] = index;
|
|
527
|
+
}
|
|
528
|
+
return result;
|
|
306
529
|
}
|
|
307
530
|
function getCompositeData(imageData, width, height, globalAlpha) {
|
|
308
531
|
if (!imageData)
|
|
@@ -328,60 +551,61 @@ function getCompositeData(imageData, width, height, globalAlpha) {
|
|
|
328
551
|
return dst ? { data: dst, width, height } : imageData;
|
|
329
552
|
}
|
|
330
553
|
function writeLayerInfo(writer, layers, psd, globalAlpha, options) {
|
|
331
|
-
writeSection(writer, 4, () =>
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
writeUint32(writer, c.length);
|
|
350
|
-
}
|
|
554
|
+
writeSection(writer, 4, () => writeLayerInfoData(writer, layers, psd, globalAlpha, options), true, options.psb);
|
|
555
|
+
}
|
|
556
|
+
function writeLayerInfoData(writer, layers, psd, globalAlpha, options) {
|
|
557
|
+
var _a;
|
|
558
|
+
writeInt16(writer, globalAlpha ? -layers.length : layers.length);
|
|
559
|
+
const layersData = layers.map((l, i) => getChannels(writer, l, i === 0, options));
|
|
560
|
+
// layer records
|
|
561
|
+
for (const layerData of layersData) {
|
|
562
|
+
const { layer, top, left, bottom, right, channels } = layerData;
|
|
563
|
+
writeInt32(writer, top);
|
|
564
|
+
writeInt32(writer, left);
|
|
565
|
+
writeInt32(writer, bottom);
|
|
566
|
+
writeInt32(writer, right);
|
|
567
|
+
writeUint16(writer, channels.length);
|
|
568
|
+
for (const c of channels) {
|
|
569
|
+
writeInt16(writer, c.id);
|
|
570
|
+
if (options.psb) {
|
|
571
|
+
writeUint64(writer, c.length);
|
|
351
572
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
writeUint8(writer, Math.round((0, helpers_1.clamp)((_a = layer.opacity) !== null && _a !== void 0 ? _a : 1, 0, 1) * 255));
|
|
355
|
-
writeUint8(writer, layer.clipping ? 1 : 0);
|
|
356
|
-
let flags = 0x08; // 1 for Photoshop 5.0 and later, tells if bit 4 has useful information
|
|
357
|
-
if (layer.transparencyProtected)
|
|
358
|
-
flags |= 0x01;
|
|
359
|
-
if (layer.hidden)
|
|
360
|
-
flags |= 0x02;
|
|
361
|
-
if (layer.vectorMask || (layer.sectionDivider && layer.sectionDivider.type !== 0 /* SectionDividerType.Other */) || layer.adjustment) {
|
|
362
|
-
flags |= 0x10; // pixel data irrelevant to appearance of document
|
|
573
|
+
else {
|
|
574
|
+
writeUint32(writer, c.length);
|
|
363
575
|
}
|
|
364
|
-
if (layer.effectsOpen)
|
|
365
|
-
flags |= 0x20;
|
|
366
|
-
writeUint8(writer, flags);
|
|
367
|
-
writeUint8(writer, 0); // filler
|
|
368
|
-
writeSection(writer, 1, () => {
|
|
369
|
-
writeLayerMaskData(writer, layer, layerData);
|
|
370
|
-
writeLayerBlendingRanges(writer, layer);
|
|
371
|
-
writePascalString(writer, (layer.name || '').substring(0, 255), 4);
|
|
372
|
-
writeAdditionalLayerInfo(writer, layer, psd, options);
|
|
373
|
-
});
|
|
374
576
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
577
|
+
writeSignature(writer, '8BIM');
|
|
578
|
+
writeSignature(writer, helpers_1.fromBlendMode[layer.blendMode] || 'norm');
|
|
579
|
+
writeUint8(writer, Math.round((0, helpers_1.clamp)((_a = layer.opacity) !== null && _a !== void 0 ? _a : 1, 0, 1) * 255));
|
|
580
|
+
writeUint8(writer, layer.clipping ? 1 : 0);
|
|
581
|
+
let flags = 0x08; // 1 for Photoshop 5.0 and later, tells if bit 4 has useful information
|
|
582
|
+
if (layer.transparencyProtected)
|
|
583
|
+
flags |= 0x01;
|
|
584
|
+
if (layer.hidden)
|
|
585
|
+
flags |= 0x02;
|
|
586
|
+
if (layer.vectorMask || (layer.sectionDivider && layer.sectionDivider.type !== 0 /* SectionDividerType.Other */) || layer.adjustment) {
|
|
587
|
+
flags |= 0x10; // pixel data irrelevant to appearance of document
|
|
588
|
+
}
|
|
589
|
+
if (layer.effectsOpen)
|
|
590
|
+
flags |= 0x20;
|
|
591
|
+
writeUint8(writer, flags);
|
|
592
|
+
writeUint8(writer, 0); // filler
|
|
593
|
+
writeSection(writer, 1, () => {
|
|
594
|
+
writeLayerMaskData(writer, layer, layerData);
|
|
595
|
+
writeLayerBlendingRanges(writer, layer);
|
|
596
|
+
writePascalString(writer, (layer.name || '').substring(0, 255), 4);
|
|
597
|
+
writeAdditionalLayerInfo(writer, layer, psd, options);
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
// layer channel image data
|
|
601
|
+
for (const layerData of layersData) {
|
|
602
|
+
for (const channel of layerData.channels) {
|
|
603
|
+
writeUint16(writer, channel.compression);
|
|
604
|
+
if (channel.data) {
|
|
605
|
+
writeBytes(writer, channel.data);
|
|
382
606
|
}
|
|
383
607
|
}
|
|
384
|
-
}
|
|
608
|
+
}
|
|
385
609
|
}
|
|
386
610
|
// real mask is only stored separately if layer has both vector and pixel masks, otherwise it's stored as the main mask
|
|
387
611
|
function getMasks(layer) {
|
|
@@ -584,6 +808,7 @@ function createThumbnail(psd) {
|
|
|
584
808
|
return canvas;
|
|
585
809
|
}
|
|
586
810
|
function getMaskChannels(tempBuffer, layerData, mask, options, realMask) {
|
|
811
|
+
var _a;
|
|
587
812
|
let top = mask.top | 0;
|
|
588
813
|
let left = mask.left | 0;
|
|
589
814
|
let right = mask.right | 0;
|
|
@@ -600,10 +825,15 @@ function getMaskChannels(tempBuffer, layerData, mask, options, realMask) {
|
|
|
600
825
|
bottom = top + height;
|
|
601
826
|
let buffer;
|
|
602
827
|
let compression;
|
|
828
|
+
const bitsPerChannel = (_a = options.bitsPerChannel) !== null && _a !== void 0 ? _a : 8;
|
|
603
829
|
if (!imageData) {
|
|
604
830
|
buffer = new Uint8Array(0);
|
|
605
831
|
compression = 1 /* Compression.RleCompressed */;
|
|
606
832
|
}
|
|
833
|
+
else if (bitsPerChannel !== 8 || !is8bit(imageData)) {
|
|
834
|
+
const { colors } = getPlanarChannels(imageData, 3 /* ColorMode.RGB */, bitsPerChannel, false);
|
|
835
|
+
({ data: buffer, compression } = encodeChannel(colors[0], width, height, options));
|
|
836
|
+
}
|
|
607
837
|
else if (options.compress) {
|
|
608
838
|
buffer = (0, helpers_1.writeDataZipWithoutPrediction)(imageData, [0]);
|
|
609
839
|
compression = 2 /* Compression.ZipWithoutPrediction */;
|
|
@@ -624,16 +854,16 @@ function bounds(obj) {
|
|
|
624
854
|
} : undefined;
|
|
625
855
|
}
|
|
626
856
|
function getChannels(writer, layer, background, options) {
|
|
857
|
+
var _a, _b;
|
|
627
858
|
let source = layer;
|
|
628
859
|
if (layer.rawData) {
|
|
629
860
|
const { colorMode, bitsPerChannel, large } = layer.rawData;
|
|
630
|
-
|
|
861
|
+
const opt = options;
|
|
862
|
+
if (large === !!options.psb && colorMode === ((_a = opt.colorMode) !== null && _a !== void 0 ? _a : 3 /* ColorMode.RGB */) && bitsPerChannel === ((_b = opt.bitsPerChannel) !== null && _b !== void 0 ? _b : 8)) {
|
|
631
863
|
// same mask mapping as in writeLayerMaskData
|
|
632
864
|
const { mask, realMask } = getMasks(layer);
|
|
633
865
|
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
866
|
}
|
|
635
|
-
if (bitsPerChannel !== 8)
|
|
636
|
-
throw new Error('Layer raw data with bitsPerChannel other than 8 is not supported for writing');
|
|
637
867
|
// raw data is in different format than the one we're writing, decode it so it can be re-encoded
|
|
638
868
|
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
869
|
// temp buffer is sized only for layers with image data, grow it and keep it for following layers
|
|
@@ -659,10 +889,8 @@ function getLayerDimentions({ canvas, imageData }) {
|
|
|
659
889
|
return { width, height };
|
|
660
890
|
}
|
|
661
891
|
function cropImageData(data, left, top, width, height) {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
}
|
|
665
|
-
const croppedData = (0, helpers_1.createImageData)(width, height);
|
|
892
|
+
const croppedData = is8bit(data) ? (0, helpers_1.createImageData)(width, height) :
|
|
893
|
+
{ width, height, data: new data.data.constructor(width * height * 4) };
|
|
666
894
|
const srcData = data.data;
|
|
667
895
|
const dstData = croppedData.data;
|
|
668
896
|
for (let y = 0; y < height; y++) {
|
|
@@ -678,6 +906,7 @@ function cropImageData(data, left, top, width, height) {
|
|
|
678
906
|
return croppedData;
|
|
679
907
|
}
|
|
680
908
|
function getLayerChannels(tempBuffer, layer, background, options) {
|
|
909
|
+
var _a, _b;
|
|
681
910
|
let top = layer.top | 0;
|
|
682
911
|
let left = layer.left | 0;
|
|
683
912
|
let right = layer.right | 0;
|
|
@@ -690,8 +919,11 @@ function getLayerChannels(tempBuffer, layer, background, options) {
|
|
|
690
919
|
];
|
|
691
920
|
let { width, height } = getLayerDimentions(layer);
|
|
692
921
|
if (!(layer.canvas || layer.imageData) || !width || !height) {
|
|
693
|
-
|
|
694
|
-
|
|
922
|
+
// layer without image data is empty, keep inverted (negative size) bounds from some files as they are
|
|
923
|
+
if (layer.right === undefined || right > left)
|
|
924
|
+
right = left;
|
|
925
|
+
if (layer.bottom === undefined || bottom > top)
|
|
926
|
+
bottom = top;
|
|
695
927
|
return { layer, top, left, right, bottom, channels };
|
|
696
928
|
}
|
|
697
929
|
right = left + width;
|
|
@@ -719,6 +951,19 @@ function getLayerChannels(tempBuffer, layer, background, options) {
|
|
|
719
951
|
if (!background || options.noBackground || layer.mask || (0, helpers_1.hasAlpha)(imageData)) {
|
|
720
952
|
channelIds.unshift(-1 /* ChannelID.Transparency */);
|
|
721
953
|
}
|
|
954
|
+
const colorMode = (_a = options.colorMode) !== null && _a !== void 0 ? _a : 3 /* ColorMode.RGB */;
|
|
955
|
+
const bitsPerChannel = (_b = options.bitsPerChannel) !== null && _b !== void 0 ? _b : 8;
|
|
956
|
+
if (colorMode !== 3 /* ColorMode.RGB */ || bitsPerChannel !== 8 || !is8bit(imageData)) {
|
|
957
|
+
const { colors, alpha } = getPlanarChannels(imageData, colorMode, bitsPerChannel, false);
|
|
958
|
+
const ids = channelIds[0] === -1 /* ChannelID.Transparency */ ? [-1 /* ChannelID.Transparency */] : [];
|
|
959
|
+
for (let i = 0; i < colors.length; i++)
|
|
960
|
+
ids.push(i);
|
|
961
|
+
channels = ids.map(id => {
|
|
962
|
+
const { data, compression } = encodeChannel(id === -1 /* ChannelID.Transparency */ ? alpha : colors[id], width, height, options);
|
|
963
|
+
return { id, compression, data, length: 2 + data.length };
|
|
964
|
+
});
|
|
965
|
+
return { layer, top, left, right, bottom, channels };
|
|
966
|
+
}
|
|
722
967
|
channels = channelIds.map(id => {
|
|
723
968
|
const offset = (0, helpers_1.offsetForChannel)(id, false); // image data is always RGBA
|
|
724
969
|
let data;
|