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.
Files changed (159) hide show
  1. package/.v8-cache/v24.7.0-x64-ef5a0af0/00d0e0b4 +0 -0
  2. package/.v8-cache/v24.7.0-x64-ef5a0af0/03e19709 +0 -0
  3. package/.v8-cache/v24.7.0-x64-ef5a0af0/05135617 +0 -0
  4. package/.v8-cache/v24.7.0-x64-ef5a0af0/0cc76730 +0 -0
  5. package/.v8-cache/v24.7.0-x64-ef5a0af0/1130885f +0 -0
  6. package/.v8-cache/v24.7.0-x64-ef5a0af0/20729831 +0 -0
  7. package/.v8-cache/v24.7.0-x64-ef5a0af0/214a593b +0 -0
  8. package/.v8-cache/v24.7.0-x64-ef5a0af0/226e2801 +0 -0
  9. package/.v8-cache/v24.7.0-x64-ef5a0af0/25f8b84a +0 -0
  10. package/.v8-cache/v24.7.0-x64-ef5a0af0/27518289 +0 -0
  11. package/.v8-cache/v24.7.0-x64-ef5a0af0/2a19133e +0 -0
  12. package/.v8-cache/v24.7.0-x64-ef5a0af0/330ef2d9 +0 -0
  13. package/.v8-cache/v24.7.0-x64-ef5a0af0/37ff16b8 +0 -0
  14. package/.v8-cache/v24.7.0-x64-ef5a0af0/3f72a97c +0 -0
  15. package/.v8-cache/v24.7.0-x64-ef5a0af0/4511bacf +0 -0
  16. package/.v8-cache/v24.7.0-x64-ef5a0af0/68416cb2 +0 -0
  17. package/.v8-cache/v24.7.0-x64-ef5a0af0/71df9cb3 +0 -0
  18. package/.v8-cache/v24.7.0-x64-ef5a0af0/75efccd4 +0 -0
  19. package/.v8-cache/v24.7.0-x64-ef5a0af0/762f9305 +0 -0
  20. package/.v8-cache/v24.7.0-x64-ef5a0af0/7bcfc50a +0 -0
  21. package/.v8-cache/v24.7.0-x64-ef5a0af0/7ea06cea +0 -0
  22. package/.v8-cache/v24.7.0-x64-ef5a0af0/825e9a05 +0 -0
  23. package/.v8-cache/v24.7.0-x64-ef5a0af0/87372817 +0 -0
  24. package/.v8-cache/v24.7.0-x64-ef5a0af0/87cf2209 +0 -0
  25. package/.v8-cache/v24.7.0-x64-ef5a0af0/891a7c73 +0 -0
  26. package/.v8-cache/v24.7.0-x64-ef5a0af0/8b68a3a2 +0 -0
  27. package/.v8-cache/v24.7.0-x64-ef5a0af0/932f0770 +0 -0
  28. package/.v8-cache/v24.7.0-x64-ef5a0af0/a1d12a93 +0 -0
  29. package/.v8-cache/v24.7.0-x64-ef5a0af0/a7e80865 +0 -0
  30. package/.v8-cache/v24.7.0-x64-ef5a0af0/ac317c9c +0 -0
  31. package/.v8-cache/v24.7.0-x64-ef5a0af0/acc36e66 +0 -0
  32. package/.v8-cache/v24.7.0-x64-ef5a0af0/b3c2fab7 +0 -0
  33. package/.v8-cache/v24.7.0-x64-ef5a0af0/b8569d6b +0 -0
  34. package/.v8-cache/v24.7.0-x64-ef5a0af0/b8b073c4 +0 -0
  35. package/.v8-cache/v24.7.0-x64-ef5a0af0/bb489c89 +0 -0
  36. package/.v8-cache/v24.7.0-x64-ef5a0af0/c0b789d2 +0 -0
  37. package/.v8-cache/v24.7.0-x64-ef5a0af0/c314aece +0 -0
  38. package/.v8-cache/v24.7.0-x64-ef5a0af0/c46725e1 +0 -0
  39. package/.v8-cache/v24.7.0-x64-ef5a0af0/c794cb34 +0 -0
  40. package/.v8-cache/v24.7.0-x64-ef5a0af0/cd4ae980 +0 -0
  41. package/.v8-cache/v24.7.0-x64-ef5a0af0/cfc49f4f +0 -0
  42. package/.v8-cache/v24.7.0-x64-ef5a0af0/cff9434a +0 -0
  43. package/.v8-cache/v24.7.0-x64-ef5a0af0/d02dd6f7 +0 -0
  44. package/.v8-cache/v24.7.0-x64-ef5a0af0/dc86f000 +0 -0
  45. package/.v8-cache/v24.7.0-x64-ef5a0af0/e03e2acd +0 -0
  46. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6735171 +0 -0
  47. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6745c2b +0 -0
  48. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6b99945 +0 -0
  49. package/.v8-cache/v24.7.0-x64-ef5a0af0/eeeebdfe +0 -0
  50. package/.v8-cache/v24.7.0-x64-ef5a0af0/f5417276 +0 -0
  51. package/.v8-cache/v24.7.0-x64-ef5a0af0/f6fd8125 +0 -0
  52. package/.v8-cache/v24.7.0-x64-ef5a0af0/f74d3518 +0 -0
  53. package/.v8-cache/v24.7.0-x64-ef5a0af0/f886deac +0 -0
  54. package/.v8-cache/v24.7.0-x64-ef5a0af0/fd0a2987 +0 -0
  55. package/.v8-cache/v24.7.0-x64-ef5a0af0/fe349423 +0 -0
  56. package/CHANGELOG.md +107 -0
  57. package/LICENSE +5 -0
  58. package/README.md +3 -6
  59. package/README_PSD.md +242 -81
  60. package/dist/abr.d.ts +11 -4
  61. package/dist/abr.js +363 -229
  62. package/dist/abr.js.map +1 -1
  63. package/dist/additionalInfo.d.ts +56 -2
  64. package/dist/additionalInfo.js +600 -364
  65. package/dist/additionalInfo.js.map +1 -1
  66. package/dist/ase.d.ts +6 -1
  67. package/dist/ase.js +61 -61
  68. package/dist/ase.js.map +1 -1
  69. package/dist/bundle.js +3096 -1541
  70. package/dist/descriptor.d.ts +25 -6
  71. package/dist/descriptor.js +380 -135
  72. package/dist/descriptor.js.map +1 -1
  73. package/dist/effectsHelpers.js +12 -12
  74. package/dist/effectsHelpers.js.map +1 -1
  75. package/dist/engineData.d.ts +3 -1
  76. package/dist/engineData.js +34 -13
  77. package/dist/engineData.js.map +1 -1
  78. package/dist/engineData2.js +50 -21
  79. package/dist/engineData2.js.map +1 -1
  80. package/dist/helpers.d.ts +6 -3
  81. package/dist/helpers.js +22 -6
  82. package/dist/helpers.js.map +1 -1
  83. package/dist/imageResources.d.ts +15 -3
  84. package/dist/imageResources.js +529 -144
  85. package/dist/imageResources.js.map +1 -1
  86. package/dist/index.js +1 -0
  87. package/dist/index.js.map +1 -1
  88. package/dist/jpeg.js +30 -10
  89. package/dist/jpeg.js.map +1 -1
  90. package/dist/psd.d.ts +154 -18
  91. package/dist/psd.js.map +1 -1
  92. package/dist/psdReader.d.ts +12 -2
  93. package/dist/psdReader.js +720 -421
  94. package/dist/psdReader.js.map +1 -1
  95. package/dist/psdWriter.d.ts +3 -0
  96. package/dist/psdWriter.js +167 -130
  97. package/dist/psdWriter.js.map +1 -1
  98. package/dist/text.js +143 -29
  99. package/dist/text.js.map +1 -1
  100. package/dist/utf8.js +36 -18
  101. package/dist/utf8.js.map +1 -1
  102. package/dist-es/abr.d.ts +11 -4
  103. package/dist-es/abr.js +363 -229
  104. package/dist-es/abr.js.map +1 -1
  105. package/dist-es/additionalInfo.d.ts +56 -2
  106. package/dist-es/additionalInfo.js +600 -364
  107. package/dist-es/additionalInfo.js.map +1 -1
  108. package/dist-es/ase.d.ts +6 -1
  109. package/dist-es/ase.js +61 -61
  110. package/dist-es/ase.js.map +1 -1
  111. package/dist-es/descriptor.d.ts +25 -6
  112. package/dist-es/descriptor.js +380 -135
  113. package/dist-es/descriptor.js.map +1 -1
  114. package/dist-es/effectsHelpers.js +12 -12
  115. package/dist-es/effectsHelpers.js.map +1 -1
  116. package/dist-es/engineData.d.ts +3 -1
  117. package/dist-es/engineData.js +34 -13
  118. package/dist-es/engineData.js.map +1 -1
  119. package/dist-es/engineData2.js +50 -21
  120. package/dist-es/engineData2.js.map +1 -1
  121. package/dist-es/helpers.d.ts +6 -3
  122. package/dist-es/helpers.js +22 -6
  123. package/dist-es/helpers.js.map +1 -1
  124. package/dist-es/imageResources.d.ts +15 -3
  125. package/dist-es/imageResources.js +529 -144
  126. package/dist-es/imageResources.js.map +1 -1
  127. package/dist-es/index.js +1 -0
  128. package/dist-es/index.js.map +1 -1
  129. package/dist-es/jpeg.js +30 -10
  130. package/dist-es/jpeg.js.map +1 -1
  131. package/dist-es/psd.d.ts +154 -18
  132. package/dist-es/psd.js.map +1 -1
  133. package/dist-es/psdReader.d.ts +12 -2
  134. package/dist-es/psdReader.js +720 -421
  135. package/dist-es/psdReader.js.map +1 -1
  136. package/dist-es/psdWriter.d.ts +3 -0
  137. package/dist-es/psdWriter.js +167 -130
  138. package/dist-es/psdWriter.js.map +1 -1
  139. package/dist-es/text.js +143 -29
  140. package/dist-es/text.js.map +1 -1
  141. package/dist-es/utf8.js +36 -18
  142. package/dist-es/utf8.js.map +1 -1
  143. package/package.json +2 -2
  144. package/src/abr.ts +380 -232
  145. package/src/additionalInfo.ts +781 -477
  146. package/src/ase.ts +66 -61
  147. package/src/descriptor.ts +386 -142
  148. package/src/effectsHelpers.ts +9 -9
  149. package/src/engineData.ts +36 -13
  150. package/src/engineData2.ts +468 -432
  151. package/src/helpers.ts +23 -5
  152. package/src/imageResources.ts +658 -167
  153. package/src/index.ts +1 -0
  154. package/src/jpeg.ts +27 -12
  155. package/src/psd.ts +158 -30
  156. package/src/psdReader.ts +732 -414
  157. package/src/psdWriter.ts +186 -132
  158. package/src/text.ts +139 -18
  159. package/src/utf8.ts +35 -21
@@ -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
- function getLargestLayerSize(layers = []) {
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 { width, height } = getLayerDimentions(layer);
153
- max = Math.max(max, 2 * height + 2 * width * height);
154
- if (layer.mask) {
155
- const { width, height } = getLayerDimentions(layer.mask);
156
- max = Math.max(max, 2 * height + 2 * width * height);
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 - offset - 4;
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
- writer.view.setUint32(offset, length, false);
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
- const data = target.imageData;
193
- if (data && (data.data instanceof Uint32Array || data.data instanceof Uint16Array)) {
194
- throw new Error('imageData has incorrect bitDepth');
195
- }
196
- if ('mask' in target && target.mask) {
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
- const maxBufferSize = Math.max(getLargestLayerSize(psd.children), 4 * 2 * psd.width * psd.height + 2 * psd.height);
226
- writer.tempBuffer = new Uint8Array(maxBufferSize);
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
- writeSection(writer, 1, () => {
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, handler.key);
263
- writePascalString(writer, '', 2);
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
- if (helpers_1.RAW_IMAGE_DATA && psd.imageDataRaw) {
281
- console.log('writing raw image data');
282
- writeBytes(writer, psd.imageDataRaw);
283
- }
284
- else {
285
- if (imageData)
286
- data.data.set(new Uint8Array(imageData.data.buffer, imageData.data.byteOffset, imageData.data.byteLength));
287
- // add weird white matte
288
- if (globalAlpha) {
289
- const size = data.width * data.height * 4;
290
- const p = data.data;
291
- for (let i = 0; i < size; i += 4) {
292
- const pa = p[i + 3];
293
- if (pa != 0 && pa != 255) {
294
- const a = pa / 255;
295
- const ra = 255 * (1 - a);
296
- p[i + 0] = p[i + 0] * a + ra;
297
- p[i + 1] = p[i + 1] * a + ra;
298
- p[i + 2] = p[i + 2] * a + ra;
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.tempBuffer, l, i === 0, options));
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
- writeUint32(writer, 0);
322
- writeUint32(writer, c.length);
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
- function writeLayerMaskData(writer, { mask, realMask }, layerData) {
359
- writeSection(writer, 1, () => {
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
- writeZeros(writer, 2);
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 * 0xff));
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' && options.psb)
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
- let newLength = writer.buffer.byteLength;
513
- do {
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, layer, mask, options, realMask) {
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 (helpers_1.RAW_IMAGE_DATA && layer[realMask ? 'realMaskDataRaw' : 'maskDataRaw']) {
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(tempBuffer, layer, background, options) {
626
+ function getChannels(writer, layer, background, options) {
627
+ let source = layer;
601
628
  if (layer.rawData) {
602
- 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(layer.mask), realMask: bounds(layer.realMask) });
603
- }
604
- const layerData = getLayerChannels(tempBuffer, layer, background, options);
605
- if (layer.mask)
606
- getMaskChannels(tempBuffer, layerData, layer, layer.mask, options, false);
607
- if (layer.realMask)
608
- getMaskChannels(tempBuffer, layerData, layer, layer.realMask, options, true);
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) || (helpers_1.RAW_IMAGE_DATA && ((_a = layer.imageDataRaw) === null || _a === void 0 ? void 0 : _a['-1']))) {
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); // TODO: psd.colorMode === ColorMode.CMYK);
723
+ const offset = (0, helpers_1.offsetForChannel)(id, false); // image data is always RGBA
682
724
  let data;
683
725
  let compression;
684
- if (helpers_1.RAW_IMAGE_DATA && layer.imageDataRaw) {
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(color.a < 0 ? (color.a * 12800) : (color.a * 12700)));
759
- writeInt16(writer, Math.round(color.b < 0 ? (color.b * 12800) : (color.b * 12700)));
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