@pixerate/editor 0.216.0 → 0.218.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.
@@ -636,6 +636,7 @@ import {
636
636
  latticeViscosity,
637
637
  linearToSrgb8,
638
638
  loadWetKernel,
639
+ moveWetSnapshot,
639
640
  normalizeFlow,
640
641
  normalizeTilt,
641
642
  normalizeWetParams,
@@ -652,7 +653,7 @@ import {
652
653
  wetGpuSupport,
653
654
  wetSimdSupported,
654
655
  wetSupport
655
- } from "../chunk-WJ2RBZMM.js";
656
+ } from "../chunk-HLBTRSQJ.js";
656
657
  import {
657
658
  BUILTIN_PROFILES,
658
659
  CmsError,
@@ -1070,6 +1071,105 @@ function startMarchingAnts(tick, stepMs = MARCHING_ANTS_STEP_MS) {
1070
1071
  return () => clearInterval(id);
1071
1072
  }
1072
1073
 
1074
+ // src/image-editor/selection/diff.ts
1075
+ function compareSelectionMasks(a, b, width, height, options = {}) {
1076
+ const minArea = Math.max(1, options.minArea ?? 4);
1077
+ const maxRegions = Math.max(1, options.maxRegions ?? 10);
1078
+ const n = width * height;
1079
+ const ad = a?.data;
1080
+ const bd = b?.data;
1081
+ const added = new Uint8Array(n);
1082
+ const removed = new Uint8Array(n);
1083
+ let areaA = 0;
1084
+ let areaB = 0;
1085
+ let inter = 0;
1086
+ let change = 0;
1087
+ let addedArea = 0;
1088
+ let removedArea = 0;
1089
+ for (let i = 0; i < n; i++) {
1090
+ const va = ad ? ad[i] : 0;
1091
+ const vb = bd ? bd[i] : 0;
1092
+ if (va !== vb) change += Math.abs(vb - va);
1093
+ const ia = va >= 128;
1094
+ const ib = vb >= 128;
1095
+ if (ia) areaA++;
1096
+ if (ib) areaB++;
1097
+ if (ia && ib) inter++;
1098
+ else if (ib) {
1099
+ added[i] = 255;
1100
+ addedArea++;
1101
+ } else if (ia) {
1102
+ removed[i] = 255;
1103
+ removedArea++;
1104
+ }
1105
+ }
1106
+ const union = areaA + areaB - inter;
1107
+ const addedMask = createSelectionMask(width, height, added);
1108
+ const removedMask = createSelectionMask(width, height, removed);
1109
+ return {
1110
+ a: { area: areaA, bounds: a?.bounds ?? null },
1111
+ b: { area: areaB, bounds: b?.bounds ?? null },
1112
+ added: { area: addedArea, ...pieces(added, width, height, addedMask.bounds, minArea, maxRegions) },
1113
+ removed: { area: removedArea, ...pieces(removed, width, height, removedMask.bounds, minArea, maxRegions) },
1114
+ iou: union ? Math.round(inter / union * 1e4) / 1e4 : 1,
1115
+ coverageChange: Math.round(change / 255),
1116
+ identical: change === 0,
1117
+ addedMask,
1118
+ removedMask
1119
+ };
1120
+ }
1121
+ function pieces(bits, W, H, bounds, minArea, maxRegions) {
1122
+ if (!bounds) return { count: 0, regions: [] };
1123
+ const { x: x0, y: y0, width: w, height: h } = bounds;
1124
+ const seen = new Uint8Array(w * h);
1125
+ const stack = new Int32Array(w * h);
1126
+ const found = [];
1127
+ for (let start = 0; start < w * h; start++) {
1128
+ if (seen[start] || !bits[(y0 + Math.floor(start / w)) * W + x0 + start % w]) continue;
1129
+ let area = 0;
1130
+ let bx0 = w;
1131
+ let by0 = h;
1132
+ let bx1 = -1;
1133
+ let by1 = -1;
1134
+ let top = 0;
1135
+ stack[top++] = start;
1136
+ seen[start] = 1;
1137
+ while (top) {
1138
+ const k = stack[--top];
1139
+ const i = k % w;
1140
+ const j = (k - i) / w;
1141
+ area++;
1142
+ if (i < bx0) bx0 = i;
1143
+ if (i > bx1) bx1 = i;
1144
+ if (j < by0) by0 = j;
1145
+ if (j > by1) by1 = j;
1146
+ for (let dy = -1; dy <= 1; dy++) {
1147
+ for (let dx = -1; dx <= 1; dx++) {
1148
+ const ni = i + dx;
1149
+ const nj = j + dy;
1150
+ if (ni < 0 || nj < 0 || ni >= w || nj >= h) continue;
1151
+ const nk = nj * w + ni;
1152
+ if (seen[nk] || !bits[(y0 + nj) * W + x0 + ni]) continue;
1153
+ seen[nk] = 1;
1154
+ stack[top++] = nk;
1155
+ }
1156
+ }
1157
+ }
1158
+ if (area >= minArea) found.push({ area, bounds: { x: x0 + bx0, y: y0 + by0, width: bx1 - bx0 + 1, height: by1 - by0 + 1 } });
1159
+ }
1160
+ found.sort((p, q) => q.area - p.area);
1161
+ return { count: found.length, regions: found.slice(0, maxRegions) };
1162
+ }
1163
+ function selectionDiffPixels(added, removed) {
1164
+ const n = added.width * added.height;
1165
+ const out = new Uint8ClampedArray(n * 4);
1166
+ for (let i = 0; i < n; i++) {
1167
+ if (added.data[i]) out.set([34, 197, 94, 140], i * 4);
1168
+ else if (removed.data[i]) out.set([239, 68, 68, 140], i * 4);
1169
+ }
1170
+ return out;
1171
+ }
1172
+
1073
1173
  // src/image-editor/layers/render.ts
1074
1174
  function isQuarterTurn(rotate) {
1075
1175
  return normalizeAngle(rotate) % 90 === 0;
@@ -1515,6 +1615,19 @@ var ImageEditorRenderer = class {
1515
1615
  }
1516
1616
  ctx.restore();
1517
1617
  }
1618
+ const diff = state.selectionDiff;
1619
+ if (diff && diff.selection === (state.selection ?? null)) {
1620
+ ctx.save();
1621
+ try {
1622
+ applyImageProjectionToContext(ctx, projection);
1623
+ ctx.beginPath();
1624
+ ctx.rect(region.x, region.y, region.width, region.height);
1625
+ ctx.clip();
1626
+ this.drawSelectionDiff(ctx, diff);
1627
+ } catch {
1628
+ }
1629
+ ctx.restore();
1630
+ }
1518
1631
  if (options.brushCursor) {
1519
1632
  const b = options.brushCursor;
1520
1633
  ctx.save();
@@ -1970,6 +2083,7 @@ var ImageEditorRenderer = class {
1970
2083
  this.onPreviewHistogram?.(histogram);
1971
2084
  }
1972
2085
  quickMaskCache = /* @__PURE__ */ new WeakMap();
2086
+ diffCache = /* @__PURE__ */ new WeakMap();
1973
2087
  outlineCache = /* @__PURE__ */ new WeakMap();
1974
2088
  emptyMaskCache = null;
1975
2089
  /** An empty selection of the image's size (Quick Mask mode with nothing selected). */
@@ -2023,6 +2137,19 @@ var ImageEditorRenderer = class {
2023
2137
  ctx.drawImage(cached.canvas, 0, 0, mask.width, mask.height);
2024
2138
  }
2025
2139
  /** Rubylith over the unselected area, drawn in natural image space. */
2140
+ drawSelectionDiff(ctx, diff) {
2141
+ let canvas = this.diffCache.get(diff);
2142
+ if (!canvas) {
2143
+ canvas = this.createCanvas(diff.added.width, diff.added.height);
2144
+ const mctx = canvas.getContext("2d");
2145
+ if (!mctx || typeof mctx.createImageData !== "function") return;
2146
+ const img = mctx.createImageData(diff.added.width, diff.added.height);
2147
+ img.data.set(selectionDiffPixels(diff.added, diff.removed));
2148
+ mctx.putImageData(img, 0, 0);
2149
+ this.diffCache.set(diff, canvas);
2150
+ }
2151
+ ctx.drawImage(canvas, 0, 0, diff.added.width, diff.added.height);
2152
+ }
2026
2153
  drawQuickMask(ctx, selection) {
2027
2154
  let canvas = this.quickMaskCache.get(selection);
2028
2155
  if (!canvas) {
@@ -2987,10 +3114,10 @@ function regions(a, W, H, b, minArea) {
2987
3114
  const kept = list.filter((r) => r.area >= minArea).sort((p, q) => q.area - p.area);
2988
3115
  return { count: kept.length, totalArea: kept.reduce((s, r) => s + r.area, 0), largest: kept.slice(0, 10).map((r) => ({ area: r.area, bounds: box(r) })) };
2989
3116
  };
2990
- const pieces = found.filter((r) => r.fg).sort((p, q) => q.area - p.area);
3117
+ const pieces2 = found.filter((r) => r.fg).sort((p, q) => q.area - p.area);
2991
3118
  return {
2992
3119
  holes: summarize(found.filter((r) => !r.fg && !r.outer)),
2993
- islands: summarize(pieces.slice(1))
3120
+ islands: summarize(pieces2.slice(1))
2994
3121
  };
2995
3122
  }
2996
3123
 
@@ -10381,6 +10508,65 @@ var ImageEditorController = class {
10381
10508
  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
10509
  return selectionUncertainty(sel, { ...this.compositePixels(region), x, y }, options);
10383
10510
  }
10511
+ /**
10512
+ * A selection to compare (`compareSelections`): the current one, the one `steps` undo steps
10513
+ * ago, a saved channel, or a layer's painted or vector mask. `null` is nothing selected.
10514
+ */
10515
+ selectionFrom(source) {
10516
+ const { width, height } = this.naturalSize;
10517
+ switch (source?.kind) {
10518
+ case "selection":
10519
+ return this.getSelection();
10520
+ case "history": {
10521
+ const steps = source.steps ?? 1;
10522
+ const depth = this.undoStack.length;
10523
+ if (!Number.isInteger(steps) || steps < 1) throw new LayerOperationError(`steps must be a whole number of undo steps back, at least 1; got ${String(source.steps)}.`);
10524
+ if (steps > depth) throw new LayerOperationError(`There ${depth === 1 ? "is 1 undo step" : `are ${depth} undo steps`}; steps must be 1..${depth || 1}${depth ? "" : " once something has been done"}.`);
10525
+ return this.undoStack[depth - steps].selection ?? null;
10526
+ }
10527
+ case "channel":
10528
+ return this.requireChannel(source.channelId).mask;
10529
+ case "layer-mask": {
10530
+ const layer = this.getLayer(source.layerId);
10531
+ if (!layer) throw new LayerOperationError(`There is no layer "${source.layerId}" (see list_layers).`);
10532
+ const mask = this.layerMaskCoverage(layer.id);
10533
+ if (!mask) throw new LayerOperationError(`Layer "${layer.name || layer.id}" has no painted mask; add one with add_layer_mask, or compare its vector-mask.`);
10534
+ return mask;
10535
+ }
10536
+ case "vector-mask": {
10537
+ const layer = this.getLayer(source.layerId);
10538
+ if (!layer) throw new LayerOperationError(`There is no layer "${source.layerId}" (see list_layers).`);
10539
+ const { vm } = this.requireVectorMask(layer.id);
10540
+ const mask = pathToSelection(vm.components, width, height, { fillRule: vm.fillRule });
10541
+ return vm.inverted ? invertSelection(mask) : mask;
10542
+ }
10543
+ default:
10544
+ throw new LayerOperationError(`Unknown selection source ${JSON.stringify(source)}. Use selection, history, channel, layer-mask or vector-mask.`);
10545
+ }
10546
+ }
10547
+ /**
10548
+ * What changed from selection `a` (default: before the last undo step) to `b` (default: the
10549
+ * current selection): added and removed area with their largest pieces, IoU and the coverage
10550
+ * change. With `show` (default true) the canvas tints added area green and removed red until the
10551
+ * selection next changes (view state, no undo step). Read-only otherwise.
10552
+ */
10553
+ compareSelections(a = { kind: "history", steps: 1 }, b = { kind: "selection" }, options = {}) {
10554
+ const { width, height } = this.naturalSize;
10555
+ const ma = this.selectionFrom(a);
10556
+ const mb = this.selectionFrom(b);
10557
+ const { addedMask, removedMask, ...diff } = compareSelectionMasks(ma, mb, width, height, options);
10558
+ if (options.show !== false) {
10559
+ this.state.selectionDiff = diff.identical ? null : { added: addedMask, removed: removedMask, selection: this.state.selection ?? null };
10560
+ this.notify();
10561
+ }
10562
+ return diff;
10563
+ }
10564
+ /** Hides the `compareSelections` overlay. */
10565
+ clearSelectionDiff() {
10566
+ if (!this.state.selectionDiff) return;
10567
+ this.state.selectionDiff = null;
10568
+ this.notify();
10569
+ }
10384
10570
  /** A layer's painted mask over the canvas as a selection mask; null without one (or for a depth mask). */
10385
10571
  layerMaskCoverage(layerId) {
10386
10572
  const layer = this.getLayer(layerId);
@@ -13146,6 +13332,65 @@ function createImageSelectionTools(controller) {
13146
13332
  return controller.inspectMaskEdge(t, { layerId, minArea });
13147
13333
  }
13148
13334
  };
13335
+ const sourceSchema = (which, dflt) => ({
13336
+ type: "object",
13337
+ properties: {
13338
+ source: { type: "string", enum: ["selection", "history", "channel", "layer-mask", "vector-mask"], description: "selection: the current one; history: as it was steps undo steps ago; channel: a saved selection (channelId, see list_channels); layer-mask / vector-mask: a layer's mask (layerId)." },
13339
+ steps: { type: "integer", minimum: 1, description: "history only: how many undo steps back (default 1, before the last edit)." },
13340
+ channelId: { type: "string", description: "channel only: the channel id from list_channels." },
13341
+ layerId: { type: "string", description: "layer-mask and vector-mask only: the layer id from list_layers." }
13342
+ },
13343
+ required: ["source"],
13344
+ additionalProperties: false,
13345
+ description: `${which} (default ${dflt}).`
13346
+ });
13347
+ const toSource = (v, fallback) => {
13348
+ if (v === void 0) return fallback;
13349
+ const o = v;
13350
+ const extra = (keys) => {
13351
+ const bad = Object.keys(o).filter((k) => k !== "source" && !keys.includes(k));
13352
+ if (bad.length) throw new Error(`${bad.join(", ")} ${bad.length === 1 ? "does" : "do"} not apply to source ${o.source}.`);
13353
+ };
13354
+ switch (o.source) {
13355
+ case "selection":
13356
+ extra([]);
13357
+ return { kind: "selection" };
13358
+ case "history":
13359
+ extra(["steps"]);
13360
+ return { kind: "history", steps: o.steps ?? 1 };
13361
+ case "channel":
13362
+ extra(["channelId"]);
13363
+ if (!o.channelId) throw new Error("source channel needs channelId (see list_channels).");
13364
+ return { kind: "channel", channelId: o.channelId };
13365
+ case "layer-mask":
13366
+ case "vector-mask":
13367
+ extra(["layerId"]);
13368
+ if (!o.layerId) throw new Error(`source ${o.source} needs layerId (see list_layers).`);
13369
+ return { kind: o.source, layerId: o.layerId };
13370
+ default:
13371
+ throw new Error(`Unknown source "${String(o.source)}". Use selection, history, channel, layer-mask or vector-mask.`);
13372
+ }
13373
+ };
13374
+ const compareTool = {
13375
+ name: "compare_selections",
13376
+ title: "Compare two selections",
13377
+ description: "Shows what changed between two selections, to check that a modify, refine or select step did what you meant. a (default: the selection before the last undo step) and b (default: the current selection) can each be the current selection, a history step, a saved channel or a layer's mask. Returns a and b (area, bounds), added and removed (area in pixels, count and the 10 largest pieces with bounds, ready for capture_view), iou 0-1, coverageChange (pixels of coverage changed, counting edges that only softened) and identical. With show (default true) the canvas tints added area green and removed red until the selection next changes (view only, no undo step).",
13378
+ inputSchema: {
13379
+ type: "object",
13380
+ properties: {
13381
+ a: sourceSchema("The selection before", "history, 1 step back"),
13382
+ b: sourceSchema("The selection after", "the current selection"),
13383
+ minArea: { type: "number", minimum: 1, maximum: 1e6, description: "Pieces smaller than this many pixels are counted but not listed (default 4)." },
13384
+ show: { type: "boolean", description: "Tint the change on the canvas (default true)." }
13385
+ },
13386
+ additionalProperties: false
13387
+ },
13388
+ annotations: { title: "Compare two selections", readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
13389
+ handler: ({ a, b, minArea, show }) => controller.compareSelections(toSource(a, { kind: "history", steps: 1 }), toSource(b, { kind: "selection" }), {
13390
+ ...minArea !== void 0 ? { minArea } : {},
13391
+ show: show !== false
13392
+ })
13393
+ };
13149
13394
  const selectRegion = {
13150
13395
  name: "select_region",
13151
13396
  title: "Select a region",
@@ -14200,6 +14445,7 @@ function createImageSelectionTools(controller) {
14200
14445
  return [
14201
14446
  getSelection,
14202
14447
  inspectEdge,
14448
+ compareTool,
14203
14449
  selectAll,
14204
14450
  deselect,
14205
14451
  invert2,
@@ -22227,6 +22473,7 @@ export {
22227
22473
  colorRange,
22228
22474
  colorRangePreset,
22229
22475
  combineSelections,
22476
+ compareSelectionMasks,
22230
22477
  compileAdjustmentLayer,
22231
22478
  componentPath,
22232
22479
  componentsCoverage,
@@ -22577,6 +22824,7 @@ export {
22577
22824
  moveCurvePoint,
22578
22825
  moveLayer,
22579
22826
  moveSmartFilter,
22827
+ moveWetSnapshot,
22580
22828
  mulMatrix3,
22581
22829
  needsLegacyBase,
22582
22830
  neutralHueRange,
@@ -22772,6 +23020,7 @@ export {
22772
23020
  selectObjectIn,
22773
23021
  selectSimilar,
22774
23022
  selectionArea,
23023
+ selectionDiffPixels,
22775
23024
  selectionFromCoverage,
22776
23025
  selectionFromPixels,
22777
23026
  selectionFromRegion,
@@ -176,6 +176,7 @@ export declare class ImageEditorRenderer {
176
176
  private applyPost;
177
177
  private publishHistogram;
178
178
  private quickMaskCache;
179
+ private diffCache;
179
180
  private outlineCache;
180
181
  private emptyMaskCache;
181
182
  /** An empty selection of the image's size (Quick Mask mode with nothing selected). */
@@ -190,6 +191,7 @@ export declare class ImageEditorRenderer {
190
191
  /** A channel's tint (colour, opacity, masked or selected area), drawn in natural image space. */
191
192
  private drawMaskOverlay;
192
193
  /** Rubylith over the unselected area, drawn in natural image space. */
194
+ private drawSelectionDiff;
193
195
  private drawQuickMask;
194
196
  /**
195
197
  * Marching ants: a white line under a black dashed one, one screen pixel wide. `scale` is
@@ -176,6 +176,7 @@ export declare class ImageEditorRenderer {
176
176
  private applyPost;
177
177
  private publishHistogram;
178
178
  private quickMaskCache;
179
+ private diffCache;
179
180
  private outlineCache;
180
181
  private emptyMaskCache;
181
182
  /** An empty selection of the image's size (Quick Mask mode with nothing selected). */
@@ -190,6 +191,7 @@ export declare class ImageEditorRenderer {
190
191
  /** A channel's tint (colour, opacity, masked or selected area), drawn in natural image space. */
191
192
  private drawMaskOverlay;
192
193
  /** Rubylith over the unselected area, drawn in natural image space. */
194
+ private drawSelectionDiff;
193
195
  private drawQuickMask;
194
196
  /**
195
197
  * Marching ants: a white line under a black dashed one, one screen pixel wide. `scale` is
@@ -0,0 +1,59 @@
1
+ /**
2
+ * What changed between two selections (`compare_selections`): the area added and removed, where,
3
+ * and how much they overlap (IoU), so an agent can check that a modify or refine step did what it
4
+ * meant to and a person can see it as a two-colour overlay.
5
+ */
6
+ import type { CropBox } from '../types.cjs';
7
+ import { type SelectionMask } from './mask.cjs';
8
+ /** One piece of added or removed area. */
9
+ export interface SelectionDiffRegion {
10
+ area: number;
11
+ bounds: CropBox;
12
+ }
13
+ export interface SelectionDiffSide {
14
+ /** Pixels at least half selected. */
15
+ area: number;
16
+ bounds: CropBox | null;
17
+ }
18
+ export interface SelectionDiff {
19
+ a: SelectionDiffSide;
20
+ b: SelectionDiffSide;
21
+ /** Selected in b but not in a: area, and the largest pieces (at most `maxRegions`). */
22
+ added: {
23
+ area: number;
24
+ count: number;
25
+ regions: SelectionDiffRegion[];
26
+ };
27
+ /** Selected in a but not in b. */
28
+ removed: {
29
+ area: number;
30
+ count: number;
31
+ regions: SelectionDiffRegion[];
32
+ };
33
+ /** Intersection over union, 0–1 (1 when both are empty). */
34
+ iou: number;
35
+ /** Σ |coverage b − coverage a| in pixels: also counts edges that only softened or hardened. */
36
+ coverageChange: number;
37
+ /** Every pixel's coverage is the same. */
38
+ identical: boolean;
39
+ }
40
+ export interface CompareSelectionsOptions {
41
+ /** Pieces smaller than this many pixels are counted in the area but not listed (default 4). */
42
+ minArea?: number;
43
+ /** At most this many pieces are listed per side (default 10). */
44
+ maxRegions?: number;
45
+ }
46
+ /**
47
+ * Compares selection `a` (before) with `b` (after) over a `width × height` canvas; `null` is
48
+ * nothing selected. Area, pieces and IoU are judged at half coverage. Also returns the added and
49
+ * removed masks for an overlay.
50
+ */
51
+ export declare function compareSelectionMasks(a: SelectionMask | null, b: SelectionMask | null, width: number, height: number, options?: CompareSelectionsOptions): SelectionDiff & {
52
+ addedMask: SelectionMask;
53
+ removedMask: SelectionMask;
54
+ };
55
+ /**
56
+ * The diff overlay's RGBA: added area tinted green, removed area red, the rest transparent
57
+ * (`width × height`, the masks' size).
58
+ */
59
+ export declare function selectionDiffPixels(added: SelectionMask, removed: SelectionMask): Uint8ClampedArray;
@@ -0,0 +1,59 @@
1
+ /**
2
+ * What changed between two selections (`compare_selections`): the area added and removed, where,
3
+ * and how much they overlap (IoU), so an agent can check that a modify or refine step did what it
4
+ * meant to and a person can see it as a two-colour overlay.
5
+ */
6
+ import type { CropBox } from '../types.js';
7
+ import { type SelectionMask } from './mask.js';
8
+ /** One piece of added or removed area. */
9
+ export interface SelectionDiffRegion {
10
+ area: number;
11
+ bounds: CropBox;
12
+ }
13
+ export interface SelectionDiffSide {
14
+ /** Pixels at least half selected. */
15
+ area: number;
16
+ bounds: CropBox | null;
17
+ }
18
+ export interface SelectionDiff {
19
+ a: SelectionDiffSide;
20
+ b: SelectionDiffSide;
21
+ /** Selected in b but not in a: area, and the largest pieces (at most `maxRegions`). */
22
+ added: {
23
+ area: number;
24
+ count: number;
25
+ regions: SelectionDiffRegion[];
26
+ };
27
+ /** Selected in a but not in b. */
28
+ removed: {
29
+ area: number;
30
+ count: number;
31
+ regions: SelectionDiffRegion[];
32
+ };
33
+ /** Intersection over union, 0–1 (1 when both are empty). */
34
+ iou: number;
35
+ /** Σ |coverage b − coverage a| in pixels: also counts edges that only softened or hardened. */
36
+ coverageChange: number;
37
+ /** Every pixel's coverage is the same. */
38
+ identical: boolean;
39
+ }
40
+ export interface CompareSelectionsOptions {
41
+ /** Pieces smaller than this many pixels are counted in the area but not listed (default 4). */
42
+ minArea?: number;
43
+ /** At most this many pieces are listed per side (default 10). */
44
+ maxRegions?: number;
45
+ }
46
+ /**
47
+ * Compares selection `a` (before) with `b` (after) over a `width × height` canvas; `null` is
48
+ * nothing selected. Area, pieces and IoU are judged at half coverage. Also returns the added and
49
+ * removed masks for an overlay.
50
+ */
51
+ export declare function compareSelectionMasks(a: SelectionMask | null, b: SelectionMask | null, width: number, height: number, options?: CompareSelectionsOptions): SelectionDiff & {
52
+ addedMask: SelectionMask;
53
+ removedMask: SelectionMask;
54
+ };
55
+ /**
56
+ * The diff overlay's RGBA: added area tinted green, removed area red, the rest transparent
57
+ * (`width × height`, the masks' size).
58
+ */
59
+ export declare function selectionDiffPixels(added: SelectionMask, removed: SelectionMask): Uint8ClampedArray;
@@ -19,3 +19,4 @@ export * from './select-and-mask.cjs';
19
19
  export * from './worker.cjs';
20
20
  export * from './inspect.cjs';
21
21
  export * from './uncertainty.cjs';
22
+ export * from './diff.cjs';
@@ -19,3 +19,4 @@ export * from './select-and-mask.js';
19
19
  export * from './worker.js';
20
20
  export * from './inspect.js';
21
21
  export * from './uncertainty.js';
22
+ export * from './diff.js';
@@ -319,6 +319,15 @@ export interface ImageEditorState {
319
319
  * growing as the brush moves). View state: not an edit, not in history, not saved.
320
320
  */
321
321
  selectionPreview?: SelectionMask | null;
322
+ /**
323
+ * `compare_selections`' overlay: the added area tinted green, the removed red. Shown while the
324
+ * selection is still `selection` (any change hides it). View state: not in history, not saved.
325
+ */
326
+ selectionDiff?: {
327
+ added: SelectionMask;
328
+ removed: SelectionMask;
329
+ selection: SelectionMask | null;
330
+ } | null;
322
331
  /** Quick Mask view: tint the unselected area instead of drawing marching ants (view state). */
323
332
  quickMask?: boolean;
324
333
  /** How the canvas shows the selection (view state; `quickMask` mirrors `'quickMask'`). */
@@ -319,6 +319,15 @@ export interface ImageEditorState {
319
319
  * growing as the brush moves). View state: not an edit, not in history, not saved.
320
320
  */
321
321
  selectionPreview?: SelectionMask | null;
322
+ /**
323
+ * `compare_selections`' overlay: the added area tinted green, the removed red. Shown while the
324
+ * selection is still `selection` (any change hides it). View state: not in history, not saved.
325
+ */
326
+ selectionDiff?: {
327
+ added: SelectionMask;
328
+ removed: SelectionMask;
329
+ selection: SelectionMask | null;
330
+ } | null;
322
331
  /** Quick Mask view: tint the unselected area instead of drawing marching ants (view state). */
323
332
  quickMask?: boolean;
324
333
  /** How the canvas shows the selection (view state; `quickMask` mirrors `'quickMask'`). */
@@ -129,7 +129,8 @@ var _chunkMQDIP4CFcjs = require('../chunk-MQDIP4CF.cjs');
129
129
 
130
130
 
131
131
 
132
- var _chunkKWNMBMKGcjs = require('../chunk-KWNMBMKG.cjs');
132
+
133
+ var _chunkXFYLA3ZOcjs = require('../chunk-XFYLA3ZO.cjs');
133
134
 
134
135
 
135
136
 
@@ -483,4 +484,5 @@ require('../chunk-XFM3OCHU.cjs');
483
484
 
484
485
 
485
486
 
486
- exports.BODY_SCATTER = _chunkKWNMBMKGcjs.BODY_SCATTER; exports.BRUSHSTROKE_MAX_SCALE = _chunkYQGCPL5Xcjs.BRUSHSTROKE_MAX_SCALE; exports.BRUSHSTROKE_MIN_SCALE = _chunkYQGCPL5Xcjs.BRUSHSTROKE_MIN_SCALE; exports.BrushstrokeCache = _chunkPQ3H2AANcjs.BrushstrokeCache; exports.CoverageMap = _chunkPQ3H2AANcjs.CoverageMap; exports.DEFAULT_BLENDER = _chunkPQ3H2AANcjs.DEFAULT_BLENDER; exports.DEFAULT_BRUSH = _chunkPQ3H2AANcjs.DEFAULT_BRUSH; exports.DEFAULT_FLOW = _chunkKWNMBMKGcjs.DEFAULT_FLOW; exports.DEFAULT_MIXER = _chunkPQ3H2AANcjs.DEFAULT_MIXER; exports.DEFAULT_PAINT_GRADIENT = _chunkPQ3H2AANcjs.DEFAULT_PAINT_GRADIENT; exports.DEFAULT_PAINT_GRADIENT_MAPPING = _chunkPQ3H2AANcjs.DEFAULT_PAINT_GRADIENT_MAPPING; exports.DEFAULT_RETOUCH = _chunkYF3YZZ6Gcjs.DEFAULT_RETOUCH; exports.DEFAULT_SMUDGE = _chunkPQ3H2AANcjs.DEFAULT_SMUDGE; exports.DEFAULT_WET_PAPER = _chunkKWNMBMKGcjs.DEFAULT_WET_PAPER; exports.DEFAULT_WET_PARAMS = _chunkKWNMBMKGcjs.DEFAULT_WET_PARAMS; exports.DRY_EPS = _chunkKWNMBMKGcjs.DRY_EPS; exports.DabWalker = _chunkPQ3H2AANcjs.DabWalker; exports.EMPTY_MIXER_LOAD = _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD; exports.EX = _chunkKWNMBMKGcjs.EX; exports.EY = _chunkKWNMBMKGcjs.EY; exports.FLAG_ALIVE = _chunkKWNMBMKGcjs.FLAG_ALIVE; exports.FLAG_BOTTOM = _chunkKWNMBMKGcjs.FLAG_BOTTOM; exports.FLAG_LEFT = _chunkKWNMBMKGcjs.FLAG_LEFT; exports.FLAG_RIGHT = _chunkKWNMBMKGcjs.FLAG_RIGHT; exports.FLAG_TOP = _chunkKWNMBMKGcjs.FLAG_TOP; exports.FLAG_WET = _chunkKWNMBMKGcjs.FLAG_WET; exports.FLAT = _chunkKWNMBMKGcjs.FLAT; exports.FREE_INTERVAL = _chunkKWNMBMKGcjs.FREE_INTERVAL; exports.GRAIN_PATTERNS = _chunkPQ3H2AANcjs.GRAIN_PATTERNS; exports.GRAIN_TILE = _chunkPQ3H2AANcjs.GRAIN_TILE; exports.HIDE = _chunkKWNMBMKGcjs.HIDE; exports.IDENTITY_CLONE_TRANSFORM = _chunkYF3YZZ6Gcjs.IDENTITY_CLONE_TRANSFORM; exports.MAX_BATCH = _chunkKWNMBMKGcjs.MAX_BATCH; exports.MAX_BRUSH_SIZE = _chunkPQ3H2AANcjs.MAX_BRUSH_SIZE; exports.MAX_TILT_ACCEL = _chunkKWNMBMKGcjs.MAX_TILT_ACCEL; exports.MAX_WET_LANES = _chunkKWNMBMKGcjs.MAX_WET_LANES; exports.MAX_WET_SLOTS = _chunkKWNMBMKGcjs.MAX_WET_SLOTS; exports.MIXER_DRAIN = _chunkPQ3H2AANcjs.MIXER_DRAIN; exports.MIXER_PICKUP_RATE = _chunkPQ3H2AANcjs.MIXER_PICKUP_RATE; exports.MIXING_MODELS = _chunkPQ3H2AANcjs.MIXING_MODELS; exports.MixerStroke = _chunkPQ3H2AANcjs.MixerStroke; exports.OPP = _chunkKWNMBMKGcjs.OPP; exports.PAINT_COLOR_JSON_SCHEMA = _chunkPQ3H2AANcjs.PAINT_COLOR_JSON_SCHEMA; exports.PAINT_GRADIENT_FULL_SPEED = _chunkPQ3H2AANcjs.PAINT_GRADIENT_FULL_SPEED; exports.PAINT_GRADIENT_JSON_SCHEMA = _chunkPQ3H2AANcjs.PAINT_GRADIENT_JSON_SCHEMA; exports.PAINT_GRADIENT_KIT = _chunkPQ3H2AANcjs.PAINT_GRADIENT_KIT; exports.PAINT_GRADIENT_MAPPINGS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_MAPPINGS; exports.PAINT_GRADIENT_MAPPING_LABELS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_MAPPING_LABELS; exports.PAINT_GRADIENT_PRESETS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_PRESETS; exports.PAINT_GRADIENT_REPEATS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_REPEATS; exports.PAINT_GRADIENT_REPEAT_LABELS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_REPEAT_LABELS; exports.PAINT_GRADIENT_SPACES = _chunkPQ3H2AANcjs.PAINT_GRADIENT_SPACES; exports.PAINT_GRADIENT_SPACE_LABELS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_SPACE_LABELS; exports.PAINT_SOURCE_JSON_SCHEMA = _chunkPQ3H2AANcjs.PAINT_SOURCE_JSON_SCHEMA; exports.PAPER_CAPACITY = _chunkKWNMBMKGcjs.PAPER_CAPACITY; exports.PARAM = _chunkKWNMBMKGcjs.PARAM; exports.PARAM_FLOATS = _chunkKWNMBMKGcjs.PARAM_FLOATS; exports.PIGMENT_BANDS = _chunkOLNIPTB3cjs.PIGMENT_BANDS; exports.PIGMENT_CHANNELS = _chunkKWNMBMKGcjs.PIGMENT_CHANNELS; exports.PIGMENT_LAMBDA_MIN = _chunkOLNIPTB3cjs.PIGMENT_LAMBDA_MIN; exports.PIGMENT_LAMBDA_STEP = _chunkOLNIPTB3cjs.PIGMENT_LAMBDA_STEP; exports.PIGMENT_UNIT = _chunkKWNMBMKGcjs.PIGMENT_UNIT; exports.PIGMENT_WAVELENGTHS = _chunkOLNIPTB3cjs.PIGMENT_WAVELENGTHS; exports.PLANE = _chunkKWNMBMKGcjs.PLANE; exports.PLANE_BYTES = _chunkKWNMBMKGcjs.PLANE_BYTES; exports.P_D0 = _chunkKWNMBMKGcjs.P_D0; exports.P_EDGE = _chunkKWNMBMKGcjs.P_EDGE; exports.P_FX = _chunkKWNMBMKGcjs.P_FX; exports.P_FY = _chunkKWNMBMKGcjs.P_FY; exports.P_H = _chunkKWNMBMKGcjs.P_H; exports.P_P0 = _chunkKWNMBMKGcjs.P_P0; exports.P_RHO = _chunkKWNMBMKGcjs.P_RHO; exports.P_SW = _chunkKWNMBMKGcjs.P_SW; exports.P_UX = _chunkKWNMBMKGcjs.P_UX; exports.P_UY = _chunkKWNMBMKGcjs.P_UY; exports.PaintExchange = _chunkPQ3H2AANcjs.PaintExchange; exports.PaintGradientSampler = _chunkPQ3H2AANcjs.PaintGradientSampler; exports.PaintSourceError = _chunkPQ3H2AANcjs.PaintSourceError; exports.PaperGrain = _chunkPQ3H2AANcjs.PaperGrain; exports.RETOUCH_SAMPLES = _chunkYF3YZZ6Gcjs.RETOUCH_SAMPLES; exports.RegionPixelSource = _chunkYF3YZZ6Gcjs.RegionPixelSource; exports.RetouchDabStroke = _chunkYF3YZZ6Gcjs.RetouchDabStroke; exports.SET_PLANES = _chunkKWNMBMKGcjs.SET_PLANES; exports.SLOT_BYTES = _chunkKWNMBMKGcjs.SLOT_BYTES; exports.SLOT_PLANES = _chunkKWNMBMKGcjs.SLOT_PLANES; exports.SLOT_SIDE = _chunkKWNMBMKGcjs.SLOT_SIDE; exports.SPOT_HEAL_TYPES = _chunkYF3YZZ6Gcjs.SPOT_HEAL_TYPES; exports.SRGB_TO_LINEAR = _chunkKWNMBMKGcjs.SRGB_TO_LINEAR; exports.STRIP = _chunkKWNMBMKGcjs.STRIP; exports.SmudgeTip = _chunkPQ3H2AANcjs.SmudgeTip; exports.StrokeCoverage = _chunkPQ3H2AANcjs.StrokeCoverage; exports.TIP_GRADIENT_STEPS = _chunkPQ3H2AANcjs.TIP_GRADIENT_STEPS; exports.TONE_RANGES = _chunkYF3YZZ6Gcjs.TONE_RANGES; exports.TileMedium = _chunkPQ3H2AANcjs.TileMedium; exports.TilePixelSource = _chunkYF3YZZ6Gcjs.TilePixelSource; exports.TileableNoise = _chunkPQ3H2AANcjs.TileableNoise; exports.TransformedPixelSource = _chunkYF3YZZ6Gcjs.TransformedPixelSource; exports.U_MAX = _chunkKWNMBMKGcjs.U_MAX; exports.W9 = _chunkKWNMBMKGcjs.W9; exports.WET_FLOW_KINDS = _chunkKWNMBMKGcjs.WET_FLOW_KINDS; exports.WET_PARAM_KEYS = _chunkKWNMBMKGcjs.WET_PARAM_KEYS; exports.WET_TILE = _chunkKWNMBMKGcjs.WET_TILE; exports.WGSL_HALO = _chunkKWNMBMKGcjs.WGSL_HALO; exports.WGSL_SHADE = _chunkKWNMBMKGcjs.WGSL_SHADE; exports.WGSL_STEP = _chunkKWNMBMKGcjs.WGSL_STEP; exports.WetGpuEngine = _chunkKWNMBMKGcjs.WetGpuEngine; exports.WetMedium = _chunkKWNMBMKGcjs.WetMedium; exports.WetWorkerPool = _chunkKWNMBMKGcjs.WetWorkerPool; exports.WetWorld = _chunkKWNMBMKGcjs.WetWorld; exports.WetWorldError = _chunkKWNMBMKGcjs.WetWorldError; exports.addPaintGradientStop = _chunkPQ3H2AANcjs.addPaintGradientStop; exports.applyRetouchToTile = _chunkYF3YZZ6Gcjs.applyRetouchToTile; exports.applyStrokeToDeepTile = _chunkPQ3H2AANcjs.applyStrokeToDeepTile; exports.applyStrokeToTile = _chunkPQ3H2AANcjs.applyStrokeToTile; exports.applyToneToDeepTile = _chunkYF3YZZ6Gcjs.applyToneToDeepTile; exports.bestOffset = _chunkMQDIP4CFcjs.bestOffset; exports.blendRadius = _chunkPQ3H2AANcjs.blendRadius; exports.blenderDab = _chunkPQ3H2AANcjs.blenderDab; exports.boxBlurPixels = _chunkYF3YZZ6Gcjs.boxBlurPixels; exports.brushstrokeDataUrl = _chunkPQ3H2AANcjs.brushstrokeDataUrl; exports.brushstrokeKey = _chunkPQ3H2AANcjs.brushstrokeKey; exports.brushstrokeSamples = _chunkPQ3H2AANcjs.brushstrokeSamples; exports.brushstrokeScaleBucket = _chunkYQGCPL5Xcjs.brushstrokeScaleBucket; exports.brushstrokeStrokeOptions = _chunkPQ3H2AANcjs.brushstrokeStrokeOptions; exports.brushstrokeToCanvas = _chunkPQ3H2AANcjs.brushstrokeToCanvas; exports.canShareMemory = _chunkKWNMBMKGcjs.canShareMemory; exports.cleanMixer = _chunkPQ3H2AANcjs.cleanMixer; exports.cloneSamplePoint = _chunkYF3YZZ6Gcjs.cloneSamplePoint; exports.contentAwareFill = _chunkMQDIP4CFcjs.contentAwareFill; exports.coverageTileCoord = _chunkPQ3H2AANcjs.coverageTileCoord; exports.createBlenderScratch = _chunkPQ3H2AANcjs.createBlenderScratch; exports.createPigment = _chunkOLNIPTB3cjs.createPigment; exports.createPigmentTools = _chunkKWNMBMKGcjs.createPigmentTools; exports.createWetGpuEngine = _chunkKWNMBMKGcjs.createWetGpuEngine; exports.dabCoverage = _chunkPQ3H2AANcjs.dabCoverage; exports.dabRect = _chunkPQ3H2AANcjs.dabRect; exports.dexp = _chunkKWNMBMKGcjs.dexp; exports.dilateMask = _chunkMQDIP4CFcjs.dilateMask; exports.dlog = _chunkKWNMBMKGcjs.dlog; exports.dodgeBurn = _chunkYF3YZZ6Gcjs.dodgeBurn; exports.dodgeBurnCurve = _chunkYF3YZZ6Gcjs.dodgeBurnCurve; exports.dpow = _chunkKWNMBMKGcjs.dpow; exports.dsincos = _chunkKWNMBMKGcjs.dsincos; exports.edgeBits = _chunkKWNMBMKGcjs.edgeBits; exports.exchangeRates = _chunkPQ3H2AANcjs.exchangeRates; exports.expNeg = _chunkKWNMBMKGcjs.expNeg; exports.filmPixel = _chunkKWNMBMKGcjs.filmPixel; exports.filmReflectance = _chunkKWNMBMKGcjs.filmReflectance; exports.findWebGpu = _chunkKWNMBMKGcjs.findWebGpu; exports.flowField = _chunkKWNMBMKGcjs.flowField; exports.gradientDabColor = _chunkPQ3H2AANcjs.gradientDabColor; exports.grainCatch = _chunkPQ3H2AANcjs.grainCatch; exports.grainHeights = _chunkPQ3H2AANcjs.grainHeights; exports.hash2 = _chunkPQ3H2AANcjs.hash2; exports.healPixels = _chunkYF3YZZ6Gcjs.healPixels; exports.hexToPaintRgb = _chunkPQ3H2AANcjs.hexToPaintRgb; exports.isBrushstrokeBrush = _chunkPQ3H2AANcjs.isBrushstrokeBrush; exports.isGradientPaint = _chunkPQ3H2AANcjs.isGradientPaint; exports.isIdentityCloneTransform = _chunkYF3YZZ6Gcjs.isIdentityCloneTransform; exports.latticeViscosity = _chunkKWNMBMKGcjs.latticeViscosity; exports.lerpPigment = _chunkOLNIPTB3cjs.lerpPigment; exports.lerpPigmentInto = _chunkOLNIPTB3cjs.lerpPigmentInto; exports.linearToSrgb8 = _chunkKWNMBMKGcjs.linearToSrgb8; exports.loadMixer = _chunkPQ3H2AANcjs.loadMixer; exports.loadWetKernel = _chunkKWNMBMKGcjs.loadWetKernel; exports.luma = _chunkYF3YZZ6Gcjs.luma; exports.maskBounds = _chunkMQDIP4CFcjs.maskBounds; exports.membraneFill = _chunkMQDIP4CFcjs.membraneFill; exports.mix32 = _chunkPQ3H2AANcjs.mix32; exports.mixPigments = _chunkOLNIPTB3cjs.mixPigments; exports.mixerRates = _chunkPQ3H2AANcjs.mixerRates; exports.normalizeBlender = _chunkPQ3H2AANcjs.normalizeBlender; exports.normalizeBrush = _chunkPQ3H2AANcjs.normalizeBrush; exports.normalizeFlow = _chunkKWNMBMKGcjs.normalizeFlow; exports.normalizeHexColor = _chunkPQ3H2AANcjs.normalizeHexColor; exports.normalizeMixer = _chunkPQ3H2AANcjs.normalizeMixer; exports.normalizeRetouch = _chunkYF3YZZ6Gcjs.normalizeRetouch; exports.normalizeSmudge = _chunkPQ3H2AANcjs.normalizeSmudge; exports.normalizeTilt = _chunkKWNMBMKGcjs.normalizeTilt; exports.normalizeWetParams = _chunkKWNMBMKGcjs.normalizeWetParams; exports.packWetParams = _chunkKWNMBMKGcjs.packWetParams; exports.paintGradientMappingFor = _chunkPQ3H2AANcjs.paintGradientMappingFor; exports.paintPathStroke = _chunkPQ3H2AANcjs.paintPathStroke; exports.paintSourceColor = _chunkPQ3H2AANcjs.paintSourceColor; exports.paintSourceCss = _chunkPQ3H2AANcjs.paintSourceCss; exports.parsePaintSource = _chunkPQ3H2AANcjs.parsePaintSource; exports.pathStrokeCoverage = _chunkPQ3H2AANcjs.pathStrokeCoverage; exports.pathStrokeDabs = _chunkPQ3H2AANcjs.pathStrokeDabs; exports.perDabStrength = _chunkYF3YZZ6Gcjs.perDabStrength; exports.pigmentColor = _chunkKWNMBMKGcjs.pigmentColor; exports.pigmentOf = _chunkKWNMBMKGcjs.pigmentOf; exports.pixelPigment = _chunkKWNMBMKGcjs.pixelPigment; exports.pointerPressure = _chunkKWNMBMKGcjs.pointerPressure; exports.pointerStrokeSample = _chunkKWNMBMKGcjs.pointerStrokeSample; exports.pointerStrokeSamples = _chunkKWNMBMKGcjs.pointerStrokeSamples; exports.put255 = _chunkPQ3H2AANcjs.put255; exports.rand01 = _chunkPQ3H2AANcjs.rand01; exports.randSigned = _chunkPQ3H2AANcjs.randSigned; exports.rasterizeDab = _chunkPQ3H2AANcjs.rasterizeDab; exports.readPaintSource = _chunkPQ3H2AANcjs.readPaintSource; exports.readSourceRegion = _chunkYF3YZZ6Gcjs.readSourceRegion; exports.reflectanceToRgb = _chunkOLNIPTB3cjs.reflectanceToRgb; exports.removePaintGradientStop = _chunkPQ3H2AANcjs.removePaintGradientStop; exports.renderBrushstroke = _chunkPQ3H2AANcjs.renderBrushstroke; exports.resolvePaintGradient = _chunkPQ3H2AANcjs.resolvePaintGradient; exports.retouchFilter = _chunkYF3YZZ6Gcjs.retouchFilter; exports.reversePaintGradient = _chunkPQ3H2AANcjs.reversePaintGradient; exports.rgbToReflectance = _chunkOLNIPTB3cjs.rgbToReflectance; exports.rgbaViewOfGrey = _chunkYF3YZZ6Gcjs.rgbaViewOfGrey; exports.saturationOf = _chunkYF3YZZ6Gcjs.saturationOf; exports.scalePaintGradient = _chunkPQ3H2AANcjs.scalePaintGradient; exports.seamlessClone = _chunkMQDIP4CFcjs.seamlessClone; exports.seedFromString = _chunkPQ3H2AANcjs.seedFromString; exports.setPigmentColor = _chunkOLNIPTB3cjs.setPigmentColor; exports.shadeTile = _chunkKWNMBMKGcjs.shadeTile; exports.smoothPoints = _chunkPQ3H2AANcjs.smoothPoints; exports.smudgeEffect = _chunkYF3YZZ6Gcjs.smudgeEffect; exports.solidPaint = _chunkPQ3H2AANcjs.solidPaint; exports.solveMembrane = _chunkMQDIP4CFcjs.solveMembrane; exports.sponge = _chunkYF3YZZ6Gcjs.sponge; exports.spongeEffect = _chunkYF3YZZ6Gcjs.spongeEffect; exports.spotHealPixels = _chunkYF3YZZ6Gcjs.spotHealPixels; exports.spotHealWindow = _chunkYF3YZZ6Gcjs.spotHealWindow; exports.srgbByteToLinear = _chunkKWNMBMKGcjs.srgbByteToLinear; exports.stepSlotTs = _chunkKWNMBMKGcjs.stepSlotTs; exports.strokeGrainCatch = _chunkPQ3H2AANcjs.strokeGrainCatch; exports.tipFalloff = _chunkPQ3H2AANcjs.tipFalloff; exports.tipReach = _chunkPQ3H2AANcjs.tipReach; exports.tipShape = _chunkPQ3H2AANcjs.tipShape; exports.toneOf = _chunkYF3YZZ6Gcjs.toneOf; exports.updatePaintGradientStop = _chunkPQ3H2AANcjs.updatePaintGradientStop; exports.walkDabs = _chunkPQ3H2AANcjs.walkDabs; exports.wetGpuSupport = _chunkKWNMBMKGcjs.wetGpuSupport; exports.wetSimdSupported = _chunkKWNMBMKGcjs.wetSimdSupported; exports.wetSupport = _chunkKWNMBMKGcjs.wetSupport;
487
+
488
+ exports.BODY_SCATTER = _chunkXFYLA3ZOcjs.BODY_SCATTER; exports.BRUSHSTROKE_MAX_SCALE = _chunkYQGCPL5Xcjs.BRUSHSTROKE_MAX_SCALE; exports.BRUSHSTROKE_MIN_SCALE = _chunkYQGCPL5Xcjs.BRUSHSTROKE_MIN_SCALE; exports.BrushstrokeCache = _chunkPQ3H2AANcjs.BrushstrokeCache; exports.CoverageMap = _chunkPQ3H2AANcjs.CoverageMap; exports.DEFAULT_BLENDER = _chunkPQ3H2AANcjs.DEFAULT_BLENDER; exports.DEFAULT_BRUSH = _chunkPQ3H2AANcjs.DEFAULT_BRUSH; exports.DEFAULT_FLOW = _chunkXFYLA3ZOcjs.DEFAULT_FLOW; exports.DEFAULT_MIXER = _chunkPQ3H2AANcjs.DEFAULT_MIXER; exports.DEFAULT_PAINT_GRADIENT = _chunkPQ3H2AANcjs.DEFAULT_PAINT_GRADIENT; exports.DEFAULT_PAINT_GRADIENT_MAPPING = _chunkPQ3H2AANcjs.DEFAULT_PAINT_GRADIENT_MAPPING; exports.DEFAULT_RETOUCH = _chunkYF3YZZ6Gcjs.DEFAULT_RETOUCH; exports.DEFAULT_SMUDGE = _chunkPQ3H2AANcjs.DEFAULT_SMUDGE; exports.DEFAULT_WET_PAPER = _chunkXFYLA3ZOcjs.DEFAULT_WET_PAPER; exports.DEFAULT_WET_PARAMS = _chunkXFYLA3ZOcjs.DEFAULT_WET_PARAMS; exports.DRY_EPS = _chunkXFYLA3ZOcjs.DRY_EPS; exports.DabWalker = _chunkPQ3H2AANcjs.DabWalker; exports.EMPTY_MIXER_LOAD = _chunkPQ3H2AANcjs.EMPTY_MIXER_LOAD; exports.EX = _chunkXFYLA3ZOcjs.EX; exports.EY = _chunkXFYLA3ZOcjs.EY; exports.FLAG_ALIVE = _chunkXFYLA3ZOcjs.FLAG_ALIVE; exports.FLAG_BOTTOM = _chunkXFYLA3ZOcjs.FLAG_BOTTOM; exports.FLAG_LEFT = _chunkXFYLA3ZOcjs.FLAG_LEFT; exports.FLAG_RIGHT = _chunkXFYLA3ZOcjs.FLAG_RIGHT; exports.FLAG_TOP = _chunkXFYLA3ZOcjs.FLAG_TOP; exports.FLAG_WET = _chunkXFYLA3ZOcjs.FLAG_WET; exports.FLAT = _chunkXFYLA3ZOcjs.FLAT; exports.FREE_INTERVAL = _chunkXFYLA3ZOcjs.FREE_INTERVAL; exports.GRAIN_PATTERNS = _chunkPQ3H2AANcjs.GRAIN_PATTERNS; exports.GRAIN_TILE = _chunkPQ3H2AANcjs.GRAIN_TILE; exports.HIDE = _chunkXFYLA3ZOcjs.HIDE; exports.IDENTITY_CLONE_TRANSFORM = _chunkYF3YZZ6Gcjs.IDENTITY_CLONE_TRANSFORM; exports.MAX_BATCH = _chunkXFYLA3ZOcjs.MAX_BATCH; exports.MAX_BRUSH_SIZE = _chunkPQ3H2AANcjs.MAX_BRUSH_SIZE; exports.MAX_TILT_ACCEL = _chunkXFYLA3ZOcjs.MAX_TILT_ACCEL; exports.MAX_WET_LANES = _chunkXFYLA3ZOcjs.MAX_WET_LANES; exports.MAX_WET_SLOTS = _chunkXFYLA3ZOcjs.MAX_WET_SLOTS; exports.MIXER_DRAIN = _chunkPQ3H2AANcjs.MIXER_DRAIN; exports.MIXER_PICKUP_RATE = _chunkPQ3H2AANcjs.MIXER_PICKUP_RATE; exports.MIXING_MODELS = _chunkPQ3H2AANcjs.MIXING_MODELS; exports.MixerStroke = _chunkPQ3H2AANcjs.MixerStroke; exports.OPP = _chunkXFYLA3ZOcjs.OPP; exports.PAINT_COLOR_JSON_SCHEMA = _chunkPQ3H2AANcjs.PAINT_COLOR_JSON_SCHEMA; exports.PAINT_GRADIENT_FULL_SPEED = _chunkPQ3H2AANcjs.PAINT_GRADIENT_FULL_SPEED; exports.PAINT_GRADIENT_JSON_SCHEMA = _chunkPQ3H2AANcjs.PAINT_GRADIENT_JSON_SCHEMA; exports.PAINT_GRADIENT_KIT = _chunkPQ3H2AANcjs.PAINT_GRADIENT_KIT; exports.PAINT_GRADIENT_MAPPINGS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_MAPPINGS; exports.PAINT_GRADIENT_MAPPING_LABELS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_MAPPING_LABELS; exports.PAINT_GRADIENT_PRESETS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_PRESETS; exports.PAINT_GRADIENT_REPEATS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_REPEATS; exports.PAINT_GRADIENT_REPEAT_LABELS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_REPEAT_LABELS; exports.PAINT_GRADIENT_SPACES = _chunkPQ3H2AANcjs.PAINT_GRADIENT_SPACES; exports.PAINT_GRADIENT_SPACE_LABELS = _chunkPQ3H2AANcjs.PAINT_GRADIENT_SPACE_LABELS; exports.PAINT_SOURCE_JSON_SCHEMA = _chunkPQ3H2AANcjs.PAINT_SOURCE_JSON_SCHEMA; exports.PAPER_CAPACITY = _chunkXFYLA3ZOcjs.PAPER_CAPACITY; exports.PARAM = _chunkXFYLA3ZOcjs.PARAM; exports.PARAM_FLOATS = _chunkXFYLA3ZOcjs.PARAM_FLOATS; exports.PIGMENT_BANDS = _chunkOLNIPTB3cjs.PIGMENT_BANDS; exports.PIGMENT_CHANNELS = _chunkXFYLA3ZOcjs.PIGMENT_CHANNELS; exports.PIGMENT_LAMBDA_MIN = _chunkOLNIPTB3cjs.PIGMENT_LAMBDA_MIN; exports.PIGMENT_LAMBDA_STEP = _chunkOLNIPTB3cjs.PIGMENT_LAMBDA_STEP; exports.PIGMENT_UNIT = _chunkXFYLA3ZOcjs.PIGMENT_UNIT; exports.PIGMENT_WAVELENGTHS = _chunkOLNIPTB3cjs.PIGMENT_WAVELENGTHS; exports.PLANE = _chunkXFYLA3ZOcjs.PLANE; exports.PLANE_BYTES = _chunkXFYLA3ZOcjs.PLANE_BYTES; exports.P_D0 = _chunkXFYLA3ZOcjs.P_D0; exports.P_EDGE = _chunkXFYLA3ZOcjs.P_EDGE; exports.P_FX = _chunkXFYLA3ZOcjs.P_FX; exports.P_FY = _chunkXFYLA3ZOcjs.P_FY; exports.P_H = _chunkXFYLA3ZOcjs.P_H; exports.P_P0 = _chunkXFYLA3ZOcjs.P_P0; exports.P_RHO = _chunkXFYLA3ZOcjs.P_RHO; exports.P_SW = _chunkXFYLA3ZOcjs.P_SW; exports.P_UX = _chunkXFYLA3ZOcjs.P_UX; exports.P_UY = _chunkXFYLA3ZOcjs.P_UY; exports.PaintExchange = _chunkPQ3H2AANcjs.PaintExchange; exports.PaintGradientSampler = _chunkPQ3H2AANcjs.PaintGradientSampler; exports.PaintSourceError = _chunkPQ3H2AANcjs.PaintSourceError; exports.PaperGrain = _chunkPQ3H2AANcjs.PaperGrain; exports.RETOUCH_SAMPLES = _chunkYF3YZZ6Gcjs.RETOUCH_SAMPLES; exports.RegionPixelSource = _chunkYF3YZZ6Gcjs.RegionPixelSource; exports.RetouchDabStroke = _chunkYF3YZZ6Gcjs.RetouchDabStroke; exports.SET_PLANES = _chunkXFYLA3ZOcjs.SET_PLANES; exports.SLOT_BYTES = _chunkXFYLA3ZOcjs.SLOT_BYTES; exports.SLOT_PLANES = _chunkXFYLA3ZOcjs.SLOT_PLANES; exports.SLOT_SIDE = _chunkXFYLA3ZOcjs.SLOT_SIDE; exports.SPOT_HEAL_TYPES = _chunkYF3YZZ6Gcjs.SPOT_HEAL_TYPES; exports.SRGB_TO_LINEAR = _chunkXFYLA3ZOcjs.SRGB_TO_LINEAR; exports.STRIP = _chunkXFYLA3ZOcjs.STRIP; exports.SmudgeTip = _chunkPQ3H2AANcjs.SmudgeTip; exports.StrokeCoverage = _chunkPQ3H2AANcjs.StrokeCoverage; exports.TIP_GRADIENT_STEPS = _chunkPQ3H2AANcjs.TIP_GRADIENT_STEPS; exports.TONE_RANGES = _chunkYF3YZZ6Gcjs.TONE_RANGES; exports.TileMedium = _chunkPQ3H2AANcjs.TileMedium; exports.TilePixelSource = _chunkYF3YZZ6Gcjs.TilePixelSource; exports.TileableNoise = _chunkPQ3H2AANcjs.TileableNoise; exports.TransformedPixelSource = _chunkYF3YZZ6Gcjs.TransformedPixelSource; exports.U_MAX = _chunkXFYLA3ZOcjs.U_MAX; exports.W9 = _chunkXFYLA3ZOcjs.W9; exports.WET_FLOW_KINDS = _chunkXFYLA3ZOcjs.WET_FLOW_KINDS; exports.WET_PARAM_KEYS = _chunkXFYLA3ZOcjs.WET_PARAM_KEYS; exports.WET_TILE = _chunkXFYLA3ZOcjs.WET_TILE; exports.WGSL_HALO = _chunkXFYLA3ZOcjs.WGSL_HALO; exports.WGSL_SHADE = _chunkXFYLA3ZOcjs.WGSL_SHADE; exports.WGSL_STEP = _chunkXFYLA3ZOcjs.WGSL_STEP; exports.WetGpuEngine = _chunkXFYLA3ZOcjs.WetGpuEngine; exports.WetMedium = _chunkXFYLA3ZOcjs.WetMedium; exports.WetWorkerPool = _chunkXFYLA3ZOcjs.WetWorkerPool; exports.WetWorld = _chunkXFYLA3ZOcjs.WetWorld; exports.WetWorldError = _chunkXFYLA3ZOcjs.WetWorldError; exports.addPaintGradientStop = _chunkPQ3H2AANcjs.addPaintGradientStop; exports.applyRetouchToTile = _chunkYF3YZZ6Gcjs.applyRetouchToTile; exports.applyStrokeToDeepTile = _chunkPQ3H2AANcjs.applyStrokeToDeepTile; exports.applyStrokeToTile = _chunkPQ3H2AANcjs.applyStrokeToTile; exports.applyToneToDeepTile = _chunkYF3YZZ6Gcjs.applyToneToDeepTile; exports.bestOffset = _chunkMQDIP4CFcjs.bestOffset; exports.blendRadius = _chunkPQ3H2AANcjs.blendRadius; exports.blenderDab = _chunkPQ3H2AANcjs.blenderDab; exports.boxBlurPixels = _chunkYF3YZZ6Gcjs.boxBlurPixels; exports.brushstrokeDataUrl = _chunkPQ3H2AANcjs.brushstrokeDataUrl; exports.brushstrokeKey = _chunkPQ3H2AANcjs.brushstrokeKey; exports.brushstrokeSamples = _chunkPQ3H2AANcjs.brushstrokeSamples; exports.brushstrokeScaleBucket = _chunkYQGCPL5Xcjs.brushstrokeScaleBucket; exports.brushstrokeStrokeOptions = _chunkPQ3H2AANcjs.brushstrokeStrokeOptions; exports.brushstrokeToCanvas = _chunkPQ3H2AANcjs.brushstrokeToCanvas; exports.canShareMemory = _chunkXFYLA3ZOcjs.canShareMemory; exports.cleanMixer = _chunkPQ3H2AANcjs.cleanMixer; exports.cloneSamplePoint = _chunkYF3YZZ6Gcjs.cloneSamplePoint; exports.contentAwareFill = _chunkMQDIP4CFcjs.contentAwareFill; exports.coverageTileCoord = _chunkPQ3H2AANcjs.coverageTileCoord; exports.createBlenderScratch = _chunkPQ3H2AANcjs.createBlenderScratch; exports.createPigment = _chunkOLNIPTB3cjs.createPigment; exports.createPigmentTools = _chunkXFYLA3ZOcjs.createPigmentTools; exports.createWetGpuEngine = _chunkXFYLA3ZOcjs.createWetGpuEngine; exports.dabCoverage = _chunkPQ3H2AANcjs.dabCoverage; exports.dabRect = _chunkPQ3H2AANcjs.dabRect; exports.dexp = _chunkXFYLA3ZOcjs.dexp; exports.dilateMask = _chunkMQDIP4CFcjs.dilateMask; exports.dlog = _chunkXFYLA3ZOcjs.dlog; exports.dodgeBurn = _chunkYF3YZZ6Gcjs.dodgeBurn; exports.dodgeBurnCurve = _chunkYF3YZZ6Gcjs.dodgeBurnCurve; exports.dpow = _chunkXFYLA3ZOcjs.dpow; exports.dsincos = _chunkXFYLA3ZOcjs.dsincos; exports.edgeBits = _chunkXFYLA3ZOcjs.edgeBits; exports.exchangeRates = _chunkPQ3H2AANcjs.exchangeRates; exports.expNeg = _chunkXFYLA3ZOcjs.expNeg; exports.filmPixel = _chunkXFYLA3ZOcjs.filmPixel; exports.filmReflectance = _chunkXFYLA3ZOcjs.filmReflectance; exports.findWebGpu = _chunkXFYLA3ZOcjs.findWebGpu; exports.flowField = _chunkXFYLA3ZOcjs.flowField; exports.gradientDabColor = _chunkPQ3H2AANcjs.gradientDabColor; exports.grainCatch = _chunkPQ3H2AANcjs.grainCatch; exports.grainHeights = _chunkPQ3H2AANcjs.grainHeights; exports.hash2 = _chunkPQ3H2AANcjs.hash2; exports.healPixels = _chunkYF3YZZ6Gcjs.healPixels; exports.hexToPaintRgb = _chunkPQ3H2AANcjs.hexToPaintRgb; exports.isBrushstrokeBrush = _chunkPQ3H2AANcjs.isBrushstrokeBrush; exports.isGradientPaint = _chunkPQ3H2AANcjs.isGradientPaint; exports.isIdentityCloneTransform = _chunkYF3YZZ6Gcjs.isIdentityCloneTransform; exports.latticeViscosity = _chunkXFYLA3ZOcjs.latticeViscosity; exports.lerpPigment = _chunkOLNIPTB3cjs.lerpPigment; exports.lerpPigmentInto = _chunkOLNIPTB3cjs.lerpPigmentInto; exports.linearToSrgb8 = _chunkXFYLA3ZOcjs.linearToSrgb8; exports.loadMixer = _chunkPQ3H2AANcjs.loadMixer; exports.loadWetKernel = _chunkXFYLA3ZOcjs.loadWetKernel; exports.luma = _chunkYF3YZZ6Gcjs.luma; exports.maskBounds = _chunkMQDIP4CFcjs.maskBounds; exports.membraneFill = _chunkMQDIP4CFcjs.membraneFill; exports.mix32 = _chunkPQ3H2AANcjs.mix32; exports.mixPigments = _chunkOLNIPTB3cjs.mixPigments; exports.mixerRates = _chunkPQ3H2AANcjs.mixerRates; exports.moveWetSnapshot = _chunkXFYLA3ZOcjs.moveWetSnapshot; exports.normalizeBlender = _chunkPQ3H2AANcjs.normalizeBlender; exports.normalizeBrush = _chunkPQ3H2AANcjs.normalizeBrush; exports.normalizeFlow = _chunkXFYLA3ZOcjs.normalizeFlow; exports.normalizeHexColor = _chunkPQ3H2AANcjs.normalizeHexColor; exports.normalizeMixer = _chunkPQ3H2AANcjs.normalizeMixer; exports.normalizeRetouch = _chunkYF3YZZ6Gcjs.normalizeRetouch; exports.normalizeSmudge = _chunkPQ3H2AANcjs.normalizeSmudge; exports.normalizeTilt = _chunkXFYLA3ZOcjs.normalizeTilt; exports.normalizeWetParams = _chunkXFYLA3ZOcjs.normalizeWetParams; exports.packWetParams = _chunkXFYLA3ZOcjs.packWetParams; exports.paintGradientMappingFor = _chunkPQ3H2AANcjs.paintGradientMappingFor; exports.paintPathStroke = _chunkPQ3H2AANcjs.paintPathStroke; exports.paintSourceColor = _chunkPQ3H2AANcjs.paintSourceColor; exports.paintSourceCss = _chunkPQ3H2AANcjs.paintSourceCss; exports.parsePaintSource = _chunkPQ3H2AANcjs.parsePaintSource; exports.pathStrokeCoverage = _chunkPQ3H2AANcjs.pathStrokeCoverage; exports.pathStrokeDabs = _chunkPQ3H2AANcjs.pathStrokeDabs; exports.perDabStrength = _chunkYF3YZZ6Gcjs.perDabStrength; exports.pigmentColor = _chunkXFYLA3ZOcjs.pigmentColor; exports.pigmentOf = _chunkXFYLA3ZOcjs.pigmentOf; exports.pixelPigment = _chunkXFYLA3ZOcjs.pixelPigment; exports.pointerPressure = _chunkXFYLA3ZOcjs.pointerPressure; exports.pointerStrokeSample = _chunkXFYLA3ZOcjs.pointerStrokeSample; exports.pointerStrokeSamples = _chunkXFYLA3ZOcjs.pointerStrokeSamples; exports.put255 = _chunkPQ3H2AANcjs.put255; exports.rand01 = _chunkPQ3H2AANcjs.rand01; exports.randSigned = _chunkPQ3H2AANcjs.randSigned; exports.rasterizeDab = _chunkPQ3H2AANcjs.rasterizeDab; exports.readPaintSource = _chunkPQ3H2AANcjs.readPaintSource; exports.readSourceRegion = _chunkYF3YZZ6Gcjs.readSourceRegion; exports.reflectanceToRgb = _chunkOLNIPTB3cjs.reflectanceToRgb; exports.removePaintGradientStop = _chunkPQ3H2AANcjs.removePaintGradientStop; exports.renderBrushstroke = _chunkPQ3H2AANcjs.renderBrushstroke; exports.resolvePaintGradient = _chunkPQ3H2AANcjs.resolvePaintGradient; exports.retouchFilter = _chunkYF3YZZ6Gcjs.retouchFilter; exports.reversePaintGradient = _chunkPQ3H2AANcjs.reversePaintGradient; exports.rgbToReflectance = _chunkOLNIPTB3cjs.rgbToReflectance; exports.rgbaViewOfGrey = _chunkYF3YZZ6Gcjs.rgbaViewOfGrey; exports.saturationOf = _chunkYF3YZZ6Gcjs.saturationOf; exports.scalePaintGradient = _chunkPQ3H2AANcjs.scalePaintGradient; exports.seamlessClone = _chunkMQDIP4CFcjs.seamlessClone; exports.seedFromString = _chunkPQ3H2AANcjs.seedFromString; exports.setPigmentColor = _chunkOLNIPTB3cjs.setPigmentColor; exports.shadeTile = _chunkXFYLA3ZOcjs.shadeTile; exports.smoothPoints = _chunkPQ3H2AANcjs.smoothPoints; exports.smudgeEffect = _chunkYF3YZZ6Gcjs.smudgeEffect; exports.solidPaint = _chunkPQ3H2AANcjs.solidPaint; exports.solveMembrane = _chunkMQDIP4CFcjs.solveMembrane; exports.sponge = _chunkYF3YZZ6Gcjs.sponge; exports.spongeEffect = _chunkYF3YZZ6Gcjs.spongeEffect; exports.spotHealPixels = _chunkYF3YZZ6Gcjs.spotHealPixels; exports.spotHealWindow = _chunkYF3YZZ6Gcjs.spotHealWindow; exports.srgbByteToLinear = _chunkXFYLA3ZOcjs.srgbByteToLinear; exports.stepSlotTs = _chunkXFYLA3ZOcjs.stepSlotTs; exports.strokeGrainCatch = _chunkPQ3H2AANcjs.strokeGrainCatch; exports.tipFalloff = _chunkPQ3H2AANcjs.tipFalloff; exports.tipReach = _chunkPQ3H2AANcjs.tipReach; exports.tipShape = _chunkPQ3H2AANcjs.tipShape; exports.toneOf = _chunkYF3YZZ6Gcjs.toneOf; exports.updatePaintGradientStop = _chunkPQ3H2AANcjs.updatePaintGradientStop; exports.walkDabs = _chunkPQ3H2AANcjs.walkDabs; exports.wetGpuSupport = _chunkXFYLA3ZOcjs.wetGpuSupport; exports.wetSimdSupported = _chunkXFYLA3ZOcjs.wetSimdSupported; exports.wetSupport = _chunkXFYLA3ZOcjs.wetSupport;
@@ -113,6 +113,7 @@ import {
113
113
  latticeViscosity,
114
114
  linearToSrgb8,
115
115
  loadWetKernel,
116
+ moveWetSnapshot,
116
117
  normalizeFlow,
117
118
  normalizeTilt,
118
119
  normalizeWetParams,
@@ -129,7 +130,7 @@ import {
129
130
  wetGpuSupport,
130
131
  wetSimdSupported,
131
132
  wetSupport
132
- } from "../chunk-WJ2RBZMM.js";
133
+ } from "../chunk-HLBTRSQJ.js";
133
134
  import {
134
135
  BrushstrokeCache,
135
136
  CoverageMap,
@@ -419,6 +420,7 @@ export {
419
420
  mix32,
420
421
  mixPigments,
421
422
  mixerRates,
423
+ moveWetSnapshot,
422
424
  normalizeBlender,
423
425
  normalizeBrush,
424
426
  normalizeFlow,
@@ -385,4 +385,23 @@ export declare class WetWorld {
385
385
  /** Float index of a slot's first plane (tests). */
386
386
  slotIndex(id: string, tx: number, ty: number): number;
387
387
  }
388
+ /** A layer's base and flow map in a moved snapshot (see `moveWetSnapshot`). */
389
+ export interface WetSnapshotLaneMove {
390
+ base?: TiledSurface;
391
+ flow?: WetFlowField | null;
392
+ }
393
+ /**
394
+ * `snap`, taken from a world `from` pixels in size, moved by (`dx`, `dy`) pixels for a world `to`
395
+ * pixels in size (a canvas grown on any side): every wet tile, with its water, pigment and
396
+ * deposits, lands `dx` / `dy` further right / down. The shift has to be whole wet tiles
397
+ * (`WET_TILE`); tiles that land outside `to` are dropped. `lane` gives each layer's base and flow
398
+ * map in the new coordinates (default: as they were). Restore it into a world of `to`'s size.
399
+ */
400
+ export declare function moveWetSnapshot(snap: WetSnapshot, from: {
401
+ width: number;
402
+ height: number;
403
+ }, to: {
404
+ width: number;
405
+ height: number;
406
+ }, dx: number, dy: number, lane?: (l: WetSnapshot['lanes'][number]) => WetSnapshotLaneMove): WetSnapshot;
388
407
  export {};