@pixerate/editor 0.218.0 → 0.218.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1039,6 +1039,44 @@ function selectionOutline(mask, threshold = 128) {
1039
1039
  }
1040
1040
  return Float32Array.from(segs);
1041
1041
  }
1042
+ function selectionOutlinePaths(mask, threshold = 128) {
1043
+ const segs = selectionOutline(mask, threshold);
1044
+ const n = segs.length / 4;
1045
+ if (!n) return [];
1046
+ const stride = mask.width + 1;
1047
+ const key2 = (x, y) => y * stride + x;
1048
+ const at = /* @__PURE__ */ new Map();
1049
+ for (let s = 0; s < n; s++) {
1050
+ for (const k of [key2(segs[s * 4], segs[s * 4 + 1]), key2(segs[s * 4 + 2], segs[s * 4 + 3])]) {
1051
+ const list = at.get(k);
1052
+ if (list) list.push(s);
1053
+ else at.set(k, [s]);
1054
+ }
1055
+ }
1056
+ const used = new Uint8Array(n);
1057
+ const paths = [];
1058
+ for (let s0 = 0; s0 < n; s0++) {
1059
+ if (used[s0]) continue;
1060
+ used[s0] = 1;
1061
+ const pts = [segs[s0 * 4], segs[s0 * 4 + 1], segs[s0 * 4 + 2], segs[s0 * 4 + 3]];
1062
+ for (const forward of [true, false]) {
1063
+ for (; ; ) {
1064
+ const x = forward ? pts[pts.length - 2] : pts[0];
1065
+ const y = forward ? pts[pts.length - 1] : pts[1];
1066
+ const next = at.get(key2(x, y))?.find((t) => !used[t]);
1067
+ if (next === void 0) break;
1068
+ used[next] = 1;
1069
+ const a = [segs[next * 4], segs[next * 4 + 1]];
1070
+ const b = [segs[next * 4 + 2], segs[next * 4 + 3]];
1071
+ const far = a[0] === x && a[1] === y ? b : a;
1072
+ if (forward) pts.push(far[0], far[1]);
1073
+ else pts.unshift(far[0], far[1]);
1074
+ }
1075
+ }
1076
+ paths.push(Float32Array.from(pts));
1077
+ }
1078
+ return paths;
1079
+ }
1042
1080
  function quickMaskPixels(mask, style = {}) {
1043
1081
  const [r, g, b] = style.color ?? [255, 0, 0];
1044
1082
  const k = Math.min(1, Math.max(0, style.opacity ?? 0.5));
@@ -2168,17 +2206,17 @@ var ImageEditorRenderer = class {
2168
2206
  * screen pixels per natural pixel (the projection scale times the host's CSS scale).
2169
2207
  */
2170
2208
  drawAnts(ctx, selection, scale, offset) {
2171
- let segs = this.outlineCache.get(selection);
2172
- if (!segs) {
2173
- segs = selectionOutline(selection);
2174
- this.outlineCache.set(selection, segs);
2209
+ let paths = this.outlineCache.get(selection);
2210
+ if (!paths) {
2211
+ paths = selectionOutlinePaths(selection);
2212
+ this.outlineCache.set(selection, paths);
2175
2213
  }
2176
- if (!segs.length) return;
2214
+ if (!paths.length) return;
2177
2215
  const px2 = 1 / (scale || 1);
2178
2216
  ctx.beginPath();
2179
- for (let i = 0; i < segs.length; i += 4) {
2180
- ctx.moveTo(segs[i], segs[i + 1]);
2181
- ctx.lineTo(segs[i + 2], segs[i + 3]);
2217
+ for (const p of paths) {
2218
+ ctx.moveTo(p[0], p[1]);
2219
+ for (let i = 2; i < p.length; i += 2) ctx.lineTo(p[i], p[i + 1]);
2182
2220
  }
2183
2221
  ctx.lineWidth = px2;
2184
2222
  ctx.strokeStyle = "#ffffff";
@@ -6003,6 +6041,10 @@ var ImageEditorController = class {
6003
6041
  this.state.imageDimensions = source.dimensions;
6004
6042
  if (!this.samplePixelsOverride) this.samplePixels = null;
6005
6043
  if (prev.width !== source.dimensions.width || prev.height !== source.dimensions.height) {
6044
+ const c = this.state.crop;
6045
+ if (c && c.x === 0 && c.y === 0 && c.width === prev.width && c.height === prev.height) {
6046
+ this.state.crop = { x: 0, y: 0, width: source.dimensions.width, height: source.dimensions.height };
6047
+ }
6006
6048
  this.state.layers = resizeLegacyMasks(this.state.layers, prev, source.dimensions);
6007
6049
  }
6008
6050
  this.dropMismatchedSelection();
@@ -23027,6 +23069,7 @@ export {
23027
23069
  selectionFromTiles,
23028
23070
  selectionModeFromModifiers,
23029
23071
  selectionOutline,
23072
+ selectionOutlinePaths,
23030
23073
  selectionRing,
23031
23074
  selectionToCoverage,
23032
23075
  selectionToPixels,
@@ -5,6 +5,13 @@ import type { SelectionMask } from './mask.cjs';
5
5
  * This is what marching ants are drawn from. Linear in the pixel count.
6
6
  */
7
7
  export declare function selectionOutline(mask: SelectionMask, threshold?: number): Float32Array;
8
+ /**
9
+ * The selection's outline (`selectionOutline`) joined into polylines, `[x0, y0, x1, y1, …]` each,
10
+ * closed loops ending where they start. Marching ants are stroked from these, so the dash runs on
11
+ * along a staircase edge instead of restarting at every one-pixel step (which made diagonal edges
12
+ * flash rather than march). A loop may break where two pixels touch only at a corner.
13
+ */
14
+ export declare function selectionOutlinePaths(mask: SelectionMask, threshold?: number): Float32Array[];
8
15
  export interface QuickMaskStyle {
9
16
  /** Overlay colour, `[r, g, b]` 0..255 (default Photoshop's rubylith red). */
10
17
  color?: readonly [number, number, number];
@@ -5,6 +5,13 @@ import type { SelectionMask } from './mask.js';
5
5
  * This is what marching ants are drawn from. Linear in the pixel count.
6
6
  */
7
7
  export declare function selectionOutline(mask: SelectionMask, threshold?: number): Float32Array;
8
+ /**
9
+ * The selection's outline (`selectionOutline`) joined into polylines, `[x0, y0, x1, y1, …]` each,
10
+ * closed loops ending where they start. Marching ants are stroked from these, so the dash runs on
11
+ * along a staircase edge instead of restarting at every one-pixel step (which made diagonal edges
12
+ * flash rather than march). A loop may break where two pixels touch only at a corner.
13
+ */
14
+ export declare function selectionOutlinePaths(mask: SelectionMask, threshold?: number): Float32Array[];
8
15
  export interface QuickMaskStyle {
9
16
  /** Overlay colour, `[r, g, b]` 0..255 (default Photoshop's rubylith red). */
10
17
  color?: readonly [number, number, number];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor",
3
- "version": "0.218.0",
3
+ "version": "0.218.1",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",