@pixerate/editor 0.108.0 → 0.110.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 (105) hide show
  1. package/dist/animation/index.cjs +5 -5
  2. package/dist/animation/index.cjs.map +1 -1
  3. package/dist/animation/index.js +9 -9
  4. package/dist/animation-editor/index.cjs +59 -59
  5. package/dist/animation-editor/index.cjs.map +1 -1
  6. package/dist/animation-editor/index.d.cts +2 -1
  7. package/dist/animation-editor/index.d.ts +2 -1
  8. package/dist/animation-editor/index.js +29 -29
  9. package/dist/blending-DXl06NSx.d.ts +486 -0
  10. package/dist/blending-aOE6Y3UE.d.cts +486 -0
  11. package/dist/canvas/index.cjs +27 -27
  12. package/dist/canvas/index.cjs.map +1 -1
  13. package/dist/canvas/index.js +11 -11
  14. package/dist/{chunk-2FGRTYJN.cjs → chunk-477UVMNR.cjs} +3 -3
  15. package/dist/{chunk-2FGRTYJN.cjs.map → chunk-477UVMNR.cjs.map} +1 -1
  16. package/dist/chunk-7VOHOPLW.cjs +1136 -0
  17. package/dist/chunk-7VOHOPLW.cjs.map +1 -0
  18. package/dist/{chunk-HUQ7EHVI.js → chunk-BLWCAQCW.js} +723 -1975
  19. package/dist/chunk-BLWCAQCW.js.map +1 -0
  20. package/dist/{chunk-SOROZ2BF.js → chunk-DMRSJBJF.js} +1939 -318
  21. package/dist/chunk-DMRSJBJF.js.map +1 -0
  22. package/dist/chunk-FTBZPI77.cjs +1 -0
  23. package/dist/chunk-FTBZPI77.cjs.map +1 -0
  24. package/dist/{chunk-WMBIJCHA.js → chunk-HNS7UHCM.js} +5 -1
  25. package/dist/{chunk-WMBIJCHA.js.map → chunk-HNS7UHCM.js.map} +1 -1
  26. package/dist/chunk-JLYX4V57.cjs +2001 -0
  27. package/dist/chunk-JLYX4V57.cjs.map +1 -0
  28. package/dist/chunk-KB2YBCVS.js +2001 -0
  29. package/dist/chunk-KB2YBCVS.js.map +1 -0
  30. package/dist/{chunk-G4BIYE6R.js → chunk-LQEEDHOP.js} +40 -2
  31. package/dist/chunk-LQEEDHOP.js.map +1 -0
  32. package/dist/{chunk-FRD3FUXI.cjs → chunk-MGL4U5WA.cjs} +2144 -523
  33. package/dist/chunk-MGL4U5WA.cjs.map +1 -0
  34. package/dist/{chunk-T3QZ6S47.cjs → chunk-POGBUYWE.cjs} +131 -93
  35. package/dist/chunk-POGBUYWE.cjs.map +1 -0
  36. package/dist/chunk-VKK5VS2T.js +1 -0
  37. package/dist/{chunk-UTEVPQTS.cjs → chunk-W3GXSUSQ.cjs} +746 -1998
  38. package/dist/chunk-W3GXSUSQ.cjs.map +1 -0
  39. package/dist/{chunk-SZ6KMZNM.cjs → chunk-XIOMMGPD.cjs} +6 -2
  40. package/dist/chunk-XIOMMGPD.cjs.map +1 -0
  41. package/dist/{chunk-JK6M6V7G.js → chunk-Z3YWVUWT.js} +4 -4
  42. package/dist/{chunk-3PHA3UKI.js → chunk-Z4BC6M74.js} +523 -121
  43. package/dist/chunk-Z4BC6M74.js.map +1 -0
  44. package/dist/{export-a9bnF7Cl.d.ts → export-CO2ryu3I.d.ts} +419 -18
  45. package/dist/{export-JGWQEPJn.d.cts → export-DRiLzP_j.d.cts} +419 -18
  46. package/dist/image-editor/index.cjs +1665 -1113
  47. package/dist/image-editor/index.cjs.map +1 -1
  48. package/dist/image-editor/index.d.cts +78 -14
  49. package/dist/image-editor/index.d.ts +78 -14
  50. package/dist/image-editor/index.js +843 -291
  51. package/dist/image-editor/index.js.map +1 -1
  52. package/dist/paint/index.cjs +28 -4
  53. package/dist/paint/index.cjs.map +1 -1
  54. package/dist/paint/index.d.cts +3 -89
  55. package/dist/paint/index.d.ts +3 -89
  56. package/dist/paint/index.js +27 -3
  57. package/dist/paint-editor/index.cjs +1056 -287
  58. package/dist/paint-editor/index.cjs.map +1 -1
  59. package/dist/paint-editor/index.d.cts +304 -22
  60. package/dist/paint-editor/index.d.ts +304 -22
  61. package/dist/paint-editor/index.js +852 -83
  62. package/dist/paint-editor/index.js.map +1 -1
  63. package/dist/raster/index.cjs +7 -3
  64. package/dist/raster/index.cjs.map +1 -1
  65. package/dist/raster/index.d.cts +3 -129
  66. package/dist/raster/index.d.ts +3 -129
  67. package/dist/raster/index.js +6 -2
  68. package/dist/{compositor-D92URMlm.d.cts → render-BEriFuqK.d.cts} +136 -2
  69. package/dist/{compositor-Kt_M4ruo.d.ts → render-tjzKaik-.d.ts} +136 -2
  70. package/dist/{surface-CkKOHGPm.d.cts → surface-C8RRg_Bh.d.cts} +109 -1
  71. package/dist/{surface-CkKOHGPm.d.ts → surface-C8RRg_Bh.d.ts} +109 -1
  72. package/dist/{svg-SseJTIrA.d.ts → svg-D_cfkhlB.d.cts} +3 -629
  73. package/dist/{svg-ClVIG4T7.d.cts → svg-wIaKLrU0.d.ts} +3 -629
  74. package/dist/types-DtL_qLFz.d.ts +630 -0
  75. package/dist/types-DyQq-WPY.d.cts +630 -0
  76. package/dist/vector/index.cjs +563 -563
  77. package/dist/vector/index.cjs.map +1 -1
  78. package/dist/vector/index.d.cts +4 -2
  79. package/dist/vector/index.d.ts +4 -2
  80. package/dist/vector/index.js +69 -69
  81. package/dist/video-editor/index.cjs +3 -3
  82. package/dist/video-editor/index.js +2 -2
  83. package/package.json +1 -1
  84. package/skills/pixerate-paint-editor/SKILL.md +49 -13
  85. package/dist/chunk-3PHA3UKI.js.map +0 -1
  86. package/dist/chunk-6BU5WU4R.cjs +0 -1
  87. package/dist/chunk-6BU5WU4R.cjs.map +0 -1
  88. package/dist/chunk-DWGZUSES.cjs +0 -694
  89. package/dist/chunk-DWGZUSES.cjs.map +0 -1
  90. package/dist/chunk-FRD3FUXI.cjs.map +0 -1
  91. package/dist/chunk-G4BIYE6R.js.map +0 -1
  92. package/dist/chunk-HUFQD4CB.cjs +0 -734
  93. package/dist/chunk-HUFQD4CB.cjs.map +0 -1
  94. package/dist/chunk-HUQ7EHVI.js.map +0 -1
  95. package/dist/chunk-KKREERP2.js +0 -694
  96. package/dist/chunk-KKREERP2.js.map +0 -1
  97. package/dist/chunk-SOROZ2BF.js.map +0 -1
  98. package/dist/chunk-SZ6KMZNM.cjs.map +0 -1
  99. package/dist/chunk-T3QZ6S47.cjs.map +0 -1
  100. package/dist/chunk-UTEVPQTS.cjs.map +0 -1
  101. package/dist/chunk-XYBJKUAU.js +0 -1
  102. package/dist/mixer-5ziHU8OS.d.cts +0 -282
  103. package/dist/mixer-DNqiYXYQ.d.ts +0 -282
  104. /package/dist/{chunk-XYBJKUAU.js.map → chunk-VKK5VS2T.js.map} +0 -0
  105. /package/dist/{chunk-JK6M6V7G.js.map → chunk-Z3YWVUWT.js.map} +0 -0
@@ -1,7 +1,9 @@
1
1
  import {
2
+ DEFAULT_GLOBAL_LIGHT,
2
3
  LAYER_EFFECT_KINDS,
3
4
  LayerEffectError,
4
5
  MAX_EFFECT_REACH,
6
+ PatternTile,
5
7
  RasterCompositor,
6
8
  TransformTooLargeError,
7
9
  ZlibError,
@@ -13,6 +15,7 @@ import {
13
15
  decodePng,
14
16
  defaultLayerEffect,
15
17
  effectColorHex,
18
+ effectFillColors,
16
19
  effectsFingerprint,
17
20
  effectsMargin,
18
21
  effectsScale,
@@ -35,9 +38,10 @@ import {
35
38
  reduceSurface,
36
39
  scaleLayerEffect,
37
40
  transformSurface,
41
+ validateEffectGradient,
38
42
  validateLayerEffects,
39
43
  warpSurface
40
- } from "./chunk-G4BIYE6R.js";
44
+ } from "./chunk-LQEEDHOP.js";
41
45
  import {
42
46
  affineFromHomography,
43
47
  applyHomography,
@@ -49,32 +53,53 @@ import {
49
53
  rectToQuad
50
54
  } from "./chunk-GX332W2C.js";
51
55
  import {
56
+ TILE,
57
+ TiledSurface,
52
58
  blendRgb,
53
59
  contentBounds,
54
60
  emptySurface,
61
+ finishTile,
55
62
  isBlendMode,
56
63
  mixPigmentRgb,
57
64
  normalizeBlendMode,
58
65
  readRegion,
59
66
  setLum,
60
67
  surfaceFromPixels,
68
+ tileKey,
69
+ tilesInRect,
61
70
  translateSurface
62
- } from "./chunk-WMBIJCHA.js";
71
+ } from "./chunk-HNS7UHCM.js";
63
72
  import {
73
+ IDENTITY_MATRIX,
64
74
  WARP_STYLES,
65
75
  applyMatrix,
76
+ bezierToSubpaths,
77
+ ellipseSubpath,
66
78
  evalWarpMesh,
79
+ expandVectorCorners,
67
80
  fitWarpMesh,
68
81
  identityWarpMesh,
69
82
  invertMatrix,
83
+ isIdentityMatrix,
70
84
  isValidWarpMesh,
71
85
  mapWarpMeshPoints,
72
86
  matrixAbout,
73
87
  multiplyMatrices,
88
+ polygonSubpath,
89
+ rectSubpath,
74
90
  rotateMatrix,
91
+ setAnchorCorner,
92
+ starSubpath,
93
+ subpathsToBezier,
75
94
  translateMatrix,
76
95
  warpPresetMesh
77
- } from "./chunk-KKREERP2.js";
96
+ } from "./chunk-BLWCAQCW.js";
97
+ import {
98
+ bezierPathBounds,
99
+ outlineStroke,
100
+ pathBoolean,
101
+ strokeOutlineRings
102
+ } from "./chunk-YCSFS3GK.js";
78
103
 
79
104
  // src/image-editor/color.ts
80
105
  var LEGACY_PROCESS_VERSION = 1;
@@ -793,12 +818,12 @@ function selectionFromPixels(pixels, options = {}) {
793
818
  const { width, height, data } = pixels;
794
819
  checkSize(width, height);
795
820
  if (data.length < width * height * 4) throw new Error("Pixel data is shorter than width \xD7 height \xD7 4");
796
- const channel = options.channel ?? "luminance";
821
+ const channel2 = options.channel ?? "luminance";
797
822
  const out = new Uint8Array(width * height);
798
823
  const threshold = options.threshold;
799
824
  for (let i = 0, p = 0; i < out.length; i++, p += 4) {
800
825
  let v;
801
- switch (channel) {
826
+ switch (channel2) {
802
827
  case "alpha":
803
828
  v = data[p + 3];
804
829
  break;
@@ -2197,6 +2222,8 @@ function removeAlpha(imageData) {
2197
2222
  }
2198
2223
 
2199
2224
  // src/image-editor/layers/model.ts
2225
+ var SHAPE_OPERATIONS = ["combine", "subtract", "intersect", "exclude"];
2226
+ var LIVE_SHAPE_KINDS = ["rect", "ellipse", "polygon", "star", "line"];
2200
2227
  var idCounter = 0;
2201
2228
  function newLayerId(prefix = "layer") {
2202
2229
  return `${prefix}_${Date.now().toString(36)}_${(++idCounter).toString(36)}`;
@@ -2224,10 +2251,16 @@ function pixelLayerBounds(layer, surface = layer.surface) {
2224
2251
  return { x: b.x + layer.offset.x, y: b.y + layer.offset.y, width: b.width, height: b.height };
2225
2252
  }
2226
2253
  function layerTypeLabel(type) {
2227
- return type === "pixel" ? "Pixel layer" : type === "adjustment" ? "Adjustment layer" : type === "group" ? "Group" : type === "smart" ? "Smart object" : "Annotation";
2254
+ return type === "pixel" ? "Pixel layer" : type === "adjustment" ? "Adjustment layer" : type === "group" ? "Group" : type === "smart" ? "Smart object" : type === "shape" ? "Shape layer" : "Annotation";
2228
2255
  }
2229
2256
  function hasSurface(layer) {
2230
- return layer.type === "pixel" || layer.type === "smart";
2257
+ return layer.type === "pixel" || layer.type === "smart" || layer.type === "shape";
2258
+ }
2259
+ function isRenderedLayer(layer) {
2260
+ return layer.type === "smart" || layer.type === "shape";
2261
+ }
2262
+ function shapeLayers(layers) {
2263
+ return flattenLayers(layers).filter((l) => l.type === "shape");
2231
2264
  }
2232
2265
  function smartObjectLayers(layers) {
2233
2266
  return flattenLayers(layers).filter((l) => l.type === "smart");
@@ -2322,8 +2355,11 @@ var PHOTO_PROFILE = {
2322
2355
  contentBlend: (l) => l.type === "annotation" && l.blend === "normal" && l.annotation.blend ? l.annotation.blend : l.blend,
2323
2356
  layerKey: (l) => l.type === "pixel" ? [l.filters ?? null, l.source ?? null] : l.type === "adjustment" ? [l.kind, l.params] : l.type === "annotation" ? l.annotation : void 0,
2324
2357
  pixelFilters: (l) => l.type === "pixel" ? pixelFilterSpec(l) : null,
2325
- /** A smart object draws its rendered pixels (contents → placement → smart filters) like a pixel layer. */
2326
- isPixelLike: (l) => l.type === "smart",
2358
+ /**
2359
+ * A smart object draws its rendered pixels (contents → placement → smart filters) like a pixel
2360
+ * layer, and a shape layer the pixels rendered from its paths.
2361
+ */
2362
+ isPixelLike: (l) => l.type === "smart" || l.type === "shape",
2327
2363
  maskSourcePlane: (source, W, H, sources) => {
2328
2364
  const opts = source;
2329
2365
  const depth = sources.depthMap?.(opts) ?? null;
@@ -2339,163 +2375,534 @@ var LayerCompositor = class extends RasterCompositor {
2339
2375
  }
2340
2376
  };
2341
2377
 
2342
- // src/image-editor/transform.ts
2343
- var FREE_TRANSFORM_MODES = ["scale", "rotate", "skew", "distort", "perspective", "warp"];
2344
- var TRANSFORM_ANCHORS = ["top-left", "top", "top-right", "left", "center", "right", "bottom-left", "bottom", "bottom-right"];
2345
- var ANCHOR_FRACTION = {
2346
- "top-left": [0, 0],
2347
- top: [0.5, 0],
2348
- "top-right": [1, 0],
2349
- left: [0, 0.5],
2350
- center: [0.5, 0.5],
2351
- right: [1, 0.5],
2352
- "bottom-left": [0, 1],
2353
- bottom: [0.5, 1],
2354
- "bottom-right": [1, 1]
2355
- };
2356
- function isTransformAnchor(value) {
2357
- return typeof value === "string" && value in ANCHOR_FRACTION;
2358
- }
2359
- function identityTransform(box) {
2360
- return { box: { ...box }, corners: boxQuad(box) };
2361
- }
2362
- function cornersFromMatrix(box, m) {
2363
- const q2 = boxQuad(box);
2364
- return [applyMatrix(m, q2[0]), applyMatrix(m, q2[1]), applyMatrix(m, q2[2]), applyMatrix(m, q2[3])];
2365
- }
2366
- function matrixFromCorners(box, corners, eps = 1e-6) {
2367
- const [tl, tr, br, bl] = corners;
2368
- const size = 1 + Math.max(box.width, box.height);
2369
- if (Math.abs(tl.x + br.x - tr.x - bl.x) > eps * size || Math.abs(tl.y + br.y - tr.y - bl.y) > eps * size) return null;
2370
- const h = rectToQuad(box, corners);
2371
- return h ? affineFromHomography(h, 1e-9) : null;
2372
- }
2373
- function transformHomography(geometry) {
2374
- const h = rectToQuad(geometry.box, geometry.corners);
2375
- if (!h) return null;
2376
- return homographyKeepsQuad(h, boxQuad(geometry.box)) ? h : null;
2377
- }
2378
- function hasWarp(geometry) {
2379
- const w = geometry.warp;
2380
- if (!w) return false;
2381
- const id = identityWarpMesh(geometry.box, w.us.length - 1, w.vs.length - 1);
2382
- if (id.points.length !== w.points.length || id.us.some((u, i) => Math.abs(u - w.us[i]) > 1e-12) || id.vs.some((v, i) => Math.abs(v - w.vs[i]) > 1e-12)) return true;
2383
- return w.points.some((p, i) => Math.abs(p.x - id.points[i].x) > 1e-6 || Math.abs(p.y - id.points[i].y) > 1e-6);
2384
- }
2385
- function isIdentityTransform(geometry) {
2386
- if (hasWarp(geometry)) return false;
2387
- const q2 = boxQuad(geometry.box);
2388
- return geometry.corners.every((c, i) => Math.abs(c.x - q2[i].x) < 1e-6 && Math.abs(c.y - q2[i].y) < 1e-6);
2389
- }
2390
- function transformMap(geometry) {
2391
- const h = transformHomography(geometry);
2392
- if (!h) return null;
2393
- const { box } = geometry;
2394
- const warp = hasWarp(geometry) ? geometry.warp : null;
2395
- if (!warp) return (p) => applyHomography(h, p);
2396
- return (p) => applyHomography(h, evalWarpMesh(warp, (p.x - box.x) / box.width, (p.y - box.y) / box.height));
2397
- }
2398
- function transformedPoint(geometry, fx, fy) {
2399
- const f = transformMap(geometry);
2400
- const { box } = geometry;
2401
- return f ? f({ x: box.x + fx * box.width, y: box.y + fy * box.height }) : null;
2402
- }
2403
- function anchorPoint(geometry, anchor) {
2404
- const [fx, fy] = ANCHOR_FRACTION[anchor];
2405
- const h = rectToQuad(geometry.box, geometry.corners);
2406
- const p = { x: geometry.box.x + fx * geometry.box.width, y: geometry.box.y + fy * geometry.box.height };
2407
- return (h && applyHomography(h, p)) ?? p;
2408
- }
2409
- function transformedBox(geometry) {
2410
- const f = transformMap(geometry);
2411
- if (!f) return null;
2412
- const { box } = geometry;
2413
- const pts = [];
2414
- const n = hasWarp(geometry) ? 16 : 1;
2415
- for (let i = 0; i <= n; i++) {
2416
- const t = i / n;
2417
- for (const [fx, fy] of [[t, 0], [1, t], [1 - t, 1], [0, 1 - t]]) {
2418
- const p = f({ x: box.x + fx * box.width, y: box.y + fy * box.height });
2419
- if (!p) return null;
2420
- pts.push(p);
2378
+ // src/image-editor/layers/vector-raster.ts
2379
+ var FLATNESS = 0.01;
2380
+ function flattenPath(path, tolerance = FLATNESS) {
2381
+ const out = [];
2382
+ let pts = [];
2383
+ let cx = 0;
2384
+ let cy = 0;
2385
+ let sx = 0;
2386
+ let sy = 0;
2387
+ const flush = () => {
2388
+ if (pts.length >= 6) out.push(Float64Array.from(pts));
2389
+ pts = [];
2390
+ };
2391
+ const steps = (dd, degree) => Math.max(1, Math.min(1024, Math.ceil(Math.sqrt(degree * (degree - 1) * dd / (8 * tolerance)))));
2392
+ for (const c of path) {
2393
+ switch (c.type) {
2394
+ case "M":
2395
+ flush();
2396
+ cx = sx = c.x;
2397
+ cy = sy = c.y;
2398
+ pts.push(cx, cy);
2399
+ break;
2400
+ case "L":
2401
+ if (!pts.length) pts.push(cx, cy);
2402
+ cx = c.x;
2403
+ cy = c.y;
2404
+ pts.push(cx, cy);
2405
+ break;
2406
+ case "Q": {
2407
+ if (!pts.length) pts.push(cx, cy);
2408
+ const n = steps(Math.hypot(cx - 2 * c.x1 + c.x, cy - 2 * c.y1 + c.y), 2);
2409
+ for (let k = 1; k <= n; k++) {
2410
+ const t = k / n;
2411
+ const u = 1 - t;
2412
+ pts.push(u * u * cx + 2 * u * t * c.x1 + t * t * c.x, u * u * cy + 2 * u * t * c.y1 + t * t * c.y);
2413
+ }
2414
+ cx = c.x;
2415
+ cy = c.y;
2416
+ break;
2417
+ }
2418
+ case "C": {
2419
+ if (!pts.length) pts.push(cx, cy);
2420
+ const dd = Math.max(Math.hypot(cx - 2 * c.x1 + c.x2, cy - 2 * c.y1 + c.y2), Math.hypot(c.x1 - 2 * c.x2 + c.x, c.y1 - 2 * c.y2 + c.y));
2421
+ const n = steps(dd, 3);
2422
+ for (let k = 1; k <= n; k++) {
2423
+ const t = k / n;
2424
+ const u = 1 - t;
2425
+ const a = u * u * u;
2426
+ const b = 3 * u * u * t;
2427
+ const d = 3 * u * t * t;
2428
+ const e = t * t * t;
2429
+ pts.push(a * cx + b * c.x1 + d * c.x2 + e * c.x, a * cy + b * c.y1 + d * c.y2 + e * c.y);
2430
+ }
2431
+ cx = c.x;
2432
+ cy = c.y;
2433
+ break;
2434
+ }
2435
+ case "Z":
2436
+ flush();
2437
+ cx = sx;
2438
+ cy = sy;
2439
+ break;
2421
2440
  }
2422
2441
  }
2423
- const xs = pts.map((p) => p.x);
2424
- const ys = pts.map((p) => p.y);
2425
- const x = Math.min(...xs);
2426
- const y = Math.min(...ys);
2427
- return { x, y, width: Math.max(...xs) - x, height: Math.max(...ys) - y };
2428
- }
2429
- function referencePoint(box, reference) {
2430
- if (reference && typeof reference === "object") return { x: reference.x, y: reference.y };
2431
- const [fx, fy] = ANCHOR_FRACTION[reference ?? "center"];
2432
- return { x: box.x + fx * box.width, y: box.y + fy * box.height };
2433
- }
2434
- function matrixFromParams(box, params) {
2435
- const sx = (params.scaleX ?? 100) / 100;
2436
- const sy = (params.scaleY ?? 100) / 100;
2437
- const tx = Math.tan((params.skewX ?? 0) * Math.PI / 180);
2438
- const ty = Math.tan((params.skewY ?? 0) * Math.PI / 180);
2439
- const ref = referencePoint(box, params.reference);
2440
- const linear = multiplyMatrices(multiplyMatrices([1, ty, tx, 1, 0, 0], [sx, 0, 0, sy, 0, 0]), rotateMatrix(params.rotate ?? 0));
2441
- return multiplyMatrices(matrixAbout(linear, ref), translateMatrix(params.dx ?? 0, params.dy ?? 0));
2442
- }
2443
- function describeTransform(geometry, reference = "center") {
2444
- const m = hasWarp(geometry) ? null : matrixFromCorners(geometry.box, geometry.corners);
2445
- const ref = referencePoint(geometry.box, reference);
2446
- const bounds = transformedBox(geometry);
2447
- if (!m) {
2448
- const h = rectToQuad(geometry.box, geometry.corners);
2449
- const at2 = (h && applyHomography(h, ref)) ?? ref;
2450
- return { affine: false, scaleX: 100, scaleY: 100, rotate: 0, skewX: 0, x: at2.x, y: at2.y, bounds, corners: geometry.corners };
2442
+ flush();
2443
+ return out.filter((p) => p.every(Number.isFinite));
2444
+ }
2445
+ function flatBounds(flat) {
2446
+ let x0 = Infinity;
2447
+ let y0 = Infinity;
2448
+ let x1 = -Infinity;
2449
+ let y1 = -Infinity;
2450
+ for (const p of flat) {
2451
+ for (let i = 0; i < p.length; i += 2) {
2452
+ if (p[i] < x0) x0 = p[i];
2453
+ if (p[i] > x1) x1 = p[i];
2454
+ if (p[i + 1] < y0) y0 = p[i + 1];
2455
+ if (p[i + 1] > y1) y1 = p[i + 1];
2456
+ }
2457
+ }
2458
+ if (x0 === Infinity) return null;
2459
+ const x = Math.floor(x0);
2460
+ const y = Math.floor(y0);
2461
+ return { x, y, width: Math.ceil(x1) - x, height: Math.ceil(y1) - y };
2462
+ }
2463
+ function drawLine(a, w, h, x0, y0, x1, y1) {
2464
+ if (y0 === y1) return;
2465
+ let dir = 1;
2466
+ if (y0 > y1) {
2467
+ dir = -1;
2468
+ let t = x0;
2469
+ x0 = x1;
2470
+ x1 = t;
2471
+ t = y0;
2472
+ y0 = y1;
2473
+ y1 = t;
2474
+ }
2475
+ if (y1 <= 0 || y0 >= h) return;
2476
+ const dxdy = (x1 - x0) / (y1 - y0);
2477
+ let x = x0;
2478
+ if (y0 < 0) x -= y0 * dxdy;
2479
+ const rowStart = Math.max(0, Math.floor(y0));
2480
+ const rowEnd = Math.min(h, Math.ceil(y1));
2481
+ for (let y = rowStart; y < rowEnd; y++) {
2482
+ const line = y * w;
2483
+ const dy = Math.min(y + 1, y1) - Math.max(y, y0);
2484
+ const xnext = x + dxdy * dy;
2485
+ const d = dy * dir;
2486
+ const xa = x < xnext ? x : xnext;
2487
+ const xb = x < xnext ? xnext : x;
2488
+ const xaFloor = Math.floor(xa);
2489
+ const xai = xaFloor;
2490
+ const xbCeil = Math.ceil(xb);
2491
+ const xbi = xbCeil;
2492
+ if (xbi <= xai + 1) {
2493
+ const xmf = 0.5 * (x + xnext) - xaFloor;
2494
+ a[line + xai] += d - d * xmf;
2495
+ a[line + xai + 1] += d * xmf;
2496
+ } else {
2497
+ const s = 1 / (xb - xa);
2498
+ const xaf = xa - xaFloor;
2499
+ const a0 = 0.5 * s * (1 - xaf) * (1 - xaf);
2500
+ const xbf = xb - xbCeil + 1;
2501
+ const am = 0.5 * s * xbf * xbf;
2502
+ a[line + xai] += d * a0;
2503
+ if (xbi === xai + 2) a[line + xai + 1] += d * (1 - a0 - am);
2504
+ else {
2505
+ const a1 = s * (1.5 - xaf);
2506
+ a[line + xai + 1] += d * (a1 - a0);
2507
+ for (let xi = xai + 2; xi < xbi - 1; xi++) a[line + xi] += d * s;
2508
+ const a2 = a1 + (xbi - xai - 3) * s;
2509
+ a[line + xbi - 1] += d * (1 - a2 - am);
2510
+ }
2511
+ a[line + xbi] += d * am;
2512
+ }
2513
+ x = xnext;
2514
+ }
2515
+ }
2516
+ function drawClippedLine(a, w, h, x0, y0, x1, y1) {
2517
+ if (y0 === y1) return;
2518
+ if (y0 <= 0 && y1 <= 0 || y0 >= h && y1 >= h) return;
2519
+ if (x0 >= 0 && x0 <= w && x1 >= 0 && x1 <= w) return drawLine(a, w, h, x0, y0, x1, y1);
2520
+ const ts = [0, 1];
2521
+ const dx = x1 - x0;
2522
+ if (dx !== 0) {
2523
+ for (const edge of [0, w]) {
2524
+ const t = (edge - x0) / dx;
2525
+ if (t > 0 && t < 1) ts.push(t);
2526
+ }
2527
+ }
2528
+ ts.sort((p, q2) => p - q2);
2529
+ const clamp4 = (v) => v < 0 ? 0 : v > w ? w : v;
2530
+ for (let i = 0; i + 1 < ts.length; i++) {
2531
+ const ta = ts[i];
2532
+ const tb = ts[i + 1];
2533
+ drawLine(a, w, h, clamp4(x0 + dx * ta), y0 + (y1 - y0) * ta, clamp4(x0 + dx * tb), y0 + (y1 - y0) * tb);
2534
+ }
2535
+ }
2536
+ function ruleCoverage(v, rule) {
2537
+ const m = v < 0 ? -v : v;
2538
+ if (rule === "nonzero") return m >= 1 ? 1 : m;
2539
+ const f = m % 2;
2540
+ return f > 1 ? 2 - f : f;
2541
+ }
2542
+ function rasterizeCoverage(flat, rect, rule = "nonzero") {
2543
+ const w = rect.width;
2544
+ const h = rect.height;
2545
+ if (w <= 0 || h <= 0 || !flat.length) return null;
2546
+ const a = new Float64Array(w * h + w + 4);
2547
+ const ox = rect.x;
2548
+ const oy = rect.y;
2549
+ let touched = false;
2550
+ for (const p of flat) {
2551
+ const n = p.length;
2552
+ for (let i = 0; i < n; i += 2) {
2553
+ const j = i + 2 < n ? i + 2 : 0;
2554
+ const ya = p[i + 1] - oy;
2555
+ const yb = p[j + 1] - oy;
2556
+ if (ya <= 0 && yb <= 0 || ya >= h && yb >= h || ya === yb) continue;
2557
+ drawClippedLine(a, w, h, p[i] - ox, ya, p[j] - ox, yb);
2558
+ touched = true;
2559
+ }
2560
+ }
2561
+ if (!touched) return null;
2562
+ const out = new Float32Array(w * h);
2563
+ let acc = 0;
2564
+ let any = false;
2565
+ for (let i = 0; i < out.length; i++) {
2566
+ acc += a[i];
2567
+ const c = ruleCoverage(acc, rule);
2568
+ if (c > 1e-6) {
2569
+ out[i] = c;
2570
+ any = true;
2571
+ }
2451
2572
  }
2452
- const [a, b, c, d] = m;
2453
- const sxAbs = Math.hypot(a, b);
2454
- const rot = Math.atan2(b, a) * 180 / Math.PI;
2455
- const shear = sxAbs ? (a * c + b * d) / sxAbs : 0;
2456
- const sy = sxAbs ? (a * d - b * c) / sxAbs : 0;
2457
- const skew = sxAbs ? Math.atan(shear / sxAbs) * 180 / Math.PI : 0;
2458
- const at = applyMatrix(m, ref);
2459
- const clean = (v) => Math.abs(v) < 1e-9 ? 0 : v;
2460
- return {
2461
- affine: true,
2462
- scaleX: clean(sxAbs * 100),
2463
- scaleY: clean(sy * 100),
2464
- rotate: clean(rot === -180 ? 180 : rot),
2465
- skewX: clean(skew),
2466
- x: at.x,
2467
- y: at.y,
2468
- bounds,
2469
- corners: geometry.corners
2470
- };
2471
- }
2472
- function applyParams(geometry, params) {
2473
- const ref = params.reference && typeof params.reference === "object" ? params.reference : anchorPoint(geometry, params.reference ?? "center");
2474
- const m = matrixFromParams({ x: ref.x, y: ref.y, width: 0, height: 0 }, { ...params, reference: ref });
2475
- const c = geometry.corners;
2476
- return [applyMatrix(m, c[0]), applyMatrix(m, c[1]), applyMatrix(m, c[2]), applyMatrix(m, c[3])];
2573
+ return any ? out : null;
2477
2574
  }
2478
- function flipCorners(geometry, axis) {
2479
- const c = anchorPoint(geometry, "center");
2480
- const m = axis === "horizontal" ? [-1, 0, 0, 1, 2 * c.x, 0] : [1, 0, 0, -1, 0, 2 * c.y];
2481
- const q2 = geometry.corners;
2482
- return [applyMatrix(m, q2[0]), applyMatrix(m, q2[1]), applyMatrix(m, q2[2]), applyMatrix(m, q2[3])];
2575
+ var SUB_ROWS = 16;
2576
+ function rasterizeCoverageSampled(flat, rect, rule = "nonzero") {
2577
+ const w = rect.width;
2578
+ const h = rect.height;
2579
+ if (w <= 0 || h <= 0 || !flat.length) return null;
2580
+ let n = 0;
2581
+ for (const p of flat) n += p.length >> 1;
2582
+ const ey0 = new Float64Array(n);
2583
+ const ey1 = new Float64Array(n);
2584
+ const ex0 = new Float64Array(n);
2585
+ const eslope = new Float64Array(n);
2586
+ const edir = new Int8Array(n);
2587
+ let count = 0;
2588
+ for (const p of flat) {
2589
+ const len = p.length;
2590
+ for (let i = 0; i < len; i += 2) {
2591
+ const j = i + 2 < len ? i + 2 : 0;
2592
+ let xa = p[i] - rect.x;
2593
+ let ya = p[i + 1] - rect.y;
2594
+ let xb = p[j] - rect.x;
2595
+ let yb = p[j + 1] - rect.y;
2596
+ if (ya === yb) continue;
2597
+ let dir = 1;
2598
+ if (ya > yb) {
2599
+ dir = -1;
2600
+ let t = xa;
2601
+ xa = xb;
2602
+ xb = t;
2603
+ t = ya;
2604
+ ya = yb;
2605
+ yb = t;
2606
+ }
2607
+ if (yb <= 0 || ya >= h) continue;
2608
+ ey0[count] = ya;
2609
+ ey1[count] = yb;
2610
+ ex0[count] = xa;
2611
+ eslope[count] = (xb - xa) / (yb - ya);
2612
+ edir[count] = dir;
2613
+ count++;
2614
+ }
2615
+ }
2616
+ if (!count) return null;
2617
+ const order = Array.from({ length: count }, (_, i) => i).sort((a, b) => ey0[a] - ey0[b]);
2618
+ const out = new Float32Array(w * h);
2619
+ const diff = new Float32Array(w + 1);
2620
+ let active = [];
2621
+ let next = 0;
2622
+ const xs = [];
2623
+ const ds = [];
2624
+ const idx = [];
2625
+ const f = 1 / SUB_ROWS;
2626
+ let any = false;
2627
+ const firstRow = Math.max(0, Math.floor(ey0[order[0]]));
2628
+ for (let y = firstRow; y < h; y++) {
2629
+ if (next >= count && !active.length) break;
2630
+ const row = y * w;
2631
+ let rowTouched = false;
2632
+ for (let s = 0; s < SUB_ROWS; s++) {
2633
+ const sy = y + (s + 0.5) * f;
2634
+ while (next < count && ey0[order[next]] <= sy) active.push(order[next++]);
2635
+ if (!active.length) continue;
2636
+ active = active.filter((e) => ey1[e] > sy);
2637
+ xs.length = 0;
2638
+ ds.length = 0;
2639
+ idx.length = 0;
2640
+ for (const e of active) {
2641
+ if (ey0[e] > sy) continue;
2642
+ idx.push(xs.length);
2643
+ xs.push(ex0[e] + (sy - ey0[e]) * eslope[e]);
2644
+ ds.push(edir[e]);
2645
+ }
2646
+ if (xs.length < 2) continue;
2647
+ idx.sort((a, b) => xs[a] - xs[b]);
2648
+ let wind = 0;
2649
+ for (let k = 0; k + 1 < idx.length; k++) {
2650
+ wind += ds[idx[k]];
2651
+ const inside = rule === "nonzero" ? wind !== 0 : (wind & 1) !== 0;
2652
+ if (!inside) continue;
2653
+ let xa = xs[idx[k]];
2654
+ let xb = xs[idx[k + 1]];
2655
+ if (xb <= 0 || xa >= w || xb <= xa) continue;
2656
+ if (xa < 0) xa = 0;
2657
+ if (xb > w) xb = w;
2658
+ const ia = Math.floor(xa);
2659
+ const ib = Math.floor(xb);
2660
+ rowTouched = true;
2661
+ if (ia === ib) {
2662
+ out[row + ia] += (xb - xa) * f;
2663
+ continue;
2664
+ }
2665
+ out[row + ia] += (ia + 1 - xa) * f;
2666
+ diff[ia + 1] += f;
2667
+ diff[ib] -= f;
2668
+ if (ib < w) out[row + ib] += (xb - ib) * f;
2669
+ }
2670
+ }
2671
+ if (!rowTouched) continue;
2672
+ let run = 0;
2673
+ for (let x = 0; x < w; x++) {
2674
+ run += diff[x];
2675
+ diff[x] = 0;
2676
+ const v = out[row + x] + run;
2677
+ out[row + x] = v > 1 ? 1 : v < 1e-6 ? 0 : v;
2678
+ if (out[row + x] > 0) any = true;
2679
+ }
2680
+ diff[w] = 0;
2681
+ }
2682
+ return any ? out : null;
2683
+ }
2684
+ function combineCoverage(a, b, op, n) {
2685
+ if (op === "combine") {
2686
+ if (!a) return b;
2687
+ if (!b) return a;
2688
+ const out2 = new Float32Array(n);
2689
+ for (let i = 0; i < n; i++) out2[i] = a[i] + b[i] - a[i] * b[i];
2690
+ return out2;
2691
+ }
2692
+ if (op === "subtract") {
2693
+ if (!a || !b) return a;
2694
+ const out2 = new Float32Array(n);
2695
+ for (let i = 0; i < n; i++) out2[i] = a[i] * (1 - b[i]);
2696
+ return out2;
2697
+ }
2698
+ if (op === "intersect") {
2699
+ if (!a || !b) return null;
2700
+ const out2 = new Float32Array(n);
2701
+ for (let i = 0; i < n; i++) out2[i] = a[i] * b[i];
2702
+ return out2;
2703
+ }
2704
+ if (!a) return b;
2705
+ if (!b) return a;
2706
+ const out = new Float32Array(n);
2707
+ for (let i = 0; i < n; i++) out[i] = a[i] + b[i] - 2 * a[i] * b[i];
2708
+ return out;
2483
2709
  }
2484
- function boxPointOf(geometry, p) {
2485
- const m = matrixFromCorners(geometry.box, geometry.corners);
2486
- if (m) return applyMatrix(invertMatrix(m), p);
2710
+
2711
+ // src/image-editor/layers/css-color.ts
2712
+ var NAMED = {
2713
+ aliceblue: "f0f8ff",
2714
+ antiquewhite: "faebd7",
2715
+ aqua: "00ffff",
2716
+ aquamarine: "7fffd4",
2717
+ azure: "f0ffff",
2718
+ beige: "f5f5dc",
2719
+ bisque: "ffe4c4",
2720
+ black: "000000",
2721
+ blanchedalmond: "ffebcd",
2722
+ blue: "0000ff",
2723
+ blueviolet: "8a2be2",
2724
+ brown: "a52a2a",
2725
+ burlywood: "deb887",
2726
+ cadetblue: "5f9ea0",
2727
+ chartreuse: "7fff00",
2728
+ chocolate: "d2691e",
2729
+ coral: "ff7f50",
2730
+ cornflowerblue: "6495ed",
2731
+ cornsilk: "fff8dc",
2732
+ crimson: "dc143c",
2733
+ cyan: "00ffff",
2734
+ darkblue: "00008b",
2735
+ darkcyan: "008b8b",
2736
+ darkgoldenrod: "b8860b",
2737
+ darkgray: "a9a9a9",
2738
+ darkgreen: "006400",
2739
+ darkgrey: "a9a9a9",
2740
+ darkkhaki: "bdb76b",
2741
+ darkmagenta: "8b008b",
2742
+ darkolivegreen: "556b2f",
2743
+ darkorange: "ff8c00",
2744
+ darkorchid: "9932cc",
2745
+ darkred: "8b0000",
2746
+ darksalmon: "e9967a",
2747
+ darkseagreen: "8fbc8f",
2748
+ darkslateblue: "483d8b",
2749
+ darkslategray: "2f4f4f",
2750
+ darkslategrey: "2f4f4f",
2751
+ darkturquoise: "00ced1",
2752
+ darkviolet: "9400d3",
2753
+ deeppink: "ff1493",
2754
+ deepskyblue: "00bfff",
2755
+ dimgray: "696969",
2756
+ dimgrey: "696969",
2757
+ dodgerblue: "1e90ff",
2758
+ firebrick: "b22222",
2759
+ floralwhite: "fffaf0",
2760
+ forestgreen: "228b22",
2761
+ fuchsia: "ff00ff",
2762
+ gainsboro: "dcdcdc",
2763
+ ghostwhite: "f8f8ff",
2764
+ gold: "ffd700",
2765
+ goldenrod: "daa520",
2766
+ gray: "808080",
2767
+ green: "008000",
2768
+ greenyellow: "adff2f",
2769
+ grey: "808080",
2770
+ honeydew: "f0fff0",
2771
+ hotpink: "ff69b4",
2772
+ indianred: "cd5c5c",
2773
+ indigo: "4b0082",
2774
+ ivory: "fffff0",
2775
+ khaki: "f0e68c",
2776
+ lavender: "e6e6fa",
2777
+ lavenderblush: "fff0f5",
2778
+ lawngreen: "7cfc00",
2779
+ lemonchiffon: "fffacd",
2780
+ lightblue: "add8e6",
2781
+ lightcoral: "f08080",
2782
+ lightcyan: "e0ffff",
2783
+ lightgoldenrodyellow: "fafad2",
2784
+ lightgray: "d3d3d3",
2785
+ lightgreen: "90ee90",
2786
+ lightgrey: "d3d3d3",
2787
+ lightpink: "ffb6c1",
2788
+ lightsalmon: "ffa07a",
2789
+ lightseagreen: "20b2aa",
2790
+ lightskyblue: "87cefa",
2791
+ lightslategray: "778899",
2792
+ lightslategrey: "778899",
2793
+ lightsteelblue: "b0c4de",
2794
+ lightyellow: "ffffe0",
2795
+ lime: "00ff00",
2796
+ limegreen: "32cd32",
2797
+ linen: "faf0e6",
2798
+ magenta: "ff00ff",
2799
+ maroon: "800000",
2800
+ mediumaquamarine: "66cdaa",
2801
+ mediumblue: "0000cd",
2802
+ mediumorchid: "ba55d3",
2803
+ mediumpurple: "9370db",
2804
+ mediumseagreen: "3cb371",
2805
+ mediumslateblue: "7b68ee",
2806
+ mediumspringgreen: "00fa9a",
2807
+ mediumturquoise: "48d1cc",
2808
+ mediumvioletred: "c71585",
2809
+ midnightblue: "191970",
2810
+ mintcream: "f5fffa",
2811
+ mistyrose: "ffe4e1",
2812
+ moccasin: "ffe4b5",
2813
+ navajowhite: "ffdead",
2814
+ navy: "000080",
2815
+ oldlace: "fdf5e6",
2816
+ olive: "808000",
2817
+ olivedrab: "6b8e23",
2818
+ orange: "ffa500",
2819
+ orangered: "ff4500",
2820
+ orchid: "da70d6",
2821
+ palegoldenrod: "eee8aa",
2822
+ palegreen: "98fb98",
2823
+ paleturquoise: "afeeee",
2824
+ palevioletred: "db7093",
2825
+ papayawhip: "ffefd5",
2826
+ peachpuff: "ffdab9",
2827
+ peru: "cd853f",
2828
+ pink: "ffc0cb",
2829
+ plum: "dda0dd",
2830
+ powderblue: "b0e0e6",
2831
+ purple: "800080",
2832
+ rebeccapurple: "663399",
2833
+ red: "ff0000",
2834
+ rosybrown: "bc8f8f",
2835
+ royalblue: "4169e1",
2836
+ saddlebrown: "8b4513",
2837
+ salmon: "fa8072",
2838
+ sandybrown: "f4a460",
2839
+ seagreen: "2e8b57",
2840
+ seashell: "fff5ee",
2841
+ sienna: "a0522d",
2842
+ silver: "c0c0c0",
2843
+ skyblue: "87ceeb",
2844
+ slateblue: "6a5acd",
2845
+ slategray: "708090",
2846
+ slategrey: "708090",
2847
+ snow: "fffafa",
2848
+ springgreen: "00ff7f",
2849
+ steelblue: "4682b4",
2850
+ tan: "d2b48c",
2851
+ teal: "008080",
2852
+ thistle: "d8bfd8",
2853
+ tomato: "ff6347",
2854
+ turquoise: "40e0d0",
2855
+ violet: "ee82ee",
2856
+ wheat: "f5deb3",
2857
+ white: "ffffff",
2858
+ whitesmoke: "f5f5f5",
2859
+ yellow: "ffff00",
2860
+ yellowgreen: "9acd32"
2861
+ };
2862
+ var hex2 = (v) => Math.round(Math.max(0, Math.min(255, v))).toString(16).padStart(2, "0");
2863
+ function channel(s) {
2864
+ return s.endsWith("%") ? parseFloat(s) / 100 * 255 : parseFloat(s);
2865
+ }
2866
+ function alphaOf(s) {
2867
+ if (s === void 0) return 1;
2868
+ const v = s.endsWith("%") ? parseFloat(s) / 100 : parseFloat(s);
2869
+ return Math.max(0, Math.min(1, v));
2870
+ }
2871
+ function hslToRgb2(h, s, l) {
2872
+ h = (h % 360 + 360) % 360;
2873
+ const c = (1 - Math.abs(2 * l - 1)) * s;
2874
+ const x = c * (1 - Math.abs(h / 60 % 2 - 1));
2875
+ const m = l - c / 2;
2876
+ const [r, g, b] = h < 60 ? [c, x, 0] : h < 120 ? [x, c, 0] : h < 180 ? [0, c, x] : h < 240 ? [0, x, c] : h < 300 ? [x, 0, c] : [c, 0, x];
2877
+ return [(r + m) * 255, (g + m) * 255, (b + m) * 255];
2878
+ }
2879
+ function parseCssColor(value) {
2880
+ if (typeof value !== "string") return null;
2881
+ const text3 = value.trim().toLowerCase();
2882
+ if (text3 === "transparent") return { hex: "#000000", alpha: 0 };
2883
+ const named = NAMED[text3];
2884
+ if (named) return { hex: `#${named}`, alpha: 1 };
2885
+ let m = /^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/.exec(text3);
2886
+ if (m) {
2887
+ let h = m[1];
2888
+ if (h.length <= 4) h = h.replace(/./g, (c) => c + c);
2889
+ return { hex: `#${h.slice(0, 6)}`, alpha: h.length === 8 ? parseInt(h.slice(6, 8), 16) / 255 : 1 };
2890
+ }
2891
+ const num3 = "([+-]?[\\d.]+%?)";
2892
+ m = new RegExp(`^rgba?\\(\\s*${num3}[\\s,]+${num3}[\\s,]+${num3}(?:\\s*[,/]\\s*${num3})?\\s*\\)$`).exec(text3);
2893
+ if (m) {
2894
+ const rgb = [m[1], m[2], m[3]].map(channel);
2895
+ if (!rgb.every(Number.isFinite)) return null;
2896
+ return { hex: `#${rgb.map(hex2).join("")}`, alpha: alphaOf(m[4]) };
2897
+ }
2898
+ m = new RegExp(`^hsla?\\(\\s*([+-]?[\\d.]+)(?:deg)?[\\s,]+${num3}[\\s,]+${num3}(?:\\s*[,/]\\s*${num3})?\\s*\\)$`).exec(text3);
2899
+ if (m) {
2900
+ const rgb = hslToRgb2(parseFloat(m[1]), parseFloat(m[2]) / 100, parseFloat(m[3]) / 100);
2901
+ if (!rgb.every(Number.isFinite)) return null;
2902
+ return { hex: `#${rgb.map(hex2).join("")}`, alpha: alphaOf(m[4]) };
2903
+ }
2487
2904
  return null;
2488
2905
  }
2489
- function presetWarp(box, preset) {
2490
- return warpPresetMesh(preset, box);
2491
- }
2492
- function isValidWarp(mesh) {
2493
- return isValidWarpMesh(mesh);
2494
- }
2495
- function geometryHomography(geometry) {
2496
- const m = matrixFromCorners(geometry.box, geometry.corners);
2497
- return m ? homographyFromAffine(m) : transformHomography(geometry);
2498
- }
2499
2906
 
2500
2907
  // src/image-editor/layers/ops.ts
2501
2908
  var LayerOperationError = class extends Error {
@@ -2555,6 +2962,7 @@ function withNewIds(layer) {
2555
2962
  const next = { ...layer, id: newLayerId(layer.type === "adjustment" ? "adj" : layer.type) };
2556
2963
  if (next.type === "group") next.children = next.children.map(withNewIds);
2557
2964
  if (next.type === "annotation") next.annotation = { ...next.annotation, id: next.id };
2965
+ if (next.type === "shape") delete next.annotation;
2558
2966
  if (next.type === "smart" && next.psd?.placed) {
2559
2967
  next.psd = { ...next.psd };
2560
2968
  delete next.psd.placed;
@@ -2584,7 +2992,7 @@ function setLayerProperties(layers, id, props) {
2584
2992
  next.opacity = props.opacity;
2585
2993
  }
2586
2994
  if (props.fillOpacity !== void 0) {
2587
- if (layer.type !== "pixel" && layer.type !== "smart") fail(`fillOpacity applies to pixel layers and smart objects; "${id}" is a ${layer.type} layer.`);
2995
+ if (layer.type !== "pixel" && layer.type !== "smart" && layer.type !== "shape") fail(`fillOpacity applies to pixel, shape and smart object layers; "${id}" is a ${layer.type} layer.`);
2588
2996
  if (!(typeof props.fillOpacity === "number" && props.fillOpacity >= 0 && props.fillOpacity <= 1)) fail("fillOpacity must be a number from 0 to 1.");
2589
2997
  const p = next;
2590
2998
  if (props.fillOpacity === 1) delete p.fillOpacity;
@@ -2733,93 +3141,1204 @@ function removeLayerFilter(layers, id, filterId) {
2733
3141
  else delete next.filters;
2734
3142
  return mapLayer(layers, id, () => next);
2735
3143
  }
2736
- function requireEffectLayer(layers, id, action) {
2737
- const layer = requireLayer(layers, id).layer;
2738
- if (layer.type === "adjustment") fail(`Layer "${layer.name || id}" is an adjustment layer; effects go on pixel, annotation and group layers.`);
2739
- requireUnlocked(layer, "all", action);
2740
- return layer;
3144
+ function requireEffectLayer(layers, id, action) {
3145
+ const layer = requireLayer(layers, id).layer;
3146
+ if (layer.type === "adjustment") fail(`Layer "${layer.name || id}" is an adjustment layer; effects go on pixel, annotation and group layers.`);
3147
+ requireUnlocked(layer, "all", action);
3148
+ return layer;
3149
+ }
3150
+ function layerEffectsOf(layer) {
3151
+ return layer.effects ?? { enabled: true, items: [] };
3152
+ }
3153
+ function guard(fn) {
3154
+ try {
3155
+ return fn();
3156
+ } catch (e) {
3157
+ if (e instanceof LayerEffectError) fail(e.message);
3158
+ throw e;
3159
+ }
3160
+ }
3161
+ function setLayerEffects(layers, id, effects) {
3162
+ const layer = requireEffectLayer(layers, id, "change its effects");
3163
+ const next = { ...layer };
3164
+ if (effects && (effects.items.length || effects.psd || effects.scale !== void 0 || !effects.enabled)) {
3165
+ guard(() => validateLayerEffects(effects));
3166
+ next.effects = effects;
3167
+ } else delete next.effects;
3168
+ return mapLayer(layers, id, () => next);
3169
+ }
3170
+ function addLayerEffect(layers, id, kind, params = {}, index) {
3171
+ if (!isLayerEffectKind(kind)) fail(`Unknown effect kind "${String(kind)}". Kinds: ${LAYER_EFFECT_KINDS.join(", ")}.`);
3172
+ const layer = requireEffectLayer(layers, id, "add effects");
3173
+ const current = layerEffectsOf(layer);
3174
+ const ids = new Set(current.items.map((e) => e.id));
3175
+ let effectId = newEffectId();
3176
+ while (ids.has(effectId)) effectId = newEffectId();
3177
+ const { id: _ignored, kind: _kind, ...rest } = params;
3178
+ const effect = guard(() => mergeLayerEffect(defaultLayerEffect(kind, effectId), rest));
3179
+ const items = [...current.items];
3180
+ const at = index === void 0 ? items.length : Math.max(0, Math.min(items.length, Math.floor(index)));
3181
+ items.splice(at, 0, effect);
3182
+ return { layers: setLayerEffects(layers, id, { ...current, items }), effect };
3183
+ }
3184
+ function requireEffect(layer, effectId) {
3185
+ const items = layer.effects?.items ?? [];
3186
+ const i = items.findIndex((e) => e.id === effectId);
3187
+ if (i === -1) fail(`Layer "${layer.name || layer.id}" has no effect "${effectId}". Effects: ${items.map((e) => `${e.id} (${e.kind})`).join(", ") || "none"}; call list_layer_effects.`);
3188
+ return i;
3189
+ }
3190
+ function updateLayerEffect(layers, id, effectId, changes) {
3191
+ const layer = requireEffectLayer(layers, id, "change its effects");
3192
+ const i = requireEffect(layer, effectId);
3193
+ const current = layer.effects;
3194
+ const items = [...current.items];
3195
+ items[i] = guard(() => mergeLayerEffect(items[i], changes));
3196
+ return setLayerEffects(layers, id, { ...current, items });
3197
+ }
3198
+ function removeLayerEffect(layers, id, effectId) {
3199
+ const layer = requireEffectLayer(layers, id, "change its effects");
3200
+ requireEffect(layer, effectId);
3201
+ const current = layer.effects;
3202
+ return setLayerEffects(layers, id, { ...current, items: current.items.filter((e) => e.id !== effectId) });
3203
+ }
3204
+ function setLayerEffectsEnabled(layers, id, enabled, scale) {
3205
+ const layer = requireEffectLayer(layers, id, "change its effects");
3206
+ if (scale !== void 0 && !(Number.isFinite(scale) && scale >= 0.01 && scale <= 10)) fail(`scale must be from 0.01 to 10 (1 = 100 %), got ${scale}.`);
3207
+ const current = layerEffectsOf(layer);
3208
+ const next = { ...current, enabled };
3209
+ if (scale !== void 0) {
3210
+ if (scale === 1) delete next.scale;
3211
+ else next.scale = scale;
3212
+ }
3213
+ return setLayerEffects(layers, id, next);
3214
+ }
3215
+ function parseBlend(value, allowPassThrough = false) {
3216
+ if (value === void 0) return void 0;
3217
+ if (value === "pass-through" && allowPassThrough) return value;
3218
+ if (!isBlendMode(value)) fail(`Unknown blend mode "${String(value)}".`);
3219
+ return value;
3220
+ }
3221
+ function unionRect(a, b) {
3222
+ if (!a) return b;
3223
+ if (!b) return a;
3224
+ const x = Math.min(a.x, b.x);
3225
+ const y = Math.min(a.y, b.y);
3226
+ return { x, y, width: Math.max(a.x + a.width, b.x + b.width) - x, height: Math.max(a.y + a.height, b.y + b.height) - y };
3227
+ }
3228
+
3229
+ // src/image-editor/layers/shape.ts
3230
+ var SHAPE_RENDER_MARGIN = MAX_EFFECT_REACH;
3231
+ var DEFAULT_MITER_LIMIT = 100;
3232
+ var fail2 = (message) => {
3233
+ throw new LayerOperationError(message);
3234
+ };
3235
+ var orientation = (anchors) => {
3236
+ let a = 0;
3237
+ for (let i = 0; i < anchors.length; i++) {
3238
+ const p = anchors[i];
3239
+ const q2 = anchors[(i + 1) % anchors.length];
3240
+ a += p.x * q2.y - q2.x * p.y;
3241
+ }
3242
+ return a;
3243
+ };
3244
+ function polygon(points) {
3245
+ const anchors = orientation(points) < 0 ? [...points].reverse() : points;
3246
+ return { closed: true, anchors: anchors.map((p) => ({ x: p.x, y: p.y })) };
3247
+ }
3248
+ function arrowheadPolygon(head, tip, dir) {
3249
+ const len = Math.max(0, head.length);
3250
+ const hw = Math.max(0, head.width) / 2;
3251
+ if (!(len > 0) || !(hw > 0)) return null;
3252
+ const n = { x: -dir.y, y: dir.x };
3253
+ const base = { x: tip.x - dir.x * len, y: tip.y - dir.y * len };
3254
+ const c = Math.max(-0.5, Math.min(0.5, head.concavity ?? 0));
3255
+ const pts = [tip, { x: base.x + n.x * hw, y: base.y + n.y * hw }];
3256
+ if (c) pts.push({ x: base.x + dir.x * len * c, y: base.y + dir.y * len * c });
3257
+ pts.push({ x: base.x - n.x * hw, y: base.y - n.y * hw });
3258
+ return polygon(pts);
3259
+ }
3260
+ function mapAnchor(a, f) {
3261
+ const p = f(a);
3262
+ const out = { x: p.x, y: p.y };
3263
+ if (a.in) out.in = f(a.in);
3264
+ if (a.out) out.out = f(a.out);
3265
+ if (a.type) out.type = a.type;
3266
+ if (a.corner) out.corner = { ...a.corner };
3267
+ return out;
3268
+ }
3269
+ function mapSubpaths(subpaths, f) {
3270
+ return expandVectorCorners(subpaths).map((sp) => ({ closed: sp.closed, anchors: sp.anchors.map((a) => mapAnchor(a, f)) }));
3271
+ }
3272
+ function liveShapeSubpaths(live) {
3273
+ let out;
3274
+ switch (live.kind) {
3275
+ case "rect": {
3276
+ const x = Math.min(live.x, live.x + live.width);
3277
+ const y = Math.min(live.y, live.y + live.height);
3278
+ const sp = rectSubpath({ x, y, width: Math.abs(live.width), height: Math.abs(live.height) });
3279
+ const r = live.radii ?? [0, 0, 0, 0];
3280
+ out = [{ closed: true, anchors: sp.anchors.map((a, i) => r[i] > 0 ? setAnchorCorner(a, r[i]) : a) }];
3281
+ break;
3282
+ }
3283
+ case "ellipse":
3284
+ out = [ellipseSubpath(normBox(live))];
3285
+ break;
3286
+ case "polygon":
3287
+ out = [polygonSubpath(normBox(live), live.sides)];
3288
+ break;
3289
+ case "star":
3290
+ out = [starSubpath(normBox(live), live.points, Math.max(0, Math.min(1, live.innerRatio)))];
3291
+ break;
3292
+ case "line": {
3293
+ const a = { x: live.x1, y: live.y1 };
3294
+ const b = { x: live.x2, y: live.y2 };
3295
+ const len = Math.hypot(b.x - a.x, b.y - a.y);
3296
+ out = [];
3297
+ if (len > 0) {
3298
+ const d = { x: (b.x - a.x) / len, y: (b.y - a.y) / len };
3299
+ const hw = Math.max(0, live.weight) / 2;
3300
+ if (hw > 0) {
3301
+ const n = { x: -d.y * hw, y: d.x * hw };
3302
+ out.push(polygon([{ x: a.x + n.x, y: a.y + n.y }, { x: b.x + n.x, y: b.y + n.y }, { x: b.x - n.x, y: b.y - n.y }, { x: a.x - n.x, y: a.y - n.y }]));
3303
+ }
3304
+ const end = live.endArrow ? arrowheadPolygon(live.endArrow, b, d) : null;
3305
+ const start = live.startArrow ? arrowheadPolygon(live.startArrow, a, { x: -d.x, y: -d.y }) : null;
3306
+ if (start) out.push(start);
3307
+ if (end) out.push(end);
3308
+ }
3309
+ break;
3310
+ }
3311
+ }
3312
+ const m = live.transform;
3313
+ return m && !isIdentityMatrix(m) ? mapSubpaths(out, (p) => applyMatrix(m, p)) : out;
3314
+ }
3315
+ function normBox(b) {
3316
+ return { x: Math.min(b.x, b.x + b.width), y: Math.min(b.y, b.y + b.height), width: Math.abs(b.width), height: Math.abs(b.height) };
3317
+ }
3318
+ function liveComponent(live, op = "combine") {
3319
+ return { op, subpaths: liveShapeSubpaths(live), live };
3320
+ }
3321
+ var pathCache = /* @__PURE__ */ new WeakMap();
3322
+ function componentPath(c) {
3323
+ let p = pathCache.get(c.subpaths);
3324
+ if (!p) {
3325
+ p = subpathsToBezier(c.subpaths);
3326
+ pathCache.set(c.subpaths, p);
3327
+ }
3328
+ return p;
3329
+ }
3330
+ var BOOLEAN_OF = { combine: "union", subtract: "difference", intersect: "intersection", exclude: "xor" };
3331
+ var outlineCache = /* @__PURE__ */ new WeakMap();
3332
+ function shapeOutline(components, fillRule = "nonzero") {
3333
+ if (components.length === 1) return componentPath(components[0]);
3334
+ let byRule = outlineCache.get(components);
3335
+ if (!byRule) {
3336
+ byRule = /* @__PURE__ */ new Map();
3337
+ outlineCache.set(components, byRule);
3338
+ }
3339
+ const hit = byRule.get(fillRule);
3340
+ if (hit) return hit;
3341
+ let acc = [];
3342
+ components.forEach((c, i) => {
3343
+ const operand = { path: componentPath(c), fillRule };
3344
+ if (i === 0) acc = pathBoolean("union", [operand]).path;
3345
+ else acc = pathBoolean(BOOLEAN_OF[c.op], [{ path: acc, fillRule: "nonzero" }, operand]).path;
3346
+ });
3347
+ byRule.set(fillRule, acc);
3348
+ return acc;
3349
+ }
3350
+ function shapeGeometryBounds(layer) {
3351
+ let b = null;
3352
+ for (const c of layer.components) {
3353
+ const cb = bezierPathBounds(componentPath(c));
3354
+ if (!cb) continue;
3355
+ b = b ? { minX: Math.min(b.minX, cb.minX), minY: Math.min(b.minY, cb.minY), maxX: Math.max(b.maxX, cb.maxX), maxY: Math.max(b.maxY, cb.maxY) } : cb;
3356
+ }
3357
+ return b ? { x: b.minX, y: b.minY, width: b.maxX - b.minX, height: b.maxY - b.minY } : null;
3358
+ }
3359
+ function closedOutline(path) {
3360
+ let open = false;
3361
+ let inSub = false;
3362
+ for (const c of path) {
3363
+ if (c.type === "M") {
3364
+ if (inSub) open = true;
3365
+ inSub = true;
3366
+ } else if (c.type === "Z") inSub = false;
3367
+ }
3368
+ return !open && !inSub;
3369
+ }
3370
+ function strokeArea(path, stroke, double) {
3371
+ const w = stroke.width * (double ? 2 : 1);
3372
+ if (!(w > 0) || !path.length) return [];
3373
+ const dashes = stroke.dashes?.length && stroke.dashes.some((d) => d > 0) ? stroke.dashes.map((d) => d * stroke.width) : void 0;
3374
+ const options = {
3375
+ width: w,
3376
+ cap: stroke.cap ?? "butt",
3377
+ join: stroke.join ?? "miter",
3378
+ miterLimit: stroke.miterLimit ?? DEFAULT_MITER_LIMIT,
3379
+ ...dashes ? { dash: dashes, dashOffset: (stroke.dashOffset ?? 0) * stroke.width } : {}
3380
+ };
3381
+ const curved = path.some((c) => c.type === "C" || c.type === "Q");
3382
+ if (curved && path.length <= CLEAN_OUTLINE_LIMIT) {
3383
+ try {
3384
+ return outlineStroke(path, options);
3385
+ } catch {
3386
+ }
3387
+ }
3388
+ return strokeOutlineRings(path, options);
3389
+ }
3390
+ var CLEAN_OUTLINE_LIMIT = 64;
3391
+ var geometryCache = /* @__PURE__ */ new WeakMap();
3392
+ function shapeGeometry(layer) {
3393
+ const hit = geometryCache.get(layer);
3394
+ if (hit) return hit;
3395
+ const fills = layer.components.map((c) => ({ op: c.op, flat: flattenPath(componentPath(c)) }));
3396
+ let stroke = null;
3397
+ let align = "center";
3398
+ const s = layer.stroke;
3399
+ if (s && s.width > 0 && layer.components.length) {
3400
+ let outline;
3401
+ try {
3402
+ outline = shapeOutline(layer.components, layer.fillRule);
3403
+ } catch {
3404
+ outline = layer.components.flatMap((c) => componentPath(c));
3405
+ }
3406
+ align = s.align && s.align !== "center" && closedOutline(outline) ? s.align : "center";
3407
+ stroke = flattenPath(strokeArea(outline, s, align !== "center"));
3408
+ }
3409
+ let bounds = null;
3410
+ const add = (b) => {
3411
+ if (!b) return;
3412
+ if (!bounds) bounds = { ...b };
3413
+ else {
3414
+ const x0 = Math.min(bounds.x, b.x);
3415
+ const y0 = Math.min(bounds.y, b.y);
3416
+ bounds = { x: x0, y: y0, width: Math.max(bounds.x + bounds.width, b.x + b.width) - x0, height: Math.max(bounds.y + bounds.height, b.y + b.height) - y0 };
3417
+ }
3418
+ };
3419
+ if (layer.fill) for (const f of fills) add(flatBounds(f.flat));
3420
+ if (stroke) add(flatBounds(stroke));
3421
+ const g = shapeGeometryBounds(layer) ?? { x: 0, y: 0, width: 0, height: 0 };
3422
+ const frame = { x: Math.floor(g.x), y: Math.floor(g.y), width: Math.max(1, Math.ceil(g.x + g.width) - Math.floor(g.x)), height: Math.max(1, Math.ceil(g.y + g.height) - Math.floor(g.y)) };
3423
+ const geo = { fills, stroke, align, bounds, frame };
3424
+ geometryCache.set(layer, geo);
3425
+ return geo;
3426
+ }
3427
+ function fillCoverage(geo, rect, rule) {
3428
+ const n = rect.width * rect.height;
3429
+ let acc = null;
3430
+ geo.fills.forEach((f, i) => {
3431
+ const cov = f.flat.length > 1 ? rasterizeCoverageSampled(f.flat, rect, rule) : rasterizeCoverage(f.flat, rect, rule);
3432
+ acc = i === 0 ? cov : combineCoverage(acc, cov, f.op, n);
3433
+ });
3434
+ return acc;
3435
+ }
3436
+ function paintContext(env) {
3437
+ const tiles = /* @__PURE__ */ new Map();
3438
+ return {
3439
+ light: DEFAULT_GLOBAL_LIGHT,
3440
+ pattern: (id) => {
3441
+ if (tiles.has(id)) return tiles.get(id);
3442
+ const p = env.pattern?.(id) ?? builtinPattern(id);
3443
+ const t = p && p.width > 0 && p.height > 0 && p.data.length >= p.width * p.height * 4 ? new PatternTile(p) : null;
3444
+ tiles.set(id, t);
3445
+ return t;
3446
+ }
3447
+ };
3448
+ }
3449
+ function shapeRenderClip(env) {
3450
+ const m = SHAPE_RENDER_MARGIN;
3451
+ return { x: -m, y: -m, width: env.width + 2 * m, height: env.height + 2 * m };
3452
+ }
3453
+ function intersect(a, b) {
3454
+ const x0 = Math.max(a.x, b.x);
3455
+ const y0 = Math.max(a.y, b.y);
3456
+ const x1 = Math.min(a.x + a.width, b.x + b.width);
3457
+ const y1 = Math.min(a.y + a.height, b.y + b.height);
3458
+ return x1 > x0 && y1 > y0 ? { x: x0, y: y0, width: x1 - x0, height: y1 - y0 } : null;
3459
+ }
3460
+ function renderShapeRect(layer, rect, env, ctx = paintContext(env)) {
3461
+ const geo = shapeGeometry(layer);
3462
+ if (!geo.bounds || !intersect(geo.bounds, rect)) return null;
3463
+ const n = rect.width * rect.height;
3464
+ const rule = layer.fillRule ?? "nonzero";
3465
+ const needArea = Boolean(layer.fill) || geo.stroke !== null && geo.align !== "center";
3466
+ const area = needArea ? fillCoverage(geo, rect, rule) : null;
3467
+ let sCov = geo.stroke ? rasterizeCoverageSampled(geo.stroke, rect, "nonzero") : null;
3468
+ if (sCov && geo.align === "inside") sCov = area ? combineCoverage(sCov, area, "intersect", n) : null;
3469
+ else if (sCov && geo.align === "outside" && area) sCov = combineCoverage(sCov, area, "subtract", n);
3470
+ const fCov = layer.fill ? area : null;
3471
+ if (!fCov && !sCov) return null;
3472
+ const canvas = { x: 0, y: 0, width: env.width, height: env.height };
3473
+ const anchor = { x: geo.frame.x, y: geo.frame.y };
3474
+ const fc = fCov ? effectFillColors(layer.fill, rect, geo.frame, canvas, anchor, ctx) : null;
3475
+ const fo = layer.fill?.opacity ?? 1;
3476
+ const sc = sCov ? effectFillColors(layer.stroke.paint, rect, geo.frame, canvas, anchor, ctx) : null;
3477
+ const so = layer.stroke?.paint.opacity ?? 1;
3478
+ const out = new Uint8ClampedArray(n * 4);
3479
+ let any = false;
3480
+ for (let i = 0; i < n; i++) {
3481
+ const k = i * 4;
3482
+ let r = 0;
3483
+ let g = 0;
3484
+ let b = 0;
3485
+ let a = 0;
3486
+ if (fCov && fCov[i] > 0) {
3487
+ a = fCov[i] * fo * fc[k + 3];
3488
+ r = fc[k];
3489
+ g = fc[k + 1];
3490
+ b = fc[k + 2];
3491
+ }
3492
+ if (sCov && sCov[i] > 0) {
3493
+ const sa = Math.min(1, sCov[i]) * so * sc[k + 3];
3494
+ const ao = sa + a * (1 - sa);
3495
+ if (ao > 0) {
3496
+ r = (sc[k] * sa + r * a * (1 - sa)) / ao;
3497
+ g = (sc[k + 1] * sa + g * a * (1 - sa)) / ao;
3498
+ b = (sc[k + 2] * sa + b * a * (1 - sa)) / ao;
3499
+ }
3500
+ a = ao;
3501
+ }
3502
+ if (a <= 0) continue;
3503
+ const ab = Math.round(Math.min(1, a) * 255);
3504
+ if (!ab) continue;
3505
+ out[k] = Math.round(r * 255);
3506
+ out[k + 1] = Math.round(g * 255);
3507
+ out[k + 2] = Math.round(b * 255);
3508
+ out[k + 3] = ab;
3509
+ any = true;
3510
+ }
3511
+ return any ? out : null;
3512
+ }
3513
+ function renderShape(layer, env, options = {}) {
3514
+ const geo = shapeGeometry(layer);
3515
+ if (!geo.bounds) return emptySurface(4);
3516
+ let region = intersect(geo.bounds, shapeRenderClip(env));
3517
+ if (region && options.clip) region = intersect(region, options.clip);
3518
+ if (!region) return emptySurface(4);
3519
+ const ctx = paintContext(env);
3520
+ const tiles = /* @__PURE__ */ new Map();
3521
+ for (const { tx, ty } of tilesInRect(region)) {
3522
+ const rect = { x: tx * TILE, y: ty * TILE, width: TILE, height: TILE };
3523
+ const px = renderShapeRect(layer, rect, env, ctx);
3524
+ if (!px) continue;
3525
+ const tile = finishTile(px, 4, 0);
3526
+ if (tile) tiles.set(tileKey(tx, ty), tile);
3527
+ }
3528
+ return new TiledSurface(4, tiles, 0);
3529
+ }
3530
+ function rerenderShape(layer, env) {
3531
+ return { ...layer, surface: renderShape(layer, env) };
3532
+ }
3533
+ var isNum = (v) => typeof v === "number" && Number.isFinite(v);
3534
+ function requireNum(v, path, min = -Infinity, max = Infinity) {
3535
+ if (!isNum(v)) fail2(`${path} must be a finite number.`);
3536
+ if (v < min || v > max) fail2(`${path} must be between ${min} and ${max}, got ${v}.`);
3537
+ return v;
3538
+ }
3539
+ function normalizeShapePaint(v, path = "paint") {
3540
+ if (typeof v === "string") {
3541
+ const c = parseCssColor(v);
3542
+ if (!c) return fail2(`${path}: "${v}" is not a colour; use #rrggbb (or #rrggbbaa, rgb(), a CSS colour name).`);
3543
+ return c.alpha < 1 ? { type: "color", color: c.hex, opacity: c.alpha } : { type: "color", color: c.hex };
3544
+ }
3545
+ if (!v || typeof v !== "object") return fail2(`${path} must be a colour string or an object with type color, gradient or pattern.`);
3546
+ const o = v;
3547
+ const opacity = o.opacity === void 0 ? void 0 : requireNum(o.opacity, `${path}.opacity`, 0, 1);
3548
+ const withOpacity = (p) => opacity !== void 0 && opacity !== 1 ? { ...p, opacity } : p;
3549
+ if (o.type === "color") {
3550
+ const c = parseCssColor(o.color);
3551
+ if (!c) return fail2(`${path}.color must be a colour such as #rrggbb.`);
3552
+ const op = (opacity ?? 1) * c.alpha;
3553
+ return op !== 1 ? { type: "color", color: c.hex, opacity: op } : { type: "color", color: c.hex };
3554
+ }
3555
+ if (o.type === "gradient") {
3556
+ const g = o.gradient;
3557
+ if (!g || typeof g !== "object") return fail2(`${path}.gradient is required: { stops: [{ position, color }], style, angle, \u2026 }.`);
3558
+ const gradient = {
3559
+ stops: Array.isArray(g.stops) ? g.stops.map((s) => ({ position: s.position, color: parseCssColor(s.color)?.hex ?? s.color })) : [],
3560
+ ...g.opacityStops ? { opacityStops: g.opacityStops } : {},
3561
+ style: g.style ?? "linear",
3562
+ angle: g.angle ?? 90,
3563
+ scale: g.scale ?? 1,
3564
+ reverse: g.reverse ?? false,
3565
+ align: g.align ?? true,
3566
+ offset: g.offset ?? { x: 0, y: 0 }
3567
+ };
3568
+ try {
3569
+ validateEffectGradient(gradient, `${path}.gradient`);
3570
+ } catch (e) {
3571
+ if (e instanceof LayerEffectError) fail2(e.message);
3572
+ throw e;
3573
+ }
3574
+ if (gradient.stops.length < 1) fail2(`${path}.gradient.stops needs at least one { position, color }.`);
3575
+ return withOpacity({ type: "gradient", gradient });
3576
+ }
3577
+ if (o.type === "pattern") {
3578
+ if (typeof o.patternId !== "string" || !o.patternId) fail2(`${path}.patternId is required (a document or built-in pattern id).`);
3579
+ const p = { type: "pattern", patternId: o.patternId, scale: o.scale === void 0 ? 1 : requireNum(o.scale, `${path}.scale`, 0.01, 10) };
3580
+ if (typeof o.name === "string") p.name = o.name;
3581
+ if (o.angle !== void 0) p.angle = requireNum(o.angle, `${path}.angle`, -360, 360);
3582
+ if (o.link !== void 0) p.link = Boolean(o.link);
3583
+ if (o.phase !== void 0) {
3584
+ const ph = o.phase;
3585
+ p.phase = { x: requireNum(ph?.x, `${path}.phase.x`), y: requireNum(ph?.y, `${path}.phase.y`) };
3586
+ }
3587
+ return withOpacity(p);
3588
+ }
3589
+ return fail2(`${path}.type must be color, gradient or pattern, got ${JSON.stringify(o.type)}.`);
3590
+ }
3591
+ function normalizeShapeStroke(v, path = "stroke", base) {
3592
+ if (!v || typeof v !== "object") return fail2(`${path} must be an object { width, paint, align?, cap?, join?, miterLimit?, dashes?, dashOffset? } or null.`);
3593
+ const o = v;
3594
+ const width = o.width === void 0 && base ? base.width : requireNum(o.width, `${path}.width`, 0, 1e4);
3595
+ const paint = o.paint === void 0 && base ? base.paint : o.paint === void 0 && o.color !== void 0 ? normalizeShapePaint(o.color, `${path}.color`) : normalizeShapePaint(o.paint, `${path}.paint`);
3596
+ const out = { width, paint };
3597
+ const pick = (k, allowed) => {
3598
+ const value = o[k] === void 0 ? base?.[k] : o[k];
3599
+ if (value === void 0) return;
3600
+ if (!allowed.includes(value)) fail2(`${path}.${k} must be one of ${allowed.join(", ")}, got ${JSON.stringify(value)}.`);
3601
+ if (value !== (k === "align" ? "center" : k === "cap" ? "butt" : "miter")) out[k] = value;
3602
+ };
3603
+ pick("align", ["inside", "center", "outside"]);
3604
+ pick("cap", ["butt", "round", "square"]);
3605
+ pick("join", ["miter", "round", "bevel"]);
3606
+ const miter = o.miterLimit === void 0 ? base?.miterLimit : requireNum(o.miterLimit, `${path}.miterLimit`, 1, 500);
3607
+ if (miter !== void 0 && miter !== DEFAULT_MITER_LIMIT) out.miterLimit = miter;
3608
+ const dashes = o.dashes === void 0 ? base?.dashes : o.dashes;
3609
+ if (dashes !== void 0 && dashes !== null) {
3610
+ if (!Array.isArray(dashes) || dashes.length > 32 || !dashes.every((d) => isNum(d) && d >= 0 && d <= 1e4)) fail2(`${path}.dashes must be at most 32 lengths 0..10000 (multiples of the width).`);
3611
+ if (dashes.length) {
3612
+ if (dashes.reduce((s, d) => s + d, 0) < 0.01) fail2(`${path}.dashes must add up to at least 0.01 widths.`);
3613
+ out.dashes = [...dashes];
3614
+ }
3615
+ }
3616
+ const offset = o.dashOffset === void 0 ? base?.dashOffset : requireNum(o.dashOffset, `${path}.dashOffset`, -1e6, 1e6);
3617
+ if (offset) out.dashOffset = offset;
3618
+ return out;
3619
+ }
3620
+ function box(o, path) {
3621
+ return {
3622
+ x: requireNum(o.x, `${path}.x`),
3623
+ y: requireNum(o.y, `${path}.y`),
3624
+ width: requireNum(o.width, `${path}.width`, -1e6, 1e6),
3625
+ height: requireNum(o.height, `${path}.height`, -1e6, 1e6)
3626
+ };
3627
+ }
3628
+ function arrowhead(v, path) {
3629
+ if (v === null || v === void 0 || v === false) return null;
3630
+ if (typeof v !== "object") return fail2(`${path} must be { width, length, concavity? } in pixels, or null.`);
3631
+ const o = v;
3632
+ const head = { width: requireNum(o.width, `${path}.width`, 0, 1e5), length: requireNum(o.length, `${path}.length`, 0, 1e5) };
3633
+ if (o.concavity !== void 0 && o.concavity !== 0) head.concavity = requireNum(o.concavity, `${path}.concavity`, -0.5, 0.5);
3634
+ return head;
3635
+ }
3636
+ function matrixArg(v, path) {
3637
+ if (v === void 0 || v === null) return void 0;
3638
+ if (!Array.isArray(v) || v.length !== 6 || !v.every(isNum)) fail2(`${path} must be six numbers [a, b, c, d, e, f].`);
3639
+ const m = v;
3640
+ if (Math.abs(m[0] * m[3] - m[1] * m[2]) < 1e-12) fail2(`${path} is singular.`);
3641
+ return isIdentityMatrix(m) ? void 0 : [...m];
3642
+ }
3643
+ function normalizeLiveShape(v, path = "shape") {
3644
+ if (!v || typeof v !== "object") return fail2(`${path} must be an object with kind ${LIVE_SHAPE_KINDS.join(", ")}.`);
3645
+ const o = v;
3646
+ const transform = matrixArg(o.transform, `${path}.transform`);
3647
+ const t = transform ? { transform } : {};
3648
+ switch (o.kind) {
3649
+ case "rect": {
3650
+ const b = box(o, path);
3651
+ let radii;
3652
+ if (o.radii !== void 0 || o.radius !== void 0) {
3653
+ const r = o.radii ?? o.radius;
3654
+ const list2 = typeof r === "number" ? [r, r, r, r] : r;
3655
+ if (!Array.isArray(list2) || list2.length !== 4 || !list2.every((x) => isNum(x) && x >= 0)) fail2(`${path}.radii must be four radii \u2265 0 [top-left, top-right, bottom-right, bottom-left] (or radius, one number).`);
3656
+ if (list2.some((x) => x > 0)) radii = [...list2];
3657
+ }
3658
+ return { kind: "rect", ...b, ...radii ? { radii } : {}, ...t };
3659
+ }
3660
+ case "ellipse":
3661
+ return { kind: "ellipse", ...box(o, path), ...t };
3662
+ case "polygon":
3663
+ return { kind: "polygon", ...box(o, path), sides: Math.round(requireNum(o.sides ?? 6, `${path}.sides`, 3, 100)), ...t };
3664
+ case "star":
3665
+ return {
3666
+ kind: "star",
3667
+ ...box(o, path),
3668
+ points: Math.round(requireNum(o.points ?? 5, `${path}.points`, 3, 100)),
3669
+ innerRatio: requireNum(o.innerRatio ?? 0.5, `${path}.innerRatio`, 0.01, 1),
3670
+ ...t
3671
+ };
3672
+ case "line": {
3673
+ const line = {
3674
+ kind: "line",
3675
+ x1: requireNum(o.x1, `${path}.x1`),
3676
+ y1: requireNum(o.y1, `${path}.y1`),
3677
+ x2: requireNum(o.x2, `${path}.x2`),
3678
+ y2: requireNum(o.y2, `${path}.y2`),
3679
+ weight: requireNum(o.weight ?? 1, `${path}.weight`, 0, 1e5),
3680
+ ...t
3681
+ };
3682
+ const s = arrowhead(o.startArrow, `${path}.startArrow`);
3683
+ const e = arrowhead(o.endArrow, `${path}.endArrow`);
3684
+ if (s) line.startArrow = s;
3685
+ if (e) line.endArrow = e;
3686
+ if (line.x1 === line.x2 && line.y1 === line.y2) fail2(`${path}: the line's two ends are the same point.`);
3687
+ return line;
3688
+ }
3689
+ default:
3690
+ return fail2(`${path}.kind must be one of ${LIVE_SHAPE_KINDS.join(", ")}, got ${JSON.stringify(o.kind)}.`);
3691
+ }
3692
+ }
3693
+ function point(v, path) {
3694
+ const p = v;
3695
+ if (!p || typeof p !== "object") return fail2(`${path} must be { x, y } in document pixels.`);
3696
+ return { x: requireNum(p.x, `${path}.x`), y: requireNum(p.y, `${path}.y`) };
3697
+ }
3698
+ function normalizeSubpaths(v, path = "subpaths") {
3699
+ if (typeof v === "string") {
3700
+ let subs;
3701
+ try {
3702
+ subs = bezierToSubpaths(v);
3703
+ } catch {
3704
+ return fail2(`${path}: could not read the SVG path data.`);
3705
+ }
3706
+ if (!subs.length || !subs.every((s) => s.anchors.every((a) => isNum(a.x) && isNum(a.y)))) fail2(`${path}: the SVG path data holds no drawable subpath.`);
3707
+ return subs;
3708
+ }
3709
+ if (!Array.isArray(v) || !v.length) return fail2(`${path} must be a non-empty array of { anchors: [{ x, y, in?, out? }], closed } (or SVG path data).`);
3710
+ return v.map((s, i) => {
3711
+ const sp = s;
3712
+ if (!sp || !Array.isArray(sp.anchors) || sp.anchors.length < 1) fail2(`${path}[${i}].anchors must be a non-empty array.`);
3713
+ const anchors = sp.anchors.map((a, j) => {
3714
+ const at = `${path}[${i}].anchors[${j}]`;
3715
+ const p = point(a, at);
3716
+ const ao = a;
3717
+ const out = { x: p.x, y: p.y };
3718
+ if (ao.in) out.in = point(ao.in, `${at}.in`);
3719
+ if (ao.out) out.out = point(ao.out, `${at}.out`);
3720
+ if (ao.corner !== void 0 && ao.corner !== null) {
3721
+ const c = ao.corner;
3722
+ const r = requireNum(typeof c === "number" ? c : c.radius, `${at}.corner.radius`, 0, 1e5);
3723
+ if (r > 0) out.corner = { radius: r };
3724
+ }
3725
+ return out;
3726
+ });
3727
+ return { anchors, closed: sp.closed !== false };
3728
+ });
3729
+ }
3730
+ function requireOperation(v, path = "op") {
3731
+ if (v === void 0) return "combine";
3732
+ if (!SHAPE_OPERATIONS.includes(v)) fail2(`${path} must be one of ${SHAPE_OPERATIONS.join(", ")}, got ${JSON.stringify(v)}.`);
3733
+ return v;
3734
+ }
3735
+ function requireShapeLayer(layers, id) {
3736
+ const layer = requireLayer(layers, id).layer;
3737
+ if (layer.type !== "shape") fail2(`Layer "${layer.name || id}" is a ${layer.type} layer, not a shape layer; list_layers shows each layer's type.`);
3738
+ return layer;
3739
+ }
3740
+ function createShapeLayer(init, env, id = newLayerId()) {
3741
+ if (!init.components.length) fail2("A shape needs at least one component.");
3742
+ const layer = {
3743
+ type: "shape",
3744
+ id,
3745
+ name: init.name ?? defaultShapeName(init.components[0]),
3746
+ visible: init.visible ?? true,
3747
+ opacity: init.opacity ?? 1,
3748
+ blend: init.blend ?? "normal",
3749
+ components: init.components,
3750
+ fill: init.fill === void 0 ? null : init.fill,
3751
+ stroke: init.stroke ?? null,
3752
+ surface: emptySurface(4),
3753
+ offset: { x: 0, y: 0 }
3754
+ };
3755
+ if (init.fillRule && init.fillRule !== "nonzero") layer.fillRule = init.fillRule;
3756
+ return rerenderShape(layer, env);
3757
+ }
3758
+ function defaultShapeName(c) {
3759
+ switch (c?.live?.kind) {
3760
+ case "rect":
3761
+ return "Rectangle";
3762
+ case "ellipse":
3763
+ return "Ellipse";
3764
+ case "polygon":
3765
+ return "Polygon";
3766
+ case "star":
3767
+ return "Star";
3768
+ case "line":
3769
+ return "Line";
3770
+ default:
3771
+ return "Shape";
3772
+ }
3773
+ }
3774
+ function updateShapeLayer(layers, id, change, env, action = "edit it") {
3775
+ const current = requireShapeLayer(layers, id);
3776
+ requireUnlocked(current, "pixels", action);
3777
+ const patch = change(current);
3778
+ const next = { ...current, ...patch };
3779
+ if (!("annotation" in patch)) delete next.annotation;
3780
+ if (!next.components.length) fail2("A shape needs at least one component; delete the layer instead.");
3781
+ const rendered = rerenderShape(next, env);
3782
+ return { layers: mapLayer(layers, id, () => rendered), layer: rendered };
3783
+ }
3784
+ function transformShapeComponents(components, f, affine) {
3785
+ return components.map((c) => {
3786
+ if (affine && c.live) {
3787
+ const live = { ...c.live, transform: multiplyMatrices(c.live.transform ?? IDENTITY_MATRIX, affine) };
3788
+ if (isIdentityMatrix(live.transform)) delete live.transform;
3789
+ return { op: c.op, subpaths: liveShapeSubpaths(live), live };
3790
+ }
3791
+ return { op: c.op, subpaths: mapSubpaths(c.subpaths, f) };
3792
+ });
3793
+ }
3794
+ function offsetShape(layer, dx, dy, env) {
3795
+ const m = translateMatrix(dx, dy);
3796
+ const next = { ...layer, components: transformShapeComponents(layer.components, (p) => ({ x: p.x + dx, y: p.y + dy }), m) };
3797
+ delete next.annotation;
3798
+ return rerenderShape(next, env);
3799
+ }
3800
+ function mergeShapeComponents(components, fillRule = "nonzero") {
3801
+ if (components.length < 2) return [...components];
3802
+ const path = shapeOutline(components, fillRule);
3803
+ const subpaths = bezierToSubpaths(path);
3804
+ if (!subpaths.length) fail2("The components combine to nothing (the operations remove every area), so there is nothing to merge into.");
3805
+ return [{ op: "combine", subpaths }];
3806
+ }
3807
+ function shapeAsPixelLayer(layer) {
3808
+ const { components: _c, fillRule: _r, fill: _f, stroke: _s, annotation: _a, psd: _p, type: _t, ...rest } = layer;
3809
+ return { ...rest, type: "pixel", surface: layer.surface, offset: { x: 0, y: 0 } };
3810
+ }
3811
+ function annotationPaint(color, opacity) {
3812
+ const c = parseCssColor(color);
3813
+ if (!c || !parseEffectColor(c.hex)) return void 0;
3814
+ const a = c.alpha * opacity;
3815
+ return a !== 1 ? { type: "color", color: c.hex, opacity: a } : { type: "color", color: c.hex };
3816
+ }
3817
+ function shapeFromAnnotation(a) {
3818
+ const opacity = a.opacity ?? 1;
3819
+ const lineWidth = "strokeWidth" in a && a.strokeWidth > 0 ? a.strokeWidth : 1;
3820
+ const rotation = a.rotation ? rotateMatrix(a.rotation, { x: a.x, y: a.y }) : void 0;
3821
+ const t = rotation ? { transform: rotation } : {};
3822
+ const live = (l) => liveComponent(l);
3823
+ switch (a.type) {
3824
+ case "rect": {
3825
+ const stroke = annotationPaint(a.strokeColor, opacity);
3826
+ if (!stroke) return null;
3827
+ const fill = a.fillColor ? annotationPaint(a.fillColor, opacity) : null;
3828
+ if (fill === void 0) return null;
3829
+ return {
3830
+ components: [live({ kind: "rect", x: a.x, y: a.y, width: a.width, height: a.height, ...t })],
3831
+ fill,
3832
+ stroke: a.strokeWidth > 0 ? { width: a.strokeWidth, paint: stroke } : null
3833
+ };
3834
+ }
3835
+ case "circle": {
3836
+ const stroke = annotationPaint(a.strokeColor, opacity);
3837
+ if (!stroke) return null;
3838
+ const fill = a.fillColor ? annotationPaint(a.fillColor, opacity) : null;
3839
+ if (fill === void 0) return null;
3840
+ const rx = Math.abs(a.radiusX);
3841
+ const ry = Math.abs(a.radiusY);
3842
+ return {
3843
+ components: [live({ kind: "ellipse", x: a.x - rx, y: a.y - ry, width: 2 * rx, height: 2 * ry, ...t })],
3844
+ fill,
3845
+ stroke: a.strokeWidth > 0 ? { width: a.strokeWidth, paint: stroke } : null
3846
+ };
3847
+ }
3848
+ case "line":
3849
+ case "arrow": {
3850
+ const paint = annotationPaint(a.strokeColor, opacity);
3851
+ if (!paint) return null;
3852
+ const line = { kind: "line", x1: a.x, y1: a.y, x2: a.endX, y2: a.endY, weight: lineWidth, ...t };
3853
+ if (a.type === "arrow") {
3854
+ const side = Math.max(12, a.strokeWidth * 3);
3855
+ line.endArrow = { width: side, length: side * Math.cos(Math.PI / 6) };
3856
+ }
3857
+ if (line.x1 === line.x2 && line.y1 === line.y2) return { components: [{ op: "combine", subpaths: [] }], fill: paint, stroke: null };
3858
+ return { components: [live(line)], fill: paint, stroke: null };
3859
+ }
3860
+ case "pen": {
3861
+ const paint = annotationPaint(a.strokeColor, opacity);
3862
+ if (!paint) return null;
3863
+ const pts = rotation ? a.points.map((p) => applyMatrix(rotation, p)) : a.points;
3864
+ const subpaths = pts.length > 1 ? [{ closed: false, anchors: pts.map((p) => ({ x: p.x, y: p.y })) }] : [];
3865
+ return {
3866
+ components: [{ op: "combine", subpaths }],
3867
+ fill: null,
3868
+ stroke: { width: lineWidth, paint, cap: "round", join: "round" }
3869
+ };
3870
+ }
3871
+ default:
3872
+ return null;
3873
+ }
3874
+ }
3875
+ function annotationBlend(a, layerBlend) {
3876
+ if (layerBlend !== "normal") return layerBlend;
3877
+ return a.blend && isBlendMode(a.blend) ? a.blend : "normal";
3878
+ }
3879
+ function annotationOfShape(layer) {
3880
+ const a = layer.annotation;
3881
+ if (!a) return null;
3882
+ const blend = layer.blend;
3883
+ if ((a.blend ?? "normal") === blend) return a;
3884
+ const out = { ...a };
3885
+ if (blend === "normal") delete out.blend;
3886
+ else out.blend = blend;
3887
+ return out;
3888
+ }
3889
+
3890
+ // src/image-editor/layers/defaults.ts
3891
+ var DEFAULT_ADJUSTMENTS_VALUES = {
3892
+ brightness: 0,
3893
+ contrast: 0,
3894
+ saturation: 0,
3895
+ exposure: 0,
3896
+ temperature: 0,
3897
+ tint: 0,
3898
+ blur: 0,
3899
+ opacity: 1,
3900
+ process: LATEST_PROCESS_VERSION
3901
+ };
3902
+ var DEFAULT_TRANSFORM_VALUES = {
3903
+ rotate: 0,
3904
+ flipH: false,
3905
+ flipV: false
3906
+ };
3907
+ var DEFAULT_DEPTH_MASK_VALUES = {
3908
+ enabled: false,
3909
+ maskSource: null,
3910
+ mode: "threshold",
3911
+ depthRange: [0, 255],
3912
+ softness: 10,
3913
+ invert: false
3914
+ };
3915
+
3916
+ // src/image-editor/layers/migrate.ts
3917
+ var BACKGROUND_LAYER_ID = "background";
3918
+ var BACKGROUND_LAYER_NAME = "Background";
3919
+ var DEVELOP_FILTER_ID = "develop";
3920
+ var BLUR_FILTER_ID = "blur";
3921
+ var legacyMaskMemo = /* @__PURE__ */ new WeakMap();
3922
+ function rasterMaskFromLegacy(mask, width, height) {
3923
+ const sel = decodeLayerMask(mask);
3924
+ if (!sel) return null;
3925
+ const sized = sel.width === width && sel.height === height ? sel : resizeSelection(sel, width, height);
3926
+ const surface = surfaceFromPixels({ width, height, data: sized.data }, 1, 0, 0, 0);
3927
+ legacyMaskMemo.set(surface, { width: mask.width, height: mask.height, rle: mask.rle });
3928
+ const out = { surface, defaultValue: 0 };
3929
+ if (mask.density !== void 0 && mask.density !== 1) out.density = mask.density;
3930
+ if (mask.feather) out.feather = mask.feather;
3931
+ if (mask.enabled === false) out.enabled = false;
3932
+ return out;
3933
+ }
3934
+ function hasResampledLegacyMask(layers, width, height) {
3935
+ for (const l of layers) {
3936
+ if (l.type !== "adjustment" || !l.mask) continue;
3937
+ const memo = legacyMaskMemo.get(l.mask.surface);
3938
+ if (memo && (memo.width !== width || memo.height !== height)) return true;
3939
+ }
3940
+ return false;
3941
+ }
3942
+ function legacyMaskOf(mask, width, height) {
3943
+ let memo = legacyMaskMemo.get(mask.surface);
3944
+ if (!memo) {
3945
+ const data = readRegion(mask.surface, { x: 0, y: 0, width, height });
3946
+ memo = { width, height, rle: encodeSelectionRle(new Uint8Array(data.buffer, data.byteOffset, data.length)) };
3947
+ legacyMaskMemo.set(mask.surface, memo);
3948
+ }
3949
+ return normalizeLayerMask({ ...memo, density: mask.density, feather: mask.feather, enabled: mask.enabled });
3950
+ }
3951
+ function sameJson(a, b) {
3952
+ return JSON.stringify(a) === JSON.stringify(b);
3953
+ }
3954
+ function isDefaultDepthMask(mask) {
3955
+ if (!mask) return true;
3956
+ return sameJson({ ...DEFAULT_DEPTH_MASK_VALUES, ...mask, depthRange: [...mask.depthRange ?? []] }, DEFAULT_DEPTH_MASK_VALUES);
3957
+ }
3958
+ function developFromAdjustments(adjustments) {
3959
+ return {
3960
+ id: DEVELOP_FILTER_ID,
3961
+ kind: "develop",
3962
+ enabled: true,
3963
+ brightness: adjustments.brightness ?? 0,
3964
+ contrast: adjustments.contrast ?? 0,
3965
+ saturation: adjustments.saturation ?? 0,
3966
+ exposure: adjustments.exposure ?? 0,
3967
+ temperature: adjustments.temperature ?? 0,
3968
+ tint: adjustments.tint ?? 0,
3969
+ process: normalizeProcessVersion(adjustments.process)
3970
+ };
3971
+ }
3972
+ function annotationLayerName(annotation) {
3973
+ switch (annotation.type) {
3974
+ case "pen":
3975
+ return "Pen";
3976
+ case "rect":
3977
+ return "Rectangle";
3978
+ case "circle":
3979
+ return "Ellipse";
3980
+ case "arrow":
3981
+ return "Arrow";
3982
+ case "line":
3983
+ return "Line";
3984
+ case "text":
3985
+ return annotation.text ? annotation.text.slice(0, 32) : "Text";
3986
+ case "image":
3987
+ return "Image";
3988
+ }
3989
+ }
3990
+ function annotationLayerFor(annotation, canvas) {
3991
+ const shape = canvas ? shapeFromAnnotation(annotation) : null;
3992
+ if (shape) {
3993
+ const layer = createShapeLayer(
3994
+ { ...shape, name: annotationLayerName(annotation), blend: annotation.blend && annotation.blend !== "normal" ? annotation.blend : "normal" },
3995
+ canvas,
3996
+ annotation.id
3997
+ );
3998
+ return { ...layer, annotation };
3999
+ }
4000
+ return {
4001
+ type: "annotation",
4002
+ id: annotation.id,
4003
+ name: annotationLayerName(annotation),
4004
+ visible: true,
4005
+ opacity: 1,
4006
+ blend: "normal",
4007
+ annotation
4008
+ };
4009
+ }
4010
+ function annotationOfLayer(layer) {
4011
+ if (layer.type === "annotation") return layer.annotation;
4012
+ if (layer.type === "shape") return annotationOfShape(layer);
4013
+ return null;
4014
+ }
4015
+ function adjustmentNodeFromLegacy(layer, width, height) {
4016
+ const node = {
4017
+ type: "adjustment",
4018
+ id: layer.id,
4019
+ name: layer.name ?? "",
4020
+ visible: layer.enabled !== false,
4021
+ opacity: layer.opacity ?? 1,
4022
+ blend: layer.blend ?? "normal",
4023
+ kind: layer.kind,
4024
+ params: JSON.parse(JSON.stringify(layer.params))
4025
+ };
4026
+ if (layer.mask) {
4027
+ const mask = rasterMaskFromLegacy(layer.mask, width, height);
4028
+ if (mask) node.mask = mask;
4029
+ }
4030
+ return node;
4031
+ }
4032
+ function backgroundLayer(filters = [], mask) {
4033
+ const layer = {
4034
+ type: "pixel",
4035
+ id: BACKGROUND_LAYER_ID,
4036
+ name: BACKGROUND_LAYER_NAME,
4037
+ visible: true,
4038
+ opacity: 1,
4039
+ blend: "normal",
4040
+ surface: emptySurface(4),
4041
+ offset: { x: 0, y: 0 },
4042
+ source: "document",
4043
+ filters
4044
+ };
4045
+ if (mask) layer.mask = mask;
4046
+ return layer;
4047
+ }
4048
+ function depthRasterMask(source) {
4049
+ const out = {
4050
+ surface: emptySurface(1, 255),
4051
+ defaultValue: 255,
4052
+ source: { ...source, depthRange: [...source.depthRange] }
4053
+ };
4054
+ if (!source.enabled) out.enabled = false;
4055
+ return out;
4056
+ }
4057
+ function layersFromLegacy(parts, width, height) {
4058
+ const stored = parts.adjustments;
4059
+ const adjustments = stored ? { ...DEFAULT_ADJUSTMENTS_VALUES, ...stored, process: normalizeProcessVersion(stored.process) } : { ...DEFAULT_ADJUSTMENTS_VALUES };
4060
+ const adjLayers = normalizeAdjustmentLayers(adjustments.layers);
4061
+ const filters = [developFromAdjustments(adjustments)];
4062
+ const blur = typeof adjustments.blur === "number" && adjustments.blur > 0 ? adjustments.blur : 0;
4063
+ let legacyOutputBlur;
4064
+ if (blur > 0) {
4065
+ if (adjLayers.length) legacyOutputBlur = blur;
4066
+ else filters.push({ id: BLUR_FILTER_ID, kind: "blur", enabled: true, radius: blur });
4067
+ }
4068
+ const depth = parts.depthMask;
4069
+ const mask = depth && !isDefaultDepthMask(depth) ? depthRasterMask({ ...DEFAULT_DEPTH_MASK_VALUES, ...depth }) : void 0;
4070
+ const layers = [backgroundLayer(filters, mask)];
4071
+ for (const l of adjLayers) layers.push(adjustmentNodeFromLegacy(l, width, height));
4072
+ for (const a of parts.annotations ?? []) layers.push(annotationLayerFor(JSON.parse(JSON.stringify(a)), { width, height }));
4073
+ const out = { layers, outputOpacity: typeof adjustments.opacity === "number" ? adjustments.opacity : 1 };
4074
+ if (legacyOutputBlur !== void 0) out.legacyOutputBlur = legacyOutputBlur;
4075
+ return out;
4076
+ }
4077
+ function legacyAdjustmentLayer(node, width, height) {
4078
+ const out = {
4079
+ id: node.id,
4080
+ kind: node.kind,
4081
+ enabled: node.visible,
4082
+ opacity: node.opacity,
4083
+ blend: node.blend,
4084
+ params: node.params
4085
+ };
4086
+ if (node.name) out.name = node.name;
4087
+ if (node.mask && !node.mask.source) out.mask = legacyMaskOf(node.mask, width, height);
4088
+ return out;
4089
+ }
4090
+ function developTarget(layers, activeLayerId) {
4091
+ const pixels = [];
4092
+ const walk = (list2) => {
4093
+ for (const l of list2) {
4094
+ if (l.type === "pixel") pixels.push(l);
4095
+ else if (l.type === "group") walk(l.children);
4096
+ }
4097
+ };
4098
+ walk(layers);
4099
+ if (activeLayerId) {
4100
+ const active = pixels.find((l) => l.id === activeLayerId);
4101
+ if (active) return active;
4102
+ }
4103
+ return pixels.find((l) => l.source === "document") ?? pixels.find((l) => l.id === BACKGROUND_LAYER_ID) ?? pixels[0] ?? null;
4104
+ }
4105
+ var isPlain = (l) => l.visible && l.opacity === 1 && l.blend === "normal" && !l.clipped && !l.mask && !l.locks && !l.effects;
4106
+ function legacyView(stack, width, height, activeLayerId = null) {
4107
+ const { layers } = stack;
4108
+ const target = developTarget(layers, activeLayerId);
4109
+ const develop = target ? developFilter(target) : void 0;
4110
+ const blur = stack.legacyOutputBlur ?? (target ? blurFilter(target)?.radius : void 0) ?? 0;
4111
+ const adjustments = {
4112
+ ...DEFAULT_ADJUSTMENTS_VALUES,
4113
+ ...develop ? {
4114
+ brightness: develop.brightness,
4115
+ contrast: develop.contrast,
4116
+ saturation: develop.saturation,
4117
+ exposure: develop.exposure,
4118
+ temperature: develop.temperature,
4119
+ tint: develop.tint ?? 0
4120
+ } : {},
4121
+ blur,
4122
+ opacity: stack.outputOpacity,
4123
+ process: develop ? normalizeProcessVersion(develop.process) : DEFAULT_ADJUSTMENTS_VALUES.process
4124
+ };
4125
+ const adjNodes = [];
4126
+ const annotations = [];
4127
+ const walk = (list2) => {
4128
+ for (const l of list2) {
4129
+ if (l.type === "adjustment") adjNodes.push(l);
4130
+ else if (l.type === "group") walk(l.children);
4131
+ else {
4132
+ const a = annotationOfLayer(l);
4133
+ if (a) annotations.push(a);
4134
+ }
4135
+ }
4136
+ };
4137
+ walk(layers);
4138
+ if (adjNodes.length) adjustments.layers = adjNodes.map((n) => legacyAdjustmentLayer(n, width, height));
4139
+ const depthSource = target?.mask?.source;
4140
+ const depthMask = depthSource ? { ...depthSource, depthRange: [...depthSource.depthRange] } : { ...DEFAULT_DEPTH_MASK_VALUES, depthRange: [...DEFAULT_DEPTH_MASK_VALUES.depthRange] };
4141
+ return { adjustments, depthMask, annotations, exact: isLegacyShape(stack) };
4142
+ }
4143
+ function isLegacyShape(stack) {
4144
+ const { layers } = stack;
4145
+ const bg = layers[0];
4146
+ if (!bg || bg.type !== "pixel" || bg.source !== "document") return false;
4147
+ if (bg.id !== BACKGROUND_LAYER_ID || bg.name !== BACKGROUND_LAYER_NAME) return false;
4148
+ if (!bg.visible || bg.opacity !== 1 || bg.blend !== "normal" || bg.clipped || bg.locks || bg.effects) return false;
4149
+ if ((bg.fillOpacity ?? 1) !== 1 || bg.offset.x !== 0 || bg.offset.y !== 0) return false;
4150
+ const filters = bg.filters ?? [];
4151
+ if (filters.length < 1 || filters.length > 2) return false;
4152
+ const [develop, blur] = filters;
4153
+ if (develop.kind !== "develop" || !develop.enabled || develop.id !== DEVELOP_FILTER_ID) return false;
4154
+ if (blur && (blur.kind !== "blur" || !blur.enabled || blur.id !== BLUR_FILTER_ID || !(blur.radius > 0))) return false;
4155
+ if (bg.mask) {
4156
+ const m = bg.mask;
4157
+ if (!m.source || m.density !== void 0 || m.feather !== void 0) return false;
4158
+ if (m.enabled !== false !== m.source.enabled) return false;
4159
+ }
4160
+ let i = 1;
4161
+ let adjustments = 0;
4162
+ for (; i < layers.length && layers[i].type === "adjustment"; i++) {
4163
+ const l = layers[i];
4164
+ if (l.clipped || l.locks || l.mask?.source || l.effects) return false;
4165
+ adjustments++;
4166
+ }
4167
+ for (; i < layers.length; i++) {
4168
+ const l = layers[i];
4169
+ if (l.type === "shape") {
4170
+ const a = l.annotation;
4171
+ if (!a || (a.blend ?? "normal") !== l.blend || !isPlain({ ...l, blend: "normal" }) || l.fillOpacity !== void 0) return false;
4172
+ if (l.id !== a.id || l.name !== annotationLayerName(a)) return false;
4173
+ continue;
4174
+ }
4175
+ if (l.type !== "annotation" || !isPlain(l) || l.id !== l.annotation.id || l.name !== annotationLayerName(l.annotation)) return false;
4176
+ }
4177
+ if (blur && adjustments) return false;
4178
+ if (stack.legacyOutputBlur !== void 0 && (!adjustments || !(stack.legacyOutputBlur > 0))) return false;
4179
+ return true;
4180
+ }
4181
+
4182
+ // src/image-editor/transform.ts
4183
+ var FREE_TRANSFORM_MODES = ["scale", "rotate", "skew", "distort", "perspective", "warp"];
4184
+ var TRANSFORM_ANCHORS = ["top-left", "top", "top-right", "left", "center", "right", "bottom-left", "bottom", "bottom-right"];
4185
+ var ANCHOR_FRACTION = {
4186
+ "top-left": [0, 0],
4187
+ top: [0.5, 0],
4188
+ "top-right": [1, 0],
4189
+ left: [0, 0.5],
4190
+ center: [0.5, 0.5],
4191
+ right: [1, 0.5],
4192
+ "bottom-left": [0, 1],
4193
+ bottom: [0.5, 1],
4194
+ "bottom-right": [1, 1]
4195
+ };
4196
+ function isTransformAnchor(value) {
4197
+ return typeof value === "string" && value in ANCHOR_FRACTION;
4198
+ }
4199
+ function identityTransform(box2) {
4200
+ return { box: { ...box2 }, corners: boxQuad(box2) };
4201
+ }
4202
+ function cornersFromMatrix(box2, m) {
4203
+ const q2 = boxQuad(box2);
4204
+ return [applyMatrix(m, q2[0]), applyMatrix(m, q2[1]), applyMatrix(m, q2[2]), applyMatrix(m, q2[3])];
4205
+ }
4206
+ function matrixFromCorners(box2, corners, eps = 1e-6) {
4207
+ const [tl, tr, br, bl] = corners;
4208
+ const size = 1 + Math.max(box2.width, box2.height);
4209
+ if (Math.abs(tl.x + br.x - tr.x - bl.x) > eps * size || Math.abs(tl.y + br.y - tr.y - bl.y) > eps * size) return null;
4210
+ const h = rectToQuad(box2, corners);
4211
+ return h ? affineFromHomography(h, 1e-9) : null;
4212
+ }
4213
+ function transformHomography(geometry) {
4214
+ const h = rectToQuad(geometry.box, geometry.corners);
4215
+ if (!h) return null;
4216
+ return homographyKeepsQuad(h, boxQuad(geometry.box)) ? h : null;
4217
+ }
4218
+ function hasWarp(geometry) {
4219
+ const w = geometry.warp;
4220
+ if (!w) return false;
4221
+ const id = identityWarpMesh(geometry.box, w.us.length - 1, w.vs.length - 1);
4222
+ if (id.points.length !== w.points.length || id.us.some((u, i) => Math.abs(u - w.us[i]) > 1e-12) || id.vs.some((v, i) => Math.abs(v - w.vs[i]) > 1e-12)) return true;
4223
+ return w.points.some((p, i) => Math.abs(p.x - id.points[i].x) > 1e-6 || Math.abs(p.y - id.points[i].y) > 1e-6);
4224
+ }
4225
+ function isIdentityTransform(geometry) {
4226
+ if (hasWarp(geometry)) return false;
4227
+ const q2 = boxQuad(geometry.box);
4228
+ return geometry.corners.every((c, i) => Math.abs(c.x - q2[i].x) < 1e-6 && Math.abs(c.y - q2[i].y) < 1e-6);
4229
+ }
4230
+ function transformMap(geometry) {
4231
+ const h = transformHomography(geometry);
4232
+ if (!h) return null;
4233
+ const { box: box2 } = geometry;
4234
+ const warp = hasWarp(geometry) ? geometry.warp : null;
4235
+ if (!warp) return (p) => applyHomography(h, p);
4236
+ return (p) => applyHomography(h, evalWarpMesh(warp, (p.x - box2.x) / box2.width, (p.y - box2.y) / box2.height));
2741
4237
  }
2742
- function layerEffectsOf(layer) {
2743
- return layer.effects ?? { enabled: true, items: [] };
4238
+ function transformedPoint(geometry, fx, fy) {
4239
+ const f = transformMap(geometry);
4240
+ const { box: box2 } = geometry;
4241
+ return f ? f({ x: box2.x + fx * box2.width, y: box2.y + fy * box2.height }) : null;
2744
4242
  }
2745
- function guard(fn) {
2746
- try {
2747
- return fn();
2748
- } catch (e) {
2749
- if (e instanceof LayerEffectError) fail(e.message);
2750
- throw e;
4243
+ function anchorPoint(geometry, anchor) {
4244
+ const [fx, fy] = ANCHOR_FRACTION[anchor];
4245
+ const h = rectToQuad(geometry.box, geometry.corners);
4246
+ const p = { x: geometry.box.x + fx * geometry.box.width, y: geometry.box.y + fy * geometry.box.height };
4247
+ return (h && applyHomography(h, p)) ?? p;
4248
+ }
4249
+ function transformedBox(geometry) {
4250
+ const f = transformMap(geometry);
4251
+ if (!f) return null;
4252
+ const { box: box2 } = geometry;
4253
+ const pts = [];
4254
+ const n = hasWarp(geometry) ? 16 : 1;
4255
+ for (let i = 0; i <= n; i++) {
4256
+ const t = i / n;
4257
+ for (const [fx, fy] of [[t, 0], [1, t], [1 - t, 1], [0, 1 - t]]) {
4258
+ const p = f({ x: box2.x + fx * box2.width, y: box2.y + fy * box2.height });
4259
+ if (!p) return null;
4260
+ pts.push(p);
4261
+ }
2751
4262
  }
4263
+ const xs = pts.map((p) => p.x);
4264
+ const ys = pts.map((p) => p.y);
4265
+ const x = Math.min(...xs);
4266
+ const y = Math.min(...ys);
4267
+ return { x, y, width: Math.max(...xs) - x, height: Math.max(...ys) - y };
2752
4268
  }
2753
- function setLayerEffects(layers, id, effects) {
2754
- const layer = requireEffectLayer(layers, id, "change its effects");
2755
- const next = { ...layer };
2756
- if (effects && (effects.items.length || effects.psd || effects.scale !== void 0 || !effects.enabled)) {
2757
- guard(() => validateLayerEffects(effects));
2758
- next.effects = effects;
2759
- } else delete next.effects;
2760
- return mapLayer(layers, id, () => next);
4269
+ function referencePoint(box2, reference) {
4270
+ if (reference && typeof reference === "object") return { x: reference.x, y: reference.y };
4271
+ const [fx, fy] = ANCHOR_FRACTION[reference ?? "center"];
4272
+ return { x: box2.x + fx * box2.width, y: box2.y + fy * box2.height };
2761
4273
  }
2762
- function addLayerEffect(layers, id, kind, params = {}, index) {
2763
- if (!isLayerEffectKind(kind)) fail(`Unknown effect kind "${String(kind)}". Kinds: ${LAYER_EFFECT_KINDS.join(", ")}.`);
2764
- const layer = requireEffectLayer(layers, id, "add effects");
2765
- const current = layerEffectsOf(layer);
2766
- const ids = new Set(current.items.map((e) => e.id));
2767
- let effectId = newEffectId();
2768
- while (ids.has(effectId)) effectId = newEffectId();
2769
- const { id: _ignored, kind: _kind, ...rest } = params;
2770
- const effect = guard(() => mergeLayerEffect(defaultLayerEffect(kind, effectId), rest));
2771
- const items = [...current.items];
2772
- const at = index === void 0 ? items.length : Math.max(0, Math.min(items.length, Math.floor(index)));
2773
- items.splice(at, 0, effect);
2774
- return { layers: setLayerEffects(layers, id, { ...current, items }), effect };
4274
+ function matrixFromParams(box2, params) {
4275
+ const sx = (params.scaleX ?? 100) / 100;
4276
+ const sy = (params.scaleY ?? 100) / 100;
4277
+ const tx = Math.tan((params.skewX ?? 0) * Math.PI / 180);
4278
+ const ty = Math.tan((params.skewY ?? 0) * Math.PI / 180);
4279
+ const ref = referencePoint(box2, params.reference);
4280
+ const linear = multiplyMatrices(multiplyMatrices([1, ty, tx, 1, 0, 0], [sx, 0, 0, sy, 0, 0]), rotateMatrix(params.rotate ?? 0));
4281
+ return multiplyMatrices(matrixAbout(linear, ref), translateMatrix(params.dx ?? 0, params.dy ?? 0));
2775
4282
  }
2776
- function requireEffect(layer, effectId) {
2777
- const items = layer.effects?.items ?? [];
2778
- const i = items.findIndex((e) => e.id === effectId);
2779
- if (i === -1) fail(`Layer "${layer.name || layer.id}" has no effect "${effectId}". Effects: ${items.map((e) => `${e.id} (${e.kind})`).join(", ") || "none"}; call list_layer_effects.`);
2780
- return i;
4283
+ function describeTransform(geometry, reference = "center") {
4284
+ const m = hasWarp(geometry) ? null : matrixFromCorners(geometry.box, geometry.corners);
4285
+ const ref = referencePoint(geometry.box, reference);
4286
+ const bounds = transformedBox(geometry);
4287
+ if (!m) {
4288
+ const h = rectToQuad(geometry.box, geometry.corners);
4289
+ const at2 = (h && applyHomography(h, ref)) ?? ref;
4290
+ return { affine: false, scaleX: 100, scaleY: 100, rotate: 0, skewX: 0, x: at2.x, y: at2.y, bounds, corners: geometry.corners };
4291
+ }
4292
+ const [a, b, c, d] = m;
4293
+ const sxAbs = Math.hypot(a, b);
4294
+ const rot = Math.atan2(b, a) * 180 / Math.PI;
4295
+ const shear = sxAbs ? (a * c + b * d) / sxAbs : 0;
4296
+ const sy = sxAbs ? (a * d - b * c) / sxAbs : 0;
4297
+ const skew = sxAbs ? Math.atan(shear / sxAbs) * 180 / Math.PI : 0;
4298
+ const at = applyMatrix(m, ref);
4299
+ const clean = (v) => Math.abs(v) < 1e-9 ? 0 : v;
4300
+ return {
4301
+ affine: true,
4302
+ scaleX: clean(sxAbs * 100),
4303
+ scaleY: clean(sy * 100),
4304
+ rotate: clean(rot === -180 ? 180 : rot),
4305
+ skewX: clean(skew),
4306
+ x: at.x,
4307
+ y: at.y,
4308
+ bounds,
4309
+ corners: geometry.corners
4310
+ };
2781
4311
  }
2782
- function updateLayerEffect(layers, id, effectId, changes) {
2783
- const layer = requireEffectLayer(layers, id, "change its effects");
2784
- const i = requireEffect(layer, effectId);
2785
- const current = layer.effects;
2786
- const items = [...current.items];
2787
- items[i] = guard(() => mergeLayerEffect(items[i], changes));
2788
- return setLayerEffects(layers, id, { ...current, items });
4312
+ function applyParams(geometry, params) {
4313
+ const ref = params.reference && typeof params.reference === "object" ? params.reference : anchorPoint(geometry, params.reference ?? "center");
4314
+ const m = matrixFromParams({ x: ref.x, y: ref.y, width: 0, height: 0 }, { ...params, reference: ref });
4315
+ const c = geometry.corners;
4316
+ return [applyMatrix(m, c[0]), applyMatrix(m, c[1]), applyMatrix(m, c[2]), applyMatrix(m, c[3])];
2789
4317
  }
2790
- function removeLayerEffect(layers, id, effectId) {
2791
- const layer = requireEffectLayer(layers, id, "change its effects");
2792
- requireEffect(layer, effectId);
2793
- const current = layer.effects;
2794
- return setLayerEffects(layers, id, { ...current, items: current.items.filter((e) => e.id !== effectId) });
4318
+ function flipCorners(geometry, axis) {
4319
+ const c = anchorPoint(geometry, "center");
4320
+ const m = axis === "horizontal" ? [-1, 0, 0, 1, 2 * c.x, 0] : [1, 0, 0, -1, 0, 2 * c.y];
4321
+ const q2 = geometry.corners;
4322
+ return [applyMatrix(m, q2[0]), applyMatrix(m, q2[1]), applyMatrix(m, q2[2]), applyMatrix(m, q2[3])];
2795
4323
  }
2796
- function setLayerEffectsEnabled(layers, id, enabled, scale) {
2797
- const layer = requireEffectLayer(layers, id, "change its effects");
2798
- if (scale !== void 0 && !(Number.isFinite(scale) && scale >= 0.01 && scale <= 10)) fail(`scale must be from 0.01 to 10 (1 = 100 %), got ${scale}.`);
2799
- const current = layerEffectsOf(layer);
2800
- const next = { ...current, enabled };
2801
- if (scale !== void 0) {
2802
- if (scale === 1) delete next.scale;
2803
- else next.scale = scale;
2804
- }
2805
- return setLayerEffects(layers, id, next);
4324
+ function boxPointOf(geometry, p) {
4325
+ const m = matrixFromCorners(geometry.box, geometry.corners);
4326
+ if (m) return applyMatrix(invertMatrix(m), p);
4327
+ return null;
2806
4328
  }
2807
- function parseBlend(value, allowPassThrough = false) {
2808
- if (value === void 0) return void 0;
2809
- if (value === "pass-through" && allowPassThrough) return value;
2810
- if (!isBlendMode(value)) fail(`Unknown blend mode "${String(value)}".`);
2811
- return value;
4329
+ function presetWarp(box2, preset) {
4330
+ return warpPresetMesh(preset, box2);
2812
4331
  }
2813
- function unionRect(a, b) {
2814
- if (!a) return b;
2815
- if (!b) return a;
2816
- const x = Math.min(a.x, b.x);
2817
- const y = Math.min(a.y, b.y);
2818
- return { x, y, width: Math.max(a.x + a.width, b.x + b.width) - x, height: Math.max(a.y + a.height, b.y + b.height) - y };
4332
+ function isValidWarp(mesh) {
4333
+ return isValidWarpMesh(mesh);
4334
+ }
4335
+ function geometryHomography(geometry) {
4336
+ const m = matrixFromCorners(geometry.box, geometry.corners);
4337
+ return m ? homographyFromAffine(m) : transformHomography(geometry);
2819
4338
  }
2820
4339
 
2821
4340
  // src/image-editor/layers/smart.ts
2822
- var fail2 = (message) => {
4341
+ var fail3 = (message) => {
2823
4342
  throw new LayerOperationError(message);
2824
4343
  };
2825
4344
  var SMART_RENDER_MARGIN = MAX_EFFECT_REACH;
@@ -2841,13 +4360,13 @@ function isContentsDecoded(contents) {
2841
4360
  function requireDecoded(layer, action) {
2842
4361
  if (isContentsDecoded(layer.contents)) return;
2843
4362
  const what = layer.contents.file?.type ? `a ${layer.contents.file.type.toUpperCase()} file` : "a file";
2844
- fail2(
4363
+ fail3(
2845
4364
  `The contents of smart object "${layer.name || layer.id}" are ${what} that could not be decoded here, so it cannot ${action}. Run prepareSmartObjects() in a browser first, or rasterize it.`
2846
4365
  );
2847
4366
  }
2848
4367
  function requireSmartObject(layers, id) {
2849
4368
  const layer = requireLayer(layers, id).layer;
2850
- if (layer.type !== "smart") fail2(`Layer "${layer.name || id}" is a ${layer.type} layer, not a smart object; convert it with convert_to_smart_object.`);
4369
+ if (layer.type !== "smart") fail3(`Layer "${layer.name || id}" is a ${layer.type} layer, not a smart object; convert it with convert_to_smart_object.`);
2851
4370
  return layer;
2852
4371
  }
2853
4372
  var contentsPixels = /* @__PURE__ */ new WeakMap();
@@ -2873,11 +4392,11 @@ function contentsSurface(contents) {
2873
4392
  return s;
2874
4393
  }
2875
4394
  function rasterContents(pixels, name, id = newContentsId()) {
2876
- if (!(pixels.width > 0 && pixels.height > 0) || pixels.data.length < pixels.width * pixels.height * 4) fail2("Contents pixels must be RGBA with width \xD7 height \xD7 4 bytes.");
4395
+ if (!(pixels.width > 0 && pixels.height > 0) || pixels.data.length < pixels.width * pixels.height * 4) fail3("Contents pixels must be RGBA with width \xD7 height \xD7 4 bytes.");
2877
4396
  return { id, name, width: pixels.width, height: pixels.height, kind: "raster", raster: surfaceFromPixels(pixels, 4, 0, 0) };
2878
4397
  }
2879
4398
  function documentContents(document, width, height, name, id = newContentsId()) {
2880
- if (!(Number.isInteger(width) && Number.isInteger(height) && width > 0 && height > 0)) fail2("Contents width and height must be positive whole numbers.");
4399
+ if (!(Number.isInteger(width) && Number.isInteger(height) && width > 0 && height > 0)) fail3("Contents width and height must be positive whole numbers.");
2881
4400
  return { id, name, width, height, kind: "document", document };
2882
4401
  }
2883
4402
  var placedCache = /* @__PURE__ */ new WeakMap();
@@ -2934,20 +4453,20 @@ function placeContents(layer, canvas, options = {}) {
2934
4453
  }
2935
4454
  source = r;
2936
4455
  }
2937
- const box = { x: 0, y: 0, width: Math.ceil(contents.width / k), height: Math.ceil(contents.height / k) };
4456
+ const box2 = { x: 0, y: 0, width: Math.ceil(contents.width / k), height: Math.ceil(contents.height / k) };
2938
4457
  try {
2939
4458
  if (hasWarp(g)) {
2940
4459
  const f = transformMap(g);
2941
- if (!f) fail2("The placement folds the contents over itself or through the horizon; keep the corners a convex quad.");
2942
- out = warpSurface(source, (p) => f({ x: p.x * k, y: p.y * k }), { interpolation, source: box, clip, step: step > 1 ? step : void 0 });
4460
+ if (!f) fail3("The placement folds the contents over itself or through the horizon; keep the corners a convex quad.");
4461
+ out = warpSurface(source, (p) => f({ x: p.x * k, y: p.y * k }), { interpolation, source: box2, clip, step: step > 1 ? step : void 0 });
2943
4462
  } else {
2944
4463
  const h = transformHomography(g);
2945
- if (!h) fail2("The placement folds the contents over itself or through the horizon; keep the corners a convex quad.");
4464
+ if (!h) fail3("The placement folds the contents over itself or through the horizon; keep the corners a convex quad.");
2946
4465
  const hk = multiplyHomographies([k, 0, 0, 0, k, 0, 0, 0, 1], h);
2947
- out = transformSurface(source, hk, { interpolation, source: box, clip, step: step > 1 ? step : void 0 });
4466
+ out = transformSurface(source, hk, { interpolation, source: box2, clip, step: step > 1 ? step : void 0 });
2948
4467
  }
2949
4468
  } catch (e) {
2950
- if (e instanceof TransformTooLargeError) fail2(e.message);
4469
+ if (e instanceof TransformTooLargeError) fail3(e.message);
2951
4470
  throw e;
2952
4471
  }
2953
4472
  }
@@ -3040,19 +4559,19 @@ function rerender(layer, canvas, action = "change") {
3040
4559
  function normalizePlacement(raw, contents) {
3041
4560
  const c = raw?.corners;
3042
4561
  if (!Array.isArray(c) || c.length !== 4 || !c.every((p) => p && Number.isFinite(p.x) && Number.isFinite(p.y))) {
3043
- fail2("corners must be four finite points [top-left, top-right, bottom-right, bottom-left] in document pixels.");
4562
+ fail3("corners must be four finite points [top-left, top-right, bottom-right, bottom-left] in document pixels.");
3044
4563
  }
3045
4564
  const out = { corners: c.map((p) => ({ x: p.x, y: p.y })) };
3046
4565
  if (raw.warp) {
3047
- if (!isValidWarpMesh(raw.warp)) fail2("warp must be a mesh with us and vs (knots from 0 to 1, increasing) and (3\xB7columns + 1) \xD7 (3\xB7rows + 1) finite points in contents pixels.");
4566
+ if (!isValidWarpMesh(raw.warp)) fail3("warp must be a mesh with us and vs (knots from 0 to 1, increasing) and (3\xB7columns + 1) \xD7 (3\xB7rows + 1) finite points in contents pixels.");
3048
4567
  out.warp = { us: [...raw.warp.us], vs: [...raw.warp.vs], points: raw.warp.points.map((p) => ({ x: p.x, y: p.y })) };
3049
4568
  }
3050
4569
  if (raw.interpolation !== void 0) {
3051
- if (!isInterpolation(raw.interpolation)) fail2(`Unknown interpolation "${String(raw.interpolation)}"; use nearest, bilinear or bicubic.`);
4570
+ if (!isInterpolation(raw.interpolation)) fail3(`Unknown interpolation "${String(raw.interpolation)}"; use nearest, bilinear or bicubic.`);
3052
4571
  if (raw.interpolation !== "bicubic") out.interpolation = raw.interpolation;
3053
4572
  }
3054
4573
  const g = { box: { x: 0, y: 0, width: contents.width, height: contents.height }, corners: out.corners, warp: out.warp ?? null };
3055
- if (!transformHomography(g)) fail2("These corners fold the contents over themselves or through the horizon; keep the quad convex.");
4574
+ if (!transformHomography(g)) fail3("These corners fold the contents over themselves or through the horizon; keep the quad convex.");
3056
4575
  if (out.warp && !hasWarp(g)) delete out.warp;
3057
4576
  return out;
3058
4577
  }
@@ -3117,7 +4636,7 @@ function translateLayer(layer, dx, dy) {
3117
4636
  if (!dx && !dy) return layer;
3118
4637
  const moveMask = (m) => {
3119
4638
  if (!m) return m;
3120
- if (m.source) fail2(`Layer "${layer.name || layer.id}" has a depth mask, which cannot move into a smart object; bake or remove it first.`);
4639
+ if (m.source) fail3(`Layer "${layer.name || layer.id}" has a depth mask, which cannot move into a smart object; bake or remove it first.`);
3121
4640
  return { ...m, surface: translateSurface(m.surface, dx, dy) };
3122
4641
  };
3123
4642
  switch (layer.type) {
@@ -3144,14 +4663,25 @@ function translateLayer(layer, dx, dy) {
3144
4663
  if (!next.mask) delete next.mask;
3145
4664
  return next;
3146
4665
  }
4666
+ case "shape": {
4667
+ const next = {
4668
+ ...layer,
4669
+ components: transformShapeComponents(layer.components, (p) => ({ x: p.x + dx, y: p.y + dy }), translateMatrix(dx, dy)),
4670
+ surface: translateSurface(layer.surface, dx, dy),
4671
+ mask: moveMask(layer.mask)
4672
+ };
4673
+ if (!next.mask) delete next.mask;
4674
+ delete next.annotation;
4675
+ return next;
4676
+ }
3147
4677
  case "annotation":
3148
- return fail2(`Annotation layer "${layer.name || layer.id}" cannot go into a smart object; rasterize it first.`);
4678
+ return fail3(`Annotation layer "${layer.name || layer.id}" cannot go into a smart object; rasterize it first.`);
3149
4679
  }
3150
4680
  }
3151
4681
  function layerExtent(layer, surfaceOf, canvas) {
3152
4682
  const full = { x: 0, y: 0, width: canvas.width, height: canvas.height };
3153
4683
  let b = null;
3154
- if (layer.type === "pixel" || layer.type === "smart") {
4684
+ if (layer.type === "pixel" || layer.type === "smart" || layer.type === "shape") {
3155
4685
  const s = layer.type === "pixel" ? surfaceOf(layer) : layer.surface;
3156
4686
  if (s.defaultValue !== 0) b = full;
3157
4687
  else {
@@ -3179,10 +4709,10 @@ function asSmartFilters(filters) {
3179
4709
  return { enabled: true, filters: filters.map((f) => ({ ...f, blend: "normal", opacity: 1 })) };
3180
4710
  }
3181
4711
  function convertToSmartObject(layers, ids, canvas, options) {
3182
- if (!ids.length) fail2("Give at least one layer id to convert.");
4712
+ if (!ids.length) fail3("Give at least one layer id to convert.");
3183
4713
  const locs = [...new Set(ids)].map((id) => requireLayer(layers, id));
3184
4714
  const parent = locs[0].parent?.id ?? null;
3185
- if (locs.some((l) => (l.parent?.id ?? null) !== parent)) fail2("Layers to convert must share one parent; move them next to each other first.");
4715
+ if (locs.some((l) => (l.parent?.id ?? null) !== parent)) fail3("Layers to convert must share one parent; move them next to each other first.");
3186
4716
  for (const l of locs) requireUnlocked(l.layer, "all", "convert it");
3187
4717
  const ordered = locs.sort((a, b) => a.index - b.index);
3188
4718
  const top = ordered[ordered.length - 1];
@@ -3190,9 +4720,9 @@ function convertToSmartObject(layers, ids, canvas, options) {
3190
4720
  const single = ordered.length === 1 ? ordered[0].layer : null;
3191
4721
  if (single && single.type === "pixel") {
3192
4722
  const surface = options.surfaceOf(single);
3193
- if (surface.defaultValue !== 0) fail2(`Layer "${single.name || single.id}" has no transparent area to crop to; it cannot be converted.`);
4723
+ if (surface.defaultValue !== 0) fail3(`Layer "${single.name || single.id}" has no transparent area to crop to; it cannot be converted.`);
3194
4724
  const b = contentBounds(surface);
3195
- if (!b) fail2(`Layer "${single.name || single.id}" has no pixels to convert; paint something first.`);
4725
+ if (!b) fail3(`Layer "${single.name || single.id}" has no pixels to convert; paint something first.`);
3196
4726
  const contents = {
3197
4727
  id: newContentsId(),
3198
4728
  name: `${single.name || "Layer"}.png`,
@@ -3224,7 +4754,7 @@ function convertToSmartObject(layers, ids, canvas, options) {
3224
4754
  } else {
3225
4755
  let b = null;
3226
4756
  for (const l of ordered) b = union(b, layerExtent(l.layer, options.surfaceOf, canvas));
3227
- if (!b || !(b.width > 0 && b.height > 0)) fail2("The layers have no pixels to convert; paint something first.");
4757
+ if (!b || !(b.width > 0 && b.height > 0)) fail3("The layers have no pixels to convert; paint something first.");
3228
4758
  const nested = ordered.map((l) => {
3229
4759
  let layer = l.layer;
3230
4760
  if (layer.type === "pixel" && layer.source === "document") {
@@ -3232,7 +4762,7 @@ function convertToSmartObject(layers, ids, canvas, options) {
3232
4762
  delete resolved.source;
3233
4763
  layer = resolved;
3234
4764
  }
3235
- if (flattenLayers([layer]).some((x) => x.type === "pixel" && x.source === "document")) fail2(`Layer "${layer.name || layer.id}" holds the document image inside a group; convert the Background on its own.`);
4765
+ if (flattenLayers([layer]).some((x) => x.type === "pixel" && x.source === "document")) fail3(`Layer "${layer.name || layer.id}" holds the document image inside a group; convert the Background on its own.`);
3236
4766
  return translateLayer(layer, -b.x, -b.y);
3237
4767
  });
3238
4768
  if (nested[0].clipped) {
@@ -3297,7 +4827,7 @@ function instancesOf(layers, contentsId) {
3297
4827
  }
3298
4828
  function replaceContents(layers, contentsId, contents, canvas) {
3299
4829
  const ids = instancesOf(layers, contentsId);
3300
- if (!ids.length) fail2(`No smart object uses contents "${contentsId}".`);
4830
+ if (!ids.length) fail3(`No smart object uses contents "${contentsId}".`);
3301
4831
  for (const id of ids) requireUnlocked(findLayer(layers, id).layer, "all", "replace its contents");
3302
4832
  const next = { ...contents, id: contentsId };
3303
4833
  let out = layers;
@@ -3330,12 +4860,12 @@ function refitPlacement(layer, contents) {
3330
4860
  return out;
3331
4861
  }
3332
4862
  function normalizeSmartFilter(raw, id = newLayerId("flt")) {
3333
- if (raw.kind === "psd") fail2("Photoshop filters can only come from a PSD; add develop or blur.");
4863
+ if (raw.kind === "psd") fail3("Photoshop filters can only come from a PSD; add develop or blur.");
3334
4864
  const base = normalizeFilter(raw, id);
3335
4865
  const blend = raw.blend ?? "normal";
3336
- if (!isBlendMode(blend) || blend === "pigment") fail2(`Unknown blend mode "${String(blend)}" for a smart filter.`);
4866
+ if (!isBlendMode(blend) || blend === "pigment") fail3(`Unknown blend mode "${String(blend)}" for a smart filter.`);
3337
4867
  const opacity = raw.opacity ?? 1;
3338
- if (!(typeof opacity === "number" && opacity >= 0 && opacity <= 1)) fail2("opacity must be a number from 0 to 1.");
4868
+ if (!(typeof opacity === "number" && opacity >= 0 && opacity <= 1)) fail3("opacity must be a number from 0 to 1.");
3339
4869
  return { ...base, blend, opacity };
3340
4870
  }
3341
4871
  function withStack(layer, stack, canvas) {
@@ -3350,7 +4880,7 @@ function stackOf(layer) {
3350
4880
  function requireFilter(layer, filterId) {
3351
4881
  const list2 = layer.smartFilters?.filters ?? [];
3352
4882
  const i = list2.findIndex((f) => f.id === filterId);
3353
- if (i === -1) fail2(`Smart object "${layer.name || layer.id}" has no smart filter "${filterId}". Filters: ${list2.map((f) => `${f.id} (${f.kind})`).join(", ") || "none"}; call get_smart_object.`);
4883
+ if (i === -1) fail3(`Smart object "${layer.name || layer.id}" has no smart filter "${filterId}". Filters: ${list2.map((f) => `${f.id} (${f.kind})`).join(", ") || "none"}; call get_smart_object.`);
3354
4884
  return i;
3355
4885
  }
3356
4886
  function editableSmart(layers, id) {
@@ -3371,15 +4901,15 @@ function updateSmartFilter(layers, id, filterId, changes, canvas) {
3371
4901
  const layer = editableSmart(layers, id);
3372
4902
  const i = requireFilter(layer, filterId);
3373
4903
  const current = layer.smartFilters.filters[i];
3374
- if ("kind" in changes && changes.kind !== current.kind) fail2("A smart filter kind cannot change; remove it and add another.");
4904
+ if ("kind" in changes && changes.kind !== current.kind) fail3("A smart filter kind cannot change; remove it and add another.");
3375
4905
  let merged;
3376
4906
  if (current.kind === "psd") {
3377
4907
  const extra = Object.keys(changes).filter((k) => !["enabled", "blend", "opacity", "kind"].includes(k));
3378
- if (extra.length) fail2(`"${current.name}" is a Photoshop filter we keep but do not model; only enabled, blend and opacity change. Unknown: ${extra.join(", ")}.`);
4908
+ if (extra.length) fail3(`"${current.name}" is a Photoshop filter we keep but do not model; only enabled, blend and opacity change. Unknown: ${extra.join(", ")}.`);
3379
4909
  const blend = changes.blend ?? current.blend;
3380
- if (!isBlendMode(blend) || blend === "pigment") fail2(`Unknown blend mode "${String(blend)}" for a smart filter.`);
4910
+ if (!isBlendMode(blend) || blend === "pigment") fail3(`Unknown blend mode "${String(blend)}" for a smart filter.`);
3381
4911
  const opacity = changes.opacity ?? current.opacity;
3382
- if (!(typeof opacity === "number" && opacity >= 0 && opacity <= 1)) fail2("opacity must be a number from 0 to 1.");
4912
+ if (!(typeof opacity === "number" && opacity >= 0 && opacity <= 1)) fail3("opacity must be a number from 0 to 1.");
3383
4913
  merged = { ...current, enabled: changes.enabled === void 0 ? current.enabled : Boolean(changes.enabled), blend, opacity };
3384
4914
  } else {
3385
4915
  merged = normalizeSmartFilter({ ...current, ...changes, kind: current.kind }, current.id);
@@ -3401,7 +4931,7 @@ function moveSmartFilter(layers, id, filterId, index, canvas) {
3401
4931
  const layer = editableSmart(layers, id);
3402
4932
  const i = requireFilter(layer, filterId);
3403
4933
  const list2 = layer.smartFilters.filters;
3404
- if (!Number.isInteger(index) || index < 0 || index >= list2.length) fail2(`index must be a whole number from 0 to ${list2.length - 1}.`);
4934
+ if (!Number.isInteger(index) || index < 0 || index >= list2.length) fail3(`index must be a whole number from 0 to ${list2.length - 1}.`);
3405
4935
  const filters = [...list2];
3406
4936
  const [f] = filters.splice(i, 1);
3407
4937
  filters.splice(index, 0, f);
@@ -3415,7 +4945,7 @@ function setSmartFiltersEnabled(layers, id, enabled, canvas) {
3415
4945
  }
3416
4946
  function setSmartFilterMask(layers, id, mask, canvas) {
3417
4947
  const layer = editableSmart(layers, id);
3418
- if (mask?.source) fail2("A smart filter mask is painted, not generated; use a raster mask.");
4948
+ if (mask?.source) fail3("A smart filter mask is painted, not generated; use a raster mask.");
3419
4949
  const stack = { ...stackOf(layer) };
3420
4950
  if (mask) stack.mask = mask;
3421
4951
  else delete stack.mask;
@@ -3424,39 +4954,13 @@ function setSmartFilterMask(layers, id, mask, canvas) {
3424
4954
  }
3425
4955
  function withFilterMaskSurface(layer, surface, canvas) {
3426
4956
  const stack = stackOf(layer);
3427
- if (!stack.mask) fail2(`Smart object "${layer.name || layer.id}" has no filter mask; add one with set_smart_filter_mask.`);
4957
+ if (!stack.mask) fail3(`Smart object "${layer.name || layer.id}" has no filter mask; add one with set_smart_filter_mask.`);
3428
4958
  return withStack(layer, { ...stack, mask: { ...stack.mask, surface } }, canvas);
3429
4959
  }
3430
4960
  function placementBounds(layer) {
3431
4961
  return transformedBox(placementGeometry(layer));
3432
4962
  }
3433
4963
 
3434
- // src/image-editor/layers/defaults.ts
3435
- var DEFAULT_ADJUSTMENTS_VALUES = {
3436
- brightness: 0,
3437
- contrast: 0,
3438
- saturation: 0,
3439
- exposure: 0,
3440
- temperature: 0,
3441
- tint: 0,
3442
- blur: 0,
3443
- opacity: 1,
3444
- process: LATEST_PROCESS_VERSION
3445
- };
3446
- var DEFAULT_TRANSFORM_VALUES = {
3447
- rotate: 0,
3448
- flipH: false,
3449
- flipV: false
3450
- };
3451
- var DEFAULT_DEPTH_MASK_VALUES = {
3452
- enabled: false,
3453
- maskSource: null,
3454
- mode: "threshold",
3455
- depthRange: [0, 255],
3456
- softness: 10,
3457
- invert: false
3458
- };
3459
-
3460
4964
  // src/image-editor/layers/serialize.ts
3461
4965
  var LAYERED_SCHEMA_VERSION = 3;
3462
4966
  function serializePatterns(patterns) {
@@ -3569,6 +5073,60 @@ function serializeLayer(layer, ctx) {
3569
5073
  return { ...base, type: "annotation", annotation: JSON.parse(JSON.stringify(layer.annotation)) };
3570
5074
  case "smart":
3571
5075
  return serializeSmartObject(layer, base, ctx);
5076
+ case "shape":
5077
+ return serializeShape(layer, base);
5078
+ }
5079
+ }
5080
+ function serializeShape(layer, base) {
5081
+ const out = {
5082
+ ...base,
5083
+ type: "shape",
5084
+ components: JSON.parse(JSON.stringify(layer.components)),
5085
+ fill: layer.fill ? JSON.parse(JSON.stringify(layer.fill)) : null,
5086
+ stroke: layer.stroke ? JSON.parse(JSON.stringify(layer.stroke)) : null
5087
+ };
5088
+ if (layer.fillRule === "evenodd") out.fillRule = "evenodd";
5089
+ if (layer.fillOpacity !== void 0 && layer.fillOpacity !== 1) out.fillOpacity = layer.fillOpacity;
5090
+ if (layer.annotation) out.annotation = JSON.parse(JSON.stringify(layer.annotation));
5091
+ if (layer.psd?.blocks?.length) out.psd = { blocks: layer.psd.blocks.map((b) => ({ key: b.key, data: bytesToBase64(b.data) })) };
5092
+ return out;
5093
+ }
5094
+ function readShape(r, base, ctx) {
5095
+ const label = `Layer "${base.name || base.id}"`;
5096
+ const canvas = ctx.canvas ?? { width: 1, height: 1 };
5097
+ const env = { ...canvas, pattern: ctx.pattern };
5098
+ try {
5099
+ let data = null;
5100
+ const a = r.annotation && typeof r.annotation === "object" && typeof r.annotation.type === "string" ? JSON.parse(JSON.stringify({ ...r.annotation, id: r.annotation.id ?? base.id })) : void 0;
5101
+ if (a) data = shapeFromAnnotation(a);
5102
+ if (!data) {
5103
+ if (!Array.isArray(r.components) || !r.components.length) throw new Error("it has no components");
5104
+ const components = r.components.map((c, i) => {
5105
+ const op = requireOperation(c?.op, `components[${i}].op`);
5106
+ if (c?.live) {
5107
+ const live = normalizeLiveShape(c.live, `components[${i}].live`);
5108
+ return { op, subpaths: liveShapeSubpaths(live), live };
5109
+ }
5110
+ return { op, subpaths: Array.isArray(c?.subpaths) && !c.subpaths.length ? [] : normalizeSubpaths(c?.subpaths, `components[${i}].subpaths`) };
5111
+ });
5112
+ data = {
5113
+ components,
5114
+ fill: r.fill === null || r.fill === void 0 ? null : normalizeShapePaint(r.fill, "fill"),
5115
+ stroke: r.stroke === null || r.stroke === void 0 ? null : normalizeShapeStroke(r.stroke, "stroke")
5116
+ };
5117
+ }
5118
+ const layer = createShapeLayer({ ...data, fillRule: r.fillRule === "evenodd" ? "evenodd" : "nonzero" }, env, base.id);
5119
+ const out = { ...layer, ...base, type: "shape", components: layer.components, fill: layer.fill, stroke: layer.stroke, surface: layer.surface, offset: { x: 0, y: 0 } };
5120
+ if (typeof r.fillOpacity === "number" && r.fillOpacity !== 1) out.fillOpacity = num(r.fillOpacity, 0, 1, 1);
5121
+ if (a && shapeFromAnnotation(a)) out.annotation = a;
5122
+ if (r.psd && Array.isArray(r.psd.blocks)) {
5123
+ const blocks = r.psd.blocks.filter((b) => b && typeof b.key === "string" && typeof b.data === "string").map((b) => ({ key: b.key, data: base64ToBytes(b.data) }));
5124
+ if (blocks.length) out.psd = { blocks };
5125
+ }
5126
+ return out;
5127
+ } catch (e) {
5128
+ ctx.problems.push(`${label}: the shape could not be read (${e instanceof Error ? e.message : String(e)})`);
5129
+ return null;
3572
5130
  }
3573
5131
  }
3574
5132
  function serializeSmartFilters(stack, layerId, ctx) {
@@ -3827,8 +5385,16 @@ function deserializeLayer(raw, ctx) {
3827
5385
  ctx.problems.push(`Layer "${base.name || id}": annotation is missing`);
3828
5386
  return null;
3829
5387
  }
3830
- return { ...base, type: "annotation", annotation: JSON.parse(JSON.stringify({ ...a, id: typeof a.id === "string" ? a.id : id })) };
5388
+ const annotation = JSON.parse(JSON.stringify({ ...a, id: typeof a.id === "string" ? a.id : id }));
5389
+ const migrated = annotationLayerFor(annotation, ctx.canvas ?? { width: 1, height: 1 });
5390
+ if (migrated.type === "shape") {
5391
+ const blend = base.blend === "normal" ? migrated.blend : base.blend;
5392
+ return { ...migrated, ...base, blend, type: "shape", id, name: base.name };
5393
+ }
5394
+ return { ...base, type: "annotation", annotation };
3831
5395
  }
5396
+ case "shape":
5397
+ return readShape(r, base, ctx);
3832
5398
  case "smart":
3833
5399
  return readSmartObject(r, base, ctx);
3834
5400
  default:
@@ -7893,6 +9459,8 @@ export {
7893
9459
  applyBoxBlur,
7894
9460
  applyDepthMask,
7895
9461
  removeAlpha,
9462
+ SHAPE_OPERATIONS,
9463
+ LIVE_SHAPE_KINDS,
7896
9464
  newLayerId,
7897
9465
  pixelLayers,
7898
9466
  developFilter,
@@ -7902,6 +9470,8 @@ export {
7902
9470
  pixelLayerBounds,
7903
9471
  layerTypeLabel,
7904
9472
  hasSurface,
9473
+ isRenderedLayer,
9474
+ shapeLayers,
7905
9475
  smartObjectLayers,
7906
9476
  adjustmentNodeAsLayer,
7907
9477
  depthMaskAlpha,
@@ -7910,28 +9480,9 @@ export {
7910
9480
  DEFAULT_ADJUSTMENTS_VALUES,
7911
9481
  DEFAULT_TRANSFORM_VALUES,
7912
9482
  DEFAULT_DEPTH_MASK_VALUES,
7913
- FREE_TRANSFORM_MODES,
7914
- TRANSFORM_ANCHORS,
7915
- isTransformAnchor,
7916
- identityTransform,
7917
- cornersFromMatrix,
7918
- matrixFromCorners,
7919
- transformHomography,
7920
- hasWarp,
7921
- isIdentityTransform,
7922
- transformMap,
7923
- transformedPoint,
7924
- anchorPoint,
7925
- transformedBox,
7926
- referencePoint,
7927
- matrixFromParams,
7928
- describeTransform,
7929
- applyParams,
7930
- flipCorners,
7931
- boxPointOf,
7932
- presetWarp,
7933
- isValidWarp,
7934
- geometryHomography,
9483
+ flattenPath,
9484
+ rasterizeCoverage,
9485
+ parseCssColor,
7935
9486
  LayerOperationError,
7936
9487
  requireLayer,
7937
9488
  requireUnlocked,
@@ -7957,6 +9508,76 @@ export {
7957
9508
  setLayerEffectsEnabled,
7958
9509
  parseBlend,
7959
9510
  unionRect,
9511
+ SHAPE_RENDER_MARGIN,
9512
+ DEFAULT_MITER_LIMIT,
9513
+ mapSubpaths,
9514
+ liveShapeSubpaths,
9515
+ liveComponent,
9516
+ componentPath,
9517
+ shapeOutline,
9518
+ shapeGeometryBounds,
9519
+ shapeRenderClip,
9520
+ renderShapeRect,
9521
+ renderShape,
9522
+ rerenderShape,
9523
+ normalizeShapePaint,
9524
+ normalizeShapeStroke,
9525
+ normalizeLiveShape,
9526
+ normalizeSubpaths,
9527
+ requireOperation,
9528
+ requireShapeLayer,
9529
+ createShapeLayer,
9530
+ defaultShapeName,
9531
+ updateShapeLayer,
9532
+ transformShapeComponents,
9533
+ offsetShape,
9534
+ mergeShapeComponents,
9535
+ shapeAsPixelLayer,
9536
+ shapeFromAnnotation,
9537
+ annotationBlend,
9538
+ annotationOfShape,
9539
+ BACKGROUND_LAYER_ID,
9540
+ BACKGROUND_LAYER_NAME,
9541
+ DEVELOP_FILTER_ID,
9542
+ BLUR_FILTER_ID,
9543
+ rasterMaskFromLegacy,
9544
+ hasResampledLegacyMask,
9545
+ legacyMaskOf,
9546
+ isDefaultDepthMask,
9547
+ developFromAdjustments,
9548
+ annotationLayerName,
9549
+ annotationLayerFor,
9550
+ annotationOfLayer,
9551
+ adjustmentNodeFromLegacy,
9552
+ backgroundLayer,
9553
+ depthRasterMask,
9554
+ layersFromLegacy,
9555
+ legacyAdjustmentLayer,
9556
+ developTarget,
9557
+ legacyView,
9558
+ isLegacyShape,
9559
+ FREE_TRANSFORM_MODES,
9560
+ TRANSFORM_ANCHORS,
9561
+ isTransformAnchor,
9562
+ identityTransform,
9563
+ cornersFromMatrix,
9564
+ matrixFromCorners,
9565
+ transformHomography,
9566
+ hasWarp,
9567
+ isIdentityTransform,
9568
+ transformMap,
9569
+ transformedPoint,
9570
+ anchorPoint,
9571
+ transformedBox,
9572
+ referencePoint,
9573
+ matrixFromParams,
9574
+ describeTransform,
9575
+ applyParams,
9576
+ flipCorners,
9577
+ boxPointOf,
9578
+ presetWarp,
9579
+ isValidWarp,
9580
+ geometryHomography,
7960
9581
  SMART_RENDER_MARGIN,
7961
9582
  newContentsId,
7962
9583
  contentsBox,
@@ -8033,4 +9654,4 @@ export {
8033
9654
  smartContentsFile,
8034
9655
  exportPsd
8035
9656
  };
8036
- //# sourceMappingURL=chunk-SOROZ2BF.js.map
9657
+ //# sourceMappingURL=chunk-DMRSJBJF.js.map