@pixerate/editor 0.113.0 → 0.114.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 (124) hide show
  1. package/THIRD_PARTY_NOTICES.md +5 -0
  2. package/dist/animation-editor/index.cjs +60 -54
  3. package/dist/animation-editor/index.cjs.map +1 -1
  4. package/dist/animation-editor/index.d.cts +3 -3
  5. package/dist/animation-editor/index.d.ts +3 -3
  6. package/dist/animation-editor/index.js +14 -8
  7. package/dist/animation-editor/index.js.map +1 -1
  8. package/dist/builtin-CY97ppKM.d.cts +354 -0
  9. package/dist/builtin-CY97ppKM.d.ts +354 -0
  10. package/dist/canvas/index.cjs +3 -3
  11. package/dist/canvas/index.cjs.map +1 -1
  12. package/dist/canvas/index.js +9 -9
  13. package/dist/chunk-26PGXHY7.cjs +753 -0
  14. package/dist/chunk-26PGXHY7.cjs.map +1 -0
  15. package/dist/chunk-2OJACTPR.cjs +929 -0
  16. package/dist/chunk-2OJACTPR.cjs.map +1 -0
  17. package/dist/chunk-3UFGNHSP.js +929 -0
  18. package/dist/chunk-3UFGNHSP.js.map +1 -0
  19. package/dist/{chunk-YGIMK4NX.cjs → chunk-5OVJHMZQ.cjs} +757 -825
  20. package/dist/chunk-5OVJHMZQ.cjs.map +1 -0
  21. package/dist/chunk-643B2HKC.cjs +534 -0
  22. package/dist/chunk-643B2HKC.cjs.map +1 -0
  23. package/dist/chunk-6FCKOS2B.js +534 -0
  24. package/dist/chunk-6FCKOS2B.js.map +1 -0
  25. package/dist/chunk-BRLYOPLT.js +2574 -0
  26. package/dist/chunk-BRLYOPLT.js.map +1 -0
  27. package/dist/{chunk-LQEEDHOP.js → chunk-C6C5PDKU.js} +759 -761
  28. package/dist/chunk-C6C5PDKU.js.map +1 -0
  29. package/dist/{chunk-4KLT7ZYS.cjs → chunk-DFQEX3AM.cjs} +29 -29
  30. package/dist/{chunk-4KLT7ZYS.cjs.map → chunk-DFQEX3AM.cjs.map} +1 -1
  31. package/dist/{chunk-CZOXTEMN.js → chunk-EUVSQ25B.js} +336 -404
  32. package/dist/chunk-EUVSQ25B.js.map +1 -0
  33. package/dist/chunk-G5NMLUVF.cjs +2574 -0
  34. package/dist/chunk-G5NMLUVF.cjs.map +1 -0
  35. package/dist/{chunk-LPYOIVLA.js → chunk-LKCYANMW.js} +3 -3
  36. package/dist/{chunk-W3GXSUSQ.cjs → chunk-LPLCWZTM.cjs} +5087 -1283
  37. package/dist/chunk-LPLCWZTM.cjs.map +1 -0
  38. package/dist/chunk-MMVHBKMM.js +753 -0
  39. package/dist/chunk-MMVHBKMM.js.map +1 -0
  40. package/dist/{chunk-POGBUYWE.cjs → chunk-ST7E3NEE.cjs} +857 -859
  41. package/dist/chunk-ST7E3NEE.cjs.map +1 -0
  42. package/dist/{chunk-7VOHOPLW.cjs → chunk-TCKIXI46.cjs} +59 -59
  43. package/dist/{chunk-7VOHOPLW.cjs.map → chunk-TCKIXI46.cjs.map} +1 -1
  44. package/dist/{chunk-XIOMMGPD.cjs → chunk-TFOPHZ3U.cjs} +1 -1
  45. package/dist/chunk-TFOPHZ3U.cjs.map +1 -0
  46. package/dist/{chunk-Z4BC6M74.js → chunk-UBIXVXPR.js} +2 -2
  47. package/dist/{chunk-BLWCAQCW.js → chunk-VXR7XWMF.js} +4574 -770
  48. package/dist/chunk-VXR7XWMF.js.map +1 -0
  49. package/dist/{chunk-HNS7UHCM.js → chunk-XK5LZKBD.js} +1 -1
  50. package/dist/chunk-XK5LZKBD.js.map +1 -0
  51. package/dist/cms/index.cjs +146 -0
  52. package/dist/cms/index.cjs.map +1 -0
  53. package/dist/cms/index.d.cts +51 -0
  54. package/dist/cms/index.d.ts +51 -0
  55. package/dist/cms/index.js +146 -0
  56. package/dist/cms/index.js.map +1 -0
  57. package/dist/{export-DIkoK8-r.d.ts → export-_1dUKwpF.d.ts} +217 -191
  58. package/dist/{export-CEXEzqBf.d.cts → export-b6NjCZ5-.d.cts} +217 -191
  59. package/dist/image-editor/index.cjs +1779 -1280
  60. package/dist/image-editor/index.cjs.map +1 -1
  61. package/dist/image-editor/index.d.cts +77 -93
  62. package/dist/image-editor/index.d.ts +77 -93
  63. package/dist/image-editor/index.js +740 -241
  64. package/dist/image-editor/index.js.map +1 -1
  65. package/dist/opentype-BZEOSOX3.d.ts +188 -0
  66. package/dist/opentype-Dpe75vL5.d.cts +188 -0
  67. package/dist/paint/index.cjs +4 -4
  68. package/dist/paint/index.d.cts +2 -2
  69. package/dist/paint/index.d.ts +2 -2
  70. package/dist/paint/index.js +3 -3
  71. package/dist/paint-editor/index.cjs +146 -145
  72. package/dist/paint-editor/index.cjs.map +1 -1
  73. package/dist/paint-editor/index.d.cts +9 -8
  74. package/dist/paint-editor/index.d.ts +9 -8
  75. package/dist/paint-editor/index.js +6 -5
  76. package/dist/paint-editor/index.js.map +1 -1
  77. package/dist/raster/index.cjs +53 -3
  78. package/dist/raster/index.cjs.map +1 -1
  79. package/dist/raster/index.d.cts +62 -91
  80. package/dist/raster/index.d.ts +62 -91
  81. package/dist/raster/index.js +59 -9
  82. package/dist/{render-BEriFuqK.d.cts → render-B1K_OoLy.d.cts} +87 -2
  83. package/dist/{render-tjzKaik-.d.ts → render-CrmHOgpo.d.ts} +87 -2
  84. package/dist/{retouch-CEIKR22W.d.cts → retouch-Bxkuc0KH.d.cts} +1 -1
  85. package/dist/{retouch-BXmeSCaB.d.ts → retouch-Do6tBs2H.d.ts} +1 -1
  86. package/dist/{surface-C8RRg_Bh.d.cts → surface-Cn30xIV6.d.cts} +6 -1
  87. package/dist/{surface-C8RRg_Bh.d.ts → surface-Cn30xIV6.d.ts} +6 -1
  88. package/dist/{svg-D_cfkhlB.d.cts → svg-BnRj0q8i.d.cts} +16 -4
  89. package/dist/{svg-wIaKLrU0.d.ts → svg-CmmBFfGT.d.ts} +16 -4
  90. package/dist/{types-DtL_qLFz.d.ts → types-1SUQKDAw.d.ts} +273 -4
  91. package/dist/{types-DyQq-WPY.d.cts → types-yINF17Ta.d.cts} +273 -4
  92. package/dist/vector/index.cjs +12170 -7343
  93. package/dist/vector/index.cjs.map +1 -1
  94. package/dist/vector/index.d.cts +1579 -16
  95. package/dist/vector/index.d.ts +1579 -16
  96. package/dist/vector/index.js +6303 -1476
  97. package/dist/vector/index.js.map +1 -1
  98. package/dist/warp-B39VsOtd.d.cts +348 -0
  99. package/dist/warp-DMN30aY6.d.ts +348 -0
  100. package/dist/zlib-dxQdgHyN.d.cts +95 -0
  101. package/dist/zlib-dxQdgHyN.d.ts +95 -0
  102. package/package.json +6 -1
  103. package/skills/pixerate-image-editor/SKILL.md +12 -36
  104. package/skills/pixerate-image-editor/references/color-management.md +37 -0
  105. package/skills/pixerate-image-editor/references/photoshop-files.md +39 -0
  106. package/skills/pixerate-vector/SKILL.md +54 -2
  107. package/dist/chunk-BLWCAQCW.js.map +0 -1
  108. package/dist/chunk-CZOXTEMN.js.map +0 -1
  109. package/dist/chunk-HNS7UHCM.js.map +0 -1
  110. package/dist/chunk-JLYX4V57.cjs +0 -2001
  111. package/dist/chunk-JLYX4V57.cjs.map +0 -1
  112. package/dist/chunk-KB2YBCVS.js +0 -2001
  113. package/dist/chunk-KB2YBCVS.js.map +0 -1
  114. package/dist/chunk-LQEEDHOP.js.map +0 -1
  115. package/dist/chunk-POGBUYWE.cjs.map +0 -1
  116. package/dist/chunk-W3GXSUSQ.cjs.map +0 -1
  117. package/dist/chunk-XIOMMGPD.cjs.map +0 -1
  118. package/dist/chunk-YGIMK4NX.cjs.map +0 -1
  119. package/dist/homography-CZ-tFjay.d.cts +0 -50
  120. package/dist/homography-Ctfjoc1b.d.ts +0 -50
  121. package/dist/warp-CWHZzE3x.d.ts +0 -118
  122. package/dist/warp-D5jz_tN7.d.cts +0 -118
  123. /package/dist/{chunk-LPYOIVLA.js.map → chunk-LKCYANMW.js.map} +0 -0
  124. /package/dist/{chunk-Z4BC6M74.js.map → chunk-UBIXVXPR.js.map} +0 -0
@@ -1,40 +1,7 @@
1
1
  import {
2
- loadDocument
3
- } from "../chunk-U5HZSFCS.js";
4
- import {
5
- createCaptureTool
6
- } from "../chunk-REOJJIY3.js";
7
- import {
8
- DEFAULT_RETOUCH,
9
- RETOUCH_SAMPLES,
10
- RegionPixelSource,
11
- RetouchDabStroke,
12
- SPOT_HEAL_TYPES,
13
- TONE_RANGES,
14
- TilePixelSource,
15
- applyRetouchToTile,
16
- bestOffset,
17
- dilateMask,
18
- dodgeBurn,
19
- dodgeBurnCurve,
20
- healPixels,
21
- luma,
22
- maskBounds,
23
- membraneFill,
24
- normalizeRetouch,
25
- perDabStrength,
26
- readSourceRegion,
27
- rgbaViewOfGrey,
28
- saturationOf,
29
- seamlessClone,
30
- smudgeEffect,
31
- solveMembrane,
32
- sponge,
33
- spongeEffect,
34
- spotHealPixels,
35
- spotHealWindow,
36
- toneOf
37
- } from "../chunk-LPYOIVLA.js";
2
+ loadOpenTypeFont,
3
+ parseOpenTypeFont
4
+ } from "../chunk-3UFGNHSP.js";
38
5
  import {
39
6
  ADJUSTMENT_KEYS,
40
7
  ADJUSTMENT_KINDS,
@@ -47,10 +14,8 @@ import {
47
14
  D50,
48
15
  D65,
49
16
  DEFAULT_ADJUSTMENTS_VALUES,
50
- DEFAULT_AUTO_LEADING,
51
17
  DEFAULT_DEPTH_MASK_VALUES,
52
18
  DEFAULT_MITER_LIMIT,
53
- DEFAULT_TEXT_STYLE,
54
19
  DEFAULT_TRANSFORM_VALUES,
55
20
  DEVELOP_FILTER_ID,
56
21
  EMPTY_DISTANCE,
@@ -102,6 +67,7 @@ import {
102
67
  annotationOfShape,
103
68
  annotationOfType,
104
69
  applyAdjustmentLayers,
70
+ applyAdjustmentLayersFloat,
105
71
  applyAdjustments,
106
72
  applyBoxBlur,
107
73
  applyDepthMask,
@@ -121,12 +87,12 @@ import {
121
87
  buildToneLut,
122
88
  calculateCropDrag,
123
89
  canvasToImagePoint,
124
- caretAt,
125
90
  clampCropToImage,
126
91
  colorBalanceChannel,
127
92
  combineSelections,
128
93
  compileAdjustmentLayer,
129
94
  componentPath,
95
+ compositeDeep,
130
96
  computeSelectionBounds,
131
97
  contentsBox,
132
98
  contentsSurface,
@@ -140,6 +106,7 @@ import {
140
106
  createShapeLayer,
141
107
  createTypeLayer,
142
108
  curveLutFromLevels,
109
+ decodeDeepSurface,
143
110
  decodeLayerChannel,
144
111
  decodeLayerMask,
145
112
  decodeMergedImage,
@@ -162,6 +129,7 @@ import {
162
129
  distanceTransform,
163
130
  documentContents,
164
131
  duplicateLayer,
132
+ encodeDeepSurface,
165
133
  encodeRle,
166
134
  encodeSelectionRle,
167
135
  encodeSurface,
@@ -170,11 +138,8 @@ import {
170
138
  exportPsd,
171
139
  featherCoverage,
172
140
  filterMaskCoverage,
173
- fitParagraphRuns,
174
- fitRuns,
175
141
  flattenPath,
176
142
  flipCorners,
177
- fontOf,
178
143
  fullSelection,
179
144
  geometryHomography,
180
145
  getAnnotationBounds,
@@ -225,7 +190,6 @@ import {
225
190
  layerTree,
226
191
  layerTypeLabel,
227
192
  layersFromLegacy,
228
- layoutText,
229
193
  legacyAdjustmentLayer,
230
194
  legacyMaskOf,
231
195
  legacyView,
@@ -262,7 +226,6 @@ import {
262
226
  offsetLayer,
263
227
  offsetShape,
264
228
  offsetSmartObject,
265
- paragraphRanges,
266
229
  parseAdjustmentBlock,
267
230
  parseBlend,
268
231
  parseCssColor,
@@ -317,7 +280,6 @@ import {
317
280
  rerenderText,
318
281
  resizeSelection,
319
282
  resolveExternalAssets,
320
- restyleRange,
321
283
  rgbToGray,
322
284
  rgbToHsl,
323
285
  runLayerFilters,
@@ -353,12 +315,9 @@ import {
353
315
  smartContentsFile,
354
316
  smartObjectLayers,
355
317
  smoothCoverage,
356
- spliceRuns,
357
318
  srgbToLinear,
358
- styleAt,
359
319
  tempTintToXy,
360
320
  temperatureToKelvin,
361
- textIndexAt,
362
321
  textMatrix,
363
322
  textOutlines,
364
323
  textRange,
@@ -399,8 +358,15 @@ import {
399
358
  writePsd,
400
359
  xyToTempTint,
401
360
  xyToUv
402
- } from "../chunk-CZOXTEMN.js";
361
+ } from "../chunk-EUVSQ25B.js";
403
362
  import {
363
+ loadDocument
364
+ } from "../chunk-U5HZSFCS.js";
365
+ import {
366
+ createCaptureTool
367
+ } from "../chunk-REOJJIY3.js";
368
+ import {
369
+ BIT_DEPTHS,
404
370
  DEFAULT_GLOBAL_LIGHT,
405
371
  DIRECT_GAUSSIAN_MAX_SIGMA,
406
372
  INTERPOLATIONS,
@@ -411,8 +377,6 @@ import {
411
377
  PatternPlacement,
412
378
  PatternTile,
413
379
  TransformTooLargeError,
414
- ZlibError,
415
- adler32,
416
380
  base64ToBytes,
417
381
  blendTile,
418
382
  builtinPattern,
@@ -420,12 +384,14 @@ import {
420
384
  bytesToBase64,
421
385
  changedTiles,
422
386
  compositeSurfaceOver,
387
+ convertSurfaceDepth,
423
388
  curveLut,
424
389
  decodePng,
390
+ deepBytes,
391
+ deepTile,
425
392
  defaultEffectGradient,
426
393
  defaultLayerEffect,
427
- deflate,
428
- deflateRaw,
394
+ depthOf,
429
395
  effectColorHex,
430
396
  effectReach,
431
397
  effectsFingerprint,
@@ -437,9 +403,8 @@ import {
437
403
  gaussianBlur,
438
404
  hasActiveEffects,
439
405
  hashNoise,
440
- inflate,
441
- inflateRaw,
442
406
  insertLayer,
407
+ isBitDepth,
443
408
  isInterpolation,
444
409
  isLayerEffectKind,
445
410
  isLayerLocked,
@@ -454,16 +419,33 @@ import {
454
419
  parseDataUrl,
455
420
  parseEffectColor,
456
421
  pngDataUrl,
422
+ proxyOf,
423
+ readDeepRegion,
457
424
  reduceSurface,
458
425
  renderLayerEffects,
459
426
  scaleLayerEffect,
427
+ srgbDecode,
428
+ srgbEncode,
429
+ surfaceDepth,
430
+ surfaceFromDeepPixels,
431
+ tileSamples,
432
+ tileSamples16,
433
+ tileSamples32,
434
+ to16From32,
435
+ to16From8,
436
+ to32From16,
437
+ to32From8,
438
+ to8From16,
439
+ to8From32,
460
440
  transformSurface,
461
441
  transformedBounds,
462
442
  validateEffectGradient,
463
443
  validateLayerEffect,
464
444
  validateLayerEffects,
465
- warpSurface
466
- } from "../chunk-LQEEDHOP.js";
445
+ warpSurface,
446
+ workingToSamples,
447
+ writeDeepRegion
448
+ } from "../chunk-C6C5PDKU.js";
467
449
  import {
468
450
  applyHomography,
469
451
  boxQuad,
@@ -473,6 +455,37 @@ import {
473
455
  multiplyHomographies,
474
456
  rectToQuad
475
457
  } from "../chunk-GX332W2C.js";
458
+ import {
459
+ DEFAULT_RETOUCH,
460
+ RETOUCH_SAMPLES,
461
+ RegionPixelSource,
462
+ RetouchDabStroke,
463
+ SPOT_HEAL_TYPES,
464
+ TONE_RANGES,
465
+ TilePixelSource,
466
+ applyRetouchToTile,
467
+ bestOffset,
468
+ dilateMask,
469
+ dodgeBurn,
470
+ dodgeBurnCurve,
471
+ healPixels,
472
+ luma,
473
+ maskBounds,
474
+ membraneFill,
475
+ normalizeRetouch,
476
+ perDabStrength,
477
+ readSourceRegion,
478
+ rgbaViewOfGrey,
479
+ saturationOf,
480
+ seamlessClone,
481
+ smudgeEffect,
482
+ solveMembrane,
483
+ sponge,
484
+ spongeEffect,
485
+ spotHealPixels,
486
+ spotHealWindow,
487
+ toneOf
488
+ } from "../chunk-LKCYANMW.js";
476
489
  import {
477
490
  CoverageMap,
478
491
  DEFAULT_BLENDER,
@@ -512,7 +525,7 @@ import {
512
525
  tipReach,
513
526
  tipShape,
514
527
  walkDabs
515
- } from "../chunk-Z4BC6M74.js";
528
+ } from "../chunk-UBIXVXPR.js";
516
529
  import {
517
530
  BLEND_MODES,
518
531
  PAINTERLY_BLEND_MODES,
@@ -566,21 +579,62 @@ import {
566
579
  translateSurface,
567
580
  uniformTile,
568
581
  writeRegion
569
- } from "../chunk-HNS7UHCM.js";
582
+ } from "../chunk-XK5LZKBD.js";
583
+ import {
584
+ BUILTIN_PROFILES,
585
+ CmsError,
586
+ GamutCheck,
587
+ INTENTS,
588
+ Transform,
589
+ builtinProfile,
590
+ builtinProfileId,
591
+ canBeDestination,
592
+ encodeProfile,
593
+ parseProfile,
594
+ sameColors,
595
+ srgbProfile,
596
+ writeJpegIcc,
597
+ writePngIcc
598
+ } from "../chunk-BRLYOPLT.js";
570
599
  import {
600
+ ZlibError,
601
+ adler32,
602
+ deflate,
603
+ deflateRaw,
604
+ inflate,
605
+ inflateRaw
606
+ } from "../chunk-6FCKOS2B.js";
607
+ import {
608
+ DEFAULT_AUTO_LEADING,
609
+ DEFAULT_TEXT_STYLE,
571
610
  WARP_STYLES,
572
611
  WARP_STYLE_LABELS,
573
612
  applyMatrix,
574
613
  bezierToSubpaths,
614
+ caretAt,
615
+ createCanvasTextShaper,
616
+ createMetricTextShaper,
617
+ createOutlineTextShaper,
618
+ cssFont,
619
+ cssFontFamily,
575
620
  dragWarpMeshPoint,
576
621
  evalWarpMesh,
622
+ fitParagraphRuns,
623
+ fitRuns,
624
+ fontOf,
577
625
  identityWarpMesh,
578
626
  invertMatrix,
627
+ layoutText,
579
628
  multiplyMatrices,
629
+ paragraphRanges,
580
630
  pullWarpMesh,
581
631
  resizeWarpMesh,
632
+ restyleRange,
582
633
  rotateMatrix,
583
634
  roundSubpaths,
635
+ spliceRuns,
636
+ styleAt,
637
+ textIndexAt,
584
638
  translateMatrix,
585
639
  warpMeshColumns,
586
640
  warpMeshEditPoints,
@@ -588,7 +642,7 @@ import {
588
642
  warpMeshParamAt,
589
643
  warpMeshRows,
590
644
  warpPresetMesh
591
- } from "../chunk-BLWCAQCW.js";
645
+ } from "../chunk-VXR7XWMF.js";
592
646
  import "../chunk-YCSFS3GK.js";
593
647
  import "../chunk-ZRJMJIQU.js";
594
648
  import "../chunk-TGD52FEZ.js";
@@ -943,6 +997,17 @@ var ImageEditorRenderer = class {
943
997
  /** Which path the last base render took. */
944
998
  lastBaseRoute = null;
945
999
  comp = null;
1000
+ /** Colour transform from the document to the display (see `color-management`), or none. */
1001
+ display = null;
1002
+ displayKey = 0;
1003
+ /** Sets the viewport's display transform (document profile → sRGB, or a soft proof); `null` for none. */
1004
+ setDisplayTransform(t) {
1005
+ if (t === this.display) return;
1006
+ this.display = t;
1007
+ this.displayKey++;
1008
+ this.comp = null;
1009
+ this.cachedBaseKey = "";
1010
+ }
946
1011
  depthMaps = /* @__PURE__ */ new Map();
947
1012
  assetVersion = 0;
948
1013
  scratch = null;
@@ -1012,13 +1077,15 @@ var ImageEditorRenderer = class {
1012
1077
  `${projection.angle},${transform.flipH},${transform.flipV}`,
1013
1078
  `${adjustments.brightness},${adjustments.contrast},${adjustments.saturation},${adjustments.exposure},${adjustments.temperature},${adjustments.tint ?? 0},${adjustments.blur},${adjustments.opacity},p${normalizeProcessVersion(adjustments.process)}`,
1014
1079
  adjustmentLayersKey(adjustments.layers),
1015
- `${depthMask.enabled},${depthMask.maskSource},${depthMask.depthRange?.[0]}-${depthMask.depthRange?.[1]},${depthMask.softness},${depthMask.invert},${depthMask.mode}`
1080
+ `${depthMask.enabled},${depthMask.maskSource},${depthMask.depthRange?.[0]}-${depthMask.depthRange?.[1]},${depthMask.softness},${depthMask.invert},${depthMask.mode}`,
1081
+ `d${this.displayKey}`
1016
1082
  ].join("|");
1017
1083
  let baseCanvas = this.cachedBaseCanvas;
1018
1084
  if (!baseCanvas || this.cachedBaseKey !== baseKey) {
1019
1085
  let histogram = null;
1020
1086
  baseCanvas = await this.renderBaseImage(img, naturalWidth, naturalHeight, region, transform, state, (pixels) => {
1021
1087
  histogram = computeHistogram(pixels);
1088
+ this.display?.(pixels.data);
1022
1089
  });
1023
1090
  this.cachedBaseCanvas = baseCanvas;
1024
1091
  this.cachedBaseKey = baseKey;
@@ -1369,6 +1436,7 @@ var ImageEditorRenderer = class {
1369
1436
  if (x1 <= x0 || y1 <= y0) return;
1370
1437
  const img = ctx.createImageData(TILE, TILE);
1371
1438
  img.data.set(data);
1439
+ this.display?.(img.data);
1372
1440
  ctx.putImageData(img, ox, oy, x0, y0, x1 - x0, y1 - y0);
1373
1441
  }
1374
1442
  /**
@@ -1411,7 +1479,7 @@ var ImageEditorRenderer = class {
1411
1479
  const rotated = getRotatedSize(region.width, region.height, transform);
1412
1480
  const baseW = Math.max(1, Math.round(rotated.width));
1413
1481
  const baseH = Math.max(1, Math.round(rotated.height));
1414
- const key = `${comp.version}|${region.x},${region.y},${region.width},${region.height}|${normalizeAngle(transform.rotate)},${transform.flipH},${transform.flipV}|${opacity}|${blur}`;
1482
+ const key = `${this.displayKey}:${comp.version}|${region.x},${region.y},${region.width},${region.height}|${normalizeAngle(transform.rotate)},${transform.flipH},${transform.flipV}|${opacity}|${blur}`;
1415
1483
  if (this.cachedPostCanvas && this.cachedPostKey === key) return { canvas: this.cachedPostCanvas, histogram: this.cachedPostHistogram };
1416
1484
  const canvas = this.cachedPostCanvas && this.cachedPostCanvas.width === baseW && this.cachedPostCanvas.height === baseH ? this.cachedPostCanvas : this.createCanvas(baseW, baseH);
1417
1485
  const ctx = canvas.getContext("2d");
@@ -2548,159 +2616,6 @@ function refineSelection(pixels, mask, params = {}) {
2548
2616
  return createSelectionMask(mask.width, mask.height, out);
2549
2617
  }
2550
2618
 
2551
- // src/image-editor/layers/text-shapers.ts
2552
- var GENERIC = /* @__PURE__ */ new Set(["serif", "sans-serif", "monospace", "cursive", "fantasy", "system-ui", "ui-serif", "ui-sans-serif", "ui-monospace", "ui-rounded", "math", "emoji", "fangsong"]);
2553
- function cssFontFamily(family) {
2554
- const f = family.trim();
2555
- if (GENERIC.has(f.toLowerCase()) || f.includes(",") || /^["']/.test(f)) return f;
2556
- return /^[A-Za-z_-][\w -]*$/.test(f) && !/\s{2}/.test(f) ? f : `"${f.replace(/"/g, '\\"')}"`;
2557
- }
2558
- function cssFont(font, family = font.family) {
2559
- return `${font.italic ? "italic " : ""}${font.weight} ${font.size}px ${cssFontFamily(family)}`;
2560
- }
2561
- function createCanvasTextShaper(options = {}) {
2562
- const make = options.createCanvas ?? ((w, h) => {
2563
- if (typeof OffscreenCanvas !== "undefined") return new OffscreenCanvas(w, h);
2564
- if (typeof document !== "undefined") {
2565
- const c = document.createElement("canvas");
2566
- c.width = w;
2567
- c.height = h;
2568
- return c;
2569
- }
2570
- return null;
2571
- });
2572
- const canvas = make(1, 1);
2573
- const ctx = canvas?.getContext("2d");
2574
- if (!ctx || typeof ctx.measureText !== "function") return null;
2575
- const fallback = options.fallbackFamily ?? "sans-serif";
2576
- const available = /* @__PURE__ */ new Map();
2577
- const widths = /* @__PURE__ */ new Map();
2578
- const metrics = /* @__PURE__ */ new Map();
2579
- const probe = "mmmmmmmmmmlli1WQ@#";
2580
- const width = (font, text) => {
2581
- ctx.font = font;
2582
- return ctx.measureText(text).width;
2583
- };
2584
- const isAvailable = (family) => {
2585
- const f = family.trim();
2586
- if (GENERIC.has(f.toLowerCase()) || f.includes(",")) return true;
2587
- let hit = available.get(f);
2588
- if (hit === void 0) {
2589
- const q = cssFontFamily(f);
2590
- hit = width(`72px ${q}, monospace`, probe) !== width("72px monospace", probe) || width(`72px ${q}, serif`, probe) !== width("72px serif", probe);
2591
- available.set(f, hit);
2592
- }
2593
- return hit;
2594
- };
2595
- return {
2596
- id: "canvas",
2597
- resolve(font) {
2598
- const ok = isAvailable(font.family);
2599
- return { family: ok ? font.family : fallback, available: ok };
2600
- },
2601
- metrics(font) {
2602
- const key = cssFont(font);
2603
- let m = metrics.get(key);
2604
- if (!m) {
2605
- ctx.font = key;
2606
- ctx.textBaseline = "alphabetic";
2607
- const t = ctx.measureText("Hg");
2608
- const ascent = t.emHeightAscent ?? t.fontBoundingBoxAscent ?? font.size * 0.8;
2609
- const descent = t.emHeightDescent ?? t.fontBoundingBoxDescent ?? font.size * 0.2;
2610
- m = { ascent: Number.isFinite(ascent) ? ascent : font.size * 0.8, descent: Number.isFinite(descent) ? descent : font.size * 0.2 };
2611
- metrics.set(key, m);
2612
- }
2613
- return m;
2614
- },
2615
- measure(text, font) {
2616
- const key = `${cssFont(font)}|${text}`;
2617
- let w = widths.get(key);
2618
- if (!w) {
2619
- ctx.font = cssFont(font);
2620
- w = [0];
2621
- for (let i = 1; i <= text.length; i++) w.push(ctx.measureText(text.slice(0, i)).width);
2622
- if (widths.size > 2e3) widths.clear();
2623
- widths.set(key, w);
2624
- }
2625
- return w;
2626
- },
2627
- draw(target, text, font, x, y) {
2628
- target.font = cssFont(font);
2629
- target.textBaseline = "alphabetic";
2630
- target.textAlign = "left";
2631
- target.fillText(text, x, y);
2632
- },
2633
- families() {
2634
- const out = /* @__PURE__ */ new Set(["sans-serif", "serif", "monospace"]);
2635
- const fonts = typeof document !== "undefined" ? document.fonts : void 0;
2636
- if (fonts && typeof fonts.forEach === "function") fonts.forEach((f) => out.add(f.family.replace(/^["']|["']$/g, "")));
2637
- return [...out];
2638
- }
2639
- };
2640
- }
2641
- function createOutlineTextShaper(fonts, options = {}) {
2642
- if (!fonts.length) throw new Error("createOutlineTextShaper needs at least one font.");
2643
- const entries = fonts.map((f) => {
2644
- const e = "face" in f ? f : { face: f };
2645
- const names = e.face.names;
2646
- const family = e.family ?? names?.fontFamily?.en ?? Object.values(names?.fontFamily ?? {})[0] ?? "Font";
2647
- const weight = e.weight ?? e.face.tables?.os2?.usWeightClass ?? 400;
2648
- const italic = e.italic ?? Boolean((e.face.tables?.os2?.fsSelection ?? 0) & 1);
2649
- const postScript = names?.postScriptName?.en ?? Object.values(names?.postScriptName ?? {})[0];
2650
- return { face: e.face, family, key: family.toLowerCase(), weight, italic, postScript };
2651
- });
2652
- const fallbackKey = (options.fallback ?? entries[0].family).toLowerCase();
2653
- const pick3 = (font) => {
2654
- if (font.postScriptName) {
2655
- const exact = entries.find((e) => e.postScript === font.postScriptName);
2656
- if (exact) return { entry: exact, available: true };
2657
- }
2658
- const key = font.family.trim().toLowerCase();
2659
- let pool = entries.filter((e) => e.key === key);
2660
- const available = pool.length > 0;
2661
- if (!available) pool = entries.filter((e) => e.key === fallbackKey);
2662
- if (!pool.length) pool = entries;
2663
- const styled = pool.filter((e) => e.italic === font.italic);
2664
- const list = styled.length ? styled : pool;
2665
- const entry = list.reduce((best, e) => Math.abs(e.weight - font.weight) < Math.abs(best.weight - font.weight) ? e : best);
2666
- return { entry, available };
2667
- };
2668
- const toPath = (cmds) => {
2669
- const out = [];
2670
- for (const c of cmds) {
2671
- if (c.type === "M" || c.type === "L") out.push({ type: c.type, x: c.x, y: c.y });
2672
- else if (c.type === "C") out.push({ type: "C", x1: c.x1, y1: c.y1, x2: c.x2, y2: c.y2, x: c.x, y: c.y });
2673
- else if (c.type === "Q") out.push({ type: "Q", x1: c.x1, y1: c.y1, x: c.x, y: c.y });
2674
- else out.push({ type: "Z" });
2675
- }
2676
- return out;
2677
- };
2678
- return {
2679
- id: options.id ?? `outline:${entries.map((e) => `${e.family}/${e.weight}${e.italic ? "i" : ""}`).join(",")}`,
2680
- resolve(font) {
2681
- const { entry, available } = pick3(font);
2682
- return { family: entry.family, available };
2683
- },
2684
- metrics(font) {
2685
- const { face } = pick3(font).entry;
2686
- const k = font.size / face.unitsPerEm;
2687
- return { ascent: face.ascender * k, descent: -face.descender * k };
2688
- },
2689
- measure(text, font) {
2690
- const { face } = pick3(font).entry;
2691
- const out = [0];
2692
- for (let i = 1; i <= text.length; i++) out.push(face.getAdvanceWidth(text.slice(0, i), font.size, { kerning: true }));
2693
- return out;
2694
- },
2695
- outline(text, font, x, y) {
2696
- return toPath(pick3(font).entry.face.getPath(text, x, y, font.size, { kerning: true }).commands);
2697
- },
2698
- families() {
2699
- return [...new Set(entries.map((e) => e.family))];
2700
- }
2701
- };
2702
- }
2703
-
2704
2619
  // src/image-editor/free-transform.ts
2705
2620
  var FREE_TRANSFORM_CONTENTS = ["auto", "layer", "selection", "selection-copy", "selection-outline"];
2706
2621
  var fail = (message) => {
@@ -3181,6 +3096,141 @@ var FreeTransformSession = class {
3181
3096
  }
3182
3097
  };
3183
3098
 
3099
+ // src/image-editor/color-management.ts
3100
+ var DEFAULT_PROOF = { intent: "relative", bpc: true, simulatePaper: false, gamutWarning: false };
3101
+ var resolved = /* @__PURE__ */ new WeakMap();
3102
+ var fromBytes = /* @__PURE__ */ new Map();
3103
+ function resolveProfile(doc) {
3104
+ if (!doc) return srgbProfile();
3105
+ const hit = resolved.get(doc);
3106
+ if (hit) return hit;
3107
+ let p;
3108
+ if (doc.builtin) p = builtinProfile(doc.builtin);
3109
+ else if (doc.icc) {
3110
+ const key = bytesToBase64(doc.icc);
3111
+ p = fromBytes.get(key) ?? parseProfile(doc.icc);
3112
+ if (fromBytes.size > 32) fromBytes.clear();
3113
+ fromBytes.set(key, p);
3114
+ } else p = srgbProfile();
3115
+ resolved.set(doc, p);
3116
+ return p;
3117
+ }
3118
+ function profileBytes(doc) {
3119
+ if (doc?.icc) return doc.icc;
3120
+ const p = resolveProfile(doc);
3121
+ return p.bytes ?? encodeProfile(p);
3122
+ }
3123
+ function documentProfile(input) {
3124
+ if (typeof input === "string") {
3125
+ const id = builtinProfileId(input);
3126
+ if (!id) throw new CmsError(`Unknown colour profile "${input}"; use one of ${BUILTIN_PROFILES.map((b) => b.id).join(", ")}, or ICC bytes.`);
3127
+ return { builtin: id, name: BUILTIN_PROFILES.find((b) => b.id === id).description };
3128
+ }
3129
+ const p = parseProfile(input);
3130
+ return { name: p.description || "Embedded profile", icc: input.slice() };
3131
+ }
3132
+ function isSrgb(doc) {
3133
+ if (!doc || doc.builtin === "srgb") return true;
3134
+ try {
3135
+ return sameColors(resolveProfile(doc), srgbProfile(), builtinProfile("lab-d50"));
3136
+ } catch {
3137
+ return false;
3138
+ }
3139
+ }
3140
+ function serializeColorProfile(doc) {
3141
+ return doc.builtin ? { builtin: doc.builtin, name: doc.name } : { name: doc.name, icc: bytesToBase64(doc.icc ?? new Uint8Array(0)) };
3142
+ }
3143
+ function deserializeColorProfile(raw, problems) {
3144
+ const r = raw;
3145
+ if (!r || typeof r !== "object") return null;
3146
+ try {
3147
+ if (r.builtin) {
3148
+ const id = builtinProfileId(r.builtin);
3149
+ if (!id) throw new CmsError(`unknown built-in profile "${r.builtin}"`);
3150
+ return documentProfile(id);
3151
+ }
3152
+ if (typeof r.icc === "string") {
3153
+ const icc = base64ToBytes(r.icc);
3154
+ parseProfile(icc);
3155
+ return { name: typeof r.name === "string" ? r.name : "Embedded profile", icc };
3156
+ }
3157
+ } catch (e) {
3158
+ problems.push(`The colour profile could not be read (${e instanceof Error ? e.message : String(e)}); the document is treated as sRGB`);
3159
+ }
3160
+ return null;
3161
+ }
3162
+ function displayTransform(doc, proof) {
3163
+ const src = resolveProfile(doc);
3164
+ const display = srgbProfile();
3165
+ let t = null;
3166
+ let gamut = null;
3167
+ if (proof) {
3168
+ const p = resolveProfile(proof.profile);
3169
+ t = Transform.proof(src, p, display, proof.intent, proof.bpc, proof.simulatePaper);
3170
+ if (proof.gamutWarning) gamut = new GamutCheck(src, p, builtinProfile("lab-d50"));
3171
+ } else if (!isSrgb(doc)) t = Transform.create(src, display, { intent: "relative", bpc: true });
3172
+ if (!t) return null;
3173
+ const tr = t;
3174
+ const g = gamut;
3175
+ return (rgba) => {
3176
+ if (g) {
3177
+ const px2 = [0, 0, 0];
3178
+ const flags = new Uint8Array(rgba.length >> 2);
3179
+ for (let i = 0, p = 0; i < rgba.length; i += 4, p++) {
3180
+ if (rgba[i + 3] === 0) continue;
3181
+ px2[0] = rgba[i] / 255;
3182
+ px2[1] = rgba[i + 1] / 255;
3183
+ px2[2] = rgba[i + 2] / 255;
3184
+ if (g.outOfGamut(px2)) flags[p] = 1;
3185
+ }
3186
+ tr.convert8(rgba, 4, rgba, 4);
3187
+ for (let i = 0, p = 0; i < rgba.length; i += 4, p++) if (flags[p]) rgba[i] = rgba[i + 1] = rgba[i + 2] = 128;
3188
+ return;
3189
+ }
3190
+ tr.convert8(rgba, 4, rgba, 4);
3191
+ };
3192
+ }
3193
+ function convertHex(hex, from, to, intent = "relative", bpc = true) {
3194
+ const m = /^#?([0-9a-f]{6})$/i.exec(hex.trim());
3195
+ if (!m) return hex;
3196
+ const n = parseInt(m[1], 16);
3197
+ const out = new Float32Array(4);
3198
+ Transform.create(from, to, { intent, bpc }).eval([(n >> 16 & 255) / 255, (n >> 8 & 255) / 255, (n & 255) / 255], out);
3199
+ const c = (v) => Math.round(Math.min(1, Math.max(0, v)) * 255).toString(16).padStart(2, "0");
3200
+ return `#${c(out[0])}${c(out[1])}${c(out[2])}`;
3201
+ }
3202
+ function convertSurfaceColors(surface, t) {
3203
+ if (surface.channels !== 4 || t.inputs !== 3 || t.outputs !== 3) return surface;
3204
+ const tiles = /* @__PURE__ */ new Map();
3205
+ const memo = /* @__PURE__ */ new Map();
3206
+ for (const [key, tile] of surface.tiles) {
3207
+ let next = memo.get(tile);
3208
+ if (next === void 0) {
3209
+ if (tile.deep instanceof Uint16Array) {
3210
+ const out = new Uint16Array(tile.deep.length);
3211
+ t.convert16(tile.deep, 4, out, 4);
3212
+ next = deepTile(out, 4, surface.defaultValue);
3213
+ } else if (tile.deep instanceof Float32Array) {
3214
+ const enc = new Float32Array(tile.deep.length);
3215
+ for (let i = 0; i < enc.length; i += 4) {
3216
+ for (let c = 0; c < 3; c++) enc[i + c] = srgbEncode(tile.deep[i + c]);
3217
+ enc[i + 3] = tile.deep[i + 3];
3218
+ }
3219
+ t.convertFloat(enc, 4, enc, 4);
3220
+ for (let i = 0; i < enc.length; i += 4) for (let c = 0; c < 3; c++) enc[i + c] = srgbDecode(enc[i + c]);
3221
+ next = deepTile(enc, 4, surface.defaultValue);
3222
+ } else {
3223
+ const out = new Uint8ClampedArray(tile.data.length);
3224
+ t.convert8(tile.data, 4, out, 4);
3225
+ next = finishTile(out, 4, surface.defaultValue);
3226
+ }
3227
+ memo.set(tile, next);
3228
+ }
3229
+ if (next) tiles.set(key, next);
3230
+ }
3231
+ return new TiledSurface(4, tiles, surface.defaultValue);
3232
+ }
3233
+
3184
3234
  // src/image-editor/controller.ts
3185
3235
  var IMAGE_EDITOR_SCHEMA_VERSION = LAYERED_SCHEMA_VERSION;
3186
3236
  var RETOUCH_TOOLS = ["clone", "heal", "spot-heal", "smudge", "dodge", "burn", "sponge"];
@@ -3593,10 +3643,23 @@ var ImageEditorController = class {
3593
3643
  if (strokeRects && this.pendingDirty !== "all") this.pendingDirty.push(...strokeRects);
3594
3644
  else if (!strokeRects) this.pendingDirty = "all";
3595
3645
  this.syncViews();
3646
+ this.syncDisplay();
3596
3647
  const currentState = { ...this.state };
3597
3648
  this.onStateChange?.(currentState);
3598
3649
  this.listeners.forEach((l) => l(currentState));
3599
3650
  }
3651
+ displayFor = null;
3652
+ /** Keeps the renderer's display transform in step with the document profile and Proof Setup. */
3653
+ syncDisplay() {
3654
+ const s = this.state;
3655
+ if (this.displayFor && this.displayFor[0] === s.colorProfile && this.displayFor[1] === s.proofSetup) return;
3656
+ this.displayFor = [s.colorProfile, s.proofSetup];
3657
+ try {
3658
+ this.renderer.setDisplayTransform(displayTransform(s.colorProfile, s.proofSetup));
3659
+ } catch {
3660
+ this.renderer.setDisplayTransform(null);
3661
+ }
3662
+ }
3600
3663
  /** Recomputes the compatibility views (`adjustments`, `depthMask`, `annotations`) when the stack changed. */
3601
3664
  syncViews() {
3602
3665
  const s = this.state;
@@ -3634,6 +3697,8 @@ var ImageEditorController = class {
3634
3697
  if (s.legacyOutputBlur !== void 0) entry.legacyOutputBlur = s.legacyOutputBlur;
3635
3698
  if (s.globalLight) entry.globalLight = { ...s.globalLight };
3636
3699
  if (s.patterns) entry.patterns = s.patterns;
3700
+ if (s.colorProfile) entry.colorProfile = s.colorProfile;
3701
+ if (s.bitDepth && s.bitDepth !== 8) entry.bitDepth = s.bitDepth;
3637
3702
  return entry;
3638
3703
  }
3639
3704
  restoreHistoryEntry(entry) {
@@ -3650,6 +3715,10 @@ var ImageEditorController = class {
3650
3715
  else delete s.globalLight;
3651
3716
  if (entry.patterns) s.patterns = entry.patterns;
3652
3717
  else delete s.patterns;
3718
+ if (entry.bitDepth) s.bitDepth = entry.bitDepth;
3719
+ else delete s.bitDepth;
3720
+ if (entry.colorProfile) s.colorProfile = entry.colorProfile;
3721
+ else delete s.colorProfile;
3653
3722
  s.selection = entry.selection;
3654
3723
  if (this.imageElement !== entry.source.element && !this.samplePixelsOverride) this.samplePixels = null;
3655
3724
  this.imageElement = entry.source.element;
@@ -4933,9 +5002,28 @@ var ImageEditorController = class {
4933
5002
  throw new LayerOperationError("Merging annotation layers needs a canvas (a browser); hide or delete them first, or merge in the browser.");
4934
5003
  }
4935
5004
  await this.renderer.prepareLayerAssets(layers, width, height);
4936
- const data = new LayerCompositor().composite(layers, rect, this.renderer.compositorSources(width, height));
5005
+ const sources = this.renderer.compositorSources(width, height);
5006
+ const depth = this.bitDepth;
5007
+ if (depth !== 8) {
5008
+ const samples = workingToSamples(compositeDeep(layers, rect, sources, depth), depth);
5009
+ return surfaceFromDeepPixels({ width: rect.width, height: rect.height, data: samples }, 4, rect.x, rect.y);
5010
+ }
5011
+ const data = new LayerCompositor().composite(layers, rect, sources);
4937
5012
  return surfaceFromPixels({ width: rect.width, height: rect.height, data }, 4, rect.x, rect.y);
4938
5013
  }
5014
+ /**
5015
+ * The composite of the layer stack at the document's bit depth (as `compositePixels`, no
5016
+ * canvas): 16-bit sRGB samples, or 32-bit linear float, over `rect` (default the canvas). At 8
5017
+ * bits, the 8-bit composite widened to 16 bits.
5018
+ */
5019
+ compositeDeepPixels(rect) {
5020
+ const { width, height } = this.naturalSize;
5021
+ const r = rect ?? { x: 0, y: 0, width, height };
5022
+ const depth = this.bitDepth;
5023
+ if (depth === 8) return { width: r.width, height: r.height, depth: 16, data: to16From8(this.compositePixels(r).data) };
5024
+ const data = workingToSamples(compositeDeep(this.state.layers, r, this.compositorSources(), depth), depth);
5025
+ return { width: r.width, height: r.height, depth, data };
5026
+ }
4939
5027
  /** Document bounds a layer can paint into (pixel content, annotations, or the canvas for the rest). */
4940
5028
  paintBounds(layer) {
4941
5029
  const { width, height } = this.naturalSize;
@@ -6138,16 +6226,37 @@ var ImageEditorController = class {
6138
6226
  const canvas = await this.renderExport(options);
6139
6227
  const format = options.format || "image/png";
6140
6228
  const quality = options.quality ?? 0.92;
6141
- return canvas.toDataURL(format, quality);
6229
+ const url = canvas.toDataURL(format, quality);
6230
+ if (!this.state.colorProfile) return url;
6231
+ const parsed = parseDataUrl(url);
6232
+ if (!parsed) return url;
6233
+ const bytes = this.withEmbeddedProfile(parsed.bytes, parsed.mime);
6234
+ return bytes === parsed.bytes ? url : `data:${parsed.mime};base64,${bytesToBase64(bytes)}`;
6235
+ }
6236
+ /** PNG or JPEG bytes with the document's ICC profile embedded (other formats unchanged). */
6237
+ withEmbeddedProfile(bytes, mime) {
6238
+ const icc = profileBytes(this.state.colorProfile);
6239
+ try {
6240
+ if (mime === "image/png") return writePngIcc(bytes, icc, this.getColorProfile().name);
6241
+ if (mime === "image/jpeg") return writeJpegIcc(bytes, icc);
6242
+ } catch {
6243
+ }
6244
+ return bytes;
6142
6245
  }
6143
6246
  async toBlob(options = {}) {
6144
6247
  const canvas = await this.renderExport(options);
6145
6248
  const format = options.format || "image/png";
6146
6249
  const quality = options.quality ?? 0.92;
6250
+ const tag = async (blob) => {
6251
+ if (!this.state.colorProfile || typeof blob.arrayBuffer !== "function") return blob;
6252
+ const bytes = new Uint8Array(await blob.arrayBuffer());
6253
+ const out = this.withEmbeddedProfile(bytes, blob.type || format);
6254
+ return out === bytes ? blob : new Blob([out], { type: blob.type || format });
6255
+ };
6147
6256
  return new Promise((resolve, reject) => {
6148
6257
  if (canvas.toBlob) {
6149
6258
  canvas.toBlob((blob) => {
6150
- if (blob) resolve(blob);
6259
+ if (blob) tag(blob).then(resolve, reject);
6151
6260
  else reject(new Error("Failed to create Blob from canvas"));
6152
6261
  }, format, quality);
6153
6262
  } else {
@@ -6160,7 +6269,7 @@ var ImageEditorController = class {
6160
6269
  for (let i = 0; i < byteStr.length; i++) {
6161
6270
  ia[i] = byteStr.charCodeAt(i);
6162
6271
  }
6163
- resolve(new Blob([ab], { type: format }));
6272
+ tag(new Blob([ab], { type: format })).then(resolve, reject);
6164
6273
  } catch (err) {
6165
6274
  reject(err);
6166
6275
  }
@@ -6680,6 +6789,162 @@ var ImageEditorController = class {
6680
6789
  return id;
6681
6790
  }
6682
6791
  // --- PSD ---
6792
+ // --- Colour management ---
6793
+ /** The document's colour profile: its name, built-in id (or `null` for an embedded profile) and colour space. */
6794
+ getColorProfile() {
6795
+ const doc = this.state.colorProfile;
6796
+ const p = resolveProfile(doc);
6797
+ return { name: doc?.name ?? p.description, builtin: doc ? doc.builtin ?? null : "srgb", colorSpace: p.colorSpace, srgb: isSrgb(doc) };
6798
+ }
6799
+ /** Resolves a profile argument (built-in id or alias, or ICC bytes) as an RGB document profile. */
6800
+ rgbProfileArg(profile, action) {
6801
+ let doc;
6802
+ try {
6803
+ doc = documentProfile(profile);
6804
+ } catch (e) {
6805
+ throw new LayerOperationError(e instanceof Error ? e.message : String(e));
6806
+ }
6807
+ const cs = resolveProfile(doc).colorSpace;
6808
+ if (cs !== "rgb") throw new LayerOperationError(`${action} needs an RGB profile; "${doc.name}" is ${cs.toUpperCase()}. The document is RGB: soft-proof CMYK with set_proof_setup, or export to it.`);
6809
+ return doc;
6810
+ }
6811
+ /**
6812
+ * Edit › Assign Profile (one undo step): the document's numbers now mean colours in
6813
+ * `profile` (a built-in id such as `display-p3`, or ICC bytes); no pixel changes, so colours
6814
+ * look different unless the profiles match. Returns the new profile.
6815
+ */
6816
+ assignProfile(profile) {
6817
+ const doc = this.rgbProfileArg(profile, "Assign Profile");
6818
+ this.recordHistory();
6819
+ if (doc.builtin === "srgb") delete this.state.colorProfile;
6820
+ else this.state.colorProfile = doc;
6821
+ this.notify();
6822
+ return this.getColorProfile();
6823
+ }
6824
+ /**
6825
+ * Edit › Convert to Profile (one undo step): every pixel layer's numbers are converted so its
6826
+ * colours stay the same in `profile` (with `intent` and black point compensation), and
6827
+ * `profile` becomes the document's. Shape and type layers, layer effects and adjustment layers
6828
+ * keep their numbers (they are listed in `unconvertedLayerIds`). Returns the profile and the
6829
+ * layers changed.
6830
+ */
6831
+ convertToProfile(profile, options = {}) {
6832
+ const doc = this.rgbProfileArg(profile, "Convert to Profile");
6833
+ const intent = options.intent ?? "relative";
6834
+ if (!INTENTS.includes(intent)) throw new LayerOperationError(`Unknown rendering intent "${String(intent)}"; use ${INTENTS.join(", ")}.`);
6835
+ const t = Transform.create(resolveProfile(this.state.colorProfile), resolveProfile(doc), { intent, bpc: options.bpc ?? true });
6836
+ const converted = [];
6837
+ const unconverted = [];
6838
+ const walk = (layers) => layers.map((l) => {
6839
+ if (l.type === "group") return { ...l, children: walk(l.children) };
6840
+ if (l.type === "pixel" && l.source !== "document") {
6841
+ converted.push(l.id);
6842
+ return { ...l, surface: convertSurfaceColors(l.surface, t) };
6843
+ }
6844
+ if (l.type === "pixel" && l.source === "document") {
6845
+ converted.push(l.id);
6846
+ return { ...l, surface: convertSurfaceColors(this.surfaceOf(l), t), source: void 0 };
6847
+ }
6848
+ if (l.type === "shape" || l.type === "type" || l.type === "smart" || l.effects?.items.length) unconverted.push(l.id);
6849
+ return l;
6850
+ });
6851
+ const next = walk(this.state.layers);
6852
+ this.commitLayers(next, () => {
6853
+ if (doc.builtin === "srgb") delete this.state.colorProfile;
6854
+ else this.state.colorProfile = doc;
6855
+ });
6856
+ return { profile: this.getColorProfile(), convertedLayerIds: converted, unconvertedLayerIds: unconverted };
6857
+ }
6858
+ /**
6859
+ * View › Proof Setup (view state, not an undo step): shows the document as `profile` would
6860
+ * reproduce it, or turns proofing off with `null`. Returns the setup.
6861
+ */
6862
+ setProofSetup(setup) {
6863
+ if (!setup) {
6864
+ this.state.proofSetup = null;
6865
+ this.notify();
6866
+ return null;
6867
+ }
6868
+ let doc;
6869
+ try {
6870
+ doc = documentProfile(setup.profile);
6871
+ } catch (e) {
6872
+ throw new LayerOperationError(e instanceof Error ? e.message : String(e));
6873
+ }
6874
+ const intent = setup.intent ?? DEFAULT_PROOF.intent;
6875
+ if (!INTENTS.includes(intent)) throw new LayerOperationError(`Unknown rendering intent "${String(intent)}"; use ${INTENTS.join(", ")}.`);
6876
+ const next = { ...DEFAULT_PROOF, ...setup, intent, profile: doc };
6877
+ if (!canBeDestination(resolveProfile(doc))) throw new LayerOperationError(`"${doc.name}" describes a source only, so colours cannot be proofed through it.`);
6878
+ this.state.proofSetup = next;
6879
+ this.notify();
6880
+ return next;
6881
+ }
6882
+ /**
6883
+ * Converts colours (`#rrggbb`) from one profile to another (default from the document's), with
6884
+ * `intent`. For RGB targets returns hex; for others the channel values 0..1 (CMYK, grey, Lab
6885
+ * encoded as L/100, (a+128)/255, (b+128)/255) and, for Lab, L*a*b*.
6886
+ */
6887
+ convertColors(colors, to, options = {}) {
6888
+ let src;
6889
+ let dst;
6890
+ try {
6891
+ src = options.from !== void 0 ? resolveProfile(documentProfile(options.from)) : resolveProfile(this.state.colorProfile);
6892
+ dst = resolveProfile(documentProfile(to));
6893
+ } catch (e) {
6894
+ throw new LayerOperationError(e instanceof Error ? e.message : String(e));
6895
+ }
6896
+ if (src.colorSpace !== "rgb") throw new LayerOperationError("Colours are given as #rrggbb, so the source profile must be RGB.");
6897
+ const t = Transform.create(src, dst, { intent: options.intent ?? "relative", bpc: options.bpc ?? true });
6898
+ const toLab = Transform.create(src, builtinProfile("lab-d50"), { intent: "relative", bpc: false });
6899
+ return colors.map((input) => {
6900
+ const m = /^#?([0-9a-f]{6})$/i.exec(input.trim());
6901
+ if (!m) throw new LayerOperationError(`Invalid colour "${input}"; use #rrggbb.`);
6902
+ const n = parseInt(m[1], 16);
6903
+ const rgb = [(n >> 16 & 255) / 255, (n >> 8 & 255) / 255, (n & 255) / 255];
6904
+ const out = new Float32Array(16);
6905
+ t.eval(rgb, out);
6906
+ const values = Array.from(out.subarray(0, t.outputs), (v) => Math.round(Math.min(1, Math.max(0, v)) * 1e4) / 1e4);
6907
+ const l = new Float32Array(3);
6908
+ toLab.eval(rgb, l);
6909
+ const lab = [Math.round(l[0] * 1e4) / 100, Math.round((l[1] * 255 - 128) * 100) / 100, Math.round((l[2] * 255 - 128) * 100) / 100];
6910
+ const res = { input, values, lab };
6911
+ if (dst.colorSpace === "rgb") res.hex = `#${values.map((v) => Math.round(v * 255).toString(16).padStart(2, "0")).join("")}`;
6912
+ return res;
6913
+ });
6914
+ }
6915
+ /** The document's bits per channel (8 unless set to 16 or 32). */
6916
+ get bitDepth() {
6917
+ return this.state.bitDepth ?? 8;
6918
+ }
6919
+ /**
6920
+ * Image › Mode › 8 / 16 / 32 Bits/Channel (one undo step). Going up costs nothing: layers keep
6921
+ * their pixels and read them at the new depth, and edits that understand depth keep the extra
6922
+ * precision from then on. Going down rounds every pixel layer's deep samples to the new depth
6923
+ * (8 bits: their 8-bit rendering; 32 → 16: colour above 1 is clipped). Returns the depth and
6924
+ * the ids of the layers whose pixels changed.
6925
+ */
6926
+ setBitDepth(depth) {
6927
+ if (!isBitDepth(depth)) throw new LayerOperationError(`Bit depth must be 8, 16 or 32, not ${String(depth)}.`);
6928
+ const changed = [];
6929
+ const convert = (layers) => layers.map((l) => {
6930
+ if (l.type === "group") {
6931
+ const children = convert(l.children);
6932
+ return children.some((c, i) => c !== l.children[i]) ? { ...l, children } : l;
6933
+ }
6934
+ if (l.type !== "pixel") return l;
6935
+ const surface = convertSurfaceDepth(l.surface, depth);
6936
+ if (surface === l.surface) return l;
6937
+ changed.push(l.id);
6938
+ return { ...l, surface };
6939
+ });
6940
+ if (depth === this.bitDepth) return { bitDepth: depth, changedLayerIds: [] };
6941
+ const next = convert(this.state.layers);
6942
+ this.commitLayers(next, () => {
6943
+ if (depth === 8) delete this.state.bitDepth;
6944
+ else this.state.bitDepth = depth;
6945
+ });
6946
+ return { bitDepth: depth, changedLayerIds: changed };
6947
+ }
6683
6948
  /** Pixels per inch of the last opened PSD (written back by `toPsd`). */
6684
6949
  psdResolution;
6685
6950
  /**
@@ -6712,6 +6977,20 @@ var ImageEditorController = class {
6712
6977
  else delete s.patterns;
6713
6978
  if (result.globalLight) s.globalLight = result.globalLight;
6714
6979
  else delete s.globalLight;
6980
+ if (result.bitDepth !== 8) s.bitDepth = result.bitDepth;
6981
+ else delete s.bitDepth;
6982
+ delete s.colorProfile;
6983
+ if (result.iccProfile) {
6984
+ try {
6985
+ const doc = documentProfile(result.iccProfile);
6986
+ const cs = resolveProfile(doc).colorSpace;
6987
+ if (cs === "rgb") {
6988
+ if (!isSrgb(doc)) s.colorProfile = doc;
6989
+ } else result.warnings.push(`The file's ${cs.toUpperCase()} profile "${doc.name}" was not used; its colours were converted to sRGB numerically.`);
6990
+ } catch (e) {
6991
+ result.warnings.push(`The embedded ICC profile could not be read (${e instanceof Error ? e.message : String(e)}); colours are read as sRGB.`);
6992
+ }
6993
+ }
6715
6994
  this.lastLoadProblems = result.warnings;
6716
6995
  this.notify();
6717
6996
  return {
@@ -6747,7 +7026,7 @@ var ImageEditorController = class {
6747
7026
  return layers;
6748
7027
  }
6749
7028
  /**
6750
- * The document as an 8-bit RGB PSD (PSB above 30 000 px) with real layers, groups, adjustment
7029
+ * The document as an RGB PSD at its bit depth (8, 16 or 32) (PSB above 30 000 px) with real layers, groups, adjustment
6751
7030
  * layers and masks, in the source frame. Layer filters and parametric (depth) masks are baked
6752
7031
  * into pixels and masks, annotation layers are rasterized (in a browser; elsewhere they are left
6753
7032
  * out), and the merged image is our composite. `warnings` lists every such change.
@@ -6873,7 +7152,9 @@ var ImageEditorController = class {
6873
7152
  if (s.legacyOutputBlur) warnings.push("The document-level blur of an old document is not written.");
6874
7153
  for (const [msg, names] of note) warnings.push(`${msg} (${names.length === 1 ? "layer" : "layers"} ${names.map((n) => `"${n}"`).join(", ")}).`);
6875
7154
  const merged = compositor.composite(prepared, canvas, { ...sources, version: `psd:${Date.now()}` });
6876
- const result = exportPsd({ width, height, layers: prepared, merged, resolution: this.psdResolution, patterns: this.state.patterns, globalLight: this.state.globalLight });
7155
+ const depth = this.bitDepth;
7156
+ const mergedDeep = depth === 8 ? void 0 : workingToSamples(compositeDeep(prepared, canvas, sources, depth), depth);
7157
+ const result = exportPsd({ width, height, layers: prepared, merged, mergedDeep, resolution: this.psdResolution, patterns: this.state.patterns, globalLight: this.state.globalLight, depth, iccProfile: profileBytes(this.state.colorProfile) });
6877
7158
  warnings.push(...result.warnings);
6878
7159
  return { bytes: result.bytes, warnings, psb: result.psb, width, height };
6879
7160
  }
@@ -7019,6 +7300,11 @@ var ImageEditorController = class {
7019
7300
  const patterns = deserializePatterns(data.patterns, problems);
7020
7301
  if (patterns.length) s.patterns = patterns;
7021
7302
  else delete s.patterns;
7303
+ if (data.bitDepth === 16 || data.bitDepth === 32) s.bitDepth = data.bitDepth;
7304
+ else delete s.bitDepth;
7305
+ const profile = data.colorProfile ? deserializeColorProfile(data.colorProfile, problems) : null;
7306
+ if (profile) s.colorProfile = profile;
7307
+ else delete s.colorProfile;
7022
7308
  if (!this.imageElement && typeof data.width === "number" && typeof data.height === "number" && data.width > 0 && data.height > 0) {
7023
7309
  s.imageDimensions = { width: data.width, height: data.height };
7024
7310
  }
@@ -7034,6 +7320,8 @@ var ImageEditorController = class {
7034
7320
  s.activeLayerId = null;
7035
7321
  delete s.globalLight;
7036
7322
  delete s.patterns;
7323
+ delete s.bitDepth;
7324
+ delete s.colorProfile;
7037
7325
  }
7038
7326
  s.activeTarget = "pixels";
7039
7327
  this.viewKey = [];
@@ -7043,7 +7331,7 @@ var ImageEditorController = class {
7043
7331
  serializeState() {
7044
7332
  const { width, height } = this.naturalSize;
7045
7333
  const view = legacyView(this.stack(), width, height, null);
7046
- if (view.exact && !this.state.globalLight && !this.state.patterns?.length) {
7334
+ if (view.exact && !this.state.globalLight && !this.state.patterns?.length && this.bitDepth === 8 && !this.state.colorProfile) {
7047
7335
  return {
7048
7336
  // States without adjustment layers keep schema 1, so they round-trip unchanged.
7049
7337
  version: view.adjustments.layers?.length ? 2 : 1,
@@ -7073,6 +7361,8 @@ var ImageEditorController = class {
7073
7361
  if (this.state.globalLight) out.globalLight = { ...this.state.globalLight };
7074
7362
  if (this.state.patterns?.length) out.patterns = serializePatterns(this.state.patterns);
7075
7363
  if (smartContents) out.smartContents = smartContents;
7364
+ if (this.state.bitDepth === 16 || this.state.bitDepth === 32) out.bitDepth = this.state.bitDepth;
7365
+ if (this.state.colorProfile) out.colorProfile = serializeColorProfile(this.state.colorProfile);
7076
7366
  return out;
7077
7367
  }
7078
7368
  };
@@ -8281,10 +8571,23 @@ function createImageEditTools(controller) {
8281
8571
  const getImageInfo = {
8282
8572
  name: "get_image_info",
8283
8573
  title: "Read image geometry",
8284
- description: "Returns the source image size in pixels, the crop box (natural image pixels, or null for the whole image), rotation in degrees, flips, the output size after crop and rotation, the number of adjustment layers and whether undo / redo are available.",
8574
+ description: "Returns the source image size in pixels, the crop box (natural image pixels, or null for the whole image), rotation in degrees, flips, the output size after crop and rotation, the bits per channel (bitDepth 8, 16 or 32), the number of adjustment layers and whether undo / redo are available.",
8285
8575
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
8286
8576
  annotations: { title: "Read image geometry", ...READ },
8287
- handler: () => ({ ...geometry2(), layers: controller.getAdjustmentLayers().length, ...history() })
8577
+ handler: () => ({ ...geometry2(), bitDepth: controller.bitDepth, layers: controller.getAdjustmentLayers().length, ...history() })
8578
+ };
8579
+ const setBitDepth = {
8580
+ name: "set_bit_depth",
8581
+ title: "Set the bit depth",
8582
+ description: "Image \u203A Mode \u203A 8 / 16 / 32 Bits/Channel. 16-bit keeps smooth gradients and survives strong adjustments without banding; 32-bit holds linear-light float values (HDR, colour above 1). Going up costs nothing and changes no pixels; going down rounds every pixel layer's extra precision away (32 \u2192 16 also clips colour above 1). The viewport always shows an 8-bit rendering. One undo step. Returns the bitDepth and changedLayerIds (the layers whose samples were rounded).",
8583
+ inputSchema: {
8584
+ type: "object",
8585
+ properties: { depth: { type: "number", enum: [8, 16, 32], description: "Bits per channel: 8, 16 or 32 (float, linear light)." } },
8586
+ required: ["depth"],
8587
+ additionalProperties: false
8588
+ },
8589
+ annotations: { title: "Set the bit depth", readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
8590
+ handler: (args) => ({ ...controller.setBitDepth(args.depth), ...history() })
8288
8591
  };
8289
8592
  const getHistogram = {
8290
8593
  name: "get_histogram",
@@ -8460,7 +8763,7 @@ function createImageEditTools(controller) {
8460
8763
  return history();
8461
8764
  }
8462
8765
  };
8463
- return [getImageInfo, getHistogram, updateLayer, removeLayer2, moveLayer2, setCrop, resetCrop, setRotation, flipImage, undo, redo];
8766
+ return [getImageInfo, getHistogram, updateLayer, removeLayer2, moveLayer2, setCrop, resetCrop, setRotation, flipImage, setBitDepth, undo, redo];
8464
8767
  }
8465
8768
 
8466
8769
  // src/image-editor/layer-tools.ts
@@ -9775,8 +10078,132 @@ function createImagePaintTools(controller) {
9775
10078
  return [paintStroke, fill, clear, getMixer, loadMixerTool, cleanMixerTool];
9776
10079
  }
9777
10080
 
9778
- // src/image-editor/retouch-tools.ts
10081
+ // src/image-editor/color-tools.ts
9779
10082
  var READ5 = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
10083
+ var PROFILE = {
10084
+ type: "string",
10085
+ description: `A built-in profile id (${BUILTIN_PROFILES.map((b) => b.id).join(", ")}; names like "Adobe RGB (1998)" work too), or "icc:<base64>" for ICC profile bytes.`
10086
+ };
10087
+ var INTENT = {
10088
+ type: "string",
10089
+ enum: [...INTENTS],
10090
+ description: "Rendering intent (default relative): relative (colorimetric, white to white, clips out-of-gamut colours: best for most photos), perceptual (compresses the whole gamut smoothly), saturation (keeps colours vivid, for graphics), absolute (keeps the source white too: for proofing a paper colour)."
10091
+ };
10092
+ function profileArg(v) {
10093
+ const s = String(v);
10094
+ if (s.startsWith("icc:")) {
10095
+ try {
10096
+ return base64ToBytes(s.slice(4));
10097
+ } catch {
10098
+ throw new LayerOperationError("The icc: value is not valid base64.");
10099
+ }
10100
+ }
10101
+ return s;
10102
+ }
10103
+ function createImageColorTools(controller) {
10104
+ const getProfile = {
10105
+ name: "get_color_profile",
10106
+ title: "Get the colour profile",
10107
+ description: "Reads the document's colour profile (its working space: name, builtin id or null for an embedded profile, colorSpace, srgb true when it means sRGB), the Proof Setup (profile, intent, bpc, simulatePaper, gamutWarning, or null) and the built-in profiles you can assign, convert to or proof with ({id, description, colorSpace}).",
10108
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
10109
+ annotations: { title: "Get the colour profile", ...READ5 },
10110
+ handler: () => {
10111
+ const proof2 = controller.getState().proofSetup;
10112
+ return {
10113
+ profile: controller.getColorProfile(),
10114
+ proof: proof2 ? { profile: proof2.profile.name, builtin: proof2.profile.builtin ?? null, intent: proof2.intent, bpc: proof2.bpc, simulatePaper: proof2.simulatePaper, gamutWarning: proof2.gamutWarning } : null,
10115
+ builtins: BUILTIN_PROFILES.map((b) => ({ ...b }))
10116
+ };
10117
+ }
10118
+ };
10119
+ const assign = {
10120
+ name: "assign_profile",
10121
+ title: "Assign a colour profile",
10122
+ description: "Edit \u203A Assign Profile: the document's RGB numbers now mean colours in `profile`; no pixel changes, so the image looks different unless the profiles match. Use it when a file was saved without its profile, or with the wrong one. To keep the look and change the numbers, use convert_to_profile. RGB profiles only. One undo step. Returns the new profile.",
10123
+ inputSchema: { type: "object", properties: { profile: PROFILE }, required: ["profile"], additionalProperties: false },
10124
+ annotations: { title: "Assign a colour profile", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
10125
+ handler: (args) => controller.assignProfile(profileArg(args.profile))
10126
+ };
10127
+ const convert = {
10128
+ name: "convert_to_profile",
10129
+ title: "Convert to a colour profile",
10130
+ description: "Edit \u203A Convert to Profile: converts every pixel layer's numbers so its colours stay the same in `profile`, then makes `profile` the document's. Colours outside the new space are mapped by `intent`. Shape and type layers, layer effects and smart objects keep their numbers and are listed in unconvertedLayerIds. RGB profiles only (proof CMYK with set_proof_setup). One undo step. Returns the profile, convertedLayerIds and unconvertedLayerIds.",
10131
+ inputSchema: {
10132
+ type: "object",
10133
+ properties: {
10134
+ profile: PROFILE,
10135
+ intent: INTENT,
10136
+ bpc: { type: "boolean", description: "Black point compensation: map black to black (default true)." }
10137
+ },
10138
+ required: ["profile"],
10139
+ additionalProperties: false
10140
+ },
10141
+ annotations: { title: "Convert to a colour profile", readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false },
10142
+ handler: (args) => controller.convertToProfile(profileArg(args.profile), { intent: args.intent, bpc: args.bpc })
10143
+ };
10144
+ const proof = {
10145
+ name: "set_proof_setup",
10146
+ title: "Set up soft proofing",
10147
+ description: "View \u203A Proof Setup: shows the document in the viewport as `profile` would reproduce it (for example coated-cmyk for a press), with `intent`; `simulatePaper` shows the paper white and ink black; `gamutWarning` greys out colours the profile cannot reproduce. Pass off true to stop proofing. View state, not an undo step; pixels never change. Check the result with capture_view. Returns the setup (or null).",
10148
+ inputSchema: {
10149
+ type: "object",
10150
+ properties: {
10151
+ profile: PROFILE,
10152
+ intent: INTENT,
10153
+ bpc: { type: "boolean", description: "Black point compensation (default true)." },
10154
+ simulatePaper: { type: "boolean", description: "Simulate paper colour and black ink (default false)." },
10155
+ gamutWarning: { type: "boolean", description: "Grey out out-of-gamut colours (default false)." },
10156
+ off: { type: "boolean", description: "Turn proofing off instead (default false)." }
10157
+ },
10158
+ additionalProperties: false
10159
+ },
10160
+ annotations: { title: "Set up soft proofing", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
10161
+ handler: (args) => {
10162
+ if (args.off === true) {
10163
+ if (args.profile !== void 0) throw new LayerOperationError("Give either off true or a profile, not both.");
10164
+ return controller.setProofSetup(null);
10165
+ }
10166
+ if (args.profile === void 0) throw new LayerOperationError("Pass the profile to proof with (e.g. coated-cmyk), or off true.");
10167
+ const s = controller.setProofSetup({
10168
+ profile: profileArg(args.profile),
10169
+ ...args.intent !== void 0 ? { intent: args.intent } : {},
10170
+ ...args.bpc !== void 0 ? { bpc: args.bpc } : {},
10171
+ ...args.simulatePaper !== void 0 ? { simulatePaper: args.simulatePaper } : {},
10172
+ ...args.gamutWarning !== void 0 ? { gamutWarning: args.gamutWarning } : {}
10173
+ });
10174
+ return { profile: s.profile.name, builtin: s.profile.builtin ?? null, intent: s.intent, bpc: s.bpc, simulatePaper: s.simulatePaper, gamutWarning: s.gamutWarning };
10175
+ }
10176
+ };
10177
+ const convertColors = {
10178
+ name: "convert_colors",
10179
+ title: "Convert colours between profiles",
10180
+ description: 'Converts 1 to 64 #rrggbb colours from the document\'s profile (or `from`) into `to` with `intent`: returns per colour the channel values 0..1 (RGB, CMYK, grey; Lab as L/100, (a+128)/255, (b+128)/255), a hex for RGB targets, and L*a*b* (D50). Use it to answer "what are these CMYK values" or to pick colours that survive printing. Changes nothing.',
10181
+ inputSchema: {
10182
+ type: "object",
10183
+ properties: {
10184
+ colors: { type: "array", minItems: 1, maxItems: 64, items: { type: "string", description: "A colour #rrggbb." }, description: "Colours in the source profile." },
10185
+ to: PROFILE,
10186
+ from: { ...PROFILE, description: `Source profile (default the document's). ${PROFILE.description}` },
10187
+ intent: INTENT,
10188
+ bpc: { type: "boolean", description: "Black point compensation (default true)." }
10189
+ },
10190
+ required: ["colors", "to"],
10191
+ additionalProperties: false
10192
+ },
10193
+ annotations: { title: "Convert colours between profiles", ...READ5 },
10194
+ handler: (args) => ({
10195
+ colors: controller.convertColors(args.colors, profileArg(args.to), {
10196
+ ...args.from !== void 0 ? { from: profileArg(args.from) } : {},
10197
+ intent: args.intent,
10198
+ bpc: args.bpc
10199
+ })
10200
+ })
10201
+ };
10202
+ return [getProfile, assign, convert, proof, convertColors];
10203
+ }
10204
+
10205
+ // src/image-editor/retouch-tools.ts
10206
+ var READ6 = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
9780
10207
  var WRITE5 = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
9781
10208
  var POINTS = {
9782
10209
  type: "array",
@@ -9980,7 +10407,7 @@ function createImageRetouchTools(controller) {
9980
10407
  title: "Get retouch options",
9981
10408
  description: "Reads the retouching tools' options (aligned, sample, spotType, range, exposure, protectTones, spongeMode, vibrance, strength) and the clone source ({x, y, offset} or null; offset is the aligned offset the last clone or heal stroke fixed). Call it before clone_stamp or heal_brush without a source to see where they will sample.",
9982
10409
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
9983
- annotations: { title: "Get retouch options", ...READ5 },
10410
+ annotations: { title: "Get retouch options", ...READ6 },
9984
10411
  handler: () => {
9985
10412
  const { retouch, cloneSource } = controller.getState().paint;
9986
10413
  return { options: { ...retouch }, cloneSource: cloneSource ? { ...cloneSource } : null };
@@ -10040,7 +10467,7 @@ function pick(args, keys) {
10040
10467
  }
10041
10468
 
10042
10469
  // src/image-editor/psd-tools.ts
10043
- var READ6 = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
10470
+ var READ7 = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
10044
10471
  var DESTRUCTIVE4 = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false };
10045
10472
  var DATA = {
10046
10473
  type: "string",
@@ -10061,7 +10488,7 @@ function createImagePsdTools(controller) {
10061
10488
  title: "Inspect a PSD",
10062
10489
  description: "Reads a PSD / PSB without opening it: size, bit depth, colour mode, resolution, and every layer (bottom first, a group before its children) with its PSD kind, the layer type it would become and its status (mapped, approximated or dropped, with notes), plus the import warnings. Changes nothing. Call before import_psd to see what would be lost.",
10063
10490
  inputSchema: { type: "object", properties: { data: DATA }, required: ["data"], additionalProperties: false },
10064
- annotations: { title: "Inspect a PSD", ...READ6 },
10491
+ annotations: { title: "Inspect a PSD", ...READ7 },
10065
10492
  handler: ({ data }) => {
10066
10493
  const res = importPsd(psdBytesFromBase64(data), { skipPixels: true });
10067
10494
  return {
@@ -10096,9 +10523,9 @@ function createImagePsdTools(controller) {
10096
10523
  const save = {
10097
10524
  name: "export_psd",
10098
10525
  title: "Export the document as a PSD",
10099
- description: "Writes the document as an 8-bit RGB PSD (a PSB above 30000 px) with real layers, groups, adjustment layers, masks and layer effects (an effects block opened from a PSD is written back unchanged while its effects are; the patterns effects use are embedded), in the source frame (crop and rotation are not applied). Smart objects are written as Photoshop smart objects (contents embedded once per linked set, placement, warp, smart filters and filter mask; a Develop smart filter has no Photoshop filter and is left out of the list). Shape layers are written as Photoshop shape layers (path, fill, stroke, live shapes) and type layers as Photoshop type layers (text, runs, paragraphs, frame; blocks opened from a PSD are written back unchanged while the layer is). Layer filters and depth masks are baked, annotation layers are rasterized (in a browser; elsewhere left out), and the merged image is the editor's composite. Changes nothing. Returns the file as base64, its size in bytes, whether it is a PSB, and warnings for everything that was changed or left out.",
10526
+ description: "Writes the document as an RGB PSD at its bit depth (8, 16 or 32; see set_bit_depth; a PSB above 30000 px) with real layers, groups, adjustment layers, masks and layer effects (an effects block opened from a PSD is written back unchanged while its effects are; the patterns effects use are embedded), in the source frame (crop and rotation are not applied). Smart objects are written as Photoshop smart objects (contents embedded once per linked set, placement, warp, smart filters and filter mask; a Develop smart filter has no Photoshop filter and is left out of the list). Shape layers are written as Photoshop shape layers (path, fill, stroke, live shapes) and type layers as Photoshop type layers (text, runs, paragraphs, frame; blocks opened from a PSD are written back unchanged while the layer is). Layer filters and depth masks are baked, annotation layers are rasterized (in a browser; elsewhere left out), and the merged image is the editor's composite. Changes nothing. Returns the file as base64, its size in bytes, whether it is a PSB, and warnings for everything that was changed or left out.",
10100
10527
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
10101
- annotations: { title: "Export the document as a PSD", ...READ6 },
10528
+ annotations: { title: "Export the document as a PSD", ...READ7 },
10102
10529
  handler: async () => {
10103
10530
  const res = await controller.toPsd();
10104
10531
  return {
@@ -10116,7 +10543,7 @@ function createImagePsdTools(controller) {
10116
10543
  }
10117
10544
 
10118
10545
  // src/image-editor/transform-tools.ts
10119
- var READ7 = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
10546
+ var READ8 = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
10120
10547
  var WRITE6 = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
10121
10548
  var round4 = (n, digits = 3) => Math.round(n * 10 ** digits) / 10 ** digits;
10122
10549
  var roundPoint = (p) => ({ x: round4(p.x), y: round4(p.y) });
@@ -10216,7 +10643,7 @@ function createImageTransformTools(controller) {
10216
10643
  },
10217
10644
  additionalProperties: false
10218
10645
  },
10219
- annotations: { title: "Get the Free Transform frame", ...READ7 },
10646
+ annotations: { title: "Get the Free Transform frame", ...READ8 },
10220
10647
  handler: (args) => {
10221
10648
  const init = targetFrom(args);
10222
10649
  if (init.content === "selection-copy") init.content = "selection";
@@ -10359,7 +10786,7 @@ function createImageTransformTools(controller) {
10359
10786
  }
10360
10787
 
10361
10788
  // src/image-editor/smart-tools.ts
10362
- var READ8 = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
10789
+ var READ9 = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
10363
10790
  var WRITE7 = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
10364
10791
  var WRITE_IDEMPOTENT4 = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false };
10365
10792
  var DESTRUCTIVE5 = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false };
@@ -10418,7 +10845,7 @@ function createImageSmartObjectTools(controller) {
10418
10845
  title: "Read a smart object",
10419
10846
  description: "Returns a smart object\u2019s contents (id shared by linked instances, name, kind raster or document, pixel size, the file type it came from, the instances placed from it), its placement (corners in document pixels, warp mesh in contents pixels, interpolation, and scale / rotate / skew in percent and degrees of the contents\u2019 own size when affine), its smart filters bottom to top (ids, kind, settings, enabled, blend, opacity; kind psd marks a Photoshop filter kept but not drawn) with the filter mask, and the rendered bounds. Changes nothing.",
10420
10847
  inputSchema: { type: "object", properties: { layerId: LAYER_ID4 }, required: ["layerId"], additionalProperties: false },
10421
- annotations: { title: "Read a smart object", ...READ8 },
10848
+ annotations: { title: "Read a smart object", ...READ9 },
10422
10849
  handler: ({ layerId }) => info(smartId(layerId))
10423
10850
  };
10424
10851
  const convert = {
@@ -10552,7 +10979,7 @@ function createImageSmartObjectTools(controller) {
10552
10979
  title: "Export a smart object\u2019s contents",
10553
10980
  description: "Layer \u203A Smart Objects \u203A Export Contents: the contents as a file, base64: the file they came from while unedited (a PSB, PNG, JPEG\u2026), else a PNG for raster contents or a PSD / PSB of a nested document. Returns name, mimeType, byteLength and base64. Changes nothing.",
10554
10981
  inputSchema: { type: "object", properties: { layerId: LAYER_ID4 }, required: ["layerId"], additionalProperties: false },
10555
- annotations: { title: "Export a smart object\u2019s contents", ...READ8 },
10982
+ annotations: { title: "Export a smart object\u2019s contents", ...READ9 },
10556
10983
  handler: ({ layerId }) => {
10557
10984
  const f = controller.exportSmartObjectContents(smartId(layerId));
10558
10985
  return { name: f.name, mimeType: f.mimeType, byteLength: f.bytes.length, base64: bytesToBase64(f.bytes) };
@@ -10719,7 +11146,7 @@ function createImageSmartObjectTools(controller) {
10719
11146
  }
10720
11147
 
10721
11148
  // src/image-editor/shape-tools.ts
10722
- var READ9 = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
11149
+ var READ10 = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
10723
11150
  var WRITE8 = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
10724
11151
  var WRITE_IDEMPOTENT5 = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false };
10725
11152
  var DESTRUCTIVE6 = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false };
@@ -10973,7 +11400,7 @@ function createImageShapeTools(controller) {
10973
11400
  title: "Read a shape layer",
10974
11401
  description: "Returns a shape layer\u2019s components bottom to top (index, path operation, the live shape it was drawn as or null, its subpaths with absolute anchors and handles, bounds), the fill rule, fill and stroke paints and settings, the path bounds and the rendered bounds (stroke included). Changes nothing.",
10975
11402
  inputSchema: { type: "object", properties: { layerId: LAYER_ID5 }, required: ["layerId"], additionalProperties: false },
10976
- annotations: { title: "Read a shape layer", ...READ9 },
11403
+ annotations: { title: "Read a shape layer", ...READ10 },
10977
11404
  handler: ({ layerId }) => controller.getShapeLayer(shapeId(layerId))
10978
11405
  };
10979
11406
  const add2 = {
@@ -11122,7 +11549,7 @@ function createImageShapeTools(controller) {
11122
11549
  }
11123
11550
 
11124
11551
  // src/image-editor/type-tools.ts
11125
- var READ10 = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
11552
+ var READ11 = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
11126
11553
  var WRITE9 = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
11127
11554
  var WRITE_IDEMPOTENT6 = { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false };
11128
11555
  var DESTRUCTIVE7 = { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: false };
@@ -11173,7 +11600,7 @@ function createImageTypeTools(controller) {
11173
11600
  title: "List fonts",
11174
11601
  description: "Returns the font families type layers can use here (generic families such as sans-serif always work). Changes nothing.",
11175
11602
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
11176
- annotations: { title: "List fonts", ...READ10 },
11603
+ annotations: { title: "List fonts", ...READ11 },
11177
11604
  handler: () => ({ families: controller.listFonts(), shaper: controller.getTextShaper()?.id ?? null })
11178
11605
  };
11179
11606
  const get = {
@@ -11181,7 +11608,7 @@ function createImageTypeTools(controller) {
11181
11608
  title: "Read a type layer",
11182
11609
  description: "Returns a type layer\u2019s text, its character runs and paragraphs as [start, end) ranges in UTF-16 code units with their styles, the frame (point text, or a paragraph box with its size), the text-to-document transform, the laid-out lines (range, baseline, width), the rendered bounds, missing fonts and whether the pixels are live or stored from a file. Call before editing text you did not write, to get the ranges. Changes nothing.",
11183
11610
  inputSchema: { type: "object", properties: { layerId: LAYER_ID6 }, required: ["layerId"], additionalProperties: false },
11184
- annotations: { title: "Read a type layer", ...READ10 },
11611
+ annotations: { title: "Read a type layer", ...READ11 },
11185
11612
  handler: ({ layerId }) => controller.getTextLayer(typeId(layerId))
11186
11613
  };
11187
11614
  const add2 = {
@@ -11534,6 +11961,7 @@ function createImageEditorToolProvider(controller, options = {}) {
11534
11961
  ...createImageTypeTools(controller),
11535
11962
  ...createImageEffectTools(controller),
11536
11963
  ...createImagePaintTools(controller),
11964
+ ...createImageColorTools(controller),
11537
11965
  ...createImageRetouchTools(controller),
11538
11966
  ...createPigmentTools(),
11539
11967
  ...createImagePsdTools(controller)
@@ -12529,6 +12957,11 @@ function extractNormalizedLayerItems(controller, fallbackImageUrl = "") {
12529
12957
  };
12530
12958
  });
12531
12959
  }
12960
+ var BIT_DEPTH_OPTIONS = [
12961
+ { depth: 8, label: "8-bit", title: "8 bits per channel" },
12962
+ { depth: 16, label: "16-bit", title: "16 bits per channel: smooth gradients, no banding under strong adjustments" },
12963
+ { depth: 32, label: "32-bit", title: "32-bit float per channel, linear light (HDR)" }
12964
+ ];
12532
12965
 
12533
12966
  // src/image-editor/layers/effects-ui.ts
12534
12967
  function layerEffectsPanel(layer) {
@@ -12703,6 +13136,25 @@ function effectControlChange(effect, key, value, patterns = []) {
12703
13136
  return { [top]: setPath(getPath(effect, top), rest, value) };
12704
13137
  }
12705
13138
 
13139
+ // src/image-editor/layers/color-ui.ts
13140
+ var WORKING_SPACE_OPTIONS = BUILTIN_PROFILES.filter((b) => b.colorSpace === "rgb").map((b) => ({ id: b.id, label: b.description }));
13141
+ var PROOF_PROFILE_OPTIONS = BUILTIN_PROFILES.map((b) => ({ id: b.id, label: b.description }));
13142
+ var INTENT_OPTIONS = [
13143
+ { id: "relative", label: "Relative Colorimetric", title: "Keep in-gamut colours exact, clip the rest (photos)" },
13144
+ { id: "perceptual", label: "Perceptual", title: "Compress the whole gamut smoothly" },
13145
+ { id: "saturation", label: "Saturation", title: "Keep colours vivid (graphics)" },
13146
+ { id: "absolute", label: "Absolute Colorimetric", title: "Keep the source white too (paper proofs)" }
13147
+ ];
13148
+ function colorPanelView(controller) {
13149
+ const profile = controller.getColorProfile();
13150
+ const proof = controller.getState().proofSetup ?? null;
13151
+ return {
13152
+ profileName: profile.name,
13153
+ workingSpace: profile.builtin,
13154
+ proof: proof ? { id: proof.profile.builtin ?? null, name: proof.profile.name, intent: proof.intent, simulatePaper: proof.simulatePaper, gamutWarning: proof.gamutWarning, bpc: proof.bpc } : null
13155
+ };
13156
+ }
13157
+
12706
13158
  // src/image-editor/psd/ui.ts
12707
13159
  var PSD_FILE_ACCEPT = ".psd,.psb,image/vnd.adobe.photoshop,application/x-photoshop";
12708
13160
  function blobBytes(file) {
@@ -13315,9 +13767,12 @@ export {
13315
13767
  AS_SHOT_KELVIN,
13316
13768
  BACKGROUND_LAYER_ID,
13317
13769
  BACKGROUND_LAYER_NAME,
13770
+ BIT_DEPTHS,
13771
+ BIT_DEPTH_OPTIONS,
13318
13772
  BLEND_MODES,
13319
13773
  BLUR_FILTER_ID,
13320
13774
  BRUSH_OPTION_CONTROLS,
13775
+ BUILTIN_PROFILES,
13321
13776
  CoverageMap,
13322
13777
  D50,
13323
13778
  D65,
@@ -13329,6 +13784,7 @@ export {
13329
13784
  DEFAULT_GLOBAL_LIGHT,
13330
13785
  DEFAULT_MITER_LIMIT,
13331
13786
  DEFAULT_MIXER,
13787
+ DEFAULT_PROOF,
13332
13788
  DEFAULT_REFINE_EDGE_PARAMS,
13333
13789
  DEFAULT_RETOUCH,
13334
13790
  DEFAULT_SELECTION_TOOL_OPTIONS,
@@ -13358,6 +13814,8 @@ export {
13358
13814
  IMAGE_EDITOR_SCHEMA_VERSION,
13359
13815
  IMAGE_EDITOR_STATE_VERSION,
13360
13816
  IMAGE_FILTER_PRESETS,
13817
+ INTENTS,
13818
+ INTENT_OPTIONS,
13361
13819
  INTERPOLATIONS,
13362
13820
  ImageEditorController,
13363
13821
  ImageEditorRenderer,
@@ -13391,6 +13849,7 @@ export {
13391
13849
  PIGMENT_LAMBDA_MIN,
13392
13850
  PIGMENT_LAMBDA_STEP,
13393
13851
  PIGMENT_WAVELENGTHS,
13852
+ PROOF_PROFILE_OPTIONS,
13394
13853
  PSD_BLEND_KEYS,
13395
13854
  PSD_FILE_ACCEPT,
13396
13855
  PaintExchange,
@@ -13444,6 +13903,7 @@ export {
13444
13903
  WARP_STYLES,
13445
13904
  WARP_STYLE_LABELS,
13446
13905
  WARP_STYLE_OPTIONS,
13906
+ WORKING_SPACE_OPTIONS,
13447
13907
  XYZ_D50_TO_SRGB,
13448
13908
  ZlibError,
13449
13909
  activeAdjustmentLayers,
@@ -13465,6 +13925,7 @@ export {
13465
13925
  annotationOfShape,
13466
13926
  annotationOfType,
13467
13927
  applyAdjustmentLayers,
13928
+ applyAdjustmentLayersFloat,
13468
13929
  applyAdjustments,
13469
13930
  applyBoxBlur,
13470
13931
  applyDepthMask,
@@ -13517,10 +13978,12 @@ export {
13517
13978
  cloneSourceLabel,
13518
13979
  collectTiles,
13519
13980
  colorBalanceChannel,
13981
+ colorPanelView,
13520
13982
  colorRange,
13521
13983
  combineSelections,
13522
13984
  compileAdjustmentLayer,
13523
13985
  componentPath,
13986
+ compositeDeep,
13524
13987
  compositeSurfaceOver,
13525
13988
  computeHistogram,
13526
13989
  computeSelectionBounds,
@@ -13530,6 +13993,9 @@ export {
13530
13993
  contractCoverage,
13531
13994
  controlPosition,
13532
13995
  controlTrackBackground,
13996
+ convertHex,
13997
+ convertSurfaceColors,
13998
+ convertSurfaceDepth,
13533
13999
  convertToSmartObject,
13534
14000
  copySmartObject,
13535
14001
  cornersFromMatrix,
@@ -13540,6 +14006,7 @@ export {
13540
14006
  createBlenderScratch,
13541
14007
  createCanvasTextShaper,
13542
14008
  createImageCaptureSurface,
14009
+ createImageColorTools,
13543
14010
  createImageEditTools,
13544
14011
  createImageEditorToolProvider,
13545
14012
  createImageEffectTools,
@@ -13552,6 +14019,7 @@ export {
13552
14019
  createImageSmartObjectTools,
13553
14020
  createImageTransformTools,
13554
14021
  createImageTypeTools,
14022
+ createMetricTextShaper,
13555
14023
  createOutlineTextShaper,
13556
14024
  createPigment,
13557
14025
  createPigmentTools,
@@ -13566,6 +14034,7 @@ export {
13566
14034
  curveSvgPath,
13567
14035
  dabCoverage,
13568
14036
  dabRect,
14037
+ decodeDeepSurface,
13569
14038
  decodeLayerChannel,
13570
14039
  decodeLayerMask,
13571
14040
  decodeMergedImage,
@@ -13573,6 +14042,8 @@ export {
13573
14042
  decodePng,
13574
14043
  decodePngDataUrl,
13575
14044
  decodeSelectionRle,
14045
+ deepBytes,
14046
+ deepTile,
13576
14047
  defaultAdjustmentParams,
13577
14048
  defaultEffectGradient,
13578
14049
  defaultLayerEffect,
@@ -13580,11 +14051,13 @@ export {
13580
14051
  deflate,
13581
14052
  deflateRaw,
13582
14053
  depthMaskAlpha,
14054
+ depthOf,
13583
14055
  depthRasterMask,
13584
14056
  describeLayer,
13585
14057
  describeLayerEffects,
13586
14058
  describeSelection,
13587
14059
  describeTransform,
14060
+ deserializeColorProfile,
13588
14061
  deserializeEffects,
13589
14062
  deserializeLayer,
13590
14063
  deserializeLayers,
@@ -13594,8 +14067,10 @@ export {
13594
14067
  developFromAdjustments,
13595
14068
  developTarget,
13596
14069
  dilateMask,
14070
+ displayTransform,
13597
14071
  distanceTransform,
13598
14072
  documentContents,
14073
+ documentProfile,
13599
14074
  dodgeBurn,
13600
14075
  dodgeBurnCurve,
13601
14076
  downloadPsd,
@@ -13614,6 +14089,7 @@ export {
13614
14089
  ellipseSelection,
13615
14090
  emptyHistogram,
13616
14091
  emptySurface,
14092
+ encodeDeepSurface,
13617
14093
  encodePng,
13618
14094
  encodeRle,
13619
14095
  encodeSelectionRle,
@@ -13689,6 +14165,7 @@ export {
13689
14165
  invertSelection,
13690
14166
  isActiveFilter,
13691
14167
  isAdjustmentKind,
14168
+ isBitDepth,
13692
14169
  isBlendMode,
13693
14170
  isContentsDecoded,
13694
14171
  isDefaultDepthMask,
@@ -13709,6 +14186,7 @@ export {
13709
14186
  isSelectionMode,
13710
14187
  isSeparableBlendMode,
13711
14188
  isSpatialFilter,
14189
+ isSrgb,
13712
14190
  isTransformAnchor,
13713
14191
  isValidWarp,
13714
14192
  kelvinToTemperature,
@@ -13746,6 +14224,7 @@ export {
13746
14224
  liveShapeSubpaths,
13747
14225
  loadImageEditorDocument,
13748
14226
  loadMixer,
14227
+ loadOpenTypeFont,
13749
14228
  luma,
13750
14229
  magicWand,
13751
14230
  mapCropHandle,
@@ -13812,6 +14291,7 @@ export {
13812
14291
  parseEffectColor,
13813
14292
  parseLfx2,
13814
14293
  parseLrfx,
14294
+ parseOpenTypeFont,
13815
14295
  parsePatternBlock,
13816
14296
  parseSelectionMode,
13817
14297
  parseVersionedDescriptor,
@@ -13834,10 +14314,12 @@ export {
13834
14314
  posterizeValue,
13835
14315
  presetWarp,
13836
14316
  previewStep,
14317
+ profileBytes,
13837
14318
  projectImagePoint,
13838
14319
  projectImageRect,
13839
14320
  projectMaskCoverage,
13840
14321
  projectQuarterTurn,
14322
+ proxyOf,
13841
14323
  psdBytesFromBase64,
13842
14324
  psdFileName,
13843
14325
  psdPatternToEffectPattern,
@@ -13851,6 +14333,7 @@ export {
13851
14333
  rasterizeDab,
13852
14334
  rasterizeSmartObject,
13853
14335
  readAdjustmentControls,
14336
+ readDeepRegion,
13854
14337
  readPixel,
13855
14338
  readPsd,
13856
14339
  readRegion,
@@ -13891,6 +14374,7 @@ export {
13891
14374
  resizeWarpMesh,
13892
14375
  resolveExternalAssets,
13893
14376
  resolveMosaicRegionBounds,
14377
+ resolveProfile,
13894
14378
  restyleRange,
13895
14379
  rgbToGray,
13896
14380
  rgbToHsl,
@@ -13912,6 +14396,7 @@ export {
13912
14396
  selectionToCoverage,
13913
14397
  selectionToPixels,
13914
14398
  selectionValueAt,
14399
+ serializeColorProfile,
13915
14400
  serializeContents,
13916
14401
  serializeEffects,
13917
14402
  serializeLayer,
@@ -13951,11 +14436,15 @@ export {
13951
14436
  spotHealPixels,
13952
14437
  spotHealWindow,
13953
14438
  srgb8ToLab,
14439
+ srgbDecode,
14440
+ srgbEncode,
13954
14441
  srgbToLinear,
13955
14442
  standardBlendMode,
13956
14443
  styleAt,
13957
14444
  sumHistograms,
13958
14445
  surfaceBytes,
14446
+ surfaceDepth,
14447
+ surfaceFromDeepPixels,
13959
14448
  surfaceFromPixels,
13960
14449
  surfacesEqual,
13961
14450
  tempTintToXy,
@@ -13967,10 +14456,19 @@ export {
13967
14456
  tileCoord,
13968
14457
  tileKey,
13969
14458
  tileRect,
14459
+ tileSamples,
14460
+ tileSamples16,
14461
+ tileSamples32,
13970
14462
  tilesInRect,
13971
14463
  tipFalloff,
13972
14464
  tipReach,
13973
14465
  tipShape,
14466
+ to16From32,
14467
+ to16From8,
14468
+ to32From16,
14469
+ to32From8,
14470
+ to8From16,
14471
+ to8From32,
13974
14472
  toHex,
13975
14473
  toTextSpace,
13976
14474
  toneCurve,
@@ -14020,6 +14518,7 @@ export {
14020
14518
  withFilterMaskSurface,
14021
14519
  withNewIds,
14022
14520
  writeAdjustmentBlock,
14521
+ writeDeepRegion,
14023
14522
  writeLfx2,
14024
14523
  writePatternBlock,
14025
14524
  writePsd,