@pixerate/editor 0.189.0 → 0.191.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/{chunk-ED4L4UD6.cjs → chunk-2UR4OX3B.cjs} +1002 -787
  2. package/dist/{chunk-M7XIC2FC.js → chunk-RQ2EM5TY.js} +333 -0
  3. package/dist/{chunk-ZEHTUR7Z.js → chunk-TXYXF32N.js} +968 -753
  4. package/dist/{chunk-5UEFNX24.cjs → chunk-ZQGZWFPU.cjs} +344 -11
  5. package/dist/image-editor/controller.d.cts +73 -1
  6. package/dist/image-editor/controller.d.ts +73 -1
  7. package/dist/image-editor/free-transform.d.cts +3 -6
  8. package/dist/image-editor/free-transform.d.ts +3 -6
  9. package/dist/image-editor/index.cjs +1382 -904
  10. package/dist/image-editor/index.d.cts +1 -0
  11. package/dist/image-editor/index.d.ts +1 -0
  12. package/dist/image-editor/index.js +485 -7
  13. package/dist/image-editor/layer-tools.d.cts +1 -1
  14. package/dist/image-editor/layer-tools.d.ts +1 -1
  15. package/dist/image-editor/layers/compositor.d.cts +3 -0
  16. package/dist/image-editor/layers/compositor.d.ts +3 -0
  17. package/dist/image-editor/layers/model.d.cts +23 -0
  18. package/dist/image-editor/layers/model.d.ts +23 -0
  19. package/dist/image-editor/layers/panel.d.cts +11 -1
  20. package/dist/image-editor/layers/panel.d.ts +11 -1
  21. package/dist/image-editor/layers/serialize.d.cts +11 -0
  22. package/dist/image-editor/layers/serialize.d.ts +11 -0
  23. package/dist/image-editor/layers/shape.d.cts +5 -0
  24. package/dist/image-editor/layers/shape.d.ts +5 -0
  25. package/dist/image-editor/layers/vector-mask.d.cts +51 -0
  26. package/dist/image-editor/layers/vector-mask.d.ts +51 -0
  27. package/dist/image-editor/psd/import.d.cts +0 -7
  28. package/dist/image-editor/psd/import.d.ts +0 -7
  29. package/dist/image-editor/selection/index.d.cts +1 -0
  30. package/dist/image-editor/selection/index.d.ts +1 -0
  31. package/dist/image-editor/selection/path.d.cts +37 -0
  32. package/dist/image-editor/selection/path.d.ts +37 -0
  33. package/dist/paint-editor/index.cjs +6 -6
  34. package/dist/paint-editor/index.js +1 -1
  35. package/dist/vector/index.cjs +21 -303
  36. package/dist/vector/index.js +15 -297
  37. package/dist/vector/trace.d.cts +8 -1
  38. package/dist/vector/trace.d.ts +8 -1
  39. package/package.json +1 -1
  40. package/skills/pixerate-image-editor/SKILL.md +8 -0
  41. package/skills/pixerate-image-selection-masking/SKILL.md +10 -2
@@ -1,4 +1,332 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/text/opentype.ts
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
+
3
+
4
+ var _chunkNM3G4VQTcjs = require('./chunk-NM3G4VQT.cjs');
5
+
6
+ // src/vector/trace.ts
7
+ var VECTOR_TRACE_MAX_PIXELS = 4e6;
8
+ function signedArea(p) {
9
+ let a = 0;
10
+ for (let i = 0; i < p.length; i++) {
11
+ const [x0, y0] = p[i];
12
+ const [x1, y1] = p[(i + 1) % p.length];
13
+ a += x0 * y1 - x1 * y0;
14
+ }
15
+ return a / 2;
16
+ }
17
+ function pointInPoly(p, poly) {
18
+ let inside = false;
19
+ for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
20
+ const [xi, yi] = poly[i];
21
+ const [xj, yj] = poly[j];
22
+ if (yi > p[1] !== yj > p[1] && p[0] < (xj - xi) * (p[1] - yi) / (yj - yi) + xi) inside = !inside;
23
+ }
24
+ return inside;
25
+ }
26
+ function vectorTraceContours(field, w, h, threshold) {
27
+ const gw = w + 2;
28
+ const gh = h + 2;
29
+ const val = (i, j) => i === 0 || j === 0 || i > w || j > h ? -Infinity : field[(j - 1) * w + (i - 1)];
30
+ const inside = (i, j) => val(i, j) >= threshold;
31
+ const pos = (i, j) => [i - 0.5, j - 0.5];
32
+ const cross = (a, b) => {
33
+ const va = val(a[0], a[1]);
34
+ const vb = val(b[0], b[1]);
35
+ let t;
36
+ if (Number.isFinite(va) && Number.isFinite(vb) && Math.abs(vb - va) > 1e-12) t = Math.min(1, Math.max(0, (threshold - va) / (vb - va)));
37
+ else if (Number.isFinite(va)) t = va >= threshold ? 0.5 : 0;
38
+ else t = vb >= threshold ? 0.5 : 1;
39
+ const pa = pos(a[0], a[1]);
40
+ const pb = pos(b[0], b[1]);
41
+ return [pa[0] + (pb[0] - pa[0]) * t, pa[1] + (pb[1] - pa[1]) * t];
42
+ };
43
+ const hid = (i, j) => 2 * (j * gw + i);
44
+ const vid = (i, j) => 2 * (j * gw + i) + 1;
45
+ const next = /* @__PURE__ */ new Map();
46
+ const point = /* @__PURE__ */ new Map();
47
+ for (let j = 0; j < gh - 1; j++) {
48
+ for (let i = 0; i < gw - 1; i++) {
49
+ const tl = inside(i, j);
50
+ const tr = inside(i + 1, j);
51
+ const br = inside(i + 1, j + 1);
52
+ const bl = inside(i, j + 1);
53
+ const code = +tl << 3 | +tr << 2 | +br << 1 | +bl;
54
+ if (code === 0 || code === 15) continue;
55
+ const top = hid(i, j);
56
+ const bottom = hid(i, j + 1);
57
+ const left = vid(i, j);
58
+ const right = vid(i + 1, j);
59
+ const seg = (a, b) => next.set(a, b);
60
+ const mean = () => (val(i, j) + val(i + 1, j) + val(i + 1, j + 1) + val(i, j + 1)) / 4;
61
+ switch (code) {
62
+ case 1:
63
+ seg(left, bottom);
64
+ break;
65
+ case 2:
66
+ seg(bottom, right);
67
+ break;
68
+ case 3:
69
+ seg(left, right);
70
+ break;
71
+ case 4:
72
+ seg(right, top);
73
+ break;
74
+ case 5:
75
+ if (mean() >= threshold) {
76
+ seg(left, top);
77
+ seg(right, bottom);
78
+ } else {
79
+ seg(left, bottom);
80
+ seg(right, top);
81
+ }
82
+ break;
83
+ case 6:
84
+ seg(bottom, top);
85
+ break;
86
+ case 7:
87
+ seg(left, top);
88
+ break;
89
+ case 8:
90
+ seg(top, left);
91
+ break;
92
+ case 9:
93
+ seg(top, bottom);
94
+ break;
95
+ case 10:
96
+ if (mean() >= threshold) {
97
+ seg(top, right);
98
+ seg(bottom, left);
99
+ } else {
100
+ seg(top, left);
101
+ seg(bottom, right);
102
+ }
103
+ break;
104
+ case 11:
105
+ seg(top, right);
106
+ break;
107
+ case 12:
108
+ seg(right, left);
109
+ break;
110
+ case 13:
111
+ seg(right, bottom);
112
+ break;
113
+ case 14:
114
+ seg(bottom, left);
115
+ break;
116
+ }
117
+ const edges = [
118
+ [top, [i, j], [i + 1, j]],
119
+ [bottom, [i, j + 1], [i + 1, j + 1]],
120
+ [left, [i, j], [i, j + 1]],
121
+ [right, [i + 1, j], [i + 1, j + 1]]
122
+ ];
123
+ for (const [e, a, b] of edges) if (inside(a[0], a[1]) !== inside(b[0], b[1]) && !point.has(e)) point.set(e, cross(a, b));
124
+ }
125
+ }
126
+ const out = [];
127
+ for (const start of [...next.keys()].sort((a, b) => a - b)) {
128
+ if (!next.has(start)) continue;
129
+ const poly = [];
130
+ let e = start;
131
+ for (; ; ) {
132
+ const n = next.get(e);
133
+ if (n === void 0) break;
134
+ next.delete(e);
135
+ const p = point.get(e);
136
+ if (p) poly.push(p);
137
+ e = n;
138
+ if (e === start) break;
139
+ }
140
+ if (poly.length >= 3) out.push(poly);
141
+ }
142
+ return out;
143
+ }
144
+ function squareContourCorners(ring) {
145
+ const n = ring.length;
146
+ if (n < 4) return ring;
147
+ const eq = (u, v) => Math.abs(u - v) < 1e-9;
148
+ const out = [];
149
+ for (let i = 0; i < n; i++) {
150
+ const a = ring[i];
151
+ const b = ring[(i + 1) % n];
152
+ const p = ring[(i - 1 + n) % n];
153
+ const q = ring[(i + 2) % n];
154
+ const diagonal = Math.abs(Math.abs(b[0] - a[0]) - 0.5) < 0.02 && Math.abs(Math.abs(b[1] - a[1]) - 0.5) < 0.02;
155
+ const wraps = i === n - 1;
156
+ let corner = null;
157
+ if (diagonal && (!wraps || out[0] === ring[0])) {
158
+ if (eq(p[1], a[1]) && !eq(p[0], a[0]) && eq(q[0], b[0]) && !eq(q[1], b[1])) corner = [b[0], a[1]];
159
+ else if (eq(p[0], a[0]) && !eq(p[1], a[1]) && eq(q[1], b[1]) && !eq(q[0], b[0])) corner = [a[0], b[1]];
160
+ }
161
+ if (!corner) {
162
+ out.push(a);
163
+ continue;
164
+ }
165
+ out.push(corner);
166
+ if (wraps) out.shift();
167
+ else i++;
168
+ }
169
+ if (out.length < 3) return ring;
170
+ const m = out.length;
171
+ for (let k = 0; k < m; k++) {
172
+ const p = out[(k - 1 + m) % m];
173
+ const a = out[k];
174
+ const q = out[(k + 1) % m];
175
+ if (Math.abs((a[0] - p[0]) * (q[1] - a[1]) - (a[1] - p[1]) * (q[0] - a[0])) > 1e-6) return k ? [...out.slice(k), ...out.slice(0, k)] : out;
176
+ }
177
+ return out;
178
+ }
179
+ function loopsOf(field, w, h, threshold, tolerance, minArea, square = false) {
180
+ const loops = [];
181
+ for (const contour of vectorTraceContours(field, w, h, threshold)) {
182
+ const raw = square ? squareContourCorners(contour) : contour;
183
+ const area = signedArea(raw);
184
+ if (Math.abs(area) < Math.max(0, minArea)) continue;
185
+ const simple = _chunkNM3G4VQTcjs.simplifyRing.call(void 0, raw, Math.max(0.01, tolerance));
186
+ if (simple.length < 3) continue;
187
+ loops.push({ raw, simple, area, depth: 0 });
188
+ }
189
+ for (const l of loops) l.depth = loops.filter((o) => o !== l && Math.abs(o.area) > Math.abs(l.area) && pointInPoly(l.simple[0], o.simple)).length;
190
+ return loops.sort((a, b) => a.depth - b.depth || Math.abs(b.area) - Math.abs(a.area));
191
+ }
192
+ function loopSubpath(l, tolerance, roundness) {
193
+ if (roundness <= 0) return { closed: true, anchors: l.simple.map(([x, y]) => ({ x, y })) };
194
+ const cornerAngleDeg = 25 + roundness * 125;
195
+ const cubics = _chunkNM3G4VQTcjs.fitCubics.call(void 0, l.raw, Math.max(0.05, tolerance), {
196
+ closed: true,
197
+ cornerAngleDeg
198
+ });
199
+ if (!cubics.length) return { closed: true, anchors: l.simple.map(([x, y]) => ({ x, y })) };
200
+ const anchors = cubics.map((c, k) => {
201
+ const prev = cubics[(k - 1 + cubics.length) % cubics.length];
202
+ return {
203
+ x: c.p0[0],
204
+ y: c.p0[1],
205
+ in: { x: prev.p2[0], y: prev.p2[1] },
206
+ out: { x: c.p1[0], y: c.p1[1] }
207
+ };
208
+ });
209
+ return { closed: true, anchors };
210
+ }
211
+ function traceField(field, w, h, threshold, o) {
212
+ const tolerance = _nullishCoalesce(o.tolerance, () => ( 1));
213
+ const roundness = Math.min(1, Math.max(0, _nullishCoalesce(o.cornerRoundness, () => ( 0.5))));
214
+ return loopsOf(field, w, h, threshold, tolerance, _nullishCoalesce(o.minArea, () => ( 10)), o.squareCorners === true).map((l) => loopSubpath(l, tolerance, roundness));
215
+ }
216
+ function quantizeVectorTraceColors(pixels, count) {
217
+ const n = pixels.width * pixels.height;
218
+ const d = pixels.data;
219
+ const opaque = [];
220
+ for (let i = 0; i < n; i++) if (d[i * 4 + 3] >= 128) opaque.push(i);
221
+ const boxes = opaque.length ? [opaque] : [];
222
+ const range = (box, c) => {
223
+ let lo = 255;
224
+ let hi = 0;
225
+ for (const i of box) {
226
+ const v = d[i * 4 + c];
227
+ if (v < lo) lo = v;
228
+ if (v > hi) hi = v;
229
+ }
230
+ return hi - lo;
231
+ };
232
+ while (boxes.length < count) {
233
+ let best = -1;
234
+ let bestRange = 0;
235
+ let bestC = 0;
236
+ boxes.forEach((b, k) => {
237
+ if (b.length < 2) return;
238
+ for (let c = 0; c < 3; c++) {
239
+ const r = range(b, c);
240
+ if (r > bestRange) {
241
+ bestRange = r;
242
+ best = k;
243
+ bestC = c;
244
+ }
245
+ }
246
+ });
247
+ if (best < 0) break;
248
+ const box = boxes[best].slice().sort((a, b) => d[a * 4 + bestC] - d[b * 4 + bestC]);
249
+ const key = (k) => d[box[k] * 4 + bestC];
250
+ const v = key(box.length >> 1);
251
+ let mid = box.findIndex((_, k) => key(k) >= v);
252
+ if (mid <= 0) mid = box.findIndex((_, k) => key(k) > v);
253
+ boxes.splice(best, 1, box.slice(0, mid), box.slice(mid));
254
+ }
255
+ const palette = boxes.map((b) => {
256
+ const s = [0, 0, 0];
257
+ for (const i of b) for (let c = 0; c < 3; c++) s[c] += d[i * 4 + c];
258
+ return [Math.round(s[0] / b.length), Math.round(s[1] / b.length), Math.round(s[2] / b.length)];
259
+ });
260
+ const index = new Int16Array(n).fill(-1);
261
+ const counts = palette.map(() => 0);
262
+ for (const i of opaque) {
263
+ let best = 0;
264
+ let bestD = Infinity;
265
+ palette.forEach((p, k) => {
266
+ const dd = (d[i * 4] - p[0]) ** 2 + (d[i * 4 + 1] - p[1]) ** 2 + (d[i * 4 + 2] - p[2]) ** 2;
267
+ if (dd < bestD) {
268
+ bestD = dd;
269
+ best = k;
270
+ }
271
+ });
272
+ index[i] = best;
273
+ counts[best]++;
274
+ }
275
+ return { palette, index, counts };
276
+ }
277
+ var hex = (c) => `#${c.map((v) => Math.round(v).toString(16).padStart(2, "0")).join("")}`;
278
+ function traceVectorImage(pixels, options = {}) {
279
+ const { width: w, height: h, data } = pixels;
280
+ if (!(w > 0 && h > 0) || data.length < w * h * 4) throw new Error("The image has no pixels (or fewer than width \xD7 height \xD7 4 values).");
281
+ if (w * h > VECTOR_TRACE_MAX_PIXELS) throw new Error(`The image has ${w * h} pixels; scale it down to at most ${VECTOR_TRACE_MAX_PIXELS} to trace it.`);
282
+ const n = w * h;
283
+ if ((_nullishCoalesce(options.mode, () => ( "threshold"))) === "threshold") {
284
+ const field = new Float32Array(n);
285
+ const byAlpha = options.channel === "alpha";
286
+ for (let i = 0; i < n; i++) {
287
+ const a = data[i * 4 + 3] / 255;
288
+ field[i] = byAlpha ? a : a * (1 - (0.2126 * data[i * 4] + 0.7152 * data[i * 4 + 1] + 0.0722 * data[i * 4 + 2]) / 255);
289
+ }
290
+ const subpaths = traceField(field, w, h, Math.min(1, Math.max(0, _nullishCoalesce(options.threshold, () => ( 0.5)))), options);
291
+ return subpaths.length ? [
292
+ {
293
+ id: "trace-1",
294
+ type: "path",
295
+ name: "Trace",
296
+ fill: _nullishCoalesce(options.color, () => ( "#000000")),
297
+ stroke: null,
298
+ fillRule: "evenodd",
299
+ subpaths
300
+ }
301
+ ] : [];
302
+ }
303
+ const count = Math.min(32, Math.max(2, Math.round(_nullishCoalesce(options.colors, () => ( 6)))));
304
+ const q = quantizeVectorTraceColors(pixels, count);
305
+ const order = q.palette.map((_, k) => k).filter((k) => q.counts[k] > 0).sort((a, b) => q.counts[b] - q.counts[a]);
306
+ const rank = new Map(order.map((k, r) => [k, r]));
307
+ const out = [];
308
+ order.forEach((k, r) => {
309
+ const field = new Uint8Array(n);
310
+ for (let i = 0; i < n; i++) {
311
+ const ix = q.index[i];
312
+ field[i] = ix >= 0 && rank.get(ix) >= r ? 1 : 0;
313
+ }
314
+ const subpaths = traceField(field, w, h, 0.5, options);
315
+ if (subpaths.length)
316
+ out.push({
317
+ id: `trace-${out.length + 1}`,
318
+ type: "path",
319
+ name: hex(q.palette[k]),
320
+ fill: hex(q.palette[k]),
321
+ stroke: null,
322
+ fillRule: "evenodd",
323
+ subpaths
324
+ });
325
+ });
326
+ return out;
327
+ }
328
+
329
+ // src/text/opentype.ts
2
330
  var fail = (message) => {
3
331
  throw new Error(message);
4
332
  };
@@ -310,22 +638,22 @@ function cffOutlines(view, t, numGlyphs) {
310
638
  const gsubrs = cffIndex(view, strings.end).items;
311
639
  if (!tops.items.length) fail("The font's CFF table has no Top DICT.");
312
640
  const top = cffDict(view, tops.items[0][0], tops.items[0][1]);
313
- const charStrings = cffIndex(view, base + (_nullishCoalesce(_optionalChain([top, 'access', _ => _.get, 'call', _2 => _2(17), 'optionalAccess', _3 => _3[0]]), () => ( fail("The font's CFF table has no CharStrings."))))).items;
641
+ const charStrings = cffIndex(view, base + (_nullishCoalesce(_optionalChain([top, 'access', _2 => _2.get, 'call', _3 => _3(17), 'optionalAccess', _4 => _4[0]]), () => ( fail("The font's CFF table has no CharStrings."))))).items;
314
642
  const privateOf = (dict) => {
315
643
  const pv = dict.get(18);
316
644
  if (!pv || pv.length < 2) return { subrs: [], defaultWidth: 0, nominalWidth: 0 };
317
645
  const [size, off] = pv;
318
646
  const priv = cffDict(view, base + off, base + off + size);
319
- const subrsOff = _optionalChain([priv, 'access', _4 => _4.get, 'call', _5 => _5(19), 'optionalAccess', _6 => _6[0]]);
647
+ const subrsOff = _optionalChain([priv, 'access', _5 => _5.get, 'call', _6 => _6(19), 'optionalAccess', _7 => _7[0]]);
320
648
  return {
321
649
  subrs: subrsOff !== void 0 ? cffIndex(view, base + off + subrsOff).items : [],
322
- defaultWidth: _nullishCoalesce(_optionalChain([priv, 'access', _7 => _7.get, 'call', _8 => _8(20), 'optionalAccess', _9 => _9[0]]), () => ( 0)),
323
- nominalWidth: _nullishCoalesce(_optionalChain([priv, 'access', _10 => _10.get, 'call', _11 => _11(21), 'optionalAccess', _12 => _12[0]]), () => ( 0))
650
+ defaultWidth: _nullishCoalesce(_optionalChain([priv, 'access', _8 => _8.get, 'call', _9 => _9(20), 'optionalAccess', _10 => _10[0]]), () => ( 0)),
651
+ nominalWidth: _nullishCoalesce(_optionalChain([priv, 'access', _11 => _11.get, 'call', _12 => _12(21), 'optionalAccess', _13 => _13[0]]), () => ( 0))
324
652
  };
325
653
  };
326
654
  let fdFor;
327
- const fdArrayOff = _optionalChain([top, 'access', _13 => _13.get, 'call', _14 => _14(1236), 'optionalAccess', _15 => _15[0]]);
328
- const fdSelectOff = _optionalChain([top, 'access', _16 => _16.get, 'call', _17 => _17(1237), 'optionalAccess', _18 => _18[0]]);
655
+ const fdArrayOff = _optionalChain([top, 'access', _14 => _14.get, 'call', _15 => _15(1236), 'optionalAccess', _16 => _16[0]]);
656
+ const fdSelectOff = _optionalChain([top, 'access', _17 => _17.get, 'call', _18 => _18(1237), 'optionalAccess', _19 => _19[0]]);
329
657
  if (fdArrayOff !== void 0 && fdSelectOff !== void 0) {
330
658
  const fds = cffIndex(view, base + fdArrayOff).items.map(([a, b]) => privateOf(cffDict(view, a, b)));
331
659
  const sel = base + fdSelectOff;
@@ -795,7 +1123,7 @@ function parseOpenTypeFont(data, options = {}) {
795
1123
  if (g < 0 || g >= numGlyphs) return 0;
796
1124
  const i = Math.min(g, numHMetrics - 1);
797
1125
  if (i >= 0 && hmtx.offset + i * 4 + 2 <= hmtx.offset + hmtx.length) return view.getUint16(hmtx.offset + i * 4);
798
- return _nullishCoalesce(_optionalChain([cffWidth, 'optionalCall', _19 => _19(g)]), () => ( 0));
1126
+ return _nullishCoalesce(_optionalChain([cffWidth, 'optionalCall', _20 => _20(g)]), () => ( 0));
799
1127
  };
800
1128
  const kernPairs = readKern(view, tables.get("kern"));
801
1129
  const gpos = gposKerning(view, tables.get("GPOS"));
@@ -849,11 +1177,11 @@ function parseOpenTypeFont(data, options = {}) {
849
1177
  kerning,
850
1178
  glyphPath,
851
1179
  getAdvanceWidth(text, fontSize, opts) {
852
- return layout(text, _optionalChain([opts, 'optionalAccess', _20 => _20.kerning]) !== false).width * fontSize / unitsPerEm;
1180
+ return layout(text, _optionalChain([opts, 'optionalAccess', _21 => _21.kerning]) !== false).width * fontSize / unitsPerEm;
853
1181
  },
854
1182
  getPath(text, x, y, fontSize, opts) {
855
1183
  const k = fontSize / unitsPerEm;
856
- const { gs, xs } = layout(text, _optionalChain([opts, 'optionalAccess', _21 => _21.kerning]) !== false);
1184
+ const { gs, xs } = layout(text, _optionalChain([opts, 'optionalAccess', _22 => _22.kerning]) !== false);
857
1185
  const commands = [];
858
1186
  gs.forEach((g, i) => {
859
1187
  const ox = x + xs[i] * k;
@@ -925,4 +1253,9 @@ async function unwoff(view) {
925
1253
 
926
1254
 
927
1255
 
928
- exports.parseOpenTypeFont = parseOpenTypeFont; exports.loadOpenTypeFont = loadOpenTypeFont;
1256
+
1257
+
1258
+
1259
+
1260
+
1261
+ exports.VECTOR_TRACE_MAX_PIXELS = VECTOR_TRACE_MAX_PIXELS; exports.vectorTraceContours = vectorTraceContours; exports.traceField = traceField; exports.quantizeVectorTraceColors = quantizeVectorTraceColors; exports.traceVectorImage = traceVectorImage; exports.parseOpenTypeFont = parseOpenTypeFont; exports.loadOpenTypeFont = loadOpenTypeFont;
@@ -1,4 +1,5 @@
1
1
  import { AdjustmentKind, AdjustmentLayer, AdjustmentParamsMap, Annotation, CreateAnnotationPayload, AspectRatio, CropBox, DepthMaskOptions, ExportOptions, ImageAdjustments, ImageDimensions, ImageEditorFit, ImageEditorOptions, ImageEditorState, ImageEditorTool, ImageTransform, ImagePoint, SerializedImageEditorState } from './types.cjs';
2
+ import { type PathToSelectionOptions, type SelectionToPathOptions } from './selection/path.cjs';
2
3
  import { RenderOptions } from './renderer.cjs';
3
4
  import { type BlendMode } from './blend.cjs';
4
5
  import type { ImageHistogram } from './histogram.cjs';
@@ -11,7 +12,7 @@ import { type RefineEdgeParams } from './selection/refine.cjs';
11
12
  import { type LayerMaskProps } from './selection/layer-mask.cjs';
12
13
  import { type AlphaChannel } from './selection/channels.cjs';
13
14
  import type { AdjustmentLayerMask } from './types.cjs';
14
- import { type LayerBlendMode, type ImageLayer, type LayerFilter, type LayerTarget, type PixelLayer, type RasterMask, type SmartContents, type SmartFilter, type SmartPlacement, type LiveShape, type ShapeFillRule, type ShapeOperation, type ShapePaint, type ShapeStroke, type ParagraphStyle, type TextStyle, type TypeLayer, type ImageEffectAnimation } from './layers/model.cjs';
15
+ import { type LayerBlendMode, type ImageLayer, type LayerFilter, type LayerTarget, type PixelLayer, type RasterMask, type SmartContents, type SmartFilter, type SmartPlacement, type LiveShape, type ShapeFillRule, type VectorMask, type ShapeOperation, type ShapePaint, type ShapeStroke, type ParagraphStyle, type TextStyle, type TypeLayer, type ImageEffectAnimation } from './layers/model.cjs';
15
16
  import { type TextLayout, type TextShaper } from './layers/text.cjs';
16
17
  import type { VectorSubpath } from '../vector/types.cjs';
17
18
  import { EffectsSlot } from '../effects/helpers.cjs';
@@ -209,6 +210,29 @@ export interface CloneSource {
209
210
  * 'star' | 'line', … }`, see `LiveShape`) or subpaths (`[{ anchors: [{ x, y, in?, out? }],
210
211
  * closed }]` or SVG path data), and how it combines with the components below it.
211
212
  */
213
+ /** A vector mask's path: several `components`, or one `shape` / `subpaths` (with `op`). */
214
+ export interface VectorMaskPathInput extends ShapeGeometryInput {
215
+ components?: ShapeGeometryInput[];
216
+ fillRule?: ShapeFillRule;
217
+ }
218
+ /** A vector mask as `getVectorMask` describes it. */
219
+ export interface VectorMaskInfo {
220
+ layerId: string;
221
+ enabled: boolean;
222
+ linked: boolean;
223
+ inverted: boolean;
224
+ fillRule: ShapeFillRule;
225
+ density: number;
226
+ feather: number;
227
+ /** Bounds of the path's anchors and handles (document px), or `null` for an empty path. */
228
+ bounds: CropBox | null;
229
+ components: Array<{
230
+ index: number;
231
+ op: ShapeOperation;
232
+ live: LiveShape | null;
233
+ subpaths: VectorSubpath[];
234
+ }>;
235
+ }
212
236
  export interface ShapeGeometryInput {
213
237
  shape?: LiveShape | Record<string, unknown>;
214
238
  subpaths?: VectorSubpath[] | string;
@@ -888,6 +912,54 @@ export declare class ImageEditorController {
888
912
  addLayerMask(id: string, from?: 'reveal-all' | 'hide-all' | 'selection' | 'hide-selection' | 'transparency', props?: LayerMaskProps): RasterMask;
889
913
  /** Removes a layer's mask (one undo step). */
890
914
  removeLayerMask(id: string): void;
915
+ /** The layer for a vector mask command: not a shape layer (its path is its vector mask), not a depth-masked one. */
916
+ private vectorMaskLayer;
917
+ private requireVectorMask;
918
+ /** Components from a path spec: `components`, or one `shape` / `subpaths`. */
919
+ private vectorMaskComponents;
920
+ /**
921
+ * Layer › Vector Mask: `reveal-all` (an empty path showing everything), `hide-all` (an empty path
922
+ * hiding everything) or `path` (reveals inside the path given). Replaces an existing vector mask;
923
+ * the raster mask is kept and both apply. One undo step. Returns the vector mask.
924
+ */
925
+ addVectorMask(id: string, from?: 'reveal-all' | 'hide-all' | 'path' | 'selection', path?: VectorMaskPathInput, trace?: SelectionToPathOptions): VectorMask;
926
+ /** The selection's outline as subpaths; throws when nothing is selected or nothing is left to trace. */
927
+ private traceSelection;
928
+ /**
929
+ * Make Work Path: the selection's outline as closed Bézier subpaths in document pixels, holes
930
+ * included (fill them even-odd), within `tolerance` px. Changes nothing. Throws without a selection.
931
+ */
932
+ selectionToPath(options?: SelectionToPathOptions): {
933
+ subpaths: VectorSubpath[];
934
+ svg: string;
935
+ fillRule: 'evenodd';
936
+ bounds: CropBox | null;
937
+ anchors: number;
938
+ };
939
+ /**
940
+ * Make Selection: fills a path (`shape`, `subpaths` or `components`, document pixels) into a
941
+ * selection combined with the current one by `mode`, with `feather` px and `antiAlias`. One
942
+ * undo step. Returns whether anything is selected.
943
+ */
944
+ pathToSelection(path: VectorMaskPathInput, options?: PathToSelectionOptions & {
945
+ mode?: SelectionMode;
946
+ }): boolean;
947
+ /** Replaces a vector mask's path (its properties are kept). One undo step. Returns the vector mask. */
948
+ setVectorMaskPath(id: string, path: VectorMaskPathInput): VectorMask;
949
+ /** Changes a vector mask's enabled, linked, inverted, density or feather (the path is kept). One undo step. */
950
+ setVectorMaskProps(id: string, props: Partial<Pick<VectorMask, 'enabled' | 'linked' | 'inverted' | 'density' | 'feather'>>): VectorMask;
951
+ /** Deletes a layer's vector mask (one undo step). */
952
+ removeVectorMask(id: string): void;
953
+ /**
954
+ * Layer › Rasterize › Vector Mask: multiplies the path (with its density and feather) into the
955
+ * layer's raster mask, adding one when there is none, and removes the vector mask. The raster
956
+ * mask keeps its own properties. One undo step. Returns the raster mask.
957
+ */
958
+ rasterizeVectorMask(id: string): RasterMask;
959
+ /** Load Selection from a vector mask's path (density and feather are not applied), inverted with `invert`. */
960
+ selectionFromVectorMask(id: string, mode?: SelectionMode, invert?: boolean): boolean;
961
+ /** A layer's vector mask with its paths (rounded), or `null` without one. */
962
+ getVectorMask(id: string): VectorMaskInfo | null;
891
963
  /** Changes any layer mask's density, feather, enabled or linked flag (pixels kept). Returns the mask. */
892
964
  setLayerMaskProps(id: string, props: LayerMaskProps, commitToHistory?: boolean): RasterMask;
893
965
  /**
@@ -1,4 +1,5 @@
1
1
  import { AdjustmentKind, AdjustmentLayer, AdjustmentParamsMap, Annotation, CreateAnnotationPayload, AspectRatio, CropBox, DepthMaskOptions, ExportOptions, ImageAdjustments, ImageDimensions, ImageEditorFit, ImageEditorOptions, ImageEditorState, ImageEditorTool, ImageTransform, ImagePoint, SerializedImageEditorState } from './types.js';
2
+ import { type PathToSelectionOptions, type SelectionToPathOptions } from './selection/path.js';
2
3
  import { RenderOptions } from './renderer.js';
3
4
  import { type BlendMode } from './blend.js';
4
5
  import type { ImageHistogram } from './histogram.js';
@@ -11,7 +12,7 @@ import { type RefineEdgeParams } from './selection/refine.js';
11
12
  import { type LayerMaskProps } from './selection/layer-mask.js';
12
13
  import { type AlphaChannel } from './selection/channels.js';
13
14
  import type { AdjustmentLayerMask } from './types.js';
14
- import { type LayerBlendMode, type ImageLayer, type LayerFilter, type LayerTarget, type PixelLayer, type RasterMask, type SmartContents, type SmartFilter, type SmartPlacement, type LiveShape, type ShapeFillRule, type ShapeOperation, type ShapePaint, type ShapeStroke, type ParagraphStyle, type TextStyle, type TypeLayer, type ImageEffectAnimation } from './layers/model.js';
15
+ import { type LayerBlendMode, type ImageLayer, type LayerFilter, type LayerTarget, type PixelLayer, type RasterMask, type SmartContents, type SmartFilter, type SmartPlacement, type LiveShape, type ShapeFillRule, type VectorMask, type ShapeOperation, type ShapePaint, type ShapeStroke, type ParagraphStyle, type TextStyle, type TypeLayer, type ImageEffectAnimation } from './layers/model.js';
15
16
  import { type TextLayout, type TextShaper } from './layers/text.js';
16
17
  import type { VectorSubpath } from '../vector/types.js';
17
18
  import { EffectsSlot } from '../effects/helpers.js';
@@ -209,6 +210,29 @@ export interface CloneSource {
209
210
  * 'star' | 'line', … }`, see `LiveShape`) or subpaths (`[{ anchors: [{ x, y, in?, out? }],
210
211
  * closed }]` or SVG path data), and how it combines with the components below it.
211
212
  */
213
+ /** A vector mask's path: several `components`, or one `shape` / `subpaths` (with `op`). */
214
+ export interface VectorMaskPathInput extends ShapeGeometryInput {
215
+ components?: ShapeGeometryInput[];
216
+ fillRule?: ShapeFillRule;
217
+ }
218
+ /** A vector mask as `getVectorMask` describes it. */
219
+ export interface VectorMaskInfo {
220
+ layerId: string;
221
+ enabled: boolean;
222
+ linked: boolean;
223
+ inverted: boolean;
224
+ fillRule: ShapeFillRule;
225
+ density: number;
226
+ feather: number;
227
+ /** Bounds of the path's anchors and handles (document px), or `null` for an empty path. */
228
+ bounds: CropBox | null;
229
+ components: Array<{
230
+ index: number;
231
+ op: ShapeOperation;
232
+ live: LiveShape | null;
233
+ subpaths: VectorSubpath[];
234
+ }>;
235
+ }
212
236
  export interface ShapeGeometryInput {
213
237
  shape?: LiveShape | Record<string, unknown>;
214
238
  subpaths?: VectorSubpath[] | string;
@@ -888,6 +912,54 @@ export declare class ImageEditorController {
888
912
  addLayerMask(id: string, from?: 'reveal-all' | 'hide-all' | 'selection' | 'hide-selection' | 'transparency', props?: LayerMaskProps): RasterMask;
889
913
  /** Removes a layer's mask (one undo step). */
890
914
  removeLayerMask(id: string): void;
915
+ /** The layer for a vector mask command: not a shape layer (its path is its vector mask), not a depth-masked one. */
916
+ private vectorMaskLayer;
917
+ private requireVectorMask;
918
+ /** Components from a path spec: `components`, or one `shape` / `subpaths`. */
919
+ private vectorMaskComponents;
920
+ /**
921
+ * Layer › Vector Mask: `reveal-all` (an empty path showing everything), `hide-all` (an empty path
922
+ * hiding everything) or `path` (reveals inside the path given). Replaces an existing vector mask;
923
+ * the raster mask is kept and both apply. One undo step. Returns the vector mask.
924
+ */
925
+ addVectorMask(id: string, from?: 'reveal-all' | 'hide-all' | 'path' | 'selection', path?: VectorMaskPathInput, trace?: SelectionToPathOptions): VectorMask;
926
+ /** The selection's outline as subpaths; throws when nothing is selected or nothing is left to trace. */
927
+ private traceSelection;
928
+ /**
929
+ * Make Work Path: the selection's outline as closed Bézier subpaths in document pixels, holes
930
+ * included (fill them even-odd), within `tolerance` px. Changes nothing. Throws without a selection.
931
+ */
932
+ selectionToPath(options?: SelectionToPathOptions): {
933
+ subpaths: VectorSubpath[];
934
+ svg: string;
935
+ fillRule: 'evenodd';
936
+ bounds: CropBox | null;
937
+ anchors: number;
938
+ };
939
+ /**
940
+ * Make Selection: fills a path (`shape`, `subpaths` or `components`, document pixels) into a
941
+ * selection combined with the current one by `mode`, with `feather` px and `antiAlias`. One
942
+ * undo step. Returns whether anything is selected.
943
+ */
944
+ pathToSelection(path: VectorMaskPathInput, options?: PathToSelectionOptions & {
945
+ mode?: SelectionMode;
946
+ }): boolean;
947
+ /** Replaces a vector mask's path (its properties are kept). One undo step. Returns the vector mask. */
948
+ setVectorMaskPath(id: string, path: VectorMaskPathInput): VectorMask;
949
+ /** Changes a vector mask's enabled, linked, inverted, density or feather (the path is kept). One undo step. */
950
+ setVectorMaskProps(id: string, props: Partial<Pick<VectorMask, 'enabled' | 'linked' | 'inverted' | 'density' | 'feather'>>): VectorMask;
951
+ /** Deletes a layer's vector mask (one undo step). */
952
+ removeVectorMask(id: string): void;
953
+ /**
954
+ * Layer › Rasterize › Vector Mask: multiplies the path (with its density and feather) into the
955
+ * layer's raster mask, adding one when there is none, and removes the vector mask. The raster
956
+ * mask keeps its own properties. One undo step. Returns the raster mask.
957
+ */
958
+ rasterizeVectorMask(id: string): RasterMask;
959
+ /** Load Selection from a vector mask's path (density and feather are not applied), inverted with `invert`. */
960
+ selectionFromVectorMask(id: string, mode?: SelectionMode, invert?: boolean): boolean;
961
+ /** A layer's vector mask with its paths (rounded), or `null` without one. */
962
+ getVectorMask(id: string): VectorMaskInfo | null;
891
963
  /** Changes any layer mask's density, feather, enabled or linked flag (pixels kept). Returns the mask. */
892
964
  setLayerMaskProps(id: string, props: LayerMaskProps, commitToHistory?: boolean): RasterMask;
893
965
  /**
@@ -1,9 +1,3 @@
1
- /**
2
- * Free Transform sessions: what a transform moves (a layer's pixels with its linked mask, a lone
3
- * unlinked mask, the selected pixels as a floating piece, a copy of them on a new layer, or just
4
- * the selection outline), a live preview while the geometry changes, and one undo step when it is
5
- * committed with full-quality resampling.
6
- */
7
1
  import { TiledSurface } from '../raster/surface.cjs';
8
2
  import { type Interpolation } from '../raster/transform.cjs';
9
3
  import { type ImageLayer, type LayerTarget, type PixelLayer, type SmartObjectLayer } from './layers/model.cjs';
@@ -147,6 +141,9 @@ export declare class FreeTransformSession {
147
141
  }, preview?: FreeTransformPreview | false): FreeTransformGeometry;
148
142
  /** Resamples every part for the current geometry and shows it (or returns it, for commit). */
149
143
  private resample;
144
+ /** A linked vector mask follows the layer's content through the same document-space map. */
145
+ private moveVectorMask;
146
+ private resampleContent;
150
147
  /**
151
148
  * A shape with its paths mapped through the current geometry and rendered again (live shapes
152
149
  * stay live under an affine map; perspective and warps turn them into plain paths). Previews
@@ -1,9 +1,3 @@
1
- /**
2
- * Free Transform sessions: what a transform moves (a layer's pixels with its linked mask, a lone
3
- * unlinked mask, the selected pixels as a floating piece, a copy of them on a new layer, or just
4
- * the selection outline), a live preview while the geometry changes, and one undo step when it is
5
- * committed with full-quality resampling.
6
- */
7
1
  import { TiledSurface } from '../raster/surface.js';
8
2
  import { type Interpolation } from '../raster/transform.js';
9
3
  import { type ImageLayer, type LayerTarget, type PixelLayer, type SmartObjectLayer } from './layers/model.js';
@@ -147,6 +141,9 @@ export declare class FreeTransformSession {
147
141
  }, preview?: FreeTransformPreview | false): FreeTransformGeometry;
148
142
  /** Resamples every part for the current geometry and shows it (or returns it, for commit). */
149
143
  private resample;
144
+ /** A linked vector mask follows the layer's content through the same document-space map. */
145
+ private moveVectorMask;
146
+ private resampleContent;
150
147
  /**
151
148
  * A shape with its paths mapped through the current geometry and rendered again (live shapes
152
149
  * stay live under an affine map; perspective and warps turn them into plain paths). Previews