ag-psd 32.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 (117) hide show
  1. package/.v8-cache/v24.7.0-x64-ef5a0af0/03e19709 +0 -0
  2. package/.v8-cache/v24.7.0-x64-ef5a0af0/05135617 +0 -0
  3. package/.v8-cache/v24.7.0-x64-ef5a0af0/1130885f +0 -0
  4. package/.v8-cache/v24.7.0-x64-ef5a0af0/226e2801 +0 -0
  5. package/.v8-cache/v24.7.0-x64-ef5a0af0/2a19133e +0 -0
  6. package/.v8-cache/v24.7.0-x64-ef5a0af0/4511bacf +0 -0
  7. package/.v8-cache/v24.7.0-x64-ef5a0af0/68416cb2 +0 -0
  8. package/.v8-cache/v24.7.0-x64-ef5a0af0/71df9cb3 +0 -0
  9. package/.v8-cache/v24.7.0-x64-ef5a0af0/7ea06cea +0 -0
  10. package/.v8-cache/v24.7.0-x64-ef5a0af0/87cf2209 +0 -0
  11. package/.v8-cache/v24.7.0-x64-ef5a0af0/932f0770 +0 -0
  12. package/.v8-cache/v24.7.0-x64-ef5a0af0/acc36e66 +0 -0
  13. package/.v8-cache/v24.7.0-x64-ef5a0af0/b3c2fab7 +0 -0
  14. package/.v8-cache/v24.7.0-x64-ef5a0af0/c0b789d2 +0 -0
  15. package/.v8-cache/v24.7.0-x64-ef5a0af0/c314aece +0 -0
  16. package/.v8-cache/v24.7.0-x64-ef5a0af0/cfc49f4f +0 -0
  17. package/.v8-cache/v24.7.0-x64-ef5a0af0/cff9434a +0 -0
  18. package/.v8-cache/v24.7.0-x64-ef5a0af0/d13d7610 +0 -0
  19. package/.v8-cache/v24.7.0-x64-ef5a0af0/dc86f000 +0 -0
  20. package/.v8-cache/v24.7.0-x64-ef5a0af0/e03e2acd +0 -0
  21. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6745c2b +0 -0
  22. package/.v8-cache/v24.7.0-x64-ef5a0af0/e6b99945 +0 -0
  23. package/.v8-cache/v24.7.0-x64-ef5a0af0/eeeebdfe +0 -0
  24. package/.v8-cache/v24.7.0-x64-ef5a0af0/f6fd8125 +0 -0
  25. package/CHANGELOG.md +100 -0
  26. package/LICENSE +5 -0
  27. package/README.md +4 -7
  28. package/README_PSD.md +261 -84
  29. package/TODO.md +13 -84
  30. package/dist/abr.d.ts +11 -4
  31. package/dist/abr.js +362 -230
  32. package/dist/abr.js.map +1 -1
  33. package/dist/additionalInfo.d.ts +56 -2
  34. package/dist/additionalInfo.js +828 -361
  35. package/dist/additionalInfo.js.map +1 -1
  36. package/dist/ase.d.ts +6 -1
  37. package/dist/ase.js +61 -61
  38. package/dist/ase.js.map +1 -1
  39. package/dist/bundle.js +3321 -1364
  40. package/dist/descriptor.d.ts +28 -13
  41. package/dist/descriptor.js +295 -241
  42. package/dist/descriptor.js.map +1 -1
  43. package/dist/helpers.d.ts +7 -1
  44. package/dist/helpers.js +246 -4
  45. package/dist/helpers.js.map +1 -1
  46. package/dist/imageResources.d.ts +15 -3
  47. package/dist/imageResources.js +586 -144
  48. package/dist/imageResources.js.map +1 -1
  49. package/dist/index.d.ts +1 -0
  50. package/dist/index.js +1 -0
  51. package/dist/index.js.map +1 -1
  52. package/dist/jpeg.js +8 -8
  53. package/dist/jpeg.js.map +1 -1
  54. package/dist/psd.d.ts +292 -19
  55. package/dist/psd.js.map +1 -1
  56. package/dist/psdReader.d.ts +10 -0
  57. package/dist/psdReader.js +424 -216
  58. package/dist/psdReader.js.map +1 -1
  59. package/dist/psdWriter.d.ts +2 -0
  60. package/dist/psdWriter.js +373 -113
  61. package/dist/psdWriter.js.map +1 -1
  62. package/dist/text.d.ts +32 -1
  63. package/dist/text.js +75 -6
  64. package/dist/text.js.map +1 -1
  65. package/dist/utf8.js +0 -3
  66. package/dist/utf8.js.map +1 -1
  67. package/dist-es/abr.d.ts +11 -4
  68. package/dist-es/abr.js +362 -230
  69. package/dist-es/abr.js.map +1 -1
  70. package/dist-es/additionalInfo.d.ts +56 -2
  71. package/dist-es/additionalInfo.js +828 -361
  72. package/dist-es/additionalInfo.js.map +1 -1
  73. package/dist-es/ase.d.ts +6 -1
  74. package/dist-es/ase.js +61 -61
  75. package/dist-es/ase.js.map +1 -1
  76. package/dist-es/descriptor.d.ts +28 -13
  77. package/dist-es/descriptor.js +295 -241
  78. package/dist-es/descriptor.js.map +1 -1
  79. package/dist-es/helpers.d.ts +7 -1
  80. package/dist-es/helpers.js +246 -4
  81. package/dist-es/helpers.js.map +1 -1
  82. package/dist-es/imageResources.d.ts +15 -3
  83. package/dist-es/imageResources.js +586 -144
  84. package/dist-es/imageResources.js.map +1 -1
  85. package/dist-es/index.d.ts +1 -0
  86. package/dist-es/index.js +1 -0
  87. package/dist-es/index.js.map +1 -1
  88. package/dist-es/jpeg.js +8 -8
  89. package/dist-es/jpeg.js.map +1 -1
  90. package/dist-es/psd.d.ts +292 -19
  91. package/dist-es/psd.js.map +1 -1
  92. package/dist-es/psdReader.d.ts +10 -0
  93. package/dist-es/psdReader.js +424 -216
  94. package/dist-es/psdReader.js.map +1 -1
  95. package/dist-es/psdWriter.d.ts +2 -0
  96. package/dist-es/psdWriter.js +373 -113
  97. package/dist-es/psdWriter.js.map +1 -1
  98. package/dist-es/text.d.ts +32 -1
  99. package/dist-es/text.js +75 -6
  100. package/dist-es/text.js.map +1 -1
  101. package/dist-es/utf8.js +0 -3
  102. package/dist-es/utf8.js.map +1 -1
  103. package/package.json +2 -2
  104. package/perf.js +312 -9
  105. package/src/abr.ts +382 -235
  106. package/src/additionalInfo.ts +1071 -482
  107. package/src/ase.ts +66 -61
  108. package/src/descriptor.ts +291 -242
  109. package/src/helpers.ts +266 -3
  110. package/src/imageResources.ts +736 -169
  111. package/src/index.ts +1 -0
  112. package/src/jpeg.ts +8 -8
  113. package/src/psd.ts +271 -32
  114. package/src/psdReader.ts +440 -221
  115. package/src/psdWriter.ts +402 -110
  116. package/src/text.ts +93 -7
  117. package/src/utf8.ts +0 -4
package/src/helpers.ts CHANGED
@@ -4,7 +4,6 @@ import { decodeJpeg } from './jpeg';
4
4
  import { PsdReader } from './psdReader';
5
5
 
6
6
  export const MOCK_HANDLERS = false;
7
- export const RAW_IMAGE_DATA = false;
8
7
 
9
8
  export const fromBlendMode: { [key: string]: string } = {};
10
9
  export const toBlendMode: { [key: string]: BlendMode } = {
@@ -41,14 +40,18 @@ export const toBlendMode: { [key: string]: BlendMode } = {
41
40
  Object.keys(toBlendMode).forEach(key => fromBlendMode[toBlendMode[key]] = key);
42
41
 
43
42
  export const layerColors: LayerColor[] = [
44
- 'none', 'red', 'orange', 'yellow', 'green', 'blue', 'violet', 'gray'
43
+ 'none', 'red', 'orange', 'yellow', 'green', 'blue', 'violet', 'gray', 'seafoam', 'indigo', 'magenta', 'fuchsia',
45
44
  ];
46
45
 
46
+ // keys not listed in documentation that Photoshop writes with 64bit length in PSB files (with 8B64 signature)
47
+ export const largeAdditionalInfoKeysObserved = ['lnk3', 'lnkE', 'pths', 'extd', 'extn', 'FELS', 'artd'];
48
+
47
49
  export const largeAdditionalInfoKeys = [
48
50
  // from documentation
49
51
  'LMsk', 'Lr16', 'Lr32', 'Layr', 'Mt16', 'Mt32', 'Mtrn', 'Alph', 'FMsk', 'lnk2', 'FEid', 'FXid', 'PxSD',
50
52
  // from guessing
51
53
  'cinf',
54
+ ...largeAdditionalInfoKeysObserved,
52
55
  ];
53
56
 
54
57
  export interface Dict {
@@ -162,9 +165,10 @@ export function clamp(value: number, min: number, max: number) {
162
165
 
163
166
  export function hasAlpha(data: PixelData) {
164
167
  const size = data.width * data.height * 4;
168
+ const max = data.data instanceof Float32Array ? 1 : (data.data instanceof Uint16Array ? 0xffff : 0xff);
165
169
 
166
170
  for (let i = 3; i < size; i += 4) {
167
- if (data.data[i] !== 255) {
171
+ if (data.data[i] !== max) {
168
172
  return true;
169
173
  }
170
174
  }
@@ -172,6 +176,154 @@ export function hasAlpha(data: PixelData) {
172
176
  return false;
173
177
  }
174
178
 
179
+ // Polynomial approximation of U.S. Web Coated (SWOP) v2 CMYK to RGB conversion (default CMYK profile of Photoshop).
180
+ // Coefficients are taken from pdf.js (DeviceCmykCS in src/core/colorspace.js, https://github.com/mozilla/pdf.js),
181
+ // Copyright 2012 Mozilla Foundation, licensed under the Apache License, Version 2.0
182
+ // (http://www.apache.org/licenses/LICENSE-2.0)
183
+ // c, m, y, k - ink values (0 - 1), output - RGB values (0 - 255, not rounded or clamped)
184
+ export function cmykToRgbValues(c: number, m: number, y: number, k: number, out: number[]) {
185
+ out[0] = 255 +
186
+ c * (-4.387332384609988 * c + 54.48615194189176 * m + 18.82290502165302 * y + 212.25662451639585 * k - 285.2331026137004) +
187
+ m * (1.7149763477362134 * m - 5.6096736904047315 * y - 17.873870861415444 * k - 5.497006427196366) +
188
+ y * (-2.5217340131683033 * y - 21.248923337353073 * k + 17.5119270841813) +
189
+ k * (-21.86122147463605 * k - 189.48180835922747);
190
+ out[1] = 255 +
191
+ c * (8.841041422036149 * c + 60.118027045597366 * m + 6.871425592049007 * y + 31.159100130055922 * k - 79.2970844816548) +
192
+ m * (-15.310361306967817 * m + 17.575251261109482 * y + 131.35250912493976 * k - 190.9453302588951) +
193
+ y * (4.444339102852739 * y + 9.8632861493405 * k - 24.86741582555878) +
194
+ k * (-20.737325471181034 * k - 187.80453709719578);
195
+ out[2] = 255 +
196
+ c * (0.8842522430003296 * c + 8.078677503112928 * m + 30.89978309703729 * y - 0.23883238689178934 * k - 14.183576799673286) +
197
+ m * (10.49593273432072 * m + 63.02378494754052 * y + 50.606957656360734 * k - 112.23884253719248) +
198
+ y * (0.03296041114873217 * y + 115.60384449646641 * k - 193.58209356861505) +
199
+ k * (-22.33816807309886 * k - 180.12613974708367);
200
+ }
201
+
202
+ const clamp01 = (v: number) => v < 0 ? 0 : (v > 1 ? 1 : v);
203
+
204
+ // finds c, m, y (with fixed k) for given RGB color using Gauss-Newton method, returns squared error
205
+ function solveCmy(r: number, g: number, b: number, k: number, x: number[]) {
206
+ const f = [0, 0, 0], f2 = [0, 0, 0], e = [0, 0, 0];
207
+ const J = [[0, 0, 0], [0, 0, 0], [0, 0, 0]];
208
+ const h = 1e-4;
209
+
210
+ for (let iteration = 0; iteration < 20; iteration++) {
211
+ cmykToRgbValues(x[0], x[1], x[2], k, f);
212
+ e[0] = f[0] - r; e[1] = f[1] - g; e[2] = f[2] - b;
213
+ if ((e[0] * e[0] + e[1] * e[1] + e[2] * e[2]) < 1e-6) break;
214
+
215
+ for (let j = 0; j < 3; j++) {
216
+ const s = x[j];
217
+ x[j] = s + h;
218
+ cmykToRgbValues(x[0], x[1], x[2], k, f2);
219
+ x[j] = s;
220
+ for (let i = 0; i < 3; i++) J[i][j] = (f2[i] - f[i]) / h;
221
+ }
222
+
223
+ const [[a, b_, c], [d, e_, f_], [g_, h_, i_]] = J;
224
+ const det = a * (e_ * i_ - f_ * h_) - b_ * (d * i_ - f_ * g_) + c * (d * h_ - e_ * g_);
225
+ if (Math.abs(det) < 1e-12) break;
226
+
227
+ const inverse = [
228
+ [(e_ * i_ - f_ * h_), (c * h_ - b_ * i_), (b_ * f_ - c * e_)],
229
+ [(f_ * g_ - d * i_), (a * i_ - c * g_), (c * d - a * f_)],
230
+ [(d * h_ - e_ * g_), (b_ * g_ - a * h_), (a * e_ - b_ * d)],
231
+ ];
232
+
233
+ let changed = 0;
234
+
235
+ for (let j = 0; j < 3; j++) {
236
+ const delta = -(inverse[j][0] * e[0] + inverse[j][1] * e[1] + inverse[j][2] * e[2]) / det;
237
+ const value = clamp01(x[j] + delta);
238
+ changed += Math.abs(value - x[j]);
239
+ x[j] = value;
240
+ }
241
+
242
+ if (changed < 1e-9) break;
243
+ }
244
+
245
+ cmykToRgbValues(x[0], x[1], x[2], k, f);
246
+ return (f[0] - r) * (f[0] - r) + (f[1] - g) * (f[1] - g) + (f[2] - b) * (f[2] - b);
247
+ }
248
+
249
+ // finds CMYK ink values (0 - 1) for RGB color (0 - 255), out of gamut colors are approximated
250
+ function rgbToCmykExact(r: number, g: number, b: number, out: number[]) {
251
+ const max = Math.max(r, g, b) / 255;
252
+ const blackGeneration = Math.pow(clamp01((1 - max - 0.2) / 0.8), 1.3);
253
+ const x = [0, 0, 0];
254
+ let bestScore = Infinity, bestError = Infinity;
255
+
256
+ const tryBlack = (k: number) => {
257
+ x[0] = clamp01(1 - r / 255 - k);
258
+ x[1] = clamp01(1 - g / 255 - k);
259
+ x[2] = clamp01(1 - b / 255 - k);
260
+ const error = solveCmy(r, g, b, k, x);
261
+ // prefer solutions close to black generation value if the error is small enough
262
+ const score = Math.max(error, 0.1) + Math.abs(k - blackGeneration) * 0.01;
263
+
264
+ if (score < bestScore) {
265
+ bestScore = score;
266
+ bestError = error;
267
+ out[0] = x[0]; out[1] = x[1]; out[2] = x[2]; out[3] = k;
268
+ }
269
+ };
270
+
271
+ tryBlack(blackGeneration);
272
+
273
+ if (bestError > 0.1) {
274
+ for (let i = 0; i <= 10; i++) tryBlack(i / 10);
275
+ }
276
+
277
+ if (bestError > 0.1) {
278
+ const k = out[3];
279
+ for (let i = -5; i <= 5; i++) tryBlack(clamp01(k + i / 100));
280
+ }
281
+ }
282
+
283
+ const cmykLutSize = 33;
284
+ let cmykLut: Float32Array | undefined;
285
+
286
+ function getCmykLut() {
287
+ if (!cmykLut) {
288
+ const n = cmykLutSize;
289
+ const lut = new Float32Array(n * n * n * 4);
290
+ const out = [0, 0, 0, 0];
291
+
292
+ for (let r = 0, o = 0; r < n; r++) {
293
+ for (let g = 0; g < n; g++) {
294
+ for (let b = 0; b < n; b++, o += 4) {
295
+ rgbToCmykExact(r * 255 / (n - 1), g * 255 / (n - 1), b * 255 / (n - 1), out);
296
+ lut[o] = out[0]; lut[o + 1] = out[1]; lut[o + 2] = out[2]; lut[o + 3] = out[3];
297
+ }
298
+ }
299
+ }
300
+
301
+ cmykLut = lut;
302
+ }
303
+
304
+ return cmykLut;
305
+ }
306
+
307
+ // inverse of cmykToRgbValues, r, g, b - RGB values (0 - 1), output - CMYK ink values (0 - 1)
308
+ export function rgbToCmykValues(r: number, g: number, b: number, out: number[]) {
309
+ const lut = getCmykLut();
310
+ const n1 = cmykLutSize - 1;
311
+ const fr = clamp01(r) * n1, fg = clamp01(g) * n1, fb = clamp01(b) * n1;
312
+ const r0 = Math.min(n1 - 1, Math.floor(fr)), g0 = Math.min(n1 - 1, Math.floor(fg)), b0 = Math.min(n1 - 1, Math.floor(fb));
313
+ const dr = fr - r0, dg = fg - g0, db = fb - b0;
314
+ out[0] = out[1] = out[2] = out[3] = 0;
315
+
316
+ for (let i = 0; i < 8; i++) {
317
+ const ir = i >> 2, ig = (i >> 1) & 1, ib = i & 1;
318
+ const w = (ir ? dr : 1 - dr) * (ig ? dg : 1 - dg) * (ib ? db : 1 - db);
319
+ const o = (((r0 + ir) * cmykLutSize + g0 + ig) * cmykLutSize + b0 + ib) * 4;
320
+ out[0] += w * lut[o];
321
+ out[1] += w * lut[o + 1];
322
+ out[2] += w * lut[o + 2];
323
+ out[3] += w * lut[o + 3];
324
+ }
325
+ }
326
+
175
327
  export function resetImageData({ data }: PixelData) {
176
328
  const alpha = (data instanceof Float32Array) ? 1.0 : ((data instanceof Uint16Array) ? 0xffff : 0xff);
177
329
 
@@ -350,6 +502,117 @@ export function writeDataRLE(buffer: Uint8Array, { data, width, height }: PixelD
350
502
  return buffer.subarray(0, o);
351
503
  }
352
504
 
505
+ // single channel of image data (8bit, 16bit or 32bit values)
506
+ export type PlanarData = Uint8Array | Uint16Array | Float32Array;
507
+
508
+ // converts channel values to big-endian bytes
509
+ export function planarToBytes(values: PlanarData): Uint8Array {
510
+ if (values instanceof Uint8Array) return values;
511
+
512
+ const bytes = new Uint8Array(values.byteLength);
513
+ const view = new DataView(bytes.buffer);
514
+
515
+ if (values instanceof Uint16Array) {
516
+ for (let i = 0; i < values.length; i++) view.setUint16(i * 2, values[i], false);
517
+ } else {
518
+ for (let i = 0; i < values.length; i++) view.setFloat32(i * 4, values[i], false);
519
+ }
520
+
521
+ return bytes;
522
+ }
523
+
524
+ // RLE (PackBits) compression of rows of bytes, prefixed with byte counts of each row
525
+ export function writeDataRLEBytes(bytes: Uint8Array, rowBytes: number, height: number, large: boolean) {
526
+ const countSize = large ? 4 : 2;
527
+ const buffer = new Uint8Array(height * (countSize + rowBytes + Math.ceil(rowBytes / 128)));
528
+ let o = countSize * height;
529
+
530
+ for (let y = 0; y < height; y++) {
531
+ const start = o;
532
+ const rowStart = y * rowBytes;
533
+ const rowEnd = rowStart + rowBytes;
534
+ let i = rowStart;
535
+
536
+ while (i < rowEnd) {
537
+ let run = 1;
538
+ while (i + run < rowEnd && run < 128 && bytes[i + run] === bytes[i]) run++;
539
+
540
+ if (run > 1) {
541
+ buffer[o++] = 257 - run;
542
+ buffer[o++] = bytes[i];
543
+ i += run;
544
+ } else {
545
+ // literal bytes until a run of at least 3 equal bytes
546
+ const countIndex = o++;
547
+ let count = 0;
548
+
549
+ while (i < rowEnd && count < 128) {
550
+ if (i + 2 < rowEnd && bytes[i] === bytes[i + 1] && bytes[i] === bytes[i + 2]) break;
551
+ buffer[o++] = bytes[i++];
552
+ count++;
553
+ }
554
+
555
+ buffer[countIndex] = count - 1;
556
+ }
557
+ }
558
+
559
+ const length = o - start;
560
+
561
+ for (let j = 0; j < countSize; j++) {
562
+ buffer[y * countSize + j] = (length >>> ((countSize - j - 1) * 8)) & 0xff;
563
+ }
564
+ }
565
+
566
+ return buffer.subarray(0, o);
567
+ }
568
+
569
+ // zip compression with prediction (values are delta encoded per row, 32bit values are split into byte planes first)
570
+ export function writeDataZipWithPrediction(values: PlanarData, width: number, height: number) {
571
+ let bytes: Uint8Array;
572
+
573
+ if (values instanceof Uint8Array) {
574
+ bytes = new Uint8Array(width * height);
575
+
576
+ for (let y = 0, i = 0; y < height; y++) {
577
+ for (let x = 0, prev = 0; x < width; x++, i++) {
578
+ bytes[i] = values[i] - prev; // Uint8Array wraps values on assignment
579
+ prev = values[i];
580
+ }
581
+ }
582
+ } else if (values instanceof Uint16Array) {
583
+ bytes = new Uint8Array(width * height * 2);
584
+
585
+ for (let y = 0, i = 0; y < height; y++) {
586
+ for (let x = 0, prev = 0; x < width; x++, i++) {
587
+ const delta = (values[i] - prev) & 0xffff;
588
+ bytes[i * 2] = delta >> 8;
589
+ bytes[i * 2 + 1] = delta & 0xff;
590
+ prev = values[i];
591
+ }
592
+ }
593
+ } else {
594
+ const rowBytes = width * 4;
595
+ const source = planarToBytes(values);
596
+ bytes = new Uint8Array(rowBytes * height);
597
+
598
+ for (let y = 0; y < height; y++) {
599
+ const row = y * rowBytes;
600
+
601
+ for (let x = 0; x < width; x++) {
602
+ for (let b = 0; b < 4; b++) {
603
+ bytes[row + b * width + x] = source[row + x * 4 + b];
604
+ }
605
+ }
606
+
607
+ for (let x = rowBytes - 1; x > 0; x--) {
608
+ bytes[row + x] -= bytes[row + x - 1];
609
+ }
610
+ }
611
+ }
612
+
613
+ return deflateSync(bytes);
614
+ }
615
+
353
616
  export function writeDataZipWithoutPrediction({ data, width, height }: PixelData, offsets: number[]) {
354
617
  const size = width * height;
355
618
  const channel = new Uint8Array(size);