@pixerate/editor 0.214.0 → 0.216.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 (40) hide show
  1. package/dist/{chunk-UOGKSVRQ.cjs → chunk-B46UQUDC.cjs} +770 -733
  2. package/dist/{chunk-W3TOZTL7.js → chunk-F7Z2MKY2.js} +115 -4
  3. package/dist/{chunk-YBJXCQTR.js → chunk-QXY4YTRI.js} +738 -701
  4. package/dist/{chunk-H65IBXFI.cjs → chunk-S55JFWDA.cjs} +350 -239
  5. package/dist/{chunk-QYLWG22N.cjs → chunk-SS53NNZO.cjs} +304 -1
  6. package/dist/{chunk-LQ5SALHK.js → chunk-U7C655AM.js} +5 -311
  7. package/dist/{chunk-L52VMQZS.cjs → chunk-UC3PWFM3.cjs} +68 -374
  8. package/dist/{chunk-L3G23N5Y.js → chunk-ZNA3IQZF.js} +304 -1
  9. package/dist/image-editor/controller.d.cts +41 -0
  10. package/dist/image-editor/controller.d.ts +41 -0
  11. package/dist/image-editor/index.cjs +1461 -1125
  12. package/dist/image-editor/index.d.cts +1 -0
  13. package/dist/image-editor/index.d.ts +1 -0
  14. package/dist/image-editor/index.js +384 -48
  15. package/dist/image-editor/layers/edge-color.d.cts +27 -0
  16. package/dist/image-editor/layers/edge-color.d.ts +27 -0
  17. package/dist/image-editor/layers/serialize.d.cts +13 -0
  18. package/dist/image-editor/layers/serialize.d.ts +13 -0
  19. package/dist/image-editor/selection/index.d.cts +1 -0
  20. package/dist/image-editor/selection/index.d.ts +1 -0
  21. package/dist/image-editor/selection/inspect.d.cts +9 -0
  22. package/dist/image-editor/selection/inspect.d.ts +9 -0
  23. package/dist/image-editor/selection/uncertainty.d.cts +40 -0
  24. package/dist/image-editor/selection/uncertainty.d.ts +40 -0
  25. package/dist/image-editor/selection-worker.cjs +4 -4
  26. package/dist/image-editor/selection-worker.js +2 -2
  27. package/dist/paint-editor/index.cjs +79 -79
  28. package/dist/paint-editor/index.js +3 -3
  29. package/dist/raster/compositor.d.cts +10 -1
  30. package/dist/raster/compositor.d.ts +10 -1
  31. package/dist/raster/deep-compositor.d.cts +1 -1
  32. package/dist/raster/deep-compositor.d.ts +1 -1
  33. package/dist/raster/index.cjs +4 -2
  34. package/dist/raster/index.js +3 -1
  35. package/dist/raster/layers.d.cts +16 -0
  36. package/dist/raster/layers.d.ts +16 -0
  37. package/package.json +1 -1
  38. package/skills/pixerate-image-editor/SKILL.md +9 -7
  39. package/skills/pixerate-image-editor/references/paths.md +12 -0
  40. package/skills/pixerate-image-selection-masking/SKILL.md +11 -11
@@ -7,9 +7,7 @@ import {
7
7
  } from "../chunk-DJBYJOHM.js";
8
8
  import {
9
9
  ColorGmm,
10
- DEFAULT_REFINE_EDGE_PARAMS,
11
10
  GAMMA,
12
- GUIDED_FILTER_EPS,
13
11
  GridGraph,
14
12
  REFINE_EDIT_KINDS,
15
13
  SelectionWorkerClient,
@@ -17,31 +15,22 @@ import {
17
15
  WORK_SIZE,
18
16
  applyRefineEdits,
19
17
  boxBlur,
20
- boxMean,
21
18
  brushStrokeMask,
22
- decontaminateColors,
23
19
  deserializeMask,
24
- edgeWidth,
25
20
  ellipsePoints,
26
21
  ellipseSelection,
27
22
  findSky,
28
23
  findSubject,
29
24
  focusArea,
30
25
  grabCutLabels,
31
- guidePlanes,
32
- guidedFilterColor,
33
26
  keepMainPieces,
34
27
  normalizeRect,
35
- normalizeRefineEdgeParams,
36
28
  otsuThreshold,
37
29
  polygonCoverage,
38
30
  polygonCoverageBox,
39
31
  polygonSelection,
40
32
  quickSelectStroke,
41
33
  rectSelection,
42
- refineCoverage,
43
- refineEdgeHalo,
44
- refineSelection,
45
34
  runSelectionJob,
46
35
  selectObjectIn,
47
36
  serializeMask,
@@ -54,7 +43,7 @@ import {
54
43
  workImage,
55
44
  workLuma,
56
45
  workPlanes
57
- } from "../chunk-LQ5SALHK.js";
46
+ } from "../chunk-U7C655AM.js";
58
47
  import {
59
48
  ADJUSTMENT_KEYS,
60
49
  ADJUSTMENT_KINDS,
@@ -72,6 +61,7 @@ import {
72
61
  DEFAULT_MITER_LIMIT,
73
62
  DEFAULT_TRANSFORM_VALUES,
74
63
  DEVELOP_FILTER_ID,
64
+ EDGE_COLOR_RADIUS,
75
65
  FREE_TRANSFORM_MODES,
76
66
  HUE_RANGE_LABELS,
77
67
  IDENTITY_MATRIX3,
@@ -127,6 +117,7 @@ import {
127
117
  applyBoxBlur,
128
118
  applyBoxBlurFloat,
129
119
  applyDepthMask,
120
+ applyEdgeUnmix,
130
121
  applyImageProjectionToContext,
131
122
  applyLegacyAdjustments,
132
123
  applyLegacyFloat,
@@ -175,6 +166,7 @@ import {
175
166
  deserializeLayer,
176
167
  deserializeLayers,
177
168
  deserializePatterns,
169
+ despillEdge,
178
170
  developFilter,
179
171
  developFromAdjustments,
180
172
  developTarget,
@@ -197,7 +189,9 @@ import {
197
189
  getViewportMetrics,
198
190
  getViewportProjection,
199
191
  groupLayers,
192
+ hasEdgeUnmix,
200
193
  hasResampledLegacyMask,
194
+ hasSoftBand,
201
195
  hasSurface,
202
196
  hasWarp,
203
197
  hexToRgb,
@@ -248,6 +242,7 @@ import {
248
242
  liveShapeSubpaths,
249
243
  mapCropHandle,
250
244
  mapSubpaths,
245
+ maskCoverageOverCanvas,
251
246
  matrixFromCorners,
252
247
  matrixFromParams,
253
248
  mergeShapeComponents,
@@ -302,6 +297,7 @@ import {
302
297
  rasterMaskFromLegacy,
303
298
  rasterizeCoverage,
304
299
  rasterizeSmartObject,
300
+ readEdgeColor,
305
301
  readPsd,
306
302
  readShapeComponents,
307
303
  referencePoint,
@@ -404,34 +400,45 @@ import {
404
400
  writePsd,
405
401
  xyToTempTint,
406
402
  xyToUv
407
- } from "../chunk-W3TOZTL7.js";
403
+ } from "../chunk-F7Z2MKY2.js";
408
404
  import {
409
405
  parseCssColor
410
406
  } from "../chunk-3CI7FRMK.js";
411
407
  import {
408
+ DEFAULT_REFINE_EDGE_PARAMS,
412
409
  EMPTY_DISTANCE,
410
+ GUIDED_FILTER_EPS,
413
411
  SELECTION_MODES,
414
412
  SELECTION_MODIFY_OPS,
415
413
  borderCoverage,
414
+ boxMean,
416
415
  combineSelections,
417
416
  computeSelectionBounds,
418
417
  contractCoverage,
419
418
  coverageToByte,
420
419
  createSelectionMask,
421
420
  decodeSelectionRle,
421
+ decontaminateColors,
422
422
  deserializeSelection,
423
423
  distanceTransform,
424
+ edgeWidth,
424
425
  encodeSelectionRle,
425
426
  expandCoverage,
426
427
  featherCoverage,
427
428
  fullSelection,
428
429
  greyMorphology,
430
+ guidePlanes,
431
+ guidedFilterColor,
429
432
  hashMaskContent,
430
433
  invertSelection,
431
434
  isSelectionEmpty,
432
435
  isSelectionMode,
433
436
  modifySelection,
437
+ normalizeRefineEdgeParams,
434
438
  parseSelectionMode,
439
+ refineCoverage,
440
+ refineEdgeHalo,
441
+ refineSelection,
435
442
  resizeSelection,
436
443
  selectionArea,
437
444
  selectionFromCoverage,
@@ -444,7 +451,7 @@ import {
444
451
  serializeSelection,
445
452
  shiftCoverage,
446
453
  smoothCoverage
447
- } from "../chunk-L3G23N5Y.js";
454
+ } from "../chunk-ZNA3IQZF.js";
448
455
  import {
449
456
  EffectRendererPool
450
457
  } from "../chunk-EHLOCVWB.js";
@@ -500,7 +507,7 @@ import {
500
507
  validateLayerEffects,
501
508
  warpSurface,
502
509
  workingToSamples
503
- } from "../chunk-YBJXCQTR.js";
510
+ } from "../chunk-QXY4YTRI.js";
504
511
  import {
505
512
  applyHomography,
506
513
  boxQuad,
@@ -2602,27 +2609,7 @@ function inspectMaskEdge(mask, image, options = {}) {
2602
2609
  if (img[i + 3] < 128) return null;
2603
2610
  return [img[i], img[i + 1], img[i + 2]];
2604
2611
  };
2605
- const premul = (x, y, c) => {
2606
- const ix = Math.min(image.width - 1, Math.max(0, x - image.x));
2607
- const iy = Math.min(image.height - 1, Math.max(0, y - image.y));
2608
- const i = (iy * image.width + ix) * 4;
2609
- return img[i + c] * img[i + 3] / 255;
2610
- };
2611
- const gradCache = /* @__PURE__ */ new Map();
2612
- const gradient = (x, y) => {
2613
- const k = y * W + x;
2614
- const hit = gradCache.get(k);
2615
- if (hit !== void 0) return hit;
2616
- let best = 0;
2617
- for (let c = 0; c < 3; c++) {
2618
- const p = (dx, dy) => premul(x + dx, y + dy, c);
2619
- const gx = p(1, -1) + 2 * p(1, 0) + p(1, 1) - p(-1, -1) - 2 * p(-1, 0) - p(-1, 1);
2620
- const gy = p(-1, 1) + 2 * p(0, 1) + p(1, 1) - p(-1, -1) - 2 * p(0, -1) - p(1, -1);
2621
- best = Math.max(best, Math.hypot(gx, gy) / 1020);
2622
- }
2623
- gradCache.set(k, best);
2624
- return best;
2625
- };
2612
+ const gradient = createGradientSampler(image, W);
2626
2613
  const B = Math.min(256, Math.max(24, Math.round(Math.max(W, H) / 12)));
2627
2614
  const BW = Math.ceil(W / B);
2628
2615
  const blocks = /* @__PURE__ */ new Map();
@@ -2777,7 +2764,7 @@ function inspectMaskEdge(mask, image, options = {}) {
2777
2764
  const worst = candidates.sort((p, q) => q.severity - p.severity).slice(0, 5).map(({ bounds, reason }) => ({ bounds, reason }));
2778
2765
  const issues = [];
2779
2766
  if (haloScore >= T2.halo)
2780
- issues.push(`Halo (haloScore ${r3(haloScore)} \u2265 ${T2.halo}): background colour is left inside the edge. Contract the edge (modify_selection contract 1\u20132 px, or refine_selection shiftEdge -20 to -40), or decontaminate on a new-layer output.`);
2767
+ issues.push(`Halo (haloScore ${r3(haloScore)} \u2265 ${T2.halo}): background colour is left inside the edge. Contract the edge (modify_selection contract 1\u20132 px, or refine_selection shiftEdge -20 to -40), or on a masked cutout set_edge_decontamination 80\u2013100.`);
2781
2768
  if (edgeLength >= 16 && edgeAlignment < T2.alignment)
2782
2769
  issues.push(`Off the image's edges (edgeAlignment ${r3(edgeAlignment)} < ${T2.alignment}): much of the edge does not follow edges in the image. Rebuild it with select_subject, select_object or quick_select, or refine_selection with a larger radius.`);
2783
2770
  if (jaggedness >= T2.jaggedness) issues.push(`Jagged (jaggedness ${r3(jaggedness)} \u2265 ${T2.jaggedness}): the outline zigzags. Smooth it (refine_selection smooth 20\u201340, or modify_selection smooth).`);
@@ -2799,6 +2786,30 @@ function inspectMaskEdge(mask, image, options = {}) {
2799
2786
  issues
2800
2787
  };
2801
2788
  }
2789
+ function createGradientSampler(image, W) {
2790
+ const img = image.data;
2791
+ const premul = (x, y, c) => {
2792
+ const ix = Math.min(image.width - 1, Math.max(0, x - image.x));
2793
+ const iy = Math.min(image.height - 1, Math.max(0, y - image.y));
2794
+ const i = (iy * image.width + ix) * 4;
2795
+ return img[i + c] * img[i + 3] / 255;
2796
+ };
2797
+ const cache = /* @__PURE__ */ new Map();
2798
+ return (x, y) => {
2799
+ const k = y * W + x;
2800
+ const hit = cache.get(k);
2801
+ if (hit !== void 0) return hit;
2802
+ let best = 0;
2803
+ for (let c = 0; c < 3; c++) {
2804
+ const p = (dx, dy) => premul(x + dx, y + dy, c);
2805
+ const gx = p(1, -1) + 2 * p(1, 0) + p(1, 1) - p(-1, -1) - 2 * p(-1, 0) - p(-1, 1);
2806
+ const gy = p(-1, 1) + 2 * p(0, 1) + p(1, 1) - p(-1, -1) - 2 * p(0, -1) - p(1, -1);
2807
+ best = Math.max(best, Math.hypot(gx, gy) / 1020);
2808
+ }
2809
+ cache.set(k, best);
2810
+ return best;
2811
+ };
2812
+ }
2802
2813
  function traceLoops(a, W, H, rx0, ry0, rx1, ry1) {
2803
2814
  const w = rx1 - rx0;
2804
2815
  const h = ry1 - ry0;
@@ -2983,6 +2994,135 @@ function regions(a, W, H, b, minArea) {
2983
2994
  };
2984
2995
  }
2985
2996
 
2997
+ // src/image-editor/selection/uncertainty.ts
2998
+ var BACKED = 0.2;
2999
+ function selectionUncertainty(mask, image, options = {}) {
3000
+ const W = mask.width;
3001
+ const H = mask.height;
3002
+ const empty = () => ({ unknown: createSelectionMask(W, H), regions: [], count: 0, confidence: 1 });
3003
+ const b = mask.bounds;
3004
+ if (!b) return empty();
3005
+ const band = Math.max(1, Math.round(options.band ?? Math.max(2, Math.min(W, H) / 200)));
3006
+ const minArea = Math.max(1, options.minArea ?? 16);
3007
+ const maxRegions = Math.max(1, options.maxRegions ?? 20);
3008
+ const a = mask.data;
3009
+ const gradient = createGradientSampler(image, W);
3010
+ const x0 = Math.max(0, b.x - band - 1);
3011
+ const y0 = Math.max(0, b.y - band - 1);
3012
+ const x1 = Math.min(W, b.x + b.width + band + 1);
3013
+ const y1 = Math.min(H, b.y + b.height + band + 1);
3014
+ const w = x1 - x0;
3015
+ const h = y1 - y0;
3016
+ const evidence = new Float32Array(w * h).fill(NaN);
3017
+ const seeds = new Uint8Array(w * h);
3018
+ let sum = 0;
3019
+ let n = 0;
3020
+ const on = (x, y) => a[y * W + x] >= 128;
3021
+ for (let y = y0; y < y1; y++) {
3022
+ for (let x = x0; x < x1; x++) {
3023
+ const v = on(x, y);
3024
+ const differs = (nx, ny) => nx >= 0 && ny >= 0 && nx < W && ny < H && on(nx, ny) !== v;
3025
+ if (!(differs(x - 1, y) || differs(x + 1, y) || differs(x, y - 1) || differs(x, y + 1))) continue;
3026
+ let g = 0;
3027
+ for (let dy = -2; dy <= 2; dy++) {
3028
+ for (let dx = -2; dx <= 2; dx++) {
3029
+ const nx = x + dx;
3030
+ const ny = y + dy;
3031
+ if (nx >= 0 && ny >= 0 && nx < W && ny < H) g = Math.max(g, gradient(nx, ny));
3032
+ }
3033
+ }
3034
+ const e = Math.min(1, g / BACKED);
3035
+ const k = (y - y0) * w + (x - x0);
3036
+ evidence[k] = e;
3037
+ sum += e;
3038
+ n++;
3039
+ if (e < 0.5) seeds[k] = 1;
3040
+ }
3041
+ }
3042
+ if (!n) return empty();
3043
+ const rows = new Uint8Array(w * h);
3044
+ for (let y = 0; y < h; y++) {
3045
+ let last = -Infinity;
3046
+ for (let x = 0; x < w; x++) {
3047
+ if (seeds[y * w + x]) last = x;
3048
+ if (x - last <= band) rows[y * w + x] = 1;
3049
+ }
3050
+ last = Infinity;
3051
+ for (let x = w - 1; x >= 0; x--) {
3052
+ if (seeds[y * w + x]) last = x;
3053
+ if (last - x <= band) rows[y * w + x] = 1;
3054
+ }
3055
+ }
3056
+ const grown = new Uint8Array(w * h);
3057
+ for (let x = 0; x < w; x++) {
3058
+ let last = -Infinity;
3059
+ for (let y = 0; y < h; y++) {
3060
+ if (rows[y * w + x]) last = y;
3061
+ if (y - last <= band) grown[y * w + x] = 1;
3062
+ }
3063
+ last = Infinity;
3064
+ for (let y = h - 1; y >= 0; y--) {
3065
+ if (rows[y * w + x]) last = y;
3066
+ if (last - y <= band) grown[y * w + x] = 1;
3067
+ }
3068
+ }
3069
+ const label = new Int32Array(w * h);
3070
+ const stack = new Int32Array(w * h);
3071
+ const found = [];
3072
+ for (let start = 0; start < w * h; start++) {
3073
+ if (!grown[start] || label[start]) continue;
3074
+ const r = { area: 0, e: 0, en: 0, bx0: w, by0: h, bx1: -1, by1: -1 };
3075
+ const id = found.length + 1;
3076
+ let top = 0;
3077
+ stack[top++] = start;
3078
+ label[start] = id;
3079
+ while (top) {
3080
+ const k = stack[--top];
3081
+ const i = k % w;
3082
+ const j = (k - i) / w;
3083
+ r.area++;
3084
+ if (!Number.isNaN(evidence[k])) {
3085
+ r.e += evidence[k];
3086
+ r.en++;
3087
+ }
3088
+ if (i < r.bx0) r.bx0 = i;
3089
+ if (i > r.bx1) r.bx1 = i;
3090
+ if (j < r.by0) r.by0 = j;
3091
+ if (j > r.by1) r.by1 = j;
3092
+ for (let dy = -1; dy <= 1; dy++) {
3093
+ for (let dx = -1; dx <= 1; dx++) {
3094
+ const ni = i + dx;
3095
+ const nj = j + dy;
3096
+ if (ni < 0 || nj < 0 || ni >= w || nj >= h) continue;
3097
+ const nk = nj * w + ni;
3098
+ if (!grown[nk] || label[nk]) continue;
3099
+ label[nk] = id;
3100
+ stack[top++] = nk;
3101
+ }
3102
+ }
3103
+ }
3104
+ found.push(r);
3105
+ }
3106
+ const unknownData = new Uint8Array(W * H);
3107
+ for (let y = 0; y < h; y++) {
3108
+ for (let x = 0; x < w; x++) {
3109
+ const id = label[y * w + x];
3110
+ if (id && found[id - 1].area >= minArea) unknownData[(y + y0) * W + x + x0] = 255;
3111
+ }
3112
+ }
3113
+ const kept = found.filter((r) => r.area >= minArea).map((r) => ({
3114
+ bounds: { x: x0 + r.bx0, y: y0 + r.by0, width: r.bx1 - r.bx0 + 1, height: r.by1 - r.by0 + 1 },
3115
+ area: r.area,
3116
+ confidence: Math.round((r.en ? r.e / r.en : 0) * 1e3) / 1e3
3117
+ })).sort((p, q) => p.confidence - q.confidence || q.area - p.area);
3118
+ return {
3119
+ unknown: createSelectionMask(W, H, unknownData),
3120
+ regions: kept.slice(0, maxRegions),
3121
+ count: kept.length,
3122
+ confidence: Math.round(sum / n * 1e3) / 1e3
3123
+ };
3124
+ }
3125
+
2986
3126
  // src/image-editor/selection/path.ts
2987
3127
  function selectionToSubpaths(mask, options = {}) {
2988
3128
  const b = mask.bounds;
@@ -6822,6 +6962,85 @@ var ImageEditorController = class {
6822
6962
  this.commitLayers(next);
6823
6963
  return { parentId: loc.parent?.id ?? null, index: loc.index };
6824
6964
  }
6965
+ /** A layer's edge colour as percentages (see `setEdgeColor`). */
6966
+ getEdgeColor(id) {
6967
+ const layer = this.requireEdgeLayer(id);
6968
+ const e = layer.edge ?? {};
6969
+ const pct2 = (v) => Math.round((v ?? 0) * 1e3) / 10;
6970
+ const hex = (c) => "#" + c.map((v) => v.toString(16).padStart(2, "0")).join("");
6971
+ return { layerId: layer.id, decontaminate: pct2(e.decontaminate), despill: pct2(e.despill), ambient: pct2(e.ambient), ambientSource: e.ambientColor ? hex(e.ambientColor) : "auto" };
6972
+ }
6973
+ requireEdgeLayer(id) {
6974
+ const layer = this.getLayer(id);
6975
+ if (!layer) throw new LayerOperationError(`There is no layer "${id}" (see list_layers).`);
6976
+ if (layer.type === "group" || layer.type === "adjustment" || layer.type === "annotation")
6977
+ throw new LayerOperationError(`Edge colour works on pixel, shape, type and smart object layers; "${layer.name || id}" is a ${layer.type} layer.`);
6978
+ return layer;
6979
+ }
6980
+ /** Whether the layer's mask (painted and vector, with feather) has a soft band. */
6981
+ maskHasSoftBand(layer) {
6982
+ const { width, height } = this.naturalSize;
6983
+ const mask = effectiveLayerMask(layer, width, height);
6984
+ if (!mask || mask.enabled === false || mask.source) return false;
6985
+ const ox = layer.type === "pixel" ? layer.offset.x : 0;
6986
+ const oy = layer.type === "pixel" ? layer.offset.y : 0;
6987
+ return hasSoftBand(maskCoverageOverCanvas(mask, ox, oy, width, height));
6988
+ }
6989
+ /**
6990
+ * Edge colour on a layer's soft edge, applied when compositing (nothing is baked); amounts in
6991
+ * percent, 0 turns one off. `decontaminate` unmixes the old background (the pixels the mask
6992
+ * hides) from the edge and `despill` limits its hue there: both need a pixel layer whose mask
6993
+ * (painted or vector) has a soft band. `ambient` pulls the edge toward what is behind the layer
6994
+ * (`ambientColor` null: sampled live) or a fixed `#rrggbb`; it needs a soft mask or soft
6995
+ * pixels at the edge. Validates everything before changing anything; one undo step (none with
6996
+ * `commitToHistory` false, for the later moves of a slider drag). Returns the effective settings.
6997
+ */
6998
+ setEdgeColor(id, change, commitToHistory = true) {
6999
+ const layer = this.requireEdgeLayer(id);
7000
+ const name = layer.name || id;
7001
+ const next = { ...layer.edge ?? {} };
7002
+ for (const k of ["decontaminate", "despill", "ambient"]) {
7003
+ const v = change[k];
7004
+ if (v === void 0) continue;
7005
+ if (typeof v !== "number" || !Number.isFinite(v) || v < 0 || v > 100) throw new LayerOperationError(`${k} must be a percentage from 0 to 100, got ${String(v)}.`);
7006
+ if (v > 0) next[k] = v / 100;
7007
+ else delete next[k];
7008
+ }
7009
+ if (change.ambientColor !== void 0) {
7010
+ if (change.ambientColor === null || change.ambientColor === "auto") delete next.ambientColor;
7011
+ else {
7012
+ const m = /^#([0-9a-f]{6})$/i.exec(String(change.ambientColor));
7013
+ if (!m) throw new LayerOperationError(`ambient colour must be "auto" or #rrggbb, got "${String(change.ambientColor)}".`);
7014
+ const n = parseInt(m[1], 16);
7015
+ next.ambientColor = [n >> 16 & 255, n >> 8 & 255, n & 255];
7016
+ }
7017
+ }
7018
+ const unmix = (next.decontaminate ?? 0) > 0 || (next.despill ?? 0) > 0;
7019
+ const turningOn = (k) => (change[k] ?? 0) > 0;
7020
+ if (unmix && (turningOn("decontaminate") || turningOn("despill"))) {
7021
+ if (layer.type !== "pixel") throw new LayerOperationError(`Decontamination and despill need a pixel layer with a soft-edged mask; "${name}" is a ${layer.type} layer (rasterize it, or use ambient).`);
7022
+ if (!this.maskHasSoftBand(layer))
7023
+ throw new LayerOperationError(`"${name}" has no mask with a soft edge: decontamination and despill work in the partly hidden band of a layer mask. Add one with add_layer_mask from a refined selection (refine_selection), or feather the mask (set_layer_mask_properties feather).`);
7024
+ }
7025
+ if (turningOn("ambient") && !this.maskHasSoftBand(layer) && layer.type === "pixel") {
7026
+ const px2 = this.getLayerPixels(layer.id);
7027
+ let soft = false;
7028
+ if (px2) for (let i = 3; i < px2.data.length && !soft; i += 4) soft = px2.data[i] > 5 && px2.data[i] < 250;
7029
+ if (!soft) throw new LayerOperationError(`"${name}" has no soft edge (no soft-edged mask and no partly transparent pixels) for the edge cast to work on.`);
7030
+ }
7031
+ const edge = next.decontaminate || next.despill || next.ambient || next.ambientColor ? next : void 0;
7032
+ this.commitLayers(
7033
+ mapLayer(this.state.layers, layer.id, (l) => {
7034
+ const out = { ...l };
7035
+ if (edge) out.edge = edge;
7036
+ else delete out.edge;
7037
+ return out;
7038
+ }),
7039
+ void 0,
7040
+ commitToHistory
7041
+ );
7042
+ return this.getEdgeColor(layer.id);
7043
+ }
6825
7044
  /**
6826
7045
  * Changes name, visibility, opacity, fill, blend, clipping, locks or (groups) collapsed state, as
6827
7046
  * one undo step (`commitToHistory = false` for the moves of a slider drag). Returns the layer.
@@ -9644,6 +9863,7 @@ var ImageEditorController = class {
9644
9863
  */
9645
9864
  async refineSelectionTo(params, output = "selection", options = {}) {
9646
9865
  const edits = options.edits ?? [];
9866
+ const regions2 = this.checkRegions(options.regions);
9647
9867
  const refineMode = options.refineMode ?? "color";
9648
9868
  if (!this.state.selection?.bounds && !edits.length) throw new Error("Nothing is selected; select first, or pass edits that paint a selection.");
9649
9869
  if (refineMode === "object" && !this.segmentation) {
@@ -9678,7 +9898,14 @@ var ImageEditorController = class {
9678
9898
  if (!clipped?.bounds) throw new Error("The segmentation model found no object in the selection; use refineMode color.");
9679
9899
  sel = clipped;
9680
9900
  }
9681
- const refined = refineSelection(pixels, sel, params, edited.refineBrush);
9901
+ let refined = refineSelection(pixels, sel, params, edited.refineBrush);
9902
+ if (regions2) {
9903
+ const data = Uint8Array.from(sel.data);
9904
+ for (const r of regions2) {
9905
+ for (let y = r.y; y < r.y + r.height; y++) data.set(refined.data.subarray(y * width + r.x, y * width + r.x + r.width), y * width + r.x);
9906
+ }
9907
+ refined = createSelectionMask(width, height, data);
9908
+ }
9682
9909
  if (!refined.bounds) throw new Error("Refining left nothing selected; use a smaller shiftEdge or contrast.");
9683
9910
  if (toDocument) {
9684
9911
  const document2 = this.documentFromSelection({ layerId, withMask: output === "new-document-with-mask", selection: refined, decontaminate: amount * 100, radius: params.radius });
@@ -10123,6 +10350,37 @@ var ImageEditorController = class {
10123
10350
  const report = inspectMaskEdge(mask, image, options);
10124
10351
  return { target, ...layer ? { layerId: layer.id } : {}, ...report };
10125
10352
  }
10353
+ /** Whole-pixel rectangles inside the canvas, or a useful error; undefined stays undefined. */
10354
+ checkRegions(regions2) {
10355
+ if (regions2 === void 0) return void 0;
10356
+ const { width, height } = this.naturalSize;
10357
+ if (!Array.isArray(regions2) || !regions2.length) throw new Error("regions must be a non-empty list of { x, y, width, height } (e.g. the uncertainRegions bounds); leave it out to refine everywhere.");
10358
+ return regions2.map((r, i) => {
10359
+ const x = Math.max(0, Math.floor(r?.x));
10360
+ const y = Math.max(0, Math.floor(r?.y));
10361
+ const x1 = Math.min(width, Math.ceil(r?.x + r?.width));
10362
+ const y1 = Math.min(height, Math.ceil(r?.y + r?.height));
10363
+ if (![x, y, x1, y1].every(Number.isFinite) || x1 <= x || y1 <= y) throw new Error(`regions[${i}] must overlap the image (0..${width} \xD7 0..${height}), got ${JSON.stringify(r)}.`);
10364
+ return { x, y, width: x1 - x, height: y1 - y };
10365
+ });
10366
+ }
10367
+ /**
10368
+ * Where the selection is unsure of itself: the stretches of its edge with no image edge under
10369
+ * them, widened into a band (a trimap's unknown), as a mask and regions with bounds and
10370
+ * confidence, least confident first. Measured against the composite. Read-only; an empty
10371
+ * selection has none.
10372
+ */
10373
+ selectionUncertainty(options = {}) {
10374
+ const { width, height } = this.naturalSize;
10375
+ const sel = this.getSelection();
10376
+ if (!sel?.bounds) return selectionUncertainty(createSelectionMask(width, height), { x: 0, y: 0, width: 0, height: 0, data: new Uint8ClampedArray(0) }, options);
10377
+ const b = sel.bounds;
10378
+ const m = Math.max(8, Math.ceil(options.band ?? Math.max(2, Math.min(width, height) / 200)) + 4);
10379
+ const x = Math.max(0, b.x - m);
10380
+ const y = Math.max(0, b.y - m);
10381
+ const region = { x, y, width: Math.min(width, b.x + b.width + m) - x, height: Math.min(height, b.y + b.height + m) - y };
10382
+ return selectionUncertainty(sel, { ...this.compositePixels(region), x, y }, options);
10383
+ }
10126
10384
  /** A layer's painted mask over the canvas as a selection mask; null without one (or for a depth mask). */
10127
10385
  layerMaskCoverage(layerId) {
10128
10386
  const layer = this.getLayer(layerId);
@@ -11372,7 +11630,9 @@ var ImageEditorController = class {
11372
11630
  return baked;
11373
11631
  };
11374
11632
  const prepare = async (layer) => {
11633
+ if (layer.edge?.ambient) add2("The live edge cast (set_edge_ambient) has no PSD equivalent and was left out", layer.name);
11375
11634
  const p = await prepareLayer(layer);
11635
+ if (p?.edge) delete p.edge;
11376
11636
  if (p?.blend === "pigment") add2("The Pigment blend mode has no PSD equivalent and was written as Normal", layer.name);
11377
11637
  return p;
11378
11638
  };
@@ -11448,6 +11708,14 @@ var ImageEditorController = class {
11448
11708
  }
11449
11709
  if (layer.filters?.some((f) => !f.enabled)) add2("Disabled layer filters were left out", layer.name);
11450
11710
  delete out.filters;
11711
+ const unmixMask = hasEdgeUnmix(layer.edge) ? effectiveLayerMask(layer, width, height) : void 0;
11712
+ if (unmixMask && !unmixMask.source && unmixMask.enabled !== false) {
11713
+ add2("Edge decontamination and despill were baked into the pixels", layer.name);
11714
+ const { x: ox, y: oy } = layer.offset;
11715
+ const data = readRegion(out.surface, { x: -ox, y: -oy, width, height });
11716
+ applyEdgeUnmix(data, maskCoverageOverCanvas(unmixMask, ox, oy, width, height), width, height, layer.edge);
11717
+ out = { ...out, surface: surfaceFromPixels({ width, height, data }, 4, -ox, -oy) };
11718
+ }
11451
11719
  const mask = bakeMask(layer);
11452
11720
  if (mask) out.mask = mask;
11453
11721
  else delete out.mask;
@@ -12825,6 +13093,10 @@ function rectOf(args) {
12825
13093
  if (rect2.width <= 0 || rect2.height <= 0) throw new Error("width and height must be greater than 0.");
12826
13094
  return rect2;
12827
13095
  }
13096
+ function uncertainty(controller) {
13097
+ const u = controller.selectionUncertainty();
13098
+ return { uncertainRegions: u.regions, uncertainCount: u.count, edgeConfidence: u.confidence };
13099
+ }
12828
13100
  function summary(controller) {
12829
13101
  const sel = controller.getSelection();
12830
13102
  const { width, height } = controller.getState().imageDimensions;
@@ -13049,7 +13321,14 @@ function createImageSelectionTools(controller) {
13049
13321
  additionalProperties: false
13050
13322
  }
13051
13323
  },
13052
- refineMode: { type: "string", enum: ["color", "object"], description: "color (default): refine from the image colours; object: from a host segmentation model's outline of the object (needs a model)." }
13324
+ refineMode: { type: "string", enum: ["color", "object"], description: "color (default): refine from the image colours; object: from a host segmentation model's outline of the object (needs a model)." },
13325
+ regions: {
13326
+ type: "array",
13327
+ minItems: 1,
13328
+ maxItems: 50,
13329
+ items: rectSchemaStrict,
13330
+ description: "Refine only inside these rectangles (natural image pixels), e.g. the uncertainRegions bounds a smart selection returned; the rest of the selection stays as it is. Default: everywhere."
13331
+ }
13053
13332
  },
13054
13333
  additionalProperties: false
13055
13334
  },
@@ -13080,7 +13359,8 @@ function createImageSelectionTools(controller) {
13080
13359
  layerId: typeof params.layerId === "string" ? params.layerId : void 0,
13081
13360
  decontaminate: num2(params.decontaminate, 0),
13082
13361
  edits,
13083
- refineMode: params.refineMode === "object" ? "object" : "color"
13362
+ refineMode: params.refineMode === "object" ? "object" : "color",
13363
+ ...params.regions !== void 0 ? { regions: params.regions } : {}
13084
13364
  }
13085
13365
  );
13086
13366
  return { ...summary(controller), ...result };
@@ -13236,7 +13516,7 @@ function createImageSelectionTools(controller) {
13236
13516
  const quickSelectTool = {
13237
13517
  name: "quick_select",
13238
13518
  title: "Quick Selection",
13239
- description: "Quick Selection: paints brush strokes (paths in natural image pixels) that each grow into the region they are painted on, out to its edges (a graph cut on colour and edges), combined with the selection by mode. With replace (default) the first stroke replaces the selection and later ones add, as the brush does; use mode subtract to take an area back out. Best for a subject or area that differs from its surroundings: paint inside it, away from the edge, then check with get_selection and capture_view and add or subtract strokes where it leaked or fell short. All strokes are one undoable step. Returns the selection.",
13519
+ description: "Quick Selection: paints brush strokes (paths in natural image pixels) that each grow into the region they are painted on, out to its edges (a graph cut on colour and edges), combined with the selection by mode. With replace (default) the first stroke replaces the selection and later ones add, as the brush does; use mode subtract to take an area back out. Best for a subject or area that differs from its surroundings: paint inside it, away from the edge, then check with get_selection and capture_view and add or subtract strokes where it leaked or fell short. All strokes are one undoable step. Returns the selection. Also returns uncertainRegions (bounds, area, confidence 0-1; up to 20, least confident first), uncertainCount and edgeConfidence: where the edge has no image edge under it. Look at those with capture_view and fix only them (refine_selection with regions).",
13240
13520
  inputSchema: {
13241
13521
  type: "object",
13242
13522
  properties: {
@@ -13267,13 +13547,13 @@ function createImageSelectionTools(controller) {
13267
13547
  enhanceEdge: args.enhanceEdge,
13268
13548
  signal: context?.signal
13269
13549
  });
13270
- return summary(controller);
13550
+ return { ...summary(controller), ...uncertainty(controller) };
13271
13551
  }
13272
13552
  };
13273
13553
  const selectObjectTool = {
13274
13554
  name: "select_object",
13275
13555
  title: "Object Selection",
13276
- description: 'Object Selection: selects the object inside rect (a box drawn loosely around it, natural image pixels; what lies outside the box is treated as background) or the object under point. With a host segmentation model it asks the model (falling back if nothing it returns fits); otherwise it finds the object from the colours of object and background and the edges between them (GrabCut). Combined with the selection by mode. A box is more reliable than a point: make it a little larger than the object on every side. Check with get_selection and capture_view, then fix with quick_select strokes or select_object with mode add / subtract. One undoable step. Returns the selection and method ("model" or "grabcut").',
13556
+ description: 'Object Selection: selects the object inside rect (a box drawn loosely around it, natural image pixels; what lies outside the box is treated as background) or the object under point. With a host segmentation model it asks the model (falling back if nothing it returns fits); otherwise it finds the object from the colours of object and background and the edges between them (GrabCut). Combined with the selection by mode. A box is more reliable than a point: make it a little larger than the object on every side. Check with get_selection and capture_view, then fix with quick_select strokes or select_object with mode add / subtract. One undoable step. Returns the selection and method ("model" or "grabcut"). Also returns uncertainRegions (bounds, area, confidence 0-1; up to 20, least confident first), uncertainCount and edgeConfidence: where the edge has no image edge under it. Look at those with capture_view and fix only them (refine_selection with regions).',
13277
13557
  inputSchema: {
13278
13558
  type: "object",
13279
13559
  properties: {
@@ -13306,7 +13586,7 @@ function createImageSelectionTools(controller) {
13306
13586
  signal: context?.signal
13307
13587
  };
13308
13588
  const { method } = rect2 ? await controller.selectObject({ rect: { x: num2(rect2.x, NaN), y: num2(rect2.y, NaN), width: num2(rect2.width, NaN), height: num2(rect2.height, NaN) } }, options) : await controller.selectObject({ point: { x: num2(point.x, NaN), y: num2(point.y, NaN) } }, options);
13309
- return { ...summary(controller), method };
13589
+ return { ...summary(controller), method, ...uncertainty(controller) };
13310
13590
  }
13311
13591
  };
13312
13592
  const selectFocusAreaTool = {
@@ -13340,7 +13620,7 @@ function createImageSelectionTools(controller) {
13340
13620
  const selectSkyTool = {
13341
13621
  name: "select_sky",
13342
13622
  title: "Select the sky",
13343
- description: 'Select \u203A Sky: selects the sky, combined with the selection by mode. With a host segmentation model it asks the model; otherwise it finds it classically (blue, or bright and grey when overcast, smooth, reaching down from the top of the image, including patches seen through branches or gaps) with the edge fitted to the skyline. Use it to replace or recolour a sky or to adjust the land without it (invert_selection). Raise threshold to take in haze or a sunset glow, lower it if bright buildings come in. One undoable step. Returns the selection, method ("model" or "classical"; threshold and softness apply to classical), and a note when no sky was found (the selection is then unchanged).',
13623
+ description: 'Select \u203A Sky: selects the sky, combined with the selection by mode. With a host segmentation model it asks the model; otherwise it finds it classically (blue, or bright and grey when overcast, smooth, reaching down from the top of the image, including patches seen through branches or gaps) with the edge fitted to the skyline. Use it to replace or recolour a sky or to adjust the land without it (invert_selection). Raise threshold to take in haze or a sunset glow, lower it if bright buildings come in. One undoable step. Returns the selection, method ("model" or "classical"; threshold and softness apply to classical), and a note when no sky was found (the selection is then unchanged). Also returns uncertainRegions (bounds, area, confidence 0-1; up to 20, least confident first), uncertainCount and edgeConfidence: where the edge has no image edge under it. Look at those with capture_view and fix only them (refine_selection with regions).',
13344
13624
  inputSchema: {
13345
13625
  type: "object",
13346
13626
  properties: {
@@ -13362,7 +13642,7 @@ function createImageSelectionTools(controller) {
13362
13642
  useModel: args.useModel,
13363
13643
  signal: context?.signal
13364
13644
  });
13365
- return { ...summary(controller), method, ...note ? { note } : {} };
13645
+ return { ...summary(controller), method, ...note ? { note } : {}, ...uncertainty(controller) };
13366
13646
  }
13367
13647
  };
13368
13648
  const magicWandTool = {
@@ -13967,7 +14247,7 @@ function createImageSubjectSelectionTool(controller) {
13967
14247
  return {
13968
14248
  name: "select_subject",
13969
14249
  title: "Select a subject",
13970
- description: 'Select \u203A Subject: selects the main subject of the flattened image, combined with the selection by mode. With a segmentation model set by the host, target (a description such as "the dog" or "the house") chooses which objects; describe one object precisely to get only that one. Without a model, the subject that stands out most from the background is found classically (superpixel saliency and GrabCut): target cannot choose it, and method "saliency" and a note say so. Then check it with capture_view and fix it with quick_select or select_object. One undoable step. Returns the selection, the objects found (label, bounds), method ("model" or "saliency") and any note; none found leaves the selection unchanged.',
14250
+ description: 'Select \u203A Subject: selects the main subject of the flattened image, combined with the selection by mode. With a segmentation model set by the host, target (a description such as "the dog" or "the house") chooses which objects; describe one object precisely to get only that one. Without a model, the subject that stands out most from the background is found classically (superpixel saliency and GrabCut): target cannot choose it, and method "saliency" and a note say so. Then check it with capture_view and fix it with quick_select or select_object. One undoable step. Returns the selection, the objects found (label, bounds), method ("model" or "saliency") and any note; none found leaves the selection unchanged. Also returns uncertainRegions (bounds, area, confidence 0-1; up to 20, least confident first), uncertainCount and edgeConfidence: where the edge has no image edge under it. Look at those with capture_view and fix only them (refine_selection with regions).',
13971
14251
  inputSchema: {
13972
14252
  type: "object",
13973
14253
  properties: {
@@ -13998,7 +14278,7 @@ function createImageSubjectSelectionTool(controller) {
13998
14278
  // Under playback the cursor traces what the model found, as a lasso, before it is selected.
13999
14279
  beforeApply: perform ? (outlines) => perform(planOutlineTrace(outlines)) : void 0
14000
14280
  });
14001
- return { ...summary(controller), found: segments, method, ...note ? { note } : {} };
14281
+ return { ...summary(controller), found: segments, method, ...note ? { note } : {}, ...uncertainty(controller) };
14002
14282
  }
14003
14283
  };
14004
14284
  }
@@ -14809,6 +15089,49 @@ function createImageLayerTools(controller) {
14809
15089
  return { view: shown?.mode ?? "off", layerId: shown?.layerId ?? null };
14810
15090
  }
14811
15091
  };
15092
+ const AMOUNT = (what) => ({ type: "number", minimum: 0, maximum: 100, description: `${what}, in percent 0-100; 0 turns it off.` });
15093
+ const EDGE_RETURNS = "Returns the layer's effective edge colour: { layerId, decontaminate, despill, ambient (percent), ambientSource }.";
15094
+ const getEdge = {
15095
+ name: "get_edge_color",
15096
+ title: "Read edge colour",
15097
+ description: `A layer's edge colour settings, applied when compositing: decontaminate, despill and ambient (percent) and ambientSource (auto or #rrggbb). ${EDGE_RETURNS}`,
15098
+ inputSchema: { type: "object", properties: { layerId: LAYER_ID2 }, required: ["layerId"], additionalProperties: false },
15099
+ annotations: { title: "Read edge colour", ...READ2 },
15100
+ handler: ({ layerId }) => controller.getEdgeColor(String(layerId))
15101
+ };
15102
+ const setDecontaminate = {
15103
+ name: "set_edge_decontamination",
15104
+ title: "Decontaminate edge colour",
15105
+ description: `Removes the old background's colour from a cutout's soft edge at composite time (nothing is baked; change or turn it off any time): each partly hidden edge pixel is unmixed from the background next to it (the pixels the layer's mask hides). Needs a pixel layer whose mask (painted or vector) has a soft band, e.g. add_layer_mask from a refined selection. Check with inspect_mask_edge (haloScore) and capture_view. One undo step. ${EDGE_RETURNS}`,
15106
+ inputSchema: { type: "object", properties: { layerId: LAYER_ID2, amount: AMOUNT("How much to replace edge colour with the subject's own (80-100 typical)") }, required: ["layerId", "amount"], additionalProperties: false },
15107
+ annotations: { title: "Decontaminate edge colour", ...WRITE_IDEMPOTENT2 },
15108
+ handler: ({ layerId, amount }) => controller.setEdgeColor(String(layerId), { decontaminate: amount })
15109
+ };
15110
+ const setDespill = {
15111
+ name: "set_edge_despill",
15112
+ title: "Despill edge colour",
15113
+ description: `Limits how much of the old background's hue a cutout's soft edge carries (a green-screen or coloured-wall fringe), at composite time: the edge colour's component toward the background's hue is taken out, luminance kept, most at the outer edge. A heuristic: it does not remove light that bounced onto the subject itself. Needs a pixel layer whose mask has a soft band. Pixels fully inside the mask never change. One undo step. ${EDGE_RETURNS}`,
15114
+ inputSchema: { type: "object", properties: { layerId: LAYER_ID2, amount: AMOUNT("How much of the background hue to take out (50-100 typical)") }, required: ["layerId", "amount"], additionalProperties: false },
15115
+ annotations: { title: "Despill edge colour", ...WRITE_IDEMPOTENT2 },
15116
+ handler: ({ layerId, amount }) => controller.setEdgeColor(String(layerId), { despill: amount })
15117
+ };
15118
+ const setAmbient = {
15119
+ name: "set_edge_ambient",
15120
+ title: "Cast the scene onto an edge",
15121
+ description: `Tints a cutout's soft edge with what is behind it now, live at composite time, so a subject moved onto a new background picks up its colour at the edge (more where the edge is more transparent). source auto samples the layers below as they change; #rrggbb uses a fixed colour. Works on soft-edged masks or partly transparent pixels (pixel, shape, type and smart object layers). One undo step. ${EDGE_RETURNS}`,
15122
+ inputSchema: {
15123
+ type: "object",
15124
+ properties: {
15125
+ layerId: LAYER_ID2,
15126
+ amount: AMOUNT("How far edge colour moves toward the background (20-50 typical)"),
15127
+ source: { type: "string", pattern: "^(auto|#[0-9a-fA-F]{6})$", description: "auto (default): what is behind the layer, sampled live; or a fixed #rrggbb." }
15128
+ },
15129
+ required: ["layerId", "amount"],
15130
+ additionalProperties: false
15131
+ },
15132
+ annotations: { title: "Cast the scene onto an edge", ...WRITE_IDEMPOTENT2 },
15133
+ handler: ({ layerId, amount, source }) => controller.setEdgeColor(String(layerId), { ambient: amount, ambientColor: source ?? "auto" })
15134
+ };
14812
15135
  const maskProps = {
14813
15136
  name: "set_layer_mask_properties",
14814
15137
  title: "Set layer mask properties",
@@ -14951,6 +15274,10 @@ function createImageLayerTools(controller) {
14951
15274
  removeMask,
14952
15275
  maskView,
14953
15276
  maskProps,
15277
+ getEdge,
15278
+ setDecontaminate,
15279
+ setDespill,
15280
+ setAmbient,
14954
15281
  viaCopy,
14955
15282
  rasterize,
14956
15283
  addFilter,
@@ -21578,6 +21905,7 @@ export {
21578
21905
  DIRECT_GAUSSIAN_MAX_SIGMA,
21579
21906
  DRY_EPS,
21580
21907
  DabWalker,
21908
+ EDGE_COLOR_RADIUS,
21581
21909
  EFFECT_LAYER_USES,
21582
21910
  EMPTY_DISTANCE,
21583
21911
  EMPTY_MIXER_LOAD,
@@ -21820,6 +22148,7 @@ export {
21820
22148
  applyBoxBlur,
21821
22149
  applyBoxBlurFloat,
21822
22150
  applyDepthMask,
22151
+ applyEdgeUnmix,
21823
22152
  applyHomography,
21824
22153
  applyImageProjectionToContext,
21825
22154
  applyLegacyAdjustments,
@@ -21925,6 +22254,7 @@ export {
21925
22254
  createAdjustmentLayer,
21926
22255
  createBlenderScratch,
21927
22256
  createCanvasTextShaper,
22257
+ createGradientSampler,
21928
22258
  createImageCaptureSurface,
21929
22259
  createImageColorTools,
21930
22260
  createImageEditTools,
@@ -21995,6 +22325,7 @@ export {
21995
22325
  deserializeMask,
21996
22326
  deserializePatterns,
21997
22327
  deserializeSelection,
22328
+ despillEdge,
21998
22329
  developFilter,
21999
22330
  developFromAdjustments,
22000
22331
  developTarget,
@@ -22090,8 +22421,10 @@ export {
22090
22421
  handleQuickMaskKey,
22091
22422
  handleSelectionShortcut,
22092
22423
  hasActiveEffects,
22424
+ hasEdgeUnmix,
22093
22425
  hasLayersBelow,
22094
22426
  hasResampledLegacyMask,
22427
+ hasSoftBand,
22095
22428
  hasSurface,
22096
22429
  hasWarp,
22097
22430
  hash2,
@@ -22221,6 +22554,7 @@ export {
22221
22554
  mapLayer,
22222
22555
  mapSubpaths,
22223
22556
  maskBounds,
22557
+ maskCoverageOverCanvas,
22224
22558
  maskFileName,
22225
22559
  maskNameFromFile,
22226
22560
  maskSummary,
@@ -22363,6 +22697,7 @@ export {
22363
22697
  rasterizeSmartObject,
22364
22698
  readAdjustmentControls,
22365
22699
  readDeepRegion,
22700
+ readEdgeColor,
22366
22701
  readPaintSource,
22367
22702
  readPaths,
22368
22703
  readPixel,
@@ -22447,6 +22782,7 @@ export {
22447
22782
  selectionToCoverage,
22448
22783
  selectionToPixels,
22449
22784
  selectionToSubpaths,
22785
+ selectionUncertainty,
22450
22786
  selectionValueAt,
22451
22787
  serializeChannels,
22452
22788
  serializeColorMode,