@pixerate/editor 0.218.1 → 0.219.1
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/{chunk-YF3YZZ6G.cjs → chunk-2ONK3ERD.cjs} +8 -8
- package/dist/{chunk-U7C655AM.js → chunk-3X244IZ5.js} +26 -4
- package/dist/{chunk-ZGJGSCUW.js → chunk-4P6H35HU.js} +1 -1
- package/dist/{chunk-MQDIP4CF.cjs → chunk-BB6TR422.cjs} +5 -1
- package/dist/{chunk-UC3PWFM3.cjs → chunk-SRMDF2VN.cjs} +35 -13
- package/dist/{chunk-XI2NV5U7.js → chunk-VWEWY4FY.js} +4 -0
- package/dist/image-editor/controller.d.cts +31 -0
- package/dist/image-editor/controller.d.ts +31 -0
- package/dist/image-editor/index.cjs +329 -261
- package/dist/image-editor/index.js +71 -3
- package/dist/image-editor/selection-worker.cjs +4 -4
- package/dist/image-editor/selection-worker.js +2 -2
- package/dist/paint/heal.d.cts +2 -0
- package/dist/paint/heal.d.ts +2 -0
- package/dist/paint/index.cjs +3 -3
- package/dist/paint/index.js +2 -2
- package/package.json +1 -1
- package/skills/pixerate-image-editor/SKILL.md +3 -4
- package/skills/pixerate-image-selection-masking/SKILL.md +9 -9
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
var
|
|
8
|
+
var _chunkBB6TR422cjs = require('./chunk-BB6TR422.cjs');
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
@@ -465,7 +465,7 @@ function toneOf(tool, settings) {
|
|
|
465
465
|
}
|
|
466
466
|
function healPixels(w, h, src, dst, mask) {
|
|
467
467
|
const out = new Float32Array(dst);
|
|
468
|
-
const bb =
|
|
468
|
+
const bb = _chunkBB6TR422cjs.maskBounds.call(void 0, w, h, mask);
|
|
469
469
|
if (!bb) return out;
|
|
470
470
|
const x0 = Math.max(0, bb[0] - 1);
|
|
471
471
|
const y0 = Math.max(0, bb[1] - 1);
|
|
@@ -487,7 +487,7 @@ function healPixels(w, h, src, dst, mask) {
|
|
|
487
487
|
}
|
|
488
488
|
}
|
|
489
489
|
}
|
|
490
|
-
const healed =
|
|
490
|
+
const healed = _chunkBB6TR422cjs.seamlessClone.call(void 0, rw, rh, 4, s, d, m);
|
|
491
491
|
for (let y = 0; y < rh; y++) {
|
|
492
492
|
for (let x = 0; x < rw; x++) {
|
|
493
493
|
const gi = ((y + y0) * w + x + x0) * 4;
|
|
@@ -498,13 +498,13 @@ function healPixels(w, h, src, dst, mask) {
|
|
|
498
498
|
return out;
|
|
499
499
|
}
|
|
500
500
|
function spotHealPixels(w, h, img, hole, size, type, onOffset) {
|
|
501
|
-
const domain =
|
|
502
|
-
const healMask =
|
|
501
|
+
const domain = _chunkBB6TR422cjs.dilateMask.call(void 0, w, h, hole, 2);
|
|
502
|
+
const healMask = _chunkBB6TR422cjs.dilateMask.call(void 0, w, h, hole, 1);
|
|
503
503
|
let filled = null;
|
|
504
504
|
let bestFound = null;
|
|
505
505
|
if (type === "proximity-match") {
|
|
506
506
|
const ring = Math.min(16, Math.max(3, Math.floor(size / 8)));
|
|
507
|
-
const off =
|
|
507
|
+
const off = _chunkBB6TR422cjs.bestOffset.call(void 0, w, h, 4, img, domain, ring, Math.floor(Math.max(w, h) / 2));
|
|
508
508
|
if (off) {
|
|
509
509
|
bestFound = off;
|
|
510
510
|
filled = new Float32Array(img);
|
|
@@ -519,8 +519,8 @@ function spotHealPixels(w, h, img, hole, size, type, onOffset) {
|
|
|
519
519
|
}
|
|
520
520
|
}
|
|
521
521
|
_optionalChain([onOffset, 'optionalCall', _ => _(filled ? bestFound : null)]);
|
|
522
|
-
if (!filled && type === "content-aware") filled =
|
|
523
|
-
if (!filled) filled =
|
|
522
|
+
if (!filled && type === "content-aware") filled = _chunkBB6TR422cjs.contentAwareFill.call(void 0, w, h, 4, img, domain);
|
|
523
|
+
if (!filled) filled = _chunkBB6TR422cjs.membraneFill.call(void 0, w, h, 4, img, domain);
|
|
524
524
|
return healPixels(w, h, filled, img, healMask);
|
|
525
525
|
}
|
|
526
526
|
function spotHealWindow(bounds, canvas) {
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "./chunk-ZNA3IQZF.js";
|
|
11
11
|
import {
|
|
12
12
|
contentAwareFill
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-VWEWY4FY.js";
|
|
14
14
|
|
|
15
15
|
// src/image-editor/selection/shapes.ts
|
|
16
16
|
function polygonCoverage(points, width, height, antiAlias = true) {
|
|
@@ -808,12 +808,15 @@ function selectObjectIn(pixels, target, options = {}) {
|
|
|
808
808
|
const px = Math.min(w - 1, Math.max(0, Math.floor(target.point.x * scale)));
|
|
809
809
|
const py = Math.min(h - 1, Math.max(0, Math.floor(target.point.y * scale)));
|
|
810
810
|
let half = Math.max(24, Math.round(0.3 * Math.max(w, h)));
|
|
811
|
+
let seed = null;
|
|
812
|
+
let reseeds = 0;
|
|
811
813
|
for (; ; ) {
|
|
812
814
|
const rx = Math.max(0, px - half);
|
|
813
815
|
const ry = Math.max(0, py - half);
|
|
814
816
|
const roi = { x: rx, y: ry, width: Math.min(w, px + half + 1) - rx, height: Math.min(h, py + half + 1) - ry };
|
|
815
817
|
const whole = roi.width === w && roi.height === h;
|
|
816
818
|
const band = Math.max(4, Math.round(0.15 * half));
|
|
819
|
+
const radius = seed ?? 0.3 * half;
|
|
817
820
|
const n = roi.width * roi.height;
|
|
818
821
|
const initial = new Uint8Array(n);
|
|
819
822
|
const hardBg = new Uint8Array(n);
|
|
@@ -822,9 +825,10 @@ function selectObjectIn(pixels, target, options = {}) {
|
|
|
822
825
|
for (let y = 0; y < roi.height; y++) {
|
|
823
826
|
for (let x = 0; x < roi.width; x++) {
|
|
824
827
|
const p = y * roi.width + x;
|
|
828
|
+
const d = Math.hypot(rx + x - px, ry + y - py);
|
|
825
829
|
if (inBand(x, y)) hardBg[p] = 1;
|
|
826
|
-
else initial[p] = 1;
|
|
827
|
-
if (
|
|
830
|
+
else if (d <= radius) initial[p] = 1;
|
|
831
|
+
if (d <= 1.5) hardFg[p] = 1;
|
|
828
832
|
}
|
|
829
833
|
}
|
|
830
834
|
const label = grabCutLabels(work, { roi, initial, hardBg, hardFg }, iterations, round);
|
|
@@ -838,6 +842,19 @@ function selectObjectIn(pixels, target, options = {}) {
|
|
|
838
842
|
}
|
|
839
843
|
}
|
|
840
844
|
}
|
|
845
|
+
if (!touches && reseeds < 2) {
|
|
846
|
+
let found = 0;
|
|
847
|
+
let seeded = 0;
|
|
848
|
+
for (let p = 0; p < n; p++) {
|
|
849
|
+
found += label[p];
|
|
850
|
+
seeded += initial[p];
|
|
851
|
+
}
|
|
852
|
+
if (found * 2 < seeded) {
|
|
853
|
+
reseeds++;
|
|
854
|
+
seed = Math.max(2, 0.7 * Math.sqrt(found / Math.PI));
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
841
858
|
if (!touches || whole) {
|
|
842
859
|
report?.(0.9, "Scaling up");
|
|
843
860
|
const out = upscaleRegion(work, pixels, roi, label, enhance);
|
|
@@ -845,6 +862,7 @@ function selectObjectIn(pixels, target, options = {}) {
|
|
|
845
862
|
return out;
|
|
846
863
|
}
|
|
847
864
|
half = Math.round(half * 1.6);
|
|
865
|
+
seed = null;
|
|
848
866
|
}
|
|
849
867
|
}
|
|
850
868
|
|
|
@@ -1574,7 +1592,11 @@ function runSelectionJob(job, pixels, onProgress) {
|
|
|
1574
1592
|
};
|
|
1575
1593
|
case "fill": {
|
|
1576
1594
|
onProgress?.(0, "Filling");
|
|
1577
|
-
const filled = contentAwareFill(pixels.width, pixels.height, 4, pixels.data, job.hole, {
|
|
1595
|
+
const filled = contentAwareFill(pixels.width, pixels.height, 4, pixels.data, job.hole, {
|
|
1596
|
+
seed: job.seed,
|
|
1597
|
+
exclude: job.exclude,
|
|
1598
|
+
onProgress: (f) => onProgress?.(f * 0.99, "Filling")
|
|
1599
|
+
});
|
|
1578
1600
|
onProgress?.(1, "Done");
|
|
1579
1601
|
return { mask: createSelectionMask(1, 1), pixels: Uint8ClampedArray.from(filled) };
|
|
1580
1602
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/paint/heal.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; }// src/paint/heal.ts
|
|
2
2
|
var TOL = 2e-3;
|
|
3
3
|
var FINE_ITERS = 30;
|
|
4
4
|
var FINE_OMEGA = 1.7;
|
|
@@ -311,6 +311,8 @@ function contentAwareFill(w, h, ch, img, hole, options = {}) {
|
|
|
311
311
|
levels.push(downsample(top, ch));
|
|
312
312
|
}
|
|
313
313
|
const random = rng(_nullishCoalesce(options.seed, () => ( w * 73856093 + h * 19349663)));
|
|
314
|
+
const totalWork = levels.reduce((n, l) => n + l.w * l.h, 0) * iters || 1;
|
|
315
|
+
let doneWork = 0;
|
|
314
316
|
let prevFill = null;
|
|
315
317
|
let prevNnf = null;
|
|
316
318
|
let prevW = 0;
|
|
@@ -441,6 +443,8 @@ function contentAwareFill(w, h, ch, img, hole, options = {}) {
|
|
|
441
443
|
}
|
|
442
444
|
}
|
|
443
445
|
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];
|
|
446
|
+
doneWork += W * H;
|
|
447
|
+
_optionalChain([options, 'access', _ => _.onProgress, 'optionalCall', _2 => _2(Math.min(1, doneWork / totalWork))]);
|
|
444
448
|
}
|
|
445
449
|
prevFill = cur;
|
|
446
450
|
prevNnf = nnf;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
var _chunkSS53NNZOcjs = require('./chunk-SS53NNZO.cjs');
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
var
|
|
13
|
+
var _chunkBB6TR422cjs = require('./chunk-BB6TR422.cjs');
|
|
14
14
|
|
|
15
15
|
// src/image-editor/selection/shapes.ts
|
|
16
16
|
function polygonCoverage(points, width, height, antiAlias = true) {
|
|
@@ -808,12 +808,15 @@ function selectObjectIn(pixels, target, options = {}) {
|
|
|
808
808
|
const px = Math.min(w - 1, Math.max(0, Math.floor(target.point.x * scale)));
|
|
809
809
|
const py = Math.min(h - 1, Math.max(0, Math.floor(target.point.y * scale)));
|
|
810
810
|
let half = Math.max(24, Math.round(0.3 * Math.max(w, h)));
|
|
811
|
+
let seed = null;
|
|
812
|
+
let reseeds = 0;
|
|
811
813
|
for (; ; ) {
|
|
812
814
|
const rx = Math.max(0, px - half);
|
|
813
815
|
const ry = Math.max(0, py - half);
|
|
814
816
|
const roi = { x: rx, y: ry, width: Math.min(w, px + half + 1) - rx, height: Math.min(h, py + half + 1) - ry };
|
|
815
817
|
const whole = roi.width === w && roi.height === h;
|
|
816
818
|
const band = Math.max(4, Math.round(0.15 * half));
|
|
819
|
+
const radius = _nullishCoalesce(seed, () => ( 0.3 * half));
|
|
817
820
|
const n = roi.width * roi.height;
|
|
818
821
|
const initial = new Uint8Array(n);
|
|
819
822
|
const hardBg = new Uint8Array(n);
|
|
@@ -822,9 +825,10 @@ function selectObjectIn(pixels, target, options = {}) {
|
|
|
822
825
|
for (let y = 0; y < roi.height; y++) {
|
|
823
826
|
for (let x = 0; x < roi.width; x++) {
|
|
824
827
|
const p = y * roi.width + x;
|
|
828
|
+
const d = Math.hypot(rx + x - px, ry + y - py);
|
|
825
829
|
if (inBand(x, y)) hardBg[p] = 1;
|
|
826
|
-
else initial[p] = 1;
|
|
827
|
-
if (
|
|
830
|
+
else if (d <= radius) initial[p] = 1;
|
|
831
|
+
if (d <= 1.5) hardFg[p] = 1;
|
|
828
832
|
}
|
|
829
833
|
}
|
|
830
834
|
const label = grabCutLabels(work, { roi, initial, hardBg, hardFg }, iterations, round);
|
|
@@ -838,6 +842,19 @@ function selectObjectIn(pixels, target, options = {}) {
|
|
|
838
842
|
}
|
|
839
843
|
}
|
|
840
844
|
}
|
|
845
|
+
if (!touches && reseeds < 2) {
|
|
846
|
+
let found = 0;
|
|
847
|
+
let seeded = 0;
|
|
848
|
+
for (let p = 0; p < n; p++) {
|
|
849
|
+
found += label[p];
|
|
850
|
+
seeded += initial[p];
|
|
851
|
+
}
|
|
852
|
+
if (found * 2 < seeded) {
|
|
853
|
+
reseeds++;
|
|
854
|
+
seed = Math.max(2, 0.7 * Math.sqrt(found / Math.PI));
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
}
|
|
841
858
|
if (!touches || whole) {
|
|
842
859
|
_optionalChain([report, 'optionalCall', _10 => _10(0.9, "Scaling up")]);
|
|
843
860
|
const out = upscaleRegion(work, pixels, roi, label, enhance);
|
|
@@ -845,6 +862,7 @@ function selectObjectIn(pixels, target, options = {}) {
|
|
|
845
862
|
return out;
|
|
846
863
|
}
|
|
847
864
|
half = Math.round(half * 1.6);
|
|
865
|
+
seed = null;
|
|
848
866
|
}
|
|
849
867
|
}
|
|
850
868
|
|
|
@@ -1574,16 +1592,20 @@ function runSelectionJob(job, pixels, onProgress) {
|
|
|
1574
1592
|
};
|
|
1575
1593
|
case "fill": {
|
|
1576
1594
|
_optionalChain([onProgress, 'optionalCall', _32 => _32(0, "Filling")]);
|
|
1577
|
-
const filled =
|
|
1578
|
-
|
|
1595
|
+
const filled = _chunkBB6TR422cjs.contentAwareFill.call(void 0, pixels.width, pixels.height, 4, pixels.data, job.hole, {
|
|
1596
|
+
seed: job.seed,
|
|
1597
|
+
exclude: job.exclude,
|
|
1598
|
+
onProgress: (f) => _optionalChain([onProgress, 'optionalCall', _33 => _33(f * 0.99, "Filling")])
|
|
1599
|
+
});
|
|
1600
|
+
_optionalChain([onProgress, 'optionalCall', _34 => _34(1, "Done")]);
|
|
1579
1601
|
return { mask: _chunkSS53NNZOcjs.createSelectionMask.call(void 0, 1, 1), pixels: Uint8ClampedArray.from(filled) };
|
|
1580
1602
|
}
|
|
1581
1603
|
case "refine-preview": {
|
|
1582
|
-
_optionalChain([onProgress, 'optionalCall',
|
|
1604
|
+
_optionalChain([onProgress, 'optionalCall', _35 => _35(0, "Refining")]);
|
|
1583
1605
|
const base = job.base ? deserializeMask(job.base) : null;
|
|
1584
1606
|
const { selection, refineBrush } = applyRefineEdits(pixels, base, job.edits, pixels.width, pixels.height);
|
|
1585
|
-
const mask = _optionalChain([selection, 'optionalAccess',
|
|
1586
|
-
_optionalChain([onProgress, 'optionalCall',
|
|
1607
|
+
const mask = _optionalChain([selection, 'optionalAccess', _36 => _36.bounds]) ? _chunkSS53NNZOcjs.refineSelection.call(void 0, pixels, selection, job.params, refineBrush) : _chunkSS53NNZOcjs.createSelectionMask.call(void 0, pixels.width, pixels.height);
|
|
1608
|
+
_optionalChain([onProgress, 'optionalCall', _37 => _37(1, "Done")]);
|
|
1587
1609
|
return { mask };
|
|
1588
1610
|
}
|
|
1589
1611
|
}
|
|
@@ -1624,7 +1646,7 @@ var SelectionWorkerClient = (_class2 = class {
|
|
|
1624
1646
|
__init9() {this.pending = /* @__PURE__ */ new Map()}
|
|
1625
1647
|
/** Runs `job` on `pixels` in the worker. Aborting `signal` terminates the worker and rejects with an `AbortError`. */
|
|
1626
1648
|
run(job, pixels, options = {}) {
|
|
1627
|
-
if (_optionalChain([options, 'access',
|
|
1649
|
+
if (_optionalChain([options, 'access', _38 => _38.signal, 'optionalAccess', _39 => _39.aborted])) return Promise.reject(abortError());
|
|
1628
1650
|
const worker = this.ensure();
|
|
1629
1651
|
let key = this.sent.get(pixels.data);
|
|
1630
1652
|
if (key === void 0) {
|
|
@@ -1635,7 +1657,7 @@ var SelectionWorkerClient = (_class2 = class {
|
|
|
1635
1657
|
const id = this.nextId++;
|
|
1636
1658
|
return new Promise((resolve, reject) => {
|
|
1637
1659
|
this.pending.set(id, { resolve, reject, onProgress: options.onProgress });
|
|
1638
|
-
_optionalChain([options, 'access',
|
|
1660
|
+
_optionalChain([options, 'access', _40 => _40.signal, 'optionalAccess', _41 => _41.addEventListener, 'call', _42 => _42(
|
|
1639
1661
|
"abort",
|
|
1640
1662
|
() => {
|
|
1641
1663
|
if (this.pending.has(id)) this.terminate(abortError());
|
|
@@ -1647,7 +1669,7 @@ var SelectionWorkerClient = (_class2 = class {
|
|
|
1647
1669
|
}
|
|
1648
1670
|
/** Stops the worker; jobs still running reject with `reason` (default an `AbortError`). */
|
|
1649
1671
|
terminate(reason = abortError()) {
|
|
1650
|
-
_optionalChain([this, 'access',
|
|
1672
|
+
_optionalChain([this, 'access', _43 => _43.worker, 'optionalAccess', _44 => _44.terminate, 'optionalCall', _45 => _45()]);
|
|
1651
1673
|
this.worker = null;
|
|
1652
1674
|
this.sent = /* @__PURE__ */ new WeakMap();
|
|
1653
1675
|
const pending = [...this.pending.values()];
|
|
@@ -1661,14 +1683,14 @@ var SelectionWorkerClient = (_class2 = class {
|
|
|
1661
1683
|
const msg = event.data;
|
|
1662
1684
|
const p = this.pending.get(msg.id);
|
|
1663
1685
|
if (!p) return;
|
|
1664
|
-
if (msg.type === "progress") _optionalChain([p, 'access',
|
|
1686
|
+
if (msg.type === "progress") _optionalChain([p, 'access', _46 => _46.onProgress, 'optionalCall', _47 => _47(msg.fraction, msg.stage)]);
|
|
1665
1687
|
else {
|
|
1666
1688
|
this.pending.delete(msg.id);
|
|
1667
1689
|
if (msg.type === "error") p.reject(new Error(msg.message));
|
|
1668
1690
|
else p.resolve({ mask: deserializeMask(msg.mask), reason: msg.reason, noiseLevel: msg.noiseLevel, pixels: msg.pixels });
|
|
1669
1691
|
}
|
|
1670
1692
|
};
|
|
1671
|
-
worker.onerror = (event) => this.terminate(new Error(_optionalChain([event, 'optionalAccess',
|
|
1693
|
+
worker.onerror = (event) => this.terminate(new Error(_optionalChain([event, 'optionalAccess', _48 => _48.message]) || "The selection worker failed."));
|
|
1672
1694
|
this.worker = worker;
|
|
1673
1695
|
return worker;
|
|
1674
1696
|
}
|
|
@@ -311,6 +311,8 @@ function contentAwareFill(w, h, ch, img, hole, options = {}) {
|
|
|
311
311
|
levels.push(downsample(top, ch));
|
|
312
312
|
}
|
|
313
313
|
const random = rng(options.seed ?? w * 73856093 + h * 19349663);
|
|
314
|
+
const totalWork = levels.reduce((n, l) => n + l.w * l.h, 0) * iters || 1;
|
|
315
|
+
let doneWork = 0;
|
|
314
316
|
let prevFill = null;
|
|
315
317
|
let prevNnf = null;
|
|
316
318
|
let prevW = 0;
|
|
@@ -441,6 +443,8 @@ function contentAwareFill(w, h, ch, img, hole, options = {}) {
|
|
|
441
443
|
}
|
|
442
444
|
}
|
|
443
445
|
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];
|
|
446
|
+
doneWork += W * H;
|
|
447
|
+
options.onProgress?.(Math.min(1, doneWork / totalWork));
|
|
444
448
|
}
|
|
445
449
|
prevFill = cur;
|
|
446
450
|
prevNnf = nnf;
|
|
@@ -285,6 +285,20 @@ export interface CloneSource {
|
|
|
285
285
|
/** A vector mask's path: several `components`, or one `shape` / `subpaths` (with `op`). */
|
|
286
286
|
/** What `inspectMaskEdge` reads: the selection, a layer's painted mask or its vector mask. */
|
|
287
287
|
export type MaskEdgeTarget = 'selection' | 'layer-mask' | 'vector-mask';
|
|
288
|
+
/** What `separateSubject` made. */
|
|
289
|
+
export interface SeparateSubjectResult {
|
|
290
|
+
/** The subject: a copy of the layer masked by the selection, on top. */
|
|
291
|
+
foregroundLayerId: string;
|
|
292
|
+
/** The background plate: a copy with the subject's area filled. */
|
|
293
|
+
plateLayerId: string;
|
|
294
|
+
/** The layer it came from (now hidden). */
|
|
295
|
+
sourceLayerId: string;
|
|
296
|
+
/** An alpha channel of the filled area, to select and fill again where the fill is wrong. */
|
|
297
|
+
fillChannelId: string;
|
|
298
|
+
filledBounds: CropBox;
|
|
299
|
+
dilate: number;
|
|
300
|
+
fill: 'content-aware' | 'none';
|
|
301
|
+
}
|
|
288
302
|
/** A selection `compareSelections` reads (see `selectionFrom`). */
|
|
289
303
|
export type SelectionSource = {
|
|
290
304
|
kind: 'selection';
|
|
@@ -1617,6 +1631,23 @@ export declare class ImageEditorController {
|
|
|
1617
1631
|
bounds: CropBox;
|
|
1618
1632
|
output: ContentAwareFillOutput;
|
|
1619
1633
|
}>;
|
|
1634
|
+
/**
|
|
1635
|
+
* Separate Subject: splits the selected subject from its background in one undo step. The
|
|
1636
|
+
* subject goes to a copy of the layer masked by the selection (on top, made active); a second
|
|
1637
|
+
* copy becomes the background plate with the subject's area, grown by `dilate` px (default 8),
|
|
1638
|
+
* Content-Aware Filled (`fill: 'none'` leaves it transparent); an alpha channel marks the filled
|
|
1639
|
+
* area so a bad patch can be selected and filled again; the source layer is hidden. The fill is
|
|
1640
|
+
* computed first, so `signal` cancels it with nothing changed. 8-bit pixel layers only. Returns
|
|
1641
|
+
* the new layers, the channel and the filled bounds.
|
|
1642
|
+
*/
|
|
1643
|
+
separateSubject(options?: {
|
|
1644
|
+
layerId?: string;
|
|
1645
|
+
dilate?: number;
|
|
1646
|
+
fill?: 'content-aware' | 'none';
|
|
1647
|
+
seed?: number;
|
|
1648
|
+
signal?: AbortSignal;
|
|
1649
|
+
onProgress?: SelectionProgress;
|
|
1650
|
+
}): Promise<SeparateSubjectResult>;
|
|
1620
1651
|
/**
|
|
1621
1652
|
* The fill without changing anything: the window, its pixels before and after (RGBA, the fill
|
|
1622
1653
|
* blended by the selection's coverage) and the sampling area used, for a preview.
|
|
@@ -285,6 +285,20 @@ export interface CloneSource {
|
|
|
285
285
|
/** A vector mask's path: several `components`, or one `shape` / `subpaths` (with `op`). */
|
|
286
286
|
/** What `inspectMaskEdge` reads: the selection, a layer's painted mask or its vector mask. */
|
|
287
287
|
export type MaskEdgeTarget = 'selection' | 'layer-mask' | 'vector-mask';
|
|
288
|
+
/** What `separateSubject` made. */
|
|
289
|
+
export interface SeparateSubjectResult {
|
|
290
|
+
/** The subject: a copy of the layer masked by the selection, on top. */
|
|
291
|
+
foregroundLayerId: string;
|
|
292
|
+
/** The background plate: a copy with the subject's area filled. */
|
|
293
|
+
plateLayerId: string;
|
|
294
|
+
/** The layer it came from (now hidden). */
|
|
295
|
+
sourceLayerId: string;
|
|
296
|
+
/** An alpha channel of the filled area, to select and fill again where the fill is wrong. */
|
|
297
|
+
fillChannelId: string;
|
|
298
|
+
filledBounds: CropBox;
|
|
299
|
+
dilate: number;
|
|
300
|
+
fill: 'content-aware' | 'none';
|
|
301
|
+
}
|
|
288
302
|
/** A selection `compareSelections` reads (see `selectionFrom`). */
|
|
289
303
|
export type SelectionSource = {
|
|
290
304
|
kind: 'selection';
|
|
@@ -1617,6 +1631,23 @@ export declare class ImageEditorController {
|
|
|
1617
1631
|
bounds: CropBox;
|
|
1618
1632
|
output: ContentAwareFillOutput;
|
|
1619
1633
|
}>;
|
|
1634
|
+
/**
|
|
1635
|
+
* Separate Subject: splits the selected subject from its background in one undo step. The
|
|
1636
|
+
* subject goes to a copy of the layer masked by the selection (on top, made active); a second
|
|
1637
|
+
* copy becomes the background plate with the subject's area, grown by `dilate` px (default 8),
|
|
1638
|
+
* Content-Aware Filled (`fill: 'none'` leaves it transparent); an alpha channel marks the filled
|
|
1639
|
+
* area so a bad patch can be selected and filled again; the source layer is hidden. The fill is
|
|
1640
|
+
* computed first, so `signal` cancels it with nothing changed. 8-bit pixel layers only. Returns
|
|
1641
|
+
* the new layers, the channel and the filled bounds.
|
|
1642
|
+
*/
|
|
1643
|
+
separateSubject(options?: {
|
|
1644
|
+
layerId?: string;
|
|
1645
|
+
dilate?: number;
|
|
1646
|
+
fill?: 'content-aware' | 'none';
|
|
1647
|
+
seed?: number;
|
|
1648
|
+
signal?: AbortSignal;
|
|
1649
|
+
onProgress?: SelectionProgress;
|
|
1650
|
+
}): Promise<SeparateSubjectResult>;
|
|
1620
1651
|
/**
|
|
1621
1652
|
* The fill without changing anything: the window, its pixels before and after (RGBA, the fill
|
|
1622
1653
|
* blended by the selection's coverage) and the sampling area used, for a preview.
|