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.
Files changed (86) hide show
  1. package/.v8-cache/v24.7.0-x64-ef5a0af0/05135617 +0 -0
  2. package/.v8-cache/v24.7.0-x64-ef5a0af0/226e2801 +0 -0
  3. package/.v8-cache/v24.7.0-x64-ef5a0af0/4511bacf +0 -0
  4. package/.v8-cache/v24.7.0-x64-ef5a0af0/68416cb2 +0 -0
  5. package/.v8-cache/v24.7.0-x64-ef5a0af0/71df9cb3 +0 -0
  6. package/.v8-cache/v24.7.0-x64-ef5a0af0/7ea06cea +0 -0
  7. package/.v8-cache/v24.7.0-x64-ef5a0af0/87cf2209 +0 -0
  8. package/.v8-cache/v24.7.0-x64-ef5a0af0/932f0770 +0 -0
  9. package/.v8-cache/v24.7.0-x64-ef5a0af0/acc36e66 +0 -0
  10. package/.v8-cache/v24.7.0-x64-ef5a0af0/b3c2fab7 +0 -0
  11. package/.v8-cache/v24.7.0-x64-ef5a0af0/c0b789d2 +0 -0
  12. package/.v8-cache/v24.7.0-x64-ef5a0af0/c314aece +0 -0
  13. package/.v8-cache/v24.7.0-x64-ef5a0af0/cfc49f4f +0 -0
  14. package/.v8-cache/v24.7.0-x64-ef5a0af0/d13d7610 +0 -0
  15. package/.v8-cache/v24.7.0-x64-ef5a0af0/dc86f000 +0 -0
  16. package/.v8-cache/v24.7.0-x64-ef5a0af0/e03e2acd +0 -0
  17. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6b99945 +0 -0
  18. package/CHANGELOG.md +22 -0
  19. package/README.md +1 -1
  20. package/README_PSD.md +25 -9
  21. package/TODO.md +13 -84
  22. package/dist/abr.js +12 -7
  23. package/dist/abr.js.map +1 -1
  24. package/dist/additionalInfo.d.ts +2 -0
  25. package/dist/additionalInfo.js +361 -75
  26. package/dist/additionalInfo.js.map +1 -1
  27. package/dist/bundle.js +1524 -389
  28. package/dist/descriptor.d.ts +14 -5
  29. package/dist/descriptor.js +102 -64
  30. package/dist/descriptor.js.map +1 -1
  31. package/dist/helpers.d.ts +6 -0
  32. package/dist/helpers.js +242 -2
  33. package/dist/helpers.js.map +1 -1
  34. package/dist/imageResources.js +84 -3
  35. package/dist/imageResources.js.map +1 -1
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.js +1 -0
  38. package/dist/index.js.map +1 -1
  39. package/dist/psd.d.ts +144 -2
  40. package/dist/psd.js.map +1 -1
  41. package/dist/psdReader.js +206 -110
  42. package/dist/psdReader.js.map +1 -1
  43. package/dist/psdWriter.d.ts +1 -0
  44. package/dist/psdWriter.js +336 -91
  45. package/dist/psdWriter.js.map +1 -1
  46. package/dist/text.d.ts +32 -1
  47. package/dist/text.js +58 -0
  48. package/dist/text.js.map +1 -1
  49. package/dist-es/abr.js +12 -7
  50. package/dist-es/abr.js.map +1 -1
  51. package/dist-es/additionalInfo.d.ts +2 -0
  52. package/dist-es/additionalInfo.js +361 -75
  53. package/dist-es/additionalInfo.js.map +1 -1
  54. package/dist-es/descriptor.d.ts +14 -5
  55. package/dist-es/descriptor.js +102 -64
  56. package/dist-es/descriptor.js.map +1 -1
  57. package/dist-es/helpers.d.ts +6 -0
  58. package/dist-es/helpers.js +242 -2
  59. package/dist-es/helpers.js.map +1 -1
  60. package/dist-es/imageResources.js +84 -3
  61. package/dist-es/imageResources.js.map +1 -1
  62. package/dist-es/index.d.ts +1 -0
  63. package/dist-es/index.js +1 -0
  64. package/dist-es/index.js.map +1 -1
  65. package/dist-es/psd.d.ts +144 -2
  66. package/dist-es/psd.js.map +1 -1
  67. package/dist-es/psdReader.js +206 -110
  68. package/dist-es/psdReader.js.map +1 -1
  69. package/dist-es/psdWriter.d.ts +1 -0
  70. package/dist-es/psdWriter.js +336 -91
  71. package/dist-es/psdWriter.js.map +1 -1
  72. package/dist-es/text.d.ts +32 -1
  73. package/dist-es/text.js +58 -0
  74. package/dist-es/text.js.map +1 -1
  75. package/package.json +1 -1
  76. package/perf.js +312 -9
  77. package/src/abr.ts +14 -7
  78. package/src/additionalInfo.ts +433 -87
  79. package/src/descriptor.ts +103 -58
  80. package/src/helpers.ts +262 -2
  81. package/src/imageResources.ts +107 -4
  82. package/src/index.ts +1 -0
  83. package/src/psd.ts +115 -2
  84. package/src/psdReader.ts +226 -108
  85. package/src/psdWriter.ts +345 -90
  86. package/src/text.ts +76 -1
@@ -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 8-bit Uint8Array or Uint8ClampedArray data is supported for writing)`);
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 bitsPerChannel = (_a = psd.bitsPerChannel) !== null && _a !== void 0 ? _a : 8;
225
- if (bitsPerChannel !== 8)
226
- throw new Error('bitsPerChannel other than 8 are not supported for writing');
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
- // 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
- }
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 ? 4 : 3); // channels
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, 3 /* ColorMode.RGB */); // we only support saving RGB right now
271
- // color mode data (empty for RGB color mode)
272
- writeUint32(writer, 0);
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
- writeLayerInfo(writer, layers, psd, globalAlpha, opt);
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
- writeUint16(writer, 1 /* Compression.RleCompressed */); // Photoshop doesn't support zip compression of composite image data
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;
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
- var _a;
333
- writeInt16(writer, globalAlpha ? -layers.length : layers.length);
334
- const layersData = layers.map((l, i) => getChannels(writer, l, i === 0, options));
335
- // layer records
336
- for (const layerData of layersData) {
337
- const { layer, top, left, bottom, right, channels } = layerData;
338
- writeInt32(writer, top);
339
- writeInt32(writer, left);
340
- writeInt32(writer, bottom);
341
- writeInt32(writer, right);
342
- writeUint16(writer, channels.length);
343
- for (const c of channels) {
344
- writeInt16(writer, c.id);
345
- if (options.psb) {
346
- writeUint64(writer, c.length);
347
- }
348
- else {
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
- writeSignature(writer, '8BIM');
353
- writeSignature(writer, helpers_1.fromBlendMode[layer.blendMode] || 'norm');
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
- // layer channel image data
376
- for (const layerData of layersData) {
377
- for (const channel of layerData.channels) {
378
- writeUint16(writer, channel.compression);
379
- if (channel.data) {
380
- writeBytes(writer, channel.data);
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
- }, true, options.psb);
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
- if (large === !!options.psb && colorMode === 3 /* ColorMode.RGB */ && bitsPerChannel === 8) {
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
- if (data.data instanceof Uint32Array || data.data instanceof Uint16Array) {
663
- throw new Error('imageData has incorrect bit depth');
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
- right = left;
694
- bottom = top;
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;