@pixerate/editor 0.214.0 → 0.215.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 (34) 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 +29 -0
  10. package/dist/image-editor/controller.d.ts +29 -0
  11. package/dist/image-editor/index.cjs +1224 -1074
  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 +166 -16
  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-worker.cjs +4 -4
  20. package/dist/image-editor/selection-worker.js +2 -2
  21. package/dist/paint-editor/index.cjs +79 -79
  22. package/dist/paint-editor/index.js +3 -3
  23. package/dist/raster/compositor.d.cts +10 -1
  24. package/dist/raster/compositor.d.ts +10 -1
  25. package/dist/raster/deep-compositor.d.cts +1 -1
  26. package/dist/raster/deep-compositor.d.ts +1 -1
  27. package/dist/raster/index.cjs +4 -2
  28. package/dist/raster/index.js +3 -1
  29. package/dist/raster/layers.d.cts +16 -0
  30. package/dist/raster/layers.d.ts +16 -0
  31. package/package.json +1 -1
  32. package/skills/pixerate-image-editor/SKILL.md +8 -6
  33. package/skills/pixerate-image-editor/references/paths.md +12 -0
  34. package/skills/pixerate-image-selection-masking/SKILL.md +8 -8
@@ -37,6 +37,7 @@ export * from './layers/smart.cjs';
37
37
  export * from './smart-tools.cjs';
38
38
  export * from './layers/shape.cjs';
39
39
  export * from './layers/vector-mask.cjs';
40
+ export * from './layers/edge-color.cjs';
40
41
  export * from './shape-tools.cjs';
41
42
  export * from './layers/text.cjs';
42
43
  export * from './layers/text-shapers.cjs';
@@ -37,6 +37,7 @@ export * from './layers/smart.js';
37
37
  export * from './smart-tools.js';
38
38
  export * from './layers/shape.js';
39
39
  export * from './layers/vector-mask.js';
40
+ export * from './layers/edge-color.js';
40
41
  export * from './shape-tools.js';
41
42
  export * from './layers/text.js';
42
43
  export * from './layers/text-shapers.js';
@@ -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,
@@ -2777,7 +2784,7 @@ function inspectMaskEdge(mask, image, options = {}) {
2777
2784
  const worst = candidates.sort((p, q) => q.severity - p.severity).slice(0, 5).map(({ bounds, reason }) => ({ bounds, reason }));
2778
2785
  const issues = [];
2779
2786
  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.`);
2787
+ 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
2788
  if (edgeLength >= 16 && edgeAlignment < T2.alignment)
2782
2789
  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
2790
  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).`);
@@ -6822,6 +6829,85 @@ var ImageEditorController = class {
6822
6829
  this.commitLayers(next);
6823
6830
  return { parentId: loc.parent?.id ?? null, index: loc.index };
6824
6831
  }
6832
+ /** A layer's edge colour as percentages (see `setEdgeColor`). */
6833
+ getEdgeColor(id) {
6834
+ const layer = this.requireEdgeLayer(id);
6835
+ const e = layer.edge ?? {};
6836
+ const pct2 = (v) => Math.round((v ?? 0) * 1e3) / 10;
6837
+ const hex = (c) => "#" + c.map((v) => v.toString(16).padStart(2, "0")).join("");
6838
+ return { layerId: layer.id, decontaminate: pct2(e.decontaminate), despill: pct2(e.despill), ambient: pct2(e.ambient), ambientSource: e.ambientColor ? hex(e.ambientColor) : "auto" };
6839
+ }
6840
+ requireEdgeLayer(id) {
6841
+ const layer = this.getLayer(id);
6842
+ if (!layer) throw new LayerOperationError(`There is no layer "${id}" (see list_layers).`);
6843
+ if (layer.type === "group" || layer.type === "adjustment" || layer.type === "annotation")
6844
+ throw new LayerOperationError(`Edge colour works on pixel, shape, type and smart object layers; "${layer.name || id}" is a ${layer.type} layer.`);
6845
+ return layer;
6846
+ }
6847
+ /** Whether the layer's mask (painted and vector, with feather) has a soft band. */
6848
+ maskHasSoftBand(layer) {
6849
+ const { width, height } = this.naturalSize;
6850
+ const mask = effectiveLayerMask(layer, width, height);
6851
+ if (!mask || mask.enabled === false || mask.source) return false;
6852
+ const ox = layer.type === "pixel" ? layer.offset.x : 0;
6853
+ const oy = layer.type === "pixel" ? layer.offset.y : 0;
6854
+ return hasSoftBand(maskCoverageOverCanvas(mask, ox, oy, width, height));
6855
+ }
6856
+ /**
6857
+ * Edge colour on a layer's soft edge, applied when compositing (nothing is baked); amounts in
6858
+ * percent, 0 turns one off. `decontaminate` unmixes the old background (the pixels the mask
6859
+ * hides) from the edge and `despill` limits its hue there: both need a pixel layer whose mask
6860
+ * (painted or vector) has a soft band. `ambient` pulls the edge toward what is behind the layer
6861
+ * (`ambientColor` null: sampled live) or a fixed `#rrggbb`; it needs a soft mask or soft
6862
+ * pixels at the edge. Validates everything before changing anything; one undo step (none with
6863
+ * `commitToHistory` false, for the later moves of a slider drag). Returns the effective settings.
6864
+ */
6865
+ setEdgeColor(id, change, commitToHistory = true) {
6866
+ const layer = this.requireEdgeLayer(id);
6867
+ const name = layer.name || id;
6868
+ const next = { ...layer.edge ?? {} };
6869
+ for (const k of ["decontaminate", "despill", "ambient"]) {
6870
+ const v = change[k];
6871
+ if (v === void 0) continue;
6872
+ 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)}.`);
6873
+ if (v > 0) next[k] = v / 100;
6874
+ else delete next[k];
6875
+ }
6876
+ if (change.ambientColor !== void 0) {
6877
+ if (change.ambientColor === null || change.ambientColor === "auto") delete next.ambientColor;
6878
+ else {
6879
+ const m = /^#([0-9a-f]{6})$/i.exec(String(change.ambientColor));
6880
+ if (!m) throw new LayerOperationError(`ambient colour must be "auto" or #rrggbb, got "${String(change.ambientColor)}".`);
6881
+ const n = parseInt(m[1], 16);
6882
+ next.ambientColor = [n >> 16 & 255, n >> 8 & 255, n & 255];
6883
+ }
6884
+ }
6885
+ const unmix = (next.decontaminate ?? 0) > 0 || (next.despill ?? 0) > 0;
6886
+ const turningOn = (k) => (change[k] ?? 0) > 0;
6887
+ if (unmix && (turningOn("decontaminate") || turningOn("despill"))) {
6888
+ 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).`);
6889
+ if (!this.maskHasSoftBand(layer))
6890
+ 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).`);
6891
+ }
6892
+ if (turningOn("ambient") && !this.maskHasSoftBand(layer) && layer.type === "pixel") {
6893
+ const px2 = this.getLayerPixels(layer.id);
6894
+ let soft = false;
6895
+ if (px2) for (let i = 3; i < px2.data.length && !soft; i += 4) soft = px2.data[i] > 5 && px2.data[i] < 250;
6896
+ 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.`);
6897
+ }
6898
+ const edge = next.decontaminate || next.despill || next.ambient || next.ambientColor ? next : void 0;
6899
+ this.commitLayers(
6900
+ mapLayer(this.state.layers, layer.id, (l) => {
6901
+ const out = { ...l };
6902
+ if (edge) out.edge = edge;
6903
+ else delete out.edge;
6904
+ return out;
6905
+ }),
6906
+ void 0,
6907
+ commitToHistory
6908
+ );
6909
+ return this.getEdgeColor(layer.id);
6910
+ }
6825
6911
  /**
6826
6912
  * Changes name, visibility, opacity, fill, blend, clipping, locks or (groups) collapsed state, as
6827
6913
  * one undo step (`commitToHistory = false` for the moves of a slider drag). Returns the layer.
@@ -11372,7 +11458,9 @@ var ImageEditorController = class {
11372
11458
  return baked;
11373
11459
  };
11374
11460
  const prepare = async (layer) => {
11461
+ if (layer.edge?.ambient) add2("The live edge cast (set_edge_ambient) has no PSD equivalent and was left out", layer.name);
11375
11462
  const p = await prepareLayer(layer);
11463
+ if (p?.edge) delete p.edge;
11376
11464
  if (p?.blend === "pigment") add2("The Pigment blend mode has no PSD equivalent and was written as Normal", layer.name);
11377
11465
  return p;
11378
11466
  };
@@ -11448,6 +11536,14 @@ var ImageEditorController = class {
11448
11536
  }
11449
11537
  if (layer.filters?.some((f) => !f.enabled)) add2("Disabled layer filters were left out", layer.name);
11450
11538
  delete out.filters;
11539
+ const unmixMask = hasEdgeUnmix(layer.edge) ? effectiveLayerMask(layer, width, height) : void 0;
11540
+ if (unmixMask && !unmixMask.source && unmixMask.enabled !== false) {
11541
+ add2("Edge decontamination and despill were baked into the pixels", layer.name);
11542
+ const { x: ox, y: oy } = layer.offset;
11543
+ const data = readRegion(out.surface, { x: -ox, y: -oy, width, height });
11544
+ applyEdgeUnmix(data, maskCoverageOverCanvas(unmixMask, ox, oy, width, height), width, height, layer.edge);
11545
+ out = { ...out, surface: surfaceFromPixels({ width, height, data }, 4, -ox, -oy) };
11546
+ }
11451
11547
  const mask = bakeMask(layer);
11452
11548
  if (mask) out.mask = mask;
11453
11549
  else delete out.mask;
@@ -14809,6 +14905,49 @@ function createImageLayerTools(controller) {
14809
14905
  return { view: shown?.mode ?? "off", layerId: shown?.layerId ?? null };
14810
14906
  }
14811
14907
  };
14908
+ const AMOUNT = (what) => ({ type: "number", minimum: 0, maximum: 100, description: `${what}, in percent 0-100; 0 turns it off.` });
14909
+ const EDGE_RETURNS = "Returns the layer's effective edge colour: { layerId, decontaminate, despill, ambient (percent), ambientSource }.";
14910
+ const getEdge = {
14911
+ name: "get_edge_color",
14912
+ title: "Read edge colour",
14913
+ description: `A layer's edge colour settings, applied when compositing: decontaminate, despill and ambient (percent) and ambientSource (auto or #rrggbb). ${EDGE_RETURNS}`,
14914
+ inputSchema: { type: "object", properties: { layerId: LAYER_ID2 }, required: ["layerId"], additionalProperties: false },
14915
+ annotations: { title: "Read edge colour", ...READ2 },
14916
+ handler: ({ layerId }) => controller.getEdgeColor(String(layerId))
14917
+ };
14918
+ const setDecontaminate = {
14919
+ name: "set_edge_decontamination",
14920
+ title: "Decontaminate edge colour",
14921
+ 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}`,
14922
+ 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 },
14923
+ annotations: { title: "Decontaminate edge colour", ...WRITE_IDEMPOTENT2 },
14924
+ handler: ({ layerId, amount }) => controller.setEdgeColor(String(layerId), { decontaminate: amount })
14925
+ };
14926
+ const setDespill = {
14927
+ name: "set_edge_despill",
14928
+ title: "Despill edge colour",
14929
+ 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}`,
14930
+ 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 },
14931
+ annotations: { title: "Despill edge colour", ...WRITE_IDEMPOTENT2 },
14932
+ handler: ({ layerId, amount }) => controller.setEdgeColor(String(layerId), { despill: amount })
14933
+ };
14934
+ const setAmbient = {
14935
+ name: "set_edge_ambient",
14936
+ title: "Cast the scene onto an edge",
14937
+ 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}`,
14938
+ inputSchema: {
14939
+ type: "object",
14940
+ properties: {
14941
+ layerId: LAYER_ID2,
14942
+ amount: AMOUNT("How far edge colour moves toward the background (20-50 typical)"),
14943
+ source: { type: "string", pattern: "^(auto|#[0-9a-fA-F]{6})$", description: "auto (default): what is behind the layer, sampled live; or a fixed #rrggbb." }
14944
+ },
14945
+ required: ["layerId", "amount"],
14946
+ additionalProperties: false
14947
+ },
14948
+ annotations: { title: "Cast the scene onto an edge", ...WRITE_IDEMPOTENT2 },
14949
+ handler: ({ layerId, amount, source }) => controller.setEdgeColor(String(layerId), { ambient: amount, ambientColor: source ?? "auto" })
14950
+ };
14812
14951
  const maskProps = {
14813
14952
  name: "set_layer_mask_properties",
14814
14953
  title: "Set layer mask properties",
@@ -14951,6 +15090,10 @@ function createImageLayerTools(controller) {
14951
15090
  removeMask,
14952
15091
  maskView,
14953
15092
  maskProps,
15093
+ getEdge,
15094
+ setDecontaminate,
15095
+ setDespill,
15096
+ setAmbient,
14954
15097
  viaCopy,
14955
15098
  rasterize,
14956
15099
  addFilter,
@@ -21578,6 +21721,7 @@ export {
21578
21721
  DIRECT_GAUSSIAN_MAX_SIGMA,
21579
21722
  DRY_EPS,
21580
21723
  DabWalker,
21724
+ EDGE_COLOR_RADIUS,
21581
21725
  EFFECT_LAYER_USES,
21582
21726
  EMPTY_DISTANCE,
21583
21727
  EMPTY_MIXER_LOAD,
@@ -21820,6 +21964,7 @@ export {
21820
21964
  applyBoxBlur,
21821
21965
  applyBoxBlurFloat,
21822
21966
  applyDepthMask,
21967
+ applyEdgeUnmix,
21823
21968
  applyHomography,
21824
21969
  applyImageProjectionToContext,
21825
21970
  applyLegacyAdjustments,
@@ -21995,6 +22140,7 @@ export {
21995
22140
  deserializeMask,
21996
22141
  deserializePatterns,
21997
22142
  deserializeSelection,
22143
+ despillEdge,
21998
22144
  developFilter,
21999
22145
  developFromAdjustments,
22000
22146
  developTarget,
@@ -22090,8 +22236,10 @@ export {
22090
22236
  handleQuickMaskKey,
22091
22237
  handleSelectionShortcut,
22092
22238
  hasActiveEffects,
22239
+ hasEdgeUnmix,
22093
22240
  hasLayersBelow,
22094
22241
  hasResampledLegacyMask,
22242
+ hasSoftBand,
22095
22243
  hasSurface,
22096
22244
  hasWarp,
22097
22245
  hash2,
@@ -22221,6 +22369,7 @@ export {
22221
22369
  mapLayer,
22222
22370
  mapSubpaths,
22223
22371
  maskBounds,
22372
+ maskCoverageOverCanvas,
22224
22373
  maskFileName,
22225
22374
  maskNameFromFile,
22226
22375
  maskSummary,
@@ -22363,6 +22512,7 @@ export {
22363
22512
  rasterizeSmartObject,
22364
22513
  readAdjustmentControls,
22365
22514
  readDeepRegion,
22515
+ readEdgeColor,
22366
22516
  readPaintSource,
22367
22517
  readPaths,
22368
22518
  readPixel,
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Edge colour on a layer's soft edge (`LayerEdgeColor`): decontamination and despill, run over the
3
+ * canvas by the compositor from the layer's own pixels and its mask, so nothing is baked. The live
4
+ * edge cast (`ambient`) needs what is behind the layer and runs in the compositor's blend step.
5
+ */
6
+ import type { LayerEdgeColor, RasterMask } from '../../raster/layers.cjs';
7
+ /** How far (px) the old background's colour is looked for around the edge. */
8
+ export declare const EDGE_COLOR_RADIUS = 8;
9
+ /** True when decontamination or despill is on (they need the layer's mask). */
10
+ export declare function hasEdgeUnmix(edge: LayerEdgeColor | undefined): boolean;
11
+ /**
12
+ * A mask's coverage (0..255) over the canvas, `width × height`, for a layer at `offset`: the
13
+ * painted mask with its feather (density only changes how much shows, not the matte).
14
+ */
15
+ export declare function maskCoverageOverCanvas(mask: RasterMask, ox: number, oy: number, width: number, height: number): Uint8ClampedArray;
16
+ /** True when a coverage has a soft band (some pixels partly covered). */
17
+ export declare function hasSoftBand(coverage: ArrayLike<number>): boolean;
18
+ /**
19
+ * Despill: along a soft edge, takes out (by `amount`, 0..1) the part of each pixel's colour that
20
+ * points the way of the old background's hue (its local mean, from what is fully outside within
21
+ * `radius`). Luminance is kept, pixels fully in or out are unchanged, and the outer edge (more
22
+ * background) gets the most. It does not remove light that bounced onto the subject itself.
23
+ * Works in place on RGBA `rgba` with `coverage` (0..255) over the same `w × h`.
24
+ */
25
+ export declare function despillEdge(rgba: Uint8ClampedArray, coverage: ArrayLike<number>, w: number, h: number, amount: number, radius?: number): void;
26
+ /** Decontamination, then despill, of canvas RGBA `rgba` (`w × h`) in place, given the mask's coverage. */
27
+ export declare function applyEdgeUnmix(rgba: Uint8ClampedArray, coverage: ArrayLike<number>, w: number, h: number, edge: LayerEdgeColor): void;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Edge colour on a layer's soft edge (`LayerEdgeColor`): decontamination and despill, run over the
3
+ * canvas by the compositor from the layer's own pixels and its mask, so nothing is baked. The live
4
+ * edge cast (`ambient`) needs what is behind the layer and runs in the compositor's blend step.
5
+ */
6
+ import type { LayerEdgeColor, RasterMask } from '../../raster/layers.js';
7
+ /** How far (px) the old background's colour is looked for around the edge. */
8
+ export declare const EDGE_COLOR_RADIUS = 8;
9
+ /** True when decontamination or despill is on (they need the layer's mask). */
10
+ export declare function hasEdgeUnmix(edge: LayerEdgeColor | undefined): boolean;
11
+ /**
12
+ * A mask's coverage (0..255) over the canvas, `width × height`, for a layer at `offset`: the
13
+ * painted mask with its feather (density only changes how much shows, not the matte).
14
+ */
15
+ export declare function maskCoverageOverCanvas(mask: RasterMask, ox: number, oy: number, width: number, height: number): Uint8ClampedArray;
16
+ /** True when a coverage has a soft band (some pixels partly covered). */
17
+ export declare function hasSoftBand(coverage: ArrayLike<number>): boolean;
18
+ /**
19
+ * Despill: along a soft edge, takes out (by `amount`, 0..1) the part of each pixel's colour that
20
+ * points the way of the old background's hue (its local mean, from what is fully outside within
21
+ * `radius`). Luminance is kept, pixels fully in or out are unchanged, and the outer edge (more
22
+ * background) gets the most. It does not remove light that bounced onto the subject itself.
23
+ * Works in place on RGBA `rgba` with `coverage` (0..255) over the same `w × h`.
24
+ */
25
+ export declare function despillEdge(rgba: Uint8ClampedArray, coverage: ArrayLike<number>, w: number, h: number, amount: number, radius?: number): void;
26
+ /** Decontamination, then despill, of canvas RGBA `rgba` (`w × h`) in place, given the mask's coverage. */
27
+ export declare function applyEdgeUnmix(rgba: Uint8ClampedArray, coverage: ArrayLike<number>, w: number, h: number, edge: LayerEdgeColor): void;
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Schema version 3 of the image editor document: the layer tree, with pixel layers and painted
3
+ * masks stored as PNG assets (cropped to their content and placed by position), plus the
4
+ * document-level crop, transform, output opacity, migrated blur and selection.
5
+ *
6
+ * Encoding is synchronous and exact (see `png.ts`). Each surface's encoding is memoised, so
7
+ * saving an unchanged layer again costs nothing.
8
+ */
9
+ import type { LayerEdgeColor } from '../../raster/layers.cjs';
1
10
  import type { SerializedDocumentPath } from '../paths.cjs';
2
11
  import type { Annotation, CropBox, DepthMaskOptions, ImageTransform } from '../types.cjs';
3
12
  import type { SerializedSelection } from '../selection/mask.cjs';
@@ -36,6 +45,8 @@ interface SerializedLayerBase {
36
45
  vectorMask?: SerializedVectorMask;
37
46
  locks?: LayerLocks;
38
47
  effects?: SerializedLayerEffects;
48
+ /** Edge colour amounts 0..1 and the cast colour (see `LayerEdgeColor`). */
49
+ edge?: LayerEdgeColor;
39
50
  }
40
51
  /** A stored vector mask: its components as JSON (document pixels) and its properties. */
41
52
  export interface SerializedVectorMask {
@@ -271,6 +282,8 @@ export interface SerializeContext {
271
282
  /** Stores a smart object's contents (once per id). Without it, smart objects cannot be written. */
272
283
  addContents?(contents: SmartContents): void;
273
284
  }
285
+ /** Stored edge colour, with amounts clamped to 0..1 (none when nothing is on). */
286
+ export declare function readEdgeColor(raw: unknown): LayerEdgeColor | undefined;
274
287
  export declare function serializeEffects(effects: LayerEffects): SerializedLayerEffects;
275
288
  /** Reads stored effects (leniently: broken effects are dropped), or `undefined`. */
276
289
  export declare function deserializeEffects(raw: unknown): LayerEffects | undefined;
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Schema version 3 of the image editor document: the layer tree, with pixel layers and painted
3
+ * masks stored as PNG assets (cropped to their content and placed by position), plus the
4
+ * document-level crop, transform, output opacity, migrated blur and selection.
5
+ *
6
+ * Encoding is synchronous and exact (see `png.ts`). Each surface's encoding is memoised, so
7
+ * saving an unchanged layer again costs nothing.
8
+ */
9
+ import type { LayerEdgeColor } from '../../raster/layers.js';
1
10
  import type { SerializedDocumentPath } from '../paths.js';
2
11
  import type { Annotation, CropBox, DepthMaskOptions, ImageTransform } from '../types.js';
3
12
  import type { SerializedSelection } from '../selection/mask.js';
@@ -36,6 +45,8 @@ interface SerializedLayerBase {
36
45
  vectorMask?: SerializedVectorMask;
37
46
  locks?: LayerLocks;
38
47
  effects?: SerializedLayerEffects;
48
+ /** Edge colour amounts 0..1 and the cast colour (see `LayerEdgeColor`). */
49
+ edge?: LayerEdgeColor;
39
50
  }
40
51
  /** A stored vector mask: its components as JSON (document pixels) and its properties. */
41
52
  export interface SerializedVectorMask {
@@ -271,6 +282,8 @@ export interface SerializeContext {
271
282
  /** Stores a smart object's contents (once per id). Without it, smart objects cannot be written. */
272
283
  addContents?(contents: SmartContents): void;
273
284
  }
285
+ /** Stored edge colour, with amounts clamped to 0..1 (none when nothing is on). */
286
+ export declare function readEdgeColor(raw: unknown): LayerEdgeColor | undefined;
274
287
  export declare function serializeEffects(effects: LayerEffects): SerializedLayerEffects;
275
288
  /** Reads stored effects (leniently: broken effects are dropped), or `undefined`. */
276
289
  export declare function deserializeEffects(raw: unknown): LayerEffects | undefined;
@@ -1,15 +1,15 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkL52VMQZScjs = require('../chunk-L52VMQZS.cjs');
4
- require('../chunk-QYLWG22N.cjs');
3
+ var _chunkUC3PWFM3cjs = require('../chunk-UC3PWFM3.cjs');
4
+ require('../chunk-SS53NNZO.cjs');
5
5
  require('../chunk-K6PXS6DO.cjs');
6
6
  require('../chunk-MQDIP4CF.cjs');
7
7
  require('../chunk-474IQG74.cjs');
8
8
 
9
9
  // src/image-editor/selection-worker.ts
10
10
  if (typeof self !== "undefined" && typeof window === "undefined" && typeof self.postMessage === "function") {
11
- _chunkL52VMQZScjs.startSelectionWorker.call(void 0, self);
11
+ _chunkUC3PWFM3cjs.startSelectionWorker.call(void 0, self);
12
12
  }
13
13
 
14
14
 
15
- exports.startSelectionWorker = _chunkL52VMQZScjs.startSelectionWorker;
15
+ exports.startSelectionWorker = _chunkUC3PWFM3cjs.startSelectionWorker;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  startSelectionWorker
3
- } from "../chunk-LQ5SALHK.js";
4
- import "../chunk-L3G23N5Y.js";
3
+ } from "../chunk-U7C655AM.js";
4
+ import "../chunk-ZNA3IQZF.js";
5
5
  import "../chunk-ZIF4UJJ6.js";
6
6
  import "../chunk-XI2NV5U7.js";
7
7
  import "../chunk-HVVR5PZW.js";