@pixerate/editor 0.209.0 → 0.211.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 (46) hide show
  1. package/dist/{chunk-QE7D7TZR.cjs → chunk-H65IBXFI.cjs} +74 -25
  2. package/dist/{chunk-X4LUGTQX.cjs → chunk-L52VMQZS.cjs} +21 -10
  3. package/dist/{chunk-DWYQPL2A.js → chunk-LQ5SALHK.js} +13 -2
  4. package/dist/chunk-MQDIP4CF.cjs +462 -0
  5. package/dist/{chunk-KFIOEOCQ.js → chunk-W3TOZTL7.js} +55 -6
  6. package/dist/chunk-XI2NV5U7.js +462 -0
  7. package/dist/{chunk-3EHPLDFQ.cjs → chunk-YF3YZZ6G.cjs} +16 -453
  8. package/dist/{chunk-USQCKRZG.js → chunk-ZGJGSCUW.js} +8 -445
  9. package/dist/image-editor/content-aware-fill-session.d.cts +109 -0
  10. package/dist/image-editor/content-aware-fill-session.d.ts +109 -0
  11. package/dist/image-editor/controller.d.cts +111 -1
  12. package/dist/image-editor/controller.d.ts +111 -1
  13. package/dist/image-editor/index.cjs +1879 -1052
  14. package/dist/image-editor/index.d.cts +2 -0
  15. package/dist/image-editor/index.d.ts +2 -0
  16. package/dist/image-editor/index.js +869 -42
  17. package/dist/image-editor/layers/serialize.d.cts +5 -8
  18. package/dist/image-editor/layers/serialize.d.ts +5 -8
  19. package/dist/image-editor/paths.d.cts +41 -0
  20. package/dist/image-editor/paths.d.ts +41 -0
  21. package/dist/image-editor/psd/export.d.cts +8 -1
  22. package/dist/image-editor/psd/export.d.ts +8 -1
  23. package/dist/image-editor/psd/import.d.cts +7 -0
  24. package/dist/image-editor/psd/import.d.ts +7 -0
  25. package/dist/image-editor/psd/paths.d.cts +11 -0
  26. package/dist/image-editor/psd/paths.d.ts +11 -0
  27. package/dist/image-editor/selection/gesture.d.cts +10 -2
  28. package/dist/image-editor/selection/gesture.d.ts +10 -2
  29. package/dist/image-editor/selection/select-and-mask.d.cts +5 -0
  30. package/dist/image-editor/selection/select-and-mask.d.ts +5 -0
  31. package/dist/image-editor/selection/worker.d.cts +10 -0
  32. package/dist/image-editor/selection/worker.d.ts +10 -0
  33. package/dist/image-editor/selection-worker.cjs +4 -3
  34. package/dist/image-editor/selection-worker.js +2 -1
  35. package/dist/image-editor/types.d.cts +12 -0
  36. package/dist/image-editor/types.d.ts +12 -0
  37. package/dist/paint/heal.d.cts +3 -1
  38. package/dist/paint/heal.d.ts +3 -1
  39. package/dist/paint/index.cjs +4 -2
  40. package/dist/paint/index.js +10 -8
  41. package/dist/paint-editor/index.cjs +6 -6
  42. package/dist/paint-editor/index.js +1 -1
  43. package/package.json +1 -1
  44. package/skills/pixerate-image-editor/SKILL.md +20 -22
  45. package/skills/pixerate-image-editor/references/layer-effects.md +24 -0
  46. package/skills/pixerate-image-selection-masking/SKILL.md +2 -1
@@ -1,3 +1,11 @@
1
+ import {
2
+ bestOffset,
3
+ contentAwareFill,
4
+ dilateMask,
5
+ maskBounds,
6
+ membraneFill,
7
+ seamlessClone
8
+ } from "./chunk-XI2NV5U7.js";
1
9
  import {
2
10
  DabWalker,
3
11
  SmudgeTip,
@@ -16,444 +24,6 @@ import {
16
24
  tileKey
17
25
  } from "./chunk-HVVR5PZW.js";
18
26
 
19
- // src/paint/heal.ts
20
- var TOL = 2e-3;
21
- var FINE_ITERS = 30;
22
- var FINE_OMEGA = 1.7;
23
- function solveMembrane(w, h, unknown, v) {
24
- if (unknown.length !== w * h || v.length !== w * h) throw new RangeError(`solveMembrane: expected ${w * h} cells, got ${unknown.length} and ${v.length}.`);
25
- const u = new Uint8Array(w * h);
26
- for (let i = 0; i < u.length; i++) u[i] = unknown[i] ? 1 : 0;
27
- solveLevel(w, h, u, v, 0);
28
- }
29
- function solveLevel(w, h, unknown, v, depth) {
30
- let nUnknown = 0;
31
- let knownSum = 0;
32
- let nKnown = 0;
33
- for (let i = 0; i < unknown.length; i++) {
34
- if (unknown[i]) nUnknown++;
35
- else {
36
- knownSum += v[i];
37
- nKnown++;
38
- }
39
- }
40
- if (nUnknown === 0 || nKnown === 0) return;
41
- if (w >= 8 && h >= 8 && nUnknown > 64 && depth < 16) {
42
- const cw = Math.ceil(w / 2);
43
- const chh = Math.ceil(h / 2);
44
- const cu = new Uint8Array(cw * chh).fill(1);
45
- const cv = new Float32Array(cw * chh);
46
- for (let cy = 0; cy < chh; cy++) {
47
- for (let cx = 0; cx < cw; cx++) {
48
- let s = 0;
49
- let n = 0;
50
- for (let k = 0; k < 4; k++) {
51
- const x = cx * 2 + (k & 1);
52
- const y = cy * 2 + (k >> 1);
53
- if (x < w && y < h && !unknown[y * w + x]) {
54
- s += v[y * w + x];
55
- n++;
56
- }
57
- }
58
- if (n > 0) {
59
- cu[cy * cw + cx] = 0;
60
- cv[cy * cw + cx] = s / n;
61
- }
62
- }
63
- }
64
- solveLevel(cw, chh, cu, cv, depth + 1);
65
- for (let y = 0; y < h; y++) {
66
- const fy = Math.min(chh - 1, Math.max(0, (y + 0.5) / 2 - 0.5));
67
- const y0 = Math.floor(fy);
68
- const y1 = Math.min(chh - 1, y0 + 1);
69
- const ty = fy - y0;
70
- for (let x = 0; x < w; x++) {
71
- if (!unknown[y * w + x]) continue;
72
- const fx = Math.min(cw - 1, Math.max(0, (x + 0.5) / 2 - 0.5));
73
- const x0 = Math.floor(fx);
74
- const x1 = Math.min(cw - 1, x0 + 1);
75
- const tx = fx - x0;
76
- const a = cv[y0 * cw + x0] + (cv[y0 * cw + x1] - cv[y0 * cw + x0]) * tx;
77
- const b = cv[y1 * cw + x0] + (cv[y1 * cw + x1] - cv[y1 * cw + x0]) * tx;
78
- v[y * w + x] = a + (b - a) * ty;
79
- }
80
- }
81
- sor(w, h, unknown, v, FINE_ITERS, FINE_OMEGA);
82
- } else {
83
- const mean = knownSum / nKnown;
84
- for (let i = 0; i < v.length; i++) if (unknown[i]) v[i] = mean;
85
- sor(w, h, unknown, v, 2e3, 1.85);
86
- }
87
- }
88
- function sor(w, h, unknown, v, maxIter, omega) {
89
- let n = 0;
90
- for (let i = 0; i < unknown.length; i++) if (unknown[i]) n++;
91
- const cells = new Uint32Array(n);
92
- const masks = new Uint8Array(n);
93
- for (let i = 0, k = 0; i < unknown.length; i++) {
94
- if (!unknown[i]) continue;
95
- const x = i % w;
96
- const y = (i - x) / w;
97
- cells[k] = i;
98
- masks[k++] = (x > 0 ? 1 : 0) | (x + 1 < w ? 2 : 0) | (y > 0 ? 4 : 0) | (y + 1 < h ? 8 : 0);
99
- }
100
- for (let it = 0; it < maxIter; it++) {
101
- let maxD = 0;
102
- for (let k = 0; k < n; k++) {
103
- const i = cells[k];
104
- const m = masks[k];
105
- let s;
106
- let c;
107
- if (m === 15) {
108
- s = v[i - 1] + v[i + 1] + v[i - w] + v[i + w];
109
- c = 4;
110
- } else {
111
- s = 0;
112
- c = 0;
113
- if (m & 1) s += v[i - 1], c++;
114
- if (m & 2) s += v[i + 1], c++;
115
- if (m & 4) s += v[i - w], c++;
116
- if (m & 8) s += v[i + w], c++;
117
- if (c === 0) continue;
118
- }
119
- const d = omega * (s / c - v[i]);
120
- v[i] += d;
121
- const ad = d < 0 ? -d : d;
122
- if (ad > maxD) maxD = ad;
123
- }
124
- if (maxD < TOL) break;
125
- }
126
- }
127
- function seamlessClone(w, h, ch, src, dst, mask) {
128
- const n = w * h;
129
- if (src.length !== n * ch || dst.length !== n * ch || mask.length !== n) throw new RangeError("seamlessClone: src, dst and mask must cover w \xD7 h pixels.");
130
- const out = new Float32Array(n * ch);
131
- const hv = new Float32Array(n);
132
- for (let c = 0; c < ch; c++) {
133
- for (let i = 0; i < n; i++) hv[i] = mask[i] ? 0 : dst[i * ch + c] - src[i * ch + c];
134
- solveMembrane(w, h, mask, hv);
135
- for (let i = 0; i < n; i++) out[i * ch + c] = mask[i] ? src[i * ch + c] + hv[i] : dst[i * ch + c];
136
- }
137
- return out;
138
- }
139
- function membraneFill(w, h, ch, img, hole) {
140
- const n = w * h;
141
- const out = new Float32Array(n * ch);
142
- const v = new Float32Array(n);
143
- for (let c = 0; c < ch; c++) {
144
- for (let i = 0; i < n; i++) v[i] = hole[i] ? 0 : img[i * ch + c];
145
- solveMembrane(w, h, hole, v);
146
- for (let i = 0; i < n; i++) out[i * ch + c] = v[i];
147
- }
148
- return out;
149
- }
150
- function dilateMask(w, h, mask, r) {
151
- const tmp = new Uint8Array(w * h);
152
- for (let y = 0; y < h; y++) {
153
- for (let x = 0; x < w; x++) {
154
- if (!mask[y * w + x]) continue;
155
- for (let xx = Math.max(0, x - r); xx <= Math.min(w - 1, x + r); xx++) tmp[y * w + xx] = 1;
156
- }
157
- }
158
- const out = new Uint8Array(w * h);
159
- for (let y = 0; y < h; y++) {
160
- for (let x = 0; x < w; x++) {
161
- if (!tmp[y * w + x]) continue;
162
- for (let yy = Math.max(0, y - r); yy <= Math.min(h - 1, y + r); yy++) out[yy * w + x] = 1;
163
- }
164
- }
165
- return out;
166
- }
167
- var MaskIntegral = class {
168
- constructor(w, h, mask) {
169
- this.w = w;
170
- this.h = h;
171
- const W = w + 1;
172
- this.s = new Uint32Array(W * (h + 1));
173
- for (let y = 0; y < h; y++) {
174
- let row = 0;
175
- for (let x = 0; x < w; x++) {
176
- row += mask[y * w + x] ? 1 : 0;
177
- this.s[(y + 1) * W + x + 1] = this.s[y * W + x + 1] + row;
178
- }
179
- }
180
- }
181
- w;
182
- h;
183
- s;
184
- /** Masked cells in [x0, x1) × [y0, y1) (clipped to the grid). */
185
- count(x0, y0, x1, y1) {
186
- const W = this.w + 1;
187
- const a = Math.max(0, Math.min(this.w, x0));
188
- const b = Math.max(0, Math.min(this.h, y0));
189
- const c = Math.max(0, Math.min(this.w, x1));
190
- const d = Math.max(0, Math.min(this.h, y1));
191
- if (c <= a || d <= b) return 0;
192
- return this.s[d * W + c] - this.s[b * W + c] - this.s[d * W + a] + this.s[b * W + a];
193
- }
194
- };
195
- function maskBounds(w, h, mask) {
196
- let x0 = w;
197
- let y0 = h;
198
- let x1 = -1;
199
- let y1 = -1;
200
- for (let y = 0; y < h; y++) {
201
- for (let x = 0; x < w; x++) {
202
- if (!mask[y * w + x]) continue;
203
- if (x < x0) x0 = x;
204
- if (x > x1) x1 = x;
205
- if (y < y0) y0 = y;
206
- if (y > y1) y1 = y;
207
- }
208
- }
209
- return x1 < 0 ? null : [x0, y0, x1 + 1, y1 + 1];
210
- }
211
- function bestOffset(w, h, ch, img, hole, ring, maxRadius) {
212
- const bb = maskBounds(w, h, hole);
213
- if (!bb) return null;
214
- const extent = Math.max(w, h);
215
- const R = Math.max(0, Math.min(extent, Math.floor(maxRadius)));
216
- if (R === 0) return null;
217
- const rg = Math.max(1, Math.min(extent, Math.floor(ring)));
218
- const hs = new MaskIntegral(w, h, hole);
219
- const x0 = bb[0] - rg;
220
- const y0 = bb[1] - rg;
221
- const x1 = bb[2] + rg;
222
- const y1 = bb[3] + rg;
223
- const pts = [];
224
- for (let y = Math.max(0, y0); y < Math.min(h, y1); y++) {
225
- for (let x = Math.max(0, x0); x < Math.min(w, x1); x++) {
226
- if (hole[y * w + x]) continue;
227
- if (hs.count(x - rg, y - rg, x + rg + 1, y + rg + 1) > 0) pts.push(x, y);
228
- }
229
- }
230
- const stride = Math.max(1, Math.floor(pts.length / 2 / 2e3));
231
- const sx = [];
232
- const sy = [];
233
- for (let i = 0; i < pts.length; i += 2 * stride) {
234
- sx.push(pts[i]);
235
- sy.push(pts[i + 1]);
236
- }
237
- const n = Math.max(1, sx.length);
238
- const evalAt = (dx, dy, cutoff) => {
239
- if (dx === 0 && dy === 0) return null;
240
- const hx0 = x0 + dx;
241
- const hy0 = y0 + dy;
242
- const hx1 = x1 + dx;
243
- const hy1 = y1 + dy;
244
- if (hx0 < 0 || hy0 < 0 || hx1 > w || hy1 > h) return null;
245
- if (hs.count(hx0, hy0, hx1, hy1) !== 0) return null;
246
- const penalty = 1 + 1e-3 * (Math.hypot(dx, dy) / R);
247
- let e = 0;
248
- for (let i = 0; i < sx.length; i++) {
249
- const a = (sy[i] * w + sx[i]) * ch;
250
- const b = ((sy[i] + dy) * w + sx[i] + dx) * ch;
251
- for (let c = 0; c < ch; c++) {
252
- const d = img[a + c] - img[b + c];
253
- e += d * d;
254
- }
255
- if ((i & 31) === 31 && e / n * penalty >= cutoff) return null;
256
- }
257
- return e / n * penalty;
258
- };
259
- const found = { best: null, e: Infinity };
260
- const consider = (dx, dy) => {
261
- const e = evalAt(dx, dy, found.e);
262
- if (e !== null && e < found.e) {
263
- found.e = e;
264
- found.best = [dx, dy];
265
- }
266
- };
267
- const step = Math.max(1, Math.floor(R / 24));
268
- for (let dy = -R; dy <= R; dy += step) for (let dx = -R; dx <= R; dx += step) consider(dx, dy);
269
- if (step > 1 && found.best) {
270
- const [bx, by] = found.best;
271
- for (let dy = by - step; dy <= by + step; dy++) for (let dx = bx - step; dx <= bx + step; dx++) consider(dx, dy);
272
- }
273
- return found.best;
274
- }
275
- var PATCH_R = 3;
276
- function rng(seed) {
277
- let a = seed >>> 0;
278
- return () => {
279
- a = a + 1831565813 >>> 0;
280
- let t = a;
281
- t = Math.imul(t ^ t >>> 15, t | 1);
282
- t ^= t + Math.imul(t ^ t >>> 7, t | 61);
283
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
284
- };
285
- }
286
- function downsample(l, ch) {
287
- const w = Math.max(1, l.w >> 1);
288
- const h = Math.max(1, l.h >> 1);
289
- const img = new Float32Array(w * h * ch);
290
- const hole = new Uint8Array(w * h);
291
- for (let y = 0; y < h; y++) {
292
- for (let x = 0; x < w; x++) {
293
- let n = 0;
294
- const o = (y * w + x) * ch;
295
- for (let dy = 0; dy < 2; dy++) {
296
- for (let dx = 0; dx < 2; dx++) {
297
- const sx = Math.min(l.w - 1, 2 * x + dx);
298
- const sy = Math.min(l.h - 1, 2 * y + dy);
299
- const i = sy * l.w + sx;
300
- if (l.hole[i]) {
301
- hole[y * w + x] = 1;
302
- continue;
303
- }
304
- for (let c = 0; c < ch; c++) img[o + c] += l.img[i * ch + c];
305
- n++;
306
- }
307
- }
308
- if (n) for (let c = 0; c < ch; c++) img[o + c] /= n;
309
- }
310
- }
311
- return { w, h, img, hole };
312
- }
313
- function contentAwareFill(w, h, ch, img, hole, options = {}) {
314
- const iters = options.iterations ?? 4;
315
- const base = { w, h, img: Float32Array.from(img), hole: Uint8Array.from(hole, (v) => v ? 1 : 0) };
316
- const levels = [base];
317
- while (levels.length < 4) {
318
- const top = levels[levels.length - 1];
319
- const b = maskBounds(top.w, top.h, top.hole);
320
- if (!b || Math.max(b[2] - b[0], b[3] - b[1]) < 4 * PATCH_R || top.w < 8 * PATCH_R || top.h < 8 * PATCH_R) break;
321
- levels.push(downsample(top, ch));
322
- }
323
- const random = rng(options.seed ?? w * 73856093 + h * 19349663);
324
- let prevFill = null;
325
- let prevNnf = null;
326
- let prevW = 0;
327
- for (let li = levels.length - 1; li >= 0; li--) {
328
- const { w: W, h: H, hole: holeL } = levels[li];
329
- const cur = new Float32Array(levels[li].img);
330
- if (prevFill) {
331
- for (let i = 0; i < W * H; i++) {
332
- if (!holeL[i]) continue;
333
- const x = Math.min(prevW - 1, i % W >> 1);
334
- const y = Math.min(Math.floor(prevFill.length / ch / prevW) - 1, Math.floor(i / W) >> 1);
335
- for (let c = 0; c < ch; c++) cur[i * ch + c] = prevFill[(y * prevW + x) * ch + c];
336
- }
337
- } else cur.set(membraneFill(W, H, ch, cur, holeL));
338
- const blocked = dilateMask(W, H, holeL, PATCH_R);
339
- const valid = [];
340
- for (let y = PATCH_R; y < H - PATCH_R; y++) for (let x = PATCH_R; x < W - PATCH_R; x++) if (!blocked[y * W + x]) valid.push(y * W + x);
341
- if (!valid.length) {
342
- prevFill = cur;
343
- prevNnf = null;
344
- prevW = W;
345
- continue;
346
- }
347
- const targets = [];
348
- for (let y = 0; y < H; y++) for (let x = 0; x < W; x++) if (blocked[y * W + x]) targets.push(y * W + x);
349
- const nnf = new Int32Array(W * H).fill(-1);
350
- const cost = new Float64Array(W * H);
351
- const clampC = (v, n) => Math.min(n - 1 - PATCH_R, Math.max(PATCH_R, v));
352
- const dist = (t, s, limit) => {
353
- const tx = clampC(t % W, W);
354
- const ty = clampC(Math.floor(t / W), H);
355
- const sx = s % W;
356
- const sy = Math.floor(s / W);
357
- let d = 0;
358
- for (let dy = -PATCH_R; dy <= PATCH_R; dy++) {
359
- const tr = (ty + dy) * W;
360
- const sr = (sy + dy) * W;
361
- for (let dx = -PATCH_R; dx <= PATCH_R; dx++) {
362
- const a = (tr + tx + dx) * ch;
363
- const b = (sr + sx + dx) * ch;
364
- for (let c = 0; c < ch; c++) {
365
- const e = cur[a + c] - cur[b + c];
366
- d += e * e;
367
- }
368
- }
369
- if (d >= limit) return d;
370
- }
371
- return d;
372
- };
373
- const isValid = (s) => {
374
- const x = s % W;
375
- const y = Math.floor(s / W);
376
- return s >= 0 && x >= PATCH_R && y >= PATCH_R && x < W - PATCH_R && y < H - PATCH_R && !blocked[s];
377
- };
378
- for (const t of targets) {
379
- let s = -1;
380
- if (prevNnf) {
381
- const px = Math.min(prevW - 1, t % W >> 1);
382
- const py = Math.floor(t / W) >> 1;
383
- const ps = prevNnf[py * prevW + px];
384
- if (ps >= 0) {
385
- const cand = (Math.floor(ps / prevW) * 2 + (Math.floor(t / W) & 1)) * W + ps % prevW * 2 + (t % W & 1);
386
- if (isValid(cand)) s = cand;
387
- }
388
- }
389
- if (s < 0) s = valid[Math.floor(random() * valid.length)];
390
- nnf[t] = s;
391
- }
392
- for (let em = 0; em < iters; em++) {
393
- for (const t of targets) cost[t] = dist(t, nnf[t], Infinity);
394
- for (let pass = 0; pass < 4; pass++) {
395
- const fwd = pass % 2 === 0;
396
- const step = fwd ? 1 : -1;
397
- for (let k = 0; k < targets.length; k++) {
398
- const t = targets[fwd ? k : targets.length - 1 - k];
399
- const tx = t % W;
400
- const ty = Math.floor(t / W);
401
- for (const [nx, ny] of [[tx - step, ty], [tx, ty - step]]) {
402
- if (nx < 0 || ny < 0 || nx >= W || ny >= H) continue;
403
- const ns = nnf[ny * W + nx];
404
- if (ns < 0) continue;
405
- const cand = ns + step * (nx === tx ? W : 1);
406
- if (!isValid(cand) || cand === nnf[t]) continue;
407
- const d = dist(t, cand, cost[t]);
408
- if (d < cost[t]) {
409
- cost[t] = d;
410
- nnf[t] = cand;
411
- }
412
- }
413
- const bx = nnf[t] % W;
414
- const by = Math.floor(nnf[t] / W);
415
- for (let r = Math.max(W, H); r >= 1; r >>= 1) {
416
- const cx = Math.round(bx + (random() * 2 - 1) * r);
417
- const cy = Math.round(by + (random() * 2 - 1) * r);
418
- const cand = cy * W + cx;
419
- if (cx < 0 || cy < 0 || cx >= W || cy >= H || !isValid(cand)) continue;
420
- const d = dist(t, cand, cost[t]);
421
- if (d < cost[t]) {
422
- cost[t] = d;
423
- nnf[t] = cand;
424
- }
425
- }
426
- }
427
- }
428
- const acc = new Float64Array(W * H * ch);
429
- const wsum = new Float64Array(W * H);
430
- for (const t of targets) {
431
- const tx = clampC(t % W, W);
432
- const ty = clampC(Math.floor(t / W), H);
433
- const s = nnf[t];
434
- const sx = s % W;
435
- const sy = Math.floor(s / W);
436
- for (let dy = -PATCH_R; dy <= PATCH_R; dy++) {
437
- for (let dx = -PATCH_R; dx <= PATCH_R; dx++) {
438
- const q = (ty + dy) * W + tx + dx;
439
- if (!holeL[q]) continue;
440
- const src = ((sy + dy) * W + sx + dx) * ch;
441
- for (let c = 0; c < ch; c++) acc[q * ch + c] += cur[src + c];
442
- wsum[q] += 1;
443
- }
444
- }
445
- }
446
- for (let q = 0; q < W * H; q++) if (holeL[q] && wsum[q] > 0) for (let c = 0; c < ch; c++) cur[q * ch + c] = acc[q * ch + c] / wsum[q];
447
- }
448
- prevFill = cur;
449
- prevNnf = nnf;
450
- prevW = W;
451
- }
452
- const out = Float32Array.from(img);
453
- for (let i = 0; i < w * h; i++) if (base.hole[i]) for (let c = 0; c < ch; c++) out[i * ch + c] = prevFill[i * ch + c];
454
- return out;
455
- }
456
-
457
27
  // src/paint/retouch.ts
458
28
  var TONE_RANGES = ["shadows", "midtones", "highlights"];
459
29
  function luma(r, g, b) {
@@ -1097,13 +667,6 @@ function unionRect(a, b) {
1097
667
  }
1098
668
 
1099
669
  export {
1100
- solveMembrane,
1101
- seamlessClone,
1102
- membraneFill,
1103
- dilateMask,
1104
- maskBounds,
1105
- bestOffset,
1106
- contentAwareFill,
1107
670
  TONE_RANGES,
1108
671
  luma,
1109
672
  dodgeBurnCurve,
@@ -0,0 +1,109 @@
1
+ /**
2
+ * The Content-Aware Fill workspace without a UI, shared by the React and Svelte workspaces: the
3
+ * sampling area (where the fill may copy from) as a paintable mask over a window around the
4
+ * selection, its presets (Auto, Rectangular, Custom), a live preview of the fill, and OK, which
5
+ * applies it as one undo step through `contentAwareFillAsync` (the same fill the
6
+ * `content_aware_fill` tool makes, with a sampling area).
7
+ */
8
+ import type { ImageEditorController } from './controller.cjs';
9
+ import type { CropBox, ImagePoint } from './types.cjs';
10
+ import { type SelectionMask } from './selection/mask.cjs';
11
+ export type ContentAwareFillSampling = 'auto' | 'rectangular' | 'custom';
12
+ export type ContentAwareFillTool = 'sampling-brush' | 'hand' | 'zoom';
13
+ export declare const CONTENT_AWARE_FILL_TOOLS: readonly {
14
+ id: ContentAwareFillTool;
15
+ label: string;
16
+ key: string;
17
+ }[];
18
+ export declare const CONTENT_AWARE_FILL_SAMPLING: readonly {
19
+ id: ContentAwareFillSampling;
20
+ label: string;
21
+ }[];
22
+ export interface ContentAwareFillSettings {
23
+ sampling: ContentAwareFillSampling;
24
+ /** Show the sampling area as a green overlay on the source view. */
25
+ showSampling: boolean;
26
+ /** The overlay's opacity, 0..1. */
27
+ opacity: number;
28
+ tool: ContentAwareFillTool;
29
+ /** Sampling Brush diameter in natural pixels. */
30
+ brushSize: number;
31
+ /** The Sampling Brush adds to the area or takes away from it. */
32
+ brushMode: 'add' | 'subtract';
33
+ /** The fill's random seed; another gives another fill to choose from. */
34
+ seed: number;
35
+ output: 'current' | 'new-layer';
36
+ /** Update the preview as the area changes (otherwise with Preview). */
37
+ livePreview: boolean;
38
+ }
39
+ export declare const DEFAULT_CONTENT_AWARE_FILL_SETTINGS: ContentAwareFillSettings;
40
+ export declare class ContentAwareFillSession {
41
+ readonly controller: ImageEditorController;
42
+ private settingsValue;
43
+ private plan;
44
+ /** The source view's window (document pixels): the selection with room around it to sample. */
45
+ private win;
46
+ private before;
47
+ private hole;
48
+ /** 1 where the fill may copy from, per window pixel. */
49
+ private area;
50
+ private after;
51
+ private lastPoint;
52
+ private readonly listeners;
53
+ private dirty;
54
+ /** Why the workspace can't fill (nothing selected, not a pixel layer), for the UI. */
55
+ error: string | null;
56
+ computing: boolean;
57
+ revision: number;
58
+ constructor(controller: ImageEditorController);
59
+ get settings(): ContentAwareFillSettings;
60
+ /** The source view's window in document pixels (`null` until opened). */
61
+ get window(): CropBox | null;
62
+ subscribe(listener: () => void): () => void;
63
+ private emit;
64
+ /**
65
+ * Reads the layer around the selection and makes the first preview. The window holds the
66
+ * selection and room around it to sample from (its larger side, at least 64 px, each way).
67
+ */
68
+ open(): Promise<boolean>;
69
+ /** Sets the area to a preset: Auto (a band around the selection), Rectangular (the whole window). */
70
+ private applySampling;
71
+ /** Changes settings; a sampling preset replaces the area, and the preview follows when live. */
72
+ update(changes: Partial<ContentAwareFillSettings>): void;
73
+ /** The sampling area as a selection of the whole image (what OK samples from). */
74
+ samplingMask(): SelectionMask | null;
75
+ /** Whether (x, y) in document pixels is in the sampling area. */
76
+ samplesAt(x: number, y: number): boolean;
77
+ /** Paints the sampling area with the brush along a stroke segment (document pixels). */
78
+ private paint;
79
+ /** Pointer down with the Sampling Brush (Alt flips add and subtract). Returns false for other tools. */
80
+ begin(point: ImagePoint, modifiers?: {
81
+ altKey?: boolean;
82
+ }): boolean;
83
+ private strokeAdds;
84
+ move(point: ImagePoint): void;
85
+ /** Pointer up: the preview follows the new area when live. */
86
+ end(): void;
87
+ /** Recomputes the preview: one at a time, the newest area following when one is running. */
88
+ refresh(): Promise<void>;
89
+ /** Resolves once no preview is being computed. */
90
+ idle(): Promise<void>;
91
+ /** The source view: the layer's pixels, the selection tinted red, the sampling area green. RGBA at the window's size. */
92
+ renderSource(): Uint8ClampedArray;
93
+ /** The preview: the window with the fill in place (the source until the first preview). */
94
+ renderPreview(): Uint8ClampedArray;
95
+ /** Clears painting and goes back to Auto. */
96
+ reset(): void;
97
+ /**
98
+ * Keys: B, H, Z for the tools, [ and ] for the brush size. Returns true when the key was used.
99
+ */
100
+ handleKey(key: string): boolean;
101
+ /** OK: fills with this sampling area, seed and output as one undo step. */
102
+ commit(options?: {
103
+ signal?: AbortSignal;
104
+ }): Promise<{
105
+ layerId: string;
106
+ bounds: CropBox;
107
+ output: 'current' | 'new-layer';
108
+ }>;
109
+ }
@@ -0,0 +1,109 @@
1
+ /**
2
+ * The Content-Aware Fill workspace without a UI, shared by the React and Svelte workspaces: the
3
+ * sampling area (where the fill may copy from) as a paintable mask over a window around the
4
+ * selection, its presets (Auto, Rectangular, Custom), a live preview of the fill, and OK, which
5
+ * applies it as one undo step through `contentAwareFillAsync` (the same fill the
6
+ * `content_aware_fill` tool makes, with a sampling area).
7
+ */
8
+ import type { ImageEditorController } from './controller.js';
9
+ import type { CropBox, ImagePoint } from './types.js';
10
+ import { type SelectionMask } from './selection/mask.js';
11
+ export type ContentAwareFillSampling = 'auto' | 'rectangular' | 'custom';
12
+ export type ContentAwareFillTool = 'sampling-brush' | 'hand' | 'zoom';
13
+ export declare const CONTENT_AWARE_FILL_TOOLS: readonly {
14
+ id: ContentAwareFillTool;
15
+ label: string;
16
+ key: string;
17
+ }[];
18
+ export declare const CONTENT_AWARE_FILL_SAMPLING: readonly {
19
+ id: ContentAwareFillSampling;
20
+ label: string;
21
+ }[];
22
+ export interface ContentAwareFillSettings {
23
+ sampling: ContentAwareFillSampling;
24
+ /** Show the sampling area as a green overlay on the source view. */
25
+ showSampling: boolean;
26
+ /** The overlay's opacity, 0..1. */
27
+ opacity: number;
28
+ tool: ContentAwareFillTool;
29
+ /** Sampling Brush diameter in natural pixels. */
30
+ brushSize: number;
31
+ /** The Sampling Brush adds to the area or takes away from it. */
32
+ brushMode: 'add' | 'subtract';
33
+ /** The fill's random seed; another gives another fill to choose from. */
34
+ seed: number;
35
+ output: 'current' | 'new-layer';
36
+ /** Update the preview as the area changes (otherwise with Preview). */
37
+ livePreview: boolean;
38
+ }
39
+ export declare const DEFAULT_CONTENT_AWARE_FILL_SETTINGS: ContentAwareFillSettings;
40
+ export declare class ContentAwareFillSession {
41
+ readonly controller: ImageEditorController;
42
+ private settingsValue;
43
+ private plan;
44
+ /** The source view's window (document pixels): the selection with room around it to sample. */
45
+ private win;
46
+ private before;
47
+ private hole;
48
+ /** 1 where the fill may copy from, per window pixel. */
49
+ private area;
50
+ private after;
51
+ private lastPoint;
52
+ private readonly listeners;
53
+ private dirty;
54
+ /** Why the workspace can't fill (nothing selected, not a pixel layer), for the UI. */
55
+ error: string | null;
56
+ computing: boolean;
57
+ revision: number;
58
+ constructor(controller: ImageEditorController);
59
+ get settings(): ContentAwareFillSettings;
60
+ /** The source view's window in document pixels (`null` until opened). */
61
+ get window(): CropBox | null;
62
+ subscribe(listener: () => void): () => void;
63
+ private emit;
64
+ /**
65
+ * Reads the layer around the selection and makes the first preview. The window holds the
66
+ * selection and room around it to sample from (its larger side, at least 64 px, each way).
67
+ */
68
+ open(): Promise<boolean>;
69
+ /** Sets the area to a preset: Auto (a band around the selection), Rectangular (the whole window). */
70
+ private applySampling;
71
+ /** Changes settings; a sampling preset replaces the area, and the preview follows when live. */
72
+ update(changes: Partial<ContentAwareFillSettings>): void;
73
+ /** The sampling area as a selection of the whole image (what OK samples from). */
74
+ samplingMask(): SelectionMask | null;
75
+ /** Whether (x, y) in document pixels is in the sampling area. */
76
+ samplesAt(x: number, y: number): boolean;
77
+ /** Paints the sampling area with the brush along a stroke segment (document pixels). */
78
+ private paint;
79
+ /** Pointer down with the Sampling Brush (Alt flips add and subtract). Returns false for other tools. */
80
+ begin(point: ImagePoint, modifiers?: {
81
+ altKey?: boolean;
82
+ }): boolean;
83
+ private strokeAdds;
84
+ move(point: ImagePoint): void;
85
+ /** Pointer up: the preview follows the new area when live. */
86
+ end(): void;
87
+ /** Recomputes the preview: one at a time, the newest area following when one is running. */
88
+ refresh(): Promise<void>;
89
+ /** Resolves once no preview is being computed. */
90
+ idle(): Promise<void>;
91
+ /** The source view: the layer's pixels, the selection tinted red, the sampling area green. RGBA at the window's size. */
92
+ renderSource(): Uint8ClampedArray;
93
+ /** The preview: the window with the fill in place (the source until the first preview). */
94
+ renderPreview(): Uint8ClampedArray;
95
+ /** Clears painting and goes back to Auto. */
96
+ reset(): void;
97
+ /**
98
+ * Keys: B, H, Z for the tools, [ and ] for the brush size. Returns true when the key was used.
99
+ */
100
+ handleKey(key: string): boolean;
101
+ /** OK: fills with this sampling area, seed and output as one undo step. */
102
+ commit(options?: {
103
+ signal?: AbortSignal;
104
+ }): Promise<{
105
+ layerId: string;
106
+ bounds: CropBox;
107
+ output: 'current' | 'new-layer';
108
+ }>;
109
+ }