@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.
- package/dist/animation/index.cjs +5 -5
- package/dist/animation/index.cjs.map +1 -1
- package/dist/animation/index.js +9 -9
- package/dist/animation-editor/index.cjs +59 -59
- package/dist/animation-editor/index.cjs.map +1 -1
- package/dist/animation-editor/index.d.cts +2 -1
- package/dist/animation-editor/index.d.ts +2 -1
- package/dist/animation-editor/index.js +29 -29
- package/dist/blending-DXl06NSx.d.ts +486 -0
- package/dist/blending-aOE6Y3UE.d.cts +486 -0
- package/dist/canvas/index.cjs +27 -27
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.js +11 -11
- package/dist/{chunk-2FGRTYJN.cjs → chunk-477UVMNR.cjs} +3 -3
- package/dist/{chunk-2FGRTYJN.cjs.map → chunk-477UVMNR.cjs.map} +1 -1
- package/dist/chunk-7VOHOPLW.cjs +1136 -0
- package/dist/chunk-7VOHOPLW.cjs.map +1 -0
- package/dist/{chunk-HUQ7EHVI.js → chunk-BLWCAQCW.js} +723 -1975
- package/dist/chunk-BLWCAQCW.js.map +1 -0
- package/dist/{chunk-SOROZ2BF.js → chunk-DMRSJBJF.js} +1939 -318
- package/dist/chunk-DMRSJBJF.js.map +1 -0
- package/dist/chunk-FTBZPI77.cjs +1 -0
- package/dist/chunk-FTBZPI77.cjs.map +1 -0
- package/dist/{chunk-WMBIJCHA.js → chunk-HNS7UHCM.js} +5 -1
- package/dist/{chunk-WMBIJCHA.js.map → chunk-HNS7UHCM.js.map} +1 -1
- package/dist/chunk-JLYX4V57.cjs +2001 -0
- package/dist/chunk-JLYX4V57.cjs.map +1 -0
- package/dist/chunk-KB2YBCVS.js +2001 -0
- package/dist/chunk-KB2YBCVS.js.map +1 -0
- package/dist/{chunk-G4BIYE6R.js → chunk-LQEEDHOP.js} +40 -2
- package/dist/chunk-LQEEDHOP.js.map +1 -0
- package/dist/{chunk-FRD3FUXI.cjs → chunk-MGL4U5WA.cjs} +2144 -523
- package/dist/chunk-MGL4U5WA.cjs.map +1 -0
- package/dist/{chunk-T3QZ6S47.cjs → chunk-POGBUYWE.cjs} +131 -93
- package/dist/chunk-POGBUYWE.cjs.map +1 -0
- package/dist/chunk-VKK5VS2T.js +1 -0
- package/dist/{chunk-UTEVPQTS.cjs → chunk-W3GXSUSQ.cjs} +746 -1998
- package/dist/chunk-W3GXSUSQ.cjs.map +1 -0
- package/dist/{chunk-SZ6KMZNM.cjs → chunk-XIOMMGPD.cjs} +6 -2
- package/dist/chunk-XIOMMGPD.cjs.map +1 -0
- package/dist/{chunk-JK6M6V7G.js → chunk-Z3YWVUWT.js} +4 -4
- package/dist/{chunk-3PHA3UKI.js → chunk-Z4BC6M74.js} +523 -121
- package/dist/chunk-Z4BC6M74.js.map +1 -0
- package/dist/{export-a9bnF7Cl.d.ts → export-CO2ryu3I.d.ts} +419 -18
- package/dist/{export-JGWQEPJn.d.cts → export-DRiLzP_j.d.cts} +419 -18
- package/dist/image-editor/index.cjs +1665 -1113
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +78 -14
- package/dist/image-editor/index.d.ts +78 -14
- package/dist/image-editor/index.js +843 -291
- package/dist/image-editor/index.js.map +1 -1
- package/dist/paint/index.cjs +28 -4
- package/dist/paint/index.cjs.map +1 -1
- package/dist/paint/index.d.cts +3 -89
- package/dist/paint/index.d.ts +3 -89
- package/dist/paint/index.js +27 -3
- package/dist/paint-editor/index.cjs +1056 -287
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +304 -22
- package/dist/paint-editor/index.d.ts +304 -22
- package/dist/paint-editor/index.js +852 -83
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/raster/index.cjs +7 -3
- package/dist/raster/index.cjs.map +1 -1
- package/dist/raster/index.d.cts +3 -129
- package/dist/raster/index.d.ts +3 -129
- package/dist/raster/index.js +6 -2
- package/dist/{compositor-D92URMlm.d.cts → render-BEriFuqK.d.cts} +136 -2
- package/dist/{compositor-Kt_M4ruo.d.ts → render-tjzKaik-.d.ts} +136 -2
- package/dist/{surface-CkKOHGPm.d.cts → surface-C8RRg_Bh.d.cts} +109 -1
- package/dist/{surface-CkKOHGPm.d.ts → surface-C8RRg_Bh.d.ts} +109 -1
- package/dist/{svg-SseJTIrA.d.ts → svg-D_cfkhlB.d.cts} +3 -629
- package/dist/{svg-ClVIG4T7.d.cts → svg-wIaKLrU0.d.ts} +3 -629
- package/dist/types-DtL_qLFz.d.ts +630 -0
- package/dist/types-DyQq-WPY.d.cts +630 -0
- package/dist/vector/index.cjs +563 -563
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +4 -2
- package/dist/vector/index.d.ts +4 -2
- package/dist/vector/index.js +69 -69
- package/dist/video-editor/index.cjs +3 -3
- package/dist/video-editor/index.js +2 -2
- package/package.json +1 -1
- package/skills/pixerate-paint-editor/SKILL.md +49 -13
- package/dist/chunk-3PHA3UKI.js.map +0 -1
- package/dist/chunk-6BU5WU4R.cjs +0 -1
- package/dist/chunk-6BU5WU4R.cjs.map +0 -1
- package/dist/chunk-DWGZUSES.cjs +0 -694
- package/dist/chunk-DWGZUSES.cjs.map +0 -1
- package/dist/chunk-FRD3FUXI.cjs.map +0 -1
- package/dist/chunk-G4BIYE6R.js.map +0 -1
- package/dist/chunk-HUFQD4CB.cjs +0 -734
- package/dist/chunk-HUFQD4CB.cjs.map +0 -1
- package/dist/chunk-HUQ7EHVI.js.map +0 -1
- package/dist/chunk-KKREERP2.js +0 -694
- package/dist/chunk-KKREERP2.js.map +0 -1
- package/dist/chunk-SOROZ2BF.js.map +0 -1
- package/dist/chunk-SZ6KMZNM.cjs.map +0 -1
- package/dist/chunk-T3QZ6S47.cjs.map +0 -1
- package/dist/chunk-UTEVPQTS.cjs.map +0 -1
- package/dist/chunk-XYBJKUAU.js +0 -1
- package/dist/mixer-5ziHU8OS.d.cts +0 -282
- package/dist/mixer-DNqiYXYQ.d.ts +0 -282
- /package/dist/{chunk-XYBJKUAU.js.map → chunk-VKK5VS2T.js.map} +0 -0
- /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-
|
|
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-
|
|
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-
|
|
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
|
|
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 (
|
|
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
|
-
/**
|
|
2326
|
-
|
|
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/
|
|
2343
|
-
var
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
}
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
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
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
const
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
const
|
|
2441
|
-
|
|
2442
|
-
}
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
if (
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2479
|
-
|
|
2480
|
-
const
|
|
2481
|
-
const
|
|
2482
|
-
|
|
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
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
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
|
|
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
|
|
2743
|
-
|
|
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
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
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
|
|
2754
|
-
|
|
2755
|
-
const
|
|
2756
|
-
|
|
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
|
|
2763
|
-
|
|
2764
|
-
const
|
|
2765
|
-
const
|
|
2766
|
-
const
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
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
|
|
2777
|
-
const
|
|
2778
|
-
const
|
|
2779
|
-
|
|
2780
|
-
|
|
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
|
|
2783
|
-
const
|
|
2784
|
-
const
|
|
2785
|
-
const
|
|
2786
|
-
|
|
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
|
|
2791
|
-
const
|
|
2792
|
-
|
|
2793
|
-
const
|
|
2794
|
-
return
|
|
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
|
|
2797
|
-
const
|
|
2798
|
-
if (
|
|
2799
|
-
|
|
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
|
|
2808
|
-
|
|
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
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
const
|
|
2818
|
-
return
|
|
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
|
|
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
|
-
|
|
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")
|
|
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)
|
|
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))
|
|
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
|
|
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)
|
|
2942
|
-
out = warpSurface(source, (p) => f({ x: p.x * k, y: p.y * k }), { interpolation, source:
|
|
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)
|
|
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:
|
|
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)
|
|
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
|
-
|
|
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))
|
|
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))
|
|
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))
|
|
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)
|
|
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
|
|
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)
|
|
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))
|
|
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)
|
|
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)
|
|
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))
|
|
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"))
|
|
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)
|
|
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")
|
|
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")
|
|
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))
|
|
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)
|
|
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)
|
|
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)
|
|
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")
|
|
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))
|
|
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)
|
|
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)
|
|
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)
|
|
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
|
-
|
|
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
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
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-
|
|
9657
|
+
//# sourceMappingURL=chunk-DMRSJBJF.js.map
|