@pixerate/editor 0.96.0 → 0.98.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 (52) hide show
  1. package/THIRD_PARTY_NOTICES.md +4 -2
  2. package/dist/animation-editor/index.cjs +55 -53
  3. package/dist/animation-editor/index.cjs.map +1 -1
  4. package/dist/animation-editor/index.d.cts +3 -2
  5. package/dist/animation-editor/index.d.ts +3 -2
  6. package/dist/animation-editor/index.js +11 -9
  7. package/dist/animation-editor/index.js.map +1 -1
  8. package/dist/chunk-DWGZUSES.cjs +694 -0
  9. package/dist/chunk-DWGZUSES.cjs.map +1 -0
  10. package/dist/chunk-GX332W2C.js +181 -0
  11. package/dist/chunk-GX332W2C.js.map +1 -0
  12. package/dist/{chunk-7MD7C6XQ.cjs → chunk-JF5PXN7W.cjs} +96 -744
  13. package/dist/chunk-JF5PXN7W.cjs.map +1 -0
  14. package/dist/chunk-KKREERP2.js +694 -0
  15. package/dist/chunk-KKREERP2.js.map +1 -0
  16. package/dist/chunk-MUUXNRU4.cjs +181 -0
  17. package/dist/chunk-MUUXNRU4.cjs.map +1 -0
  18. package/dist/{chunk-KFTQ2PKP.js → chunk-WIR377F6.js} +1001 -61
  19. package/dist/chunk-WIR377F6.js.map +1 -0
  20. package/dist/{chunk-T422YCHR.cjs → chunk-WT6ZAWJP.cjs} +1023 -83
  21. package/dist/chunk-WT6ZAWJP.cjs.map +1 -0
  22. package/dist/{chunk-4JLB2USO.js → chunk-YGZNGDIT.js} +45 -693
  23. package/dist/chunk-YGZNGDIT.js.map +1 -0
  24. package/dist/image-editor/index.cjs +1519 -360
  25. package/dist/image-editor/index.cjs.map +1 -1
  26. package/dist/image-editor/index.d.cts +378 -5
  27. package/dist/image-editor/index.d.ts +378 -5
  28. package/dist/image-editor/index.js +1244 -85
  29. package/dist/image-editor/index.js.map +1 -1
  30. package/dist/raster/index.cjs +25 -2
  31. package/dist/raster/index.cjs.map +1 -1
  32. package/dist/raster/index.d.cts +143 -4
  33. package/dist/raster/index.d.ts +143 -4
  34. package/dist/raster/index.js +25 -2
  35. package/dist/{svg-ymShkNtu.d.cts → svg-C9r284x8.d.cts} +6 -9
  36. package/dist/{svg-Dh17jCyA.d.ts → svg-MVKBqwD4.d.ts} +6 -9
  37. package/dist/types-CwMPoRMU.d.cts +40 -0
  38. package/dist/types-CwMPoRMU.d.ts +40 -0
  39. package/dist/vector/index.cjs +490 -554
  40. package/dist/vector/index.cjs.map +1 -1
  41. package/dist/vector/index.d.cts +154 -292
  42. package/dist/vector/index.d.ts +154 -292
  43. package/dist/vector/index.js +91 -155
  44. package/dist/vector/index.js.map +1 -1
  45. package/dist/warp-B8rAguzs.d.ts +165 -0
  46. package/dist/warp-nYs2DPrx.d.cts +165 -0
  47. package/package.json +1 -1
  48. package/skills/pixerate-image-editor/SKILL.md +49 -3
  49. package/dist/chunk-4JLB2USO.js.map +0 -1
  50. package/dist/chunk-7MD7C6XQ.cjs.map +0 -1
  51. package/dist/chunk-KFTQ2PKP.js.map +0 -1
  52. package/dist/chunk-T422YCHR.cjs.map +0 -1
@@ -1,8 +1,21 @@
1
+ import {
2
+ applyHomography,
3
+ boxQuad,
4
+ cornerBounds,
5
+ homographyFrontSide,
6
+ homographyW,
7
+ invertHomography,
8
+ isIdentityHomography,
9
+ multiplyHomographies
10
+ } from "./chunk-GX332W2C.js";
1
11
  import {
2
12
  TILE,
13
+ TiledSurface,
3
14
  blendChannel,
4
15
  blendRgb,
16
+ contentBounds,
5
17
  emptySurface,
18
+ finishTile,
6
19
  isBlendMode,
7
20
  isSeparableBlendMode,
8
21
  mixPigment8,
@@ -10,7 +23,9 @@ import {
10
23
  readRegion,
11
24
  surfaceFromPixels,
12
25
  tileKey,
13
- tilesInRect
26
+ tilesInRect,
27
+ translateSurface,
28
+ writeRegion
14
29
  } from "./chunk-HEW6KDNI.js";
15
30
 
16
31
  // src/raster/blur.ts
@@ -644,14 +659,14 @@ var PatternTile = class {
644
659
  let b = 0;
645
660
  let a = 0;
646
661
  for (let dy = 0; dy < 2; dy++) {
647
- const wy = dy ? fy : 1 - fy;
648
- if (wy === 0) continue;
662
+ const wy2 = dy ? fy : 1 - fy;
663
+ if (wy2 === 0) continue;
649
664
  const yi = ((y0 + dy) % this.h + this.h) % this.h;
650
665
  for (let dx = 0; dx < 2; dx++) {
651
- const wx = dx ? fx : 1 - fx;
652
- if (wx === 0) continue;
666
+ const wx2 = dx ? fx : 1 - fx;
667
+ if (wx2 === 0) continue;
653
668
  const xi = ((x0 + dx) % this.w + this.w) % this.w;
654
- const k = wx * wy;
669
+ const k = wx2 * wy2;
655
670
  const i = (yi * this.w + xi) * 4;
656
671
  r += this.px[i] * k;
657
672
  g += this.px[i + 1] * k;
@@ -1080,11 +1095,18 @@ function boxLine(src, x0, dst, bw) {
1080
1095
  }
1081
1096
  }
1082
1097
  var BLUR_EPS = 1e-7;
1098
+ var BLUR_FIX = 2 ** 32;
1083
1099
  function tentLine(src, dst, bw, mid) {
1084
1100
  const [r] = boxGeom(bw);
1085
- boxLine(src, -r, mid.subarray(0, src.length + 2 * r), bw);
1086
- boxLine(mid.subarray(0, src.length + 2 * r), r, dst, bw);
1087
- for (let i = 0; i < dst.length; i++) if (Math.abs(dst[i]) < BLUR_EPS) dst[i] = 0;
1101
+ const m = mid.subarray(0, src.length + 2 * r);
1102
+ for (let i = 0; i < src.length; i++) src[i] = Math.round(src[i] * BLUR_FIX);
1103
+ boxLine(src, -r, m, bw);
1104
+ for (let i = 0; i < m.length; i++) m[i] = Math.round(m[i]);
1105
+ boxLine(m, r, dst, bw);
1106
+ for (let i = 0; i < dst.length; i++) {
1107
+ const v = dst[i] / BLUR_FIX;
1108
+ dst[i] = Math.abs(v) < BLUR_EPS ? 0 : v;
1109
+ }
1088
1110
  }
1089
1111
  function tent(m, w) {
1090
1112
  if (!Number.isFinite(w) || m.v.length === 0) return;
@@ -1386,25 +1408,41 @@ function effectsAreLocal(items) {
1386
1408
  }
1387
1409
  });
1388
1410
  }
1411
+ function shapeBounds(shape, rect) {
1412
+ const { width: w, height: h } = rect;
1413
+ const rowHit = (y) => {
1414
+ for (let x = 0, i = y * w; x < w; x++, i++) if (shape[i] > INSIDE_EPS) return true;
1415
+ return false;
1416
+ };
1417
+ let y0 = 0;
1418
+ while (y0 < h && !rowHit(y0)) y0++;
1419
+ if (y0 === h) return null;
1420
+ let y1 = h - 1;
1421
+ while (!rowHit(y1)) y1--;
1422
+ let x0 = w;
1423
+ let x1 = -1;
1424
+ for (let y = y0; y <= y1; y++) {
1425
+ const o = y * w;
1426
+ for (let x = 0; x < x0; x++) {
1427
+ if (shape[o + x] > INSIDE_EPS) {
1428
+ x0 = x;
1429
+ break;
1430
+ }
1431
+ }
1432
+ for (let x = w - 1; x > x1; x--) {
1433
+ if (shape[o + x] > INSIDE_EPS) {
1434
+ x1 = x;
1435
+ break;
1436
+ }
1437
+ }
1438
+ }
1439
+ return { x: rect.x + x0, y: rect.y + y0, width: x1 - x0 + 1, height: y1 - y0 + 1 };
1440
+ }
1389
1441
  function buildEffectMaps(items, shape, rect, ctx) {
1390
1442
  const w = rect.width;
1391
1443
  const h = rect.height;
1392
1444
  const s = { w, h, v: shape };
1393
- let x0 = Infinity;
1394
- let y0 = Infinity;
1395
- let x1 = -Infinity;
1396
- let y1 = -Infinity;
1397
- for (let y = 0; y < h; y++) {
1398
- for (let x = 0; x < w; x++) {
1399
- if (shape[y * w + x] > INSIDE_EPS) {
1400
- if (x < x0) x0 = x;
1401
- if (x > x1) x1 = x;
1402
- if (y < y0) y0 = y;
1403
- if (y > y1) y1 = y;
1404
- }
1405
- }
1406
- }
1407
- const bounds = x0 === Infinity ? null : { x: rect.x + x0, y: rect.y + y0, width: x1 - x0 + 1, height: y1 - y0 + 1 };
1445
+ const bounds = shapeBounds(shape, rect);
1408
1446
  const anchor = bounds ? { x: bounds.x, y: bounds.y } : { x: rect.x, y: rect.y };
1409
1447
  const per = [];
1410
1448
  const bevelPaint = [];
@@ -1452,6 +1490,81 @@ function buildEffectMaps(items, shape, rect, ctx) {
1452
1490
  for (const d of [din, dout, doutExact]) if (d) bytes += d.byteLength;
1453
1491
  return { rect, items, bounds, shape, per, bevelPaint, din, dout, doutExact, frames, bytes };
1454
1492
  }
1493
+ var rectArea = (r) => r.width * r.height;
1494
+ function clipRect(a, b) {
1495
+ const x0 = Math.max(a.x, b.x);
1496
+ const y0 = Math.max(a.y, b.y);
1497
+ const x1 = Math.min(a.x + a.width, b.x + b.width);
1498
+ const y1 = Math.min(a.y + a.height, b.y + b.height);
1499
+ return x1 > x0 && y1 > y0 ? { x: x0, y: y0, width: x1 - x0, height: y1 - y0 } : null;
1500
+ }
1501
+ var growRect = (r, m) => ({ x: r.x - m, y: r.y - m, width: r.width + 2 * m, height: r.height + 2 * m });
1502
+ function unionRect(a, b) {
1503
+ const x0 = Math.min(a.x, b.x);
1504
+ const y0 = Math.min(a.y, b.y);
1505
+ return { x: x0, y: y0, width: Math.max(a.x + a.width, b.x + b.width) - x0, height: Math.max(a.y + a.height, b.y + b.height) - y0 };
1506
+ }
1507
+ function rectMinus(r, cut) {
1508
+ const i = clipRect(r, cut);
1509
+ if (!i) return [r];
1510
+ const parts = [
1511
+ { x: r.x, y: r.y, width: r.width, height: i.y - r.y },
1512
+ { x: r.x, y: i.y + i.height, width: r.width, height: r.y + r.height - (i.y + i.height) },
1513
+ { x: r.x, y: i.y, width: i.x - r.x, height: i.height },
1514
+ { x: i.x + i.width, y: i.y, width: r.x + r.width - (i.x + i.width), height: i.height }
1515
+ ];
1516
+ return parts.filter((p) => p.width > 0 && p.height > 0);
1517
+ }
1518
+ function pasteMap(dst, to, src, from, a) {
1519
+ for (let y = a.y; y < a.y + a.height; y++) {
1520
+ const s = (y - from.y) * from.width + (a.x - from.x);
1521
+ dst.set(src.subarray(s, s + a.width), (y - to.y) * to.width + (a.x - to.x));
1522
+ }
1523
+ }
1524
+ function patchEffectMaps(prev, items, rect, changed, margin, shapeOver, ctx) {
1525
+ if (!effectsAreLocal(items) || rectArea(rect) <= 0) return null;
1526
+ let areas = [];
1527
+ for (const c of changed) {
1528
+ const a = clipRect(growRect(c, margin), rect);
1529
+ if (a) areas.push(a);
1530
+ }
1531
+ areas.push(...rectMinus(rect, prev.rect));
1532
+ for (let merged = true; merged; ) {
1533
+ merged = false;
1534
+ for (let i = 0; i < areas.length && !merged; i++) {
1535
+ for (let j = i + 1; j < areas.length; j++) {
1536
+ if (clipRect(growRect(areas[i], 2 * margin), areas[j])) {
1537
+ areas[i] = unionRect(areas[i], areas[j]);
1538
+ areas.splice(j, 1);
1539
+ merged = true;
1540
+ break;
1541
+ }
1542
+ }
1543
+ }
1544
+ }
1545
+ const windows = areas.map((a) => clipRect(growRect(a, margin), rect));
1546
+ if (windows.reduce((n, w) => n + rectArea(w), 0) > rectArea(rect) / 2) return null;
1547
+ const same = rect.x === prev.rect.x && rect.y === prev.rect.y && rect.width === prev.rect.width && rect.height === prev.rect.height;
1548
+ const take = (v) => same ? v : crop(v, prev.rect, rect, 0);
1549
+ const shape = take(prev.shape);
1550
+ const per = prev.per.map((maps) => maps.map(take));
1551
+ const din = prev.din && take(prev.din);
1552
+ const dout = prev.dout && take(prev.dout);
1553
+ const doutExact = prev.doutExact && take(prev.doutExact);
1554
+ areas.forEach((a, k) => {
1555
+ const w = windows[k];
1556
+ const part = buildEffectMaps(items, shapeOver(w), w, ctx);
1557
+ pasteMap(shape, rect, part.shape, w, a);
1558
+ per.forEach((maps, i) => maps.forEach((m, j) => pasteMap(m, rect, part.per[i][j], w, a)));
1559
+ if (din) pasteMap(din, rect, part.din, w, a);
1560
+ if (dout) pasteMap(dout, rect, part.dout, w, a);
1561
+ if (doutExact) pasteMap(doutExact, rect, part.doutExact, w, a);
1562
+ });
1563
+ let bytes = shape.byteLength;
1564
+ for (const maps of per) for (const m of maps) bytes += m.byteLength;
1565
+ for (const d of [din, dout, doutExact]) if (d) bytes += d.byteLength;
1566
+ return { rect, items, bounds: shapeBounds(shape, rect), shape, per, bevelPaint: prev.bevelPaint, din, dout, doutExact, frames: /* @__PURE__ */ new Map(), bytes };
1567
+ }
1455
1568
  var rgbScratchB = [0, 0, 0];
1456
1569
  var rgbScratchS = [0, 0, 0];
1457
1570
  var rgbScratchM = [0, 0, 0];
@@ -1650,15 +1763,15 @@ function gradientLayout(style, angle, scale, offset, frame) {
1650
1763
  const half = chordOf(a) * Math.max(scale, 1e-3) / 2;
1651
1764
  const dx = Math.cos(a) * half;
1652
1765
  const dy = -Math.sin(a) * half;
1653
- const snap = (v2) => Math.floor(v2 + 1e-4);
1654
- const end = [snap(cx + dx), snap(cy + dy)];
1766
+ const snap2 = (v2) => Math.floor(v2 + 1e-4);
1767
+ const end = [snap2(cx + dx), snap2(cy + dy)];
1655
1768
  let start;
1656
1769
  let mid;
1657
1770
  if (style === "reflected") {
1658
1771
  start = [cx, cy];
1659
1772
  mid = [cx, cy];
1660
1773
  } else {
1661
- start = [snap(cx - dx), snap(cy - dy)];
1774
+ start = [snap2(cx - dx), snap2(cy - dy)];
1662
1775
  mid = [(start[0] + end[0]) / 2, (start[1] + end[1]) / 2];
1663
1776
  }
1664
1777
  const v = [end[0] - start[0], end[1] - start[1]];
@@ -2084,8 +2197,11 @@ var RasterCompositor = class {
2084
2197
  patternTiles = /* @__PURE__ */ new WeakMap();
2085
2198
  reachCache = /* @__PURE__ */ new WeakMap();
2086
2199
  fxContext = null;
2087
- lastStats = { tiles: 0, cached: 0, stepsRun: 0 };
2200
+ lastStats = { tiles: 0, cached: 0, stepsRun: 0, effectsBuilt: 0, effectsPatched: 0 };
2088
2201
  hint = -1;
2202
+ /** The `dirty` rectangles of the current call, as passed. */
2203
+ callDirty = null;
2204
+ callStats = this.lastStats;
2089
2205
  /** Drops every cached tile and plane. */
2090
2206
  clear() {
2091
2207
  this.tiles.clear();
@@ -2133,7 +2249,9 @@ var RasterCompositor = class {
2133
2249
  const steps = this.buildSteps(layers);
2134
2250
  this.hint = options.checkpointLayerId ? this.stepIndexOf(steps, options.checkpointLayerId) : -1;
2135
2251
  this.stamp++;
2136
- const stats = { tiles: 0, cached: 0, stepsRun: 0 };
2252
+ this.callDirty = options.dirty ?? null;
2253
+ const stats = { tiles: 0, cached: 0, stepsRun: 0, effectsBuilt: 0, effectsPatched: 0 };
2254
+ this.callStats = stats;
2137
2255
  const grow = options.dirty ? this.effectsReach(layers) : 0;
2138
2256
  const dirty = grow > 0 ? options.dirty.map((d) => inflateRect(d, grow)) : options.dirty;
2139
2257
  for (const { tx, ty } of tilesInRect(rect)) {
@@ -2149,8 +2267,10 @@ var RasterCompositor = class {
2149
2267
  tile(layers, tx, ty, sources) {
2150
2268
  this.setSources(sources);
2151
2269
  this.stamp++;
2152
- const stats = { tiles: 1, cached: 0, stepsRun: 0 };
2270
+ const stats = { tiles: 1, cached: 0, stepsRun: 0, effectsBuilt: 0, effectsPatched: 0 };
2271
+ this.callStats = stats;
2153
2272
  this.hint = -1;
2273
+ this.callDirty = null;
2154
2274
  const data = this.compositeTile(layers, this.buildSteps(layers), tx, ty, stats);
2155
2275
  this.lastStats = stats;
2156
2276
  this.evict();
@@ -2220,7 +2340,8 @@ var RasterCompositor = class {
2220
2340
  let first = 0;
2221
2341
  if (entry) {
2222
2342
  while (first < sigs.length && first < entry.sigs.length && sameSig(entry.sigs[first], sigs[first])) first++;
2223
- if (first === sigs.length && first === entry.sigs.length) {
2343
+ if (first === sigs.length && first === entry.sigs.length || this.changedOnlyNearby(steps, sigs, entry, dirty)) {
2344
+ entry.sigs = sigs;
2224
2345
  entry.stamp = this.stamp;
2225
2346
  stats.cached++;
2226
2347
  return entry.result;
@@ -2285,6 +2406,20 @@ var RasterCompositor = class {
2285
2406
  this.cacheBytes += this.entryBytes(next) - prevBytes;
2286
2407
  return result;
2287
2408
  }
2409
+ /**
2410
+ * Whether a tile with no part in this call's (grown) `dirty` rectangles changed only in what local
2411
+ * effects read beyond it: every changed step is a pixel layer with local effects whose changes stay
2412
+ * inside `dirty` (and so are farther than its margin), which leaves the tile's pixels as they were.
2413
+ */
2414
+ changedOnlyNearby(steps, sigs, entry, dirty) {
2415
+ if (this.callDirty === null || dirty || entry.stamp !== this.stamp - 1 || sigs.length !== entry.sigs.length) return false;
2416
+ return steps.every((step, i) => {
2417
+ if (sameSig(entry.sigs[i], sigs[i])) return true;
2418
+ if (step.kind !== "content" || entry.sigs[i].key !== sigs[i].key) return false;
2419
+ const { layer, clipped } = step;
2420
+ return this.hasEffects(layer) && effectsAreLocal(activeEffectItems(layer.effects)) && this.dirtyCoversShape(layer) && clipped.every((c) => this.dirtyCoversShape(c));
2421
+ });
2422
+ }
2288
2423
  entryBytes(e) {
2289
2424
  let n = e.result === ZERO_TILE ? 0 : TILE_BYTES;
2290
2425
  for (const cp of e.checkpoints.values()) n += cp.data.byteLength;
@@ -2415,14 +2550,20 @@ var RasterCompositor = class {
2415
2550
  if (!b) return { x: 0, y: 0, width: 0, height: 0 };
2416
2551
  return intersectRect({ x: b.x + dx - m, y: b.y + dy - m, width: b.width + 2 * m, height: b.height + 2 * m }, canvas);
2417
2552
  }
2553
+ /** The signature of a layer's shape (its surface tiles and mask refs) per tile of `region`. */
2554
+ shapeSigs(layer, region) {
2555
+ const sigs = /* @__PURE__ */ new Map();
2556
+ if (region.width <= 0 || region.height <= 0) return sigs;
2557
+ const steps = isGroup(layer) ? this.buildSteps(layer.children ?? []) : null;
2558
+ for (const t of tilesInRect(region)) {
2559
+ sigs.set(tileKey(t.tx, t.ty), steps ? this.plainGroupSig(layer, steps, t.tx, t.ty) : this.plainContentSig(layer, t.tx, t.ty));
2560
+ }
2561
+ return sigs;
2562
+ }
2418
2563
  /** The surface tiles (and mask refs) a layer's shape reads over `region`. */
2419
2564
  shapeRefs(layer, region) {
2420
2565
  const refs = [];
2421
- if (region.width <= 0 || region.height <= 0) return refs;
2422
- for (const t of tilesInRect(region)) {
2423
- const sig = isGroup(layer) ? this.plainGroupSig(layer, this.buildSteps(layer.children ?? []), t.tx, t.ty) : this.plainContentSig(layer, t.tx, t.ty);
2424
- for (const r of sig.refs) refs.push(r);
2425
- }
2566
+ for (const sig of this.shapeSigs(layer, region).values()) for (const r of sig.refs) refs.push(r);
2426
2567
  return refs;
2427
2568
  }
2428
2569
  lightKey() {
@@ -2448,18 +2589,28 @@ var RasterCompositor = class {
2448
2589
  const memo = this.effectMemo.get(layer);
2449
2590
  if (memo && memo.stamp === this.stamp) return memo.maps;
2450
2591
  const region = this.effectRegion(layer);
2451
- const key = `${this.propsKey(layer)}${this.lightKey()}|${region.x},${region.y},${region.width},${region.height}`;
2452
- const refs = this.shapeRefs(layer, region);
2592
+ const key = this.propsKey(layer) + this.lightKey();
2593
+ const tiles = this.shapeSigs(layer, region);
2453
2594
  const hit = this.effectCache.get(layer.id);
2454
- let maps;
2455
- if (hit && hit.key === key && sameSig({ key, refs: hit.refs }, { key, refs })) {
2456
- this.effectCache.delete(layer.id);
2457
- this.effectCache.set(layer.id, hit);
2458
- maps = hit.maps;
2595
+ this.effectCache.delete(layer.id);
2596
+ let entry;
2597
+ if (hit && hit.key === key && sameRect(hit.region, region) && [...tiles].every(([k, sig]) => sameSig(hit.tiles.get(k), sig))) {
2598
+ entry = hit;
2459
2599
  } else {
2460
- maps = buildEffectMaps(activeEffectItems(layer.effects), this.layerShape(layer, region), region, this.effectContext());
2461
- this.effectCache.delete(layer.id);
2462
- this.effectCache.set(layer.id, { key, refs, maps });
2600
+ const items = activeEffectItems(layer.effects);
2601
+ const ctx = this.effectContext();
2602
+ let maps = null;
2603
+ if (hit && hit.key === key && items.every((e) => effectReach(e) <= MAX_EFFECT_REACH)) {
2604
+ const changed = this.shapeChanges(layer, hit, tiles, region);
2605
+ maps = patchEffectMaps(hit.maps, items, region, changed, effectsMargin(layer.effects), (r) => this.layerShape(layer, r), ctx);
2606
+ }
2607
+ if (maps) this.callStats.effectsPatched++;
2608
+ else this.callStats.effectsBuilt++;
2609
+ entry = { key, region, tiles, maps: maps ?? buildEffectMaps(items, this.layerShape(layer, region), region, ctx), stamp: 0 };
2610
+ }
2611
+ entry.stamp = this.stamp;
2612
+ this.effectCache.set(layer.id, entry);
2613
+ if (entry !== hit) {
2463
2614
  let bytes = 0;
2464
2615
  for (const e of this.effectCache.values()) bytes += e.maps.bytes;
2465
2616
  while (this.effectCache.size > 1 && (this.effectCache.size > 32 || bytes > this.maxCacheBytes / 2)) {
@@ -2468,8 +2619,42 @@ var RasterCompositor = class {
2468
2619
  this.effectCache.delete(oldest);
2469
2620
  }
2470
2621
  }
2471
- this.effectMemo.set(layer, { stamp: this.stamp, maps });
2472
- return maps;
2622
+ this.effectMemo.set(layer, { stamp: this.stamp, maps: entry.maps });
2623
+ return entry.maps;
2624
+ }
2625
+ /**
2626
+ * Where a layer's shape may differ from the one `prev` was built from: the tiles whose signature
2627
+ * changed (a tile new to the region that reads nothing did not), narrowed to this call's `dirty`
2628
+ * rectangles when `prev` is from the previous call and they cover every change of the shape.
2629
+ */
2630
+ shapeChanges(layer, prev, tiles, region) {
2631
+ const out = [];
2632
+ for (const { tx, ty } of tilesInRect(region)) {
2633
+ const sig = tiles.get(tileKey(tx, ty));
2634
+ const old = prev.tiles.get(tileKey(tx, ty));
2635
+ if (old ? sameSig(old, sig) : sig.refs.every((r2) => r2 === null || r2 === 0)) continue;
2636
+ const r = intersectRect({ x: tx * TILE, y: ty * TILE, width: TILE, height: TILE }, region);
2637
+ if (r.width > 0 && r.height > 0) out.push(r);
2638
+ }
2639
+ const dirty = this.callDirty;
2640
+ if (!dirty || prev.stamp !== this.stamp - 1 || !this.dirtyCoversShape(layer)) return out;
2641
+ const narrowed = [];
2642
+ for (const r of out) {
2643
+ for (const d of dirty) {
2644
+ const a = intersectRect(r, { x: Math.floor(d.x), y: Math.floor(d.y), width: Math.ceil(d.x + d.width) - Math.floor(d.x), height: Math.ceil(d.y + d.height) - Math.floor(d.y) });
2645
+ if (a.width > 0 && a.height > 0) narrowed.push(a);
2646
+ }
2647
+ }
2648
+ return narrowed;
2649
+ }
2650
+ /**
2651
+ * Whether a change to a layer stays inside the `dirty` rectangles that name it: a pixel layer
2652
+ * without filters or a feathered or parametric mask (those spread a change beyond it).
2653
+ */
2654
+ dirtyCoversShape(layer) {
2655
+ if (!isPixel(layer) || this.profile.pixelFilters?.(layer)) return false;
2656
+ const mask = layer.mask;
2657
+ return !mask || mask.enabled === false || !mask.source && !((mask.feather ?? 0) > 0);
2473
2658
  }
2474
2659
  /** A layer's alpha over `r` after its mask (0..1, quantised to bytes as tiles see it). */
2475
2660
  layerShape(layer, r) {
@@ -2796,6 +2981,9 @@ function copyRect(data, r, a) {
2796
2981
  function pasteRect(data, r, part, a) {
2797
2982
  for (let y = 0; y < a.height; y++) data.set(part.subarray(y * a.width * 4, (y + 1) * a.width * 4), ((a.y - r.y + y) * r.width + (a.x - r.x)) * 4);
2798
2983
  }
2984
+ function sameRect(a, b) {
2985
+ return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
2986
+ }
2799
2987
  function inflateRect(r, m) {
2800
2988
  return { x: r.x - m, y: r.y - m, width: r.width + 2 * m, height: r.height + 2 * m };
2801
2989
  }
@@ -2813,6 +3001,747 @@ function changedTiles(a, b) {
2813
3001
  return out;
2814
3002
  }
2815
3003
 
3004
+ // src/raster/transform.ts
3005
+ var INTERPOLATIONS = ["nearest", "bilinear", "bicubic"];
3006
+ function isInterpolation(value) {
3007
+ return typeof value === "string" && INTERPOLATIONS.includes(value);
3008
+ }
3009
+ var MAX_TRANSFORM_PIXELS = 1 << 27;
3010
+ var TransformTooLargeError = class extends Error {
3011
+ constructor(message) {
3012
+ super(message);
3013
+ this.name = "TransformTooLargeError";
3014
+ }
3015
+ };
3016
+ var KEYS_A = -0.75;
3017
+ function keysWeight(x) {
3018
+ x = Math.abs(x);
3019
+ if (x < 1) return ((KEYS_A + 2) * x - (KEYS_A + 3)) * x * x + 1;
3020
+ if (x < 2) return KEYS_A * (((x - 5) * x + 8) * x - 4);
3021
+ return 0;
3022
+ }
3023
+ var snap = (t) => Math.abs(t - Math.round(t)) < 1e-7 ? Math.round(t) : t;
3024
+ var MAX_TAPS = 20;
3025
+ var tapI0 = 0;
3026
+ var tapN = 0;
3027
+ function cubicTaps(u, s, w) {
3028
+ if (s <= 1.0001) {
3029
+ const f = Math.floor(u);
3030
+ const t = u - f;
3031
+ w[0] = keysWeight(1 + t);
3032
+ w[1] = keysWeight(t);
3033
+ w[2] = keysWeight(1 - t);
3034
+ w[3] = keysWeight(2 - t);
3035
+ tapI0 = f - 1;
3036
+ tapN = 4;
3037
+ return;
3038
+ }
3039
+ const r = 2 * s;
3040
+ const i0 = Math.ceil(u - r);
3041
+ const n = Math.min(MAX_TAPS, Math.floor(u + r) - i0 + 1);
3042
+ let sum = 0;
3043
+ for (let k = 0; k < n; k++) {
3044
+ const v = keysWeight((i0 + k - u) / s);
3045
+ w[k] = v;
3046
+ sum += v;
3047
+ }
3048
+ if (sum !== 0) for (let k = 0; k < n; k++) w[k] /= sum;
3049
+ tapI0 = i0;
3050
+ tapN = n;
3051
+ }
3052
+ function intersect(a, b) {
3053
+ const x = Math.max(a.x, b.x);
3054
+ const y = Math.max(a.y, b.y);
3055
+ const x1 = Math.min(a.x + a.width, b.x + b.width);
3056
+ const y1 = Math.min(a.y + a.height, b.y + b.height);
3057
+ return x1 > x && y1 > y ? { x, y, width: x1 - x, height: y1 - y } : null;
3058
+ }
3059
+ function hasTilesIn(src, r) {
3060
+ for (const { tx, ty } of tilesInRect(r)) if (src.getTile(tx, ty)) return true;
3061
+ return false;
3062
+ }
3063
+ function readWindow(src, srcRect, rect) {
3064
+ const n = src.channels === 4 ? 4 : 2;
3065
+ const data = new Float32Array(rect.width * rect.height * n);
3066
+ const win = { x: rect.x, y: rect.y, w: rect.width, h: rect.height, n, data };
3067
+ const r = intersect(rect, srcRect);
3068
+ if (!r) return win;
3069
+ const px = readRegion(src, r);
3070
+ for (let y = 0; y < r.height; y++) {
3071
+ let s = y * r.width * src.channels;
3072
+ let d = ((r.y - rect.y + y) * rect.width + (r.x - rect.x)) * n;
3073
+ if (n === 4) {
3074
+ for (let x = 0; x < r.width; x++, s += 4, d += 4) {
3075
+ const a = px[s + 3] / 255;
3076
+ data[d] = px[s] * a;
3077
+ data[d + 1] = px[s + 1] * a;
3078
+ data[d + 2] = px[s + 2] * a;
3079
+ data[d + 3] = a;
3080
+ }
3081
+ } else {
3082
+ for (let x = 0; x < r.width; x++, s++, d += 2) {
3083
+ data[d] = px[s];
3084
+ data[d + 1] = 1;
3085
+ }
3086
+ }
3087
+ }
3088
+ return win;
3089
+ }
3090
+ var scratch = new Uint8ClampedArray(4);
3091
+ var scratchWord = new Uint32Array(scratch.buffer);
3092
+ var wx = new Float64Array(MAX_TAPS);
3093
+ var wy = new Float64Array(MAX_TAPS);
3094
+ function sample(win, interp, u, v, su, sv, acc) {
3095
+ const n = win.n;
3096
+ const d = win.data;
3097
+ for (let c = 0; c < n; c++) acc[c] = 0;
3098
+ if (interp === "nearest") {
3099
+ const ix = Math.floor(u + 0.5) - win.x;
3100
+ const iy = Math.floor(v + 0.5) - win.y;
3101
+ if (ix < 0 || iy < 0 || ix >= win.w || iy >= win.h) return;
3102
+ const o = (iy * win.w + ix) * n;
3103
+ for (let c = 0; c < n; c++) acc[c] = d[o + c];
3104
+ return;
3105
+ }
3106
+ if (interp === "bilinear") {
3107
+ const fx = Math.floor(u);
3108
+ const fy = Math.floor(v);
3109
+ const tx = u - fx;
3110
+ const ty = v - fy;
3111
+ for (let j = 0; j < 2; j++) {
3112
+ const iy = fy + j - win.y;
3113
+ if (iy < 0 || iy >= win.h) continue;
3114
+ const kyw = j ? ty : 1 - ty;
3115
+ if (!kyw) continue;
3116
+ for (let i = 0; i < 2; i++) {
3117
+ const ix = fx + i - win.x;
3118
+ if (ix < 0 || ix >= win.w) continue;
3119
+ const k = (i ? tx : 1 - tx) * kyw;
3120
+ if (!k) continue;
3121
+ const o = (iy * win.w + ix) * n;
3122
+ for (let c = 0; c < n; c++) acc[c] += d[o + c] * k;
3123
+ }
3124
+ }
3125
+ return;
3126
+ }
3127
+ u = snap(u);
3128
+ v = snap(v);
3129
+ cubicTaps(u, su, wx);
3130
+ const x0 = tapI0 - win.x;
3131
+ const nu = tapN;
3132
+ cubicTaps(v, sv, wy);
3133
+ const y0 = tapI0 - win.y;
3134
+ const nv = tapN;
3135
+ const W = win.w;
3136
+ if (n === 4 && nu === 4 && nv === 4 && x0 >= 0 && y0 >= 0 && x0 + 4 <= W && y0 + 4 <= win.h) {
3137
+ let r = 0;
3138
+ let g = 0;
3139
+ let b = 0;
3140
+ let a = 0;
3141
+ const w0 = wx[0];
3142
+ const w1 = wx[1];
3143
+ const w2 = wx[2];
3144
+ const w3 = wx[3];
3145
+ for (let j = 0; j < 4; j++) {
3146
+ const kyw = wy[j];
3147
+ if (kyw === 0) continue;
3148
+ let o = ((y0 + j) * W + x0) * 4;
3149
+ const r0 = d[o] * w0 + d[o + 4] * w1 + d[o + 8] * w2 + d[o + 12] * w3;
3150
+ const g0 = d[o + 1] * w0 + d[o + 5] * w1 + d[o + 9] * w2 + d[o + 13] * w3;
3151
+ const b0 = d[o + 2] * w0 + d[o + 6] * w1 + d[o + 10] * w2 + d[o + 14] * w3;
3152
+ const a0 = d[o + 3] * w0 + d[o + 7] * w1 + d[o + 11] * w2 + d[o + 15] * w3;
3153
+ r += r0 * kyw;
3154
+ g += g0 * kyw;
3155
+ b += b0 * kyw;
3156
+ a += a0 * kyw;
3157
+ }
3158
+ acc[0] = r;
3159
+ acc[1] = g;
3160
+ acc[2] = b;
3161
+ acc[3] = a;
3162
+ return;
3163
+ }
3164
+ for (let j = 0; j < nv; j++) {
3165
+ const kyw = wy[j];
3166
+ if (kyw === 0) continue;
3167
+ const iy = y0 + j;
3168
+ if (iy < 0 || iy >= win.h) continue;
3169
+ const row = iy * W;
3170
+ for (let i = 0; i < nu; i++) {
3171
+ const k = wx[i] * kyw;
3172
+ if (k === 0) continue;
3173
+ const ix = x0 + i;
3174
+ if (ix < 0 || ix >= W) continue;
3175
+ const o = (row + ix) * n;
3176
+ if (n === 4) {
3177
+ acc[0] += d[o] * k;
3178
+ acc[1] += d[o + 1] * k;
3179
+ acc[2] += d[o + 2] * k;
3180
+ acc[3] += d[o + 3] * k;
3181
+ } else {
3182
+ acc[0] += d[o] * k;
3183
+ acc[1] += d[o + 1] * k;
3184
+ }
3185
+ }
3186
+ }
3187
+ }
3188
+ function store(out, o, acc, ch, def) {
3189
+ if (ch === 4) {
3190
+ const al = acc[3] > 1 ? 1 : acc[3];
3191
+ if (!(al > 1e-6)) return false;
3192
+ out[o] = acc[0] / al;
3193
+ out[o + 1] = acc[1] / al;
3194
+ out[o + 2] = acc[2] / al;
3195
+ out[o + 3] = al * 255;
3196
+ return true;
3197
+ }
3198
+ const inside = acc[1] > 1 ? 1 : acc[1];
3199
+ if (!(inside > 1e-6)) return false;
3200
+ out[o] = acc[0] + def * (1 - inside);
3201
+ return true;
3202
+ }
3203
+ function boxReduce(src, srcRect, kx, ky) {
3204
+ const ch = src.channels;
3205
+ const rx0 = Math.floor(srcRect.x / kx);
3206
+ const ry0 = Math.floor(srcRect.y / ky);
3207
+ const rx1 = Math.ceil((srcRect.x + srcRect.width) / kx);
3208
+ const ry1 = Math.ceil((srcRect.y + srcRect.height) / ky);
3209
+ const rw = rx1 - rx0;
3210
+ const rect = { x: rx0, y: ry0, width: rw, height: ry1 - ry0 };
3211
+ let out = emptySurface(ch, src.defaultValue);
3212
+ const rowsPer = Math.max(1, Math.floor(TILE / ky));
3213
+ const area = kx * ky;
3214
+ for (let ry = ry0; ry < ry1; ry += rowsPer) {
3215
+ const rows = Math.min(rowsPer, ry1 - ry);
3216
+ const strip = { x: rx0 * kx, y: ry * ky, width: rw * kx, height: rows * ky };
3217
+ const px = readRegion(src, strip);
3218
+ const red = new Uint8ClampedArray(rw * rows * ch);
3219
+ for (let j = 0; j < rows; j++) {
3220
+ for (let i = 0; i < rw; i++) {
3221
+ if (ch === 4) {
3222
+ let a = 0;
3223
+ let r = 0;
3224
+ let g = 0;
3225
+ let b = 0;
3226
+ for (let y = 0; y < ky; y++) {
3227
+ let s = ((j * ky + y) * strip.width + i * kx) * 4;
3228
+ for (let x = 0; x < kx; x++, s += 4) {
3229
+ const al = px[s + 3];
3230
+ a += al;
3231
+ r += px[s] * al;
3232
+ g += px[s + 1] * al;
3233
+ b += px[s + 2] * al;
3234
+ }
3235
+ }
3236
+ const o = (j * rw + i) * 4;
3237
+ if (a > 0) {
3238
+ red[o] = r / a;
3239
+ red[o + 1] = g / a;
3240
+ red[o + 2] = b / a;
3241
+ red[o + 3] = a / area;
3242
+ }
3243
+ } else {
3244
+ let v = 0;
3245
+ for (let y = 0; y < ky; y++) {
3246
+ const s = (j * ky + y) * strip.width + i * kx;
3247
+ for (let x = 0; x < kx; x++) v += px[s + x];
3248
+ }
3249
+ red[j * rw + i] = v / area;
3250
+ }
3251
+ }
3252
+ }
3253
+ out = writeRegion(out, { x: rx0, y: ry, width: rw, height: rows }, red);
3254
+ }
3255
+ return { surface: out, rect };
3256
+ }
3257
+ function reduceSurface(src, factor) {
3258
+ const k = Math.max(1, Math.floor(factor));
3259
+ const b = contentBounds(src);
3260
+ if (k === 1 || !b) return src;
3261
+ return boxReduce(src, b, k, k).surface;
3262
+ }
3263
+ function inverseStretch(inv, X, Y) {
3264
+ const w = inv[6] * X + inv[7] * Y + inv[8];
3265
+ if (!(Math.abs(w) > 1e-12)) return [1, 1];
3266
+ const U = (inv[0] * X + inv[1] * Y + inv[2]) / w;
3267
+ const V = (inv[3] * X + inv[4] * Y + inv[5]) / w;
3268
+ const ux = (inv[0] - U * inv[6]) / w;
3269
+ const uy = (inv[1] - U * inv[7]) / w;
3270
+ const vx = (inv[3] - V * inv[6]) / w;
3271
+ const vy = (inv[4] - V * inv[7]) / w;
3272
+ return [Math.hypot(ux, uy), Math.hypot(vx, vy)];
3273
+ }
3274
+ var reduction = (s) => s >= 2 && Number.isFinite(s) ? Math.min(64, 2 ** Math.floor(Math.log2(s))) : 1;
3275
+ function normalizeStep(step) {
3276
+ if (!step || !(step > 1)) return 1;
3277
+ return Math.min(64, 2 ** Math.floor(Math.log2(step)));
3278
+ }
3279
+ function checkBudget(dst, what = "result") {
3280
+ if (dst.width * dst.height > MAX_TRANSFORM_PIXELS) {
3281
+ throw new TransformTooLargeError(
3282
+ `The transformed ${what} would be ${dst.width} \xD7 ${dst.height} px, more than the ${MAX_TRANSFORM_PIXELS} px limit; use a smaller scale.`
3283
+ );
3284
+ }
3285
+ }
3286
+ function assemble(src, tiles) {
3287
+ return new TiledSurface(src.channels, tiles, src.defaultValue);
3288
+ }
3289
+ function transformedBounds(source, h) {
3290
+ if (!(source.width > 0 && source.height > 0) || !invertHomography(h)) return null;
3291
+ const pts = boxQuad(source).map((p) => applyHomography(h, p));
3292
+ if (pts.some((p) => !p)) return null;
3293
+ return cornerBounds(pts);
3294
+ }
3295
+ function transformSurface(src, h, options = {}) {
3296
+ const interp = options.interpolation ?? "bicubic";
3297
+ const ch = src.channels;
3298
+ const def = src.defaultValue;
3299
+ const empty = emptySurface(ch, def);
3300
+ const given = options.source;
3301
+ const step = normalizeStep(options.step);
3302
+ if (isIdentityHomography(h, 1e-12) && !given && !options.clip) return src;
3303
+ const dx = h[2] / h[8];
3304
+ const dy = h[5] / h[8];
3305
+ if (!given && !options.clip && isIdentityHomography([h[0], h[1], 0, h[3], h[4], 0, h[6], h[7], h[8]], 1e-12) && Math.abs(dx - Math.round(dx)) < 1e-9 && Math.abs(dy - Math.round(dy)) < 1e-9) {
3306
+ return translateSurface(src, Math.round(dx), Math.round(dy));
3307
+ }
3308
+ let srcRect = given ? intersect(given, given) : contentBounds(src);
3309
+ if (!srcRect) return empty;
3310
+ let H = h;
3311
+ let S = src;
3312
+ if (interp === "bicubic") {
3313
+ const inv0 = invertHomography(H);
3314
+ const c = applyHomography(H, { x: srcRect.x + srcRect.width / 2, y: srcRect.y + srcRect.height / 2 });
3315
+ if (inv0 && c) {
3316
+ const [su, sv] = inverseStretch(inv0, c.x, c.y);
3317
+ const kx = reduction(su);
3318
+ const ky = reduction(sv);
3319
+ if (kx > 1 || ky > 1) {
3320
+ const red = boxReduce(src, srcRect, kx, ky);
3321
+ S = red.surface;
3322
+ srcRect = red.rect;
3323
+ H = multiplyHomographies([kx, 0, 0, 0, ky, 0, 0, 0, 1], H);
3324
+ }
3325
+ }
3326
+ }
3327
+ const inv = invertHomography(H);
3328
+ if (!inv) return empty;
3329
+ const sr = srcRect;
3330
+ const fwd = [
3331
+ { x: sr.x - 1, y: sr.y - 1 },
3332
+ { x: sr.x + sr.width + 1, y: sr.y - 1 },
3333
+ { x: sr.x + sr.width + 1, y: sr.y + sr.height + 1 },
3334
+ { x: sr.x - 1, y: sr.y + sr.height + 1 }
3335
+ ].map((p) => homographyW(H, p));
3336
+ const side = fwd.every((w) => w > 0) ? 1 : fwd.every((w) => w < 0) ? -1 : 0;
3337
+ const wLo = 1 / fwd.reduce((m, w) => Math.max(m, Math.abs(w)), 0);
3338
+ const front = side === 0 ? homographyFrontSide(H, sr) : 0;
3339
+ const corners = boxQuad(sr).map((p) => applyHomography(H, p));
3340
+ const bounds = corners.every(Boolean) ? cornerBounds(corners) : null;
3341
+ let dst;
3342
+ if (options.clip && bounds && side !== 0) dst = intersect(bounds, options.clip);
3343
+ else dst = options.clip ?? bounds;
3344
+ if (!dst) return empty;
3345
+ if (!options.clip) checkBudget(dst);
3346
+ const affine = Math.abs(inv[6]) < 1e-15 && Math.abs(inv[7]) < 1e-15;
3347
+ const [su0, sv0] = inverseStretch(inv, dst.x + dst.width / 2, dst.y + dst.height / 2);
3348
+ const widen = (s) => interp === "bicubic" ? Math.min(4, Math.max(1, s)) : 1;
3349
+ const maxWiden = interp === "bicubic" ? 4 : 1;
3350
+ const pad = 3 + Math.ceil(2 * maxWiden);
3351
+ const margin = interp === "bicubic" ? Math.max(1, widen(su0), widen(sv0)) : 1;
3352
+ const tiles = /* @__PURE__ */ new Map();
3353
+ const acc = new Float64Array(4);
3354
+ const sx0 = sr.x;
3355
+ const sy0 = sr.y;
3356
+ const sx1 = sr.x + sr.width - 1;
3357
+ const sy1 = sr.y + sr.height - 1;
3358
+ for (const { tx, ty } of tilesInRect(dst)) {
3359
+ const t = intersect({ x: tx * TILE, y: ty * TILE, width: TILE, height: TILE }, dst);
3360
+ if (!t) continue;
3361
+ const tc = [
3362
+ { x: t.x, y: t.y },
3363
+ { x: t.x + t.width, y: t.y },
3364
+ { x: t.x + t.width, y: t.y + t.height },
3365
+ { x: t.x, y: t.y + t.height }
3366
+ ];
3367
+ const wc = tc.map((p) => homographyW(inv, p));
3368
+ let poly = tc;
3369
+ if (side !== 0) {
3370
+ const dd = wc.map((w) => side * w - wLo);
3371
+ poly = [];
3372
+ for (let i = 0; i < 4; i++) {
3373
+ const p = tc[i];
3374
+ const q = tc[(i + 1) % 4];
3375
+ const dp = dd[i];
3376
+ const dq = dd[(i + 1) % 4];
3377
+ if (dp >= 0) poly.push(p);
3378
+ if (dp >= 0 !== dq >= 0) {
3379
+ const k = dp / (dp - dq);
3380
+ poly.push({ x: p.x + (q.x - p.x) * k, y: p.y + (q.y - p.y) * k });
3381
+ }
3382
+ }
3383
+ }
3384
+ if (!poly.length) continue;
3385
+ const straddles = side === 0 && wc.some((w) => w <= 0) && wc.some((w) => w >= 0);
3386
+ let foot;
3387
+ if (straddles) foot = sr;
3388
+ else {
3389
+ const fp = poly.map((p) => applyHomography(inv, p));
3390
+ if (fp.some((p) => !p)) foot = sr;
3391
+ else {
3392
+ const xs = fp.map((p) => p.x);
3393
+ const ys = fp.map((p) => p.y);
3394
+ const fx0 = Math.floor(Math.min(...xs)) - pad;
3395
+ const fy0 = Math.floor(Math.min(...ys)) - pad;
3396
+ const fx1 = Math.ceil(Math.max(...xs)) + pad;
3397
+ const fy1 = Math.ceil(Math.max(...ys)) + pad;
3398
+ foot = intersect({ x: fx0, y: fy0, width: fx1 - fx0, height: fy1 - fy0 }, { x: sr.x - pad, y: sr.y - pad, width: sr.width + 2 * pad, height: sr.height + 2 * pad });
3399
+ }
3400
+ }
3401
+ if (!foot) continue;
3402
+ const readable = intersect(foot, sr);
3403
+ if (!readable || !hasTilesIn(S, readable)) continue;
3404
+ const win = readWindow(S, sr, foot);
3405
+ const out = new Uint8ClampedArray(TILE * TILE * ch);
3406
+ if (def) out.fill(def);
3407
+ let any = false;
3408
+ const ox = tx * TILE;
3409
+ const oy = ty * TILE;
3410
+ let su = widen(su0);
3411
+ let sv = widen(sv0);
3412
+ const ystart = t.y - ((t.y - oy) % step + step) % step;
3413
+ const xstart = t.x - ((t.x - ox) % step + step) % step;
3414
+ const blocks = Math.ceil((t.x + t.width - xstart) / step);
3415
+ const words = ch === 4 ? new Uint32Array(out.buffer) : null;
3416
+ const rowVals = new Float64Array(blocks);
3417
+ const rowSet = new Uint8Array(blocks);
3418
+ for (let y = ystart; y < t.y + t.height; y += step) {
3419
+ const Y = y + step / 2;
3420
+ let anyInRow = false;
3421
+ for (let bi = 0, x = xstart; bi < blocks; bi++, x += step) {
3422
+ rowSet[bi] = 0;
3423
+ const X = x + step / 2;
3424
+ const w = inv[6] * X + inv[7] * Y + inv[8];
3425
+ if (front !== 0 && front * w <= 0) continue;
3426
+ const u = (inv[0] * X + inv[1] * Y + inv[2]) / w - 0.5;
3427
+ const v = (inv[3] * X + inv[4] * Y + inv[5]) / w - 0.5;
3428
+ if (!(u >= sx0 - margin && v >= sy0 - margin && u <= sx1 + margin && v <= sy1 + margin)) continue;
3429
+ if (!affine && interp === "bicubic") {
3430
+ const s = inverseStretch(inv, X, Y);
3431
+ su = widen(s[0]);
3432
+ sv = widen(s[1]);
3433
+ }
3434
+ sample(win, interp, u, v, su, sv, acc);
3435
+ if (!store(scratch, 0, acc, ch, def)) continue;
3436
+ rowSet[bi] = 1;
3437
+ rowVals[bi] = ch === 4 ? scratchWord[0] : scratch[0];
3438
+ anyInRow = true;
3439
+ }
3440
+ if (!anyInRow) continue;
3441
+ any = true;
3442
+ const y0 = Math.max(y, t.y);
3443
+ const y1 = Math.min(y + step, t.y + t.height);
3444
+ for (let yy = y0; yy < y1; yy++) {
3445
+ const row = (yy - oy) * TILE - ox;
3446
+ for (let bi = 0, x = xstart; bi < blocks; bi++, x += step) {
3447
+ if (!rowSet[bi]) continue;
3448
+ const x0 = Math.max(x, t.x);
3449
+ const x1 = Math.min(x + step, t.x + t.width);
3450
+ const val = rowVals[bi];
3451
+ if (words) for (let xx = x0; xx < x1; xx++) words[row + xx] = val;
3452
+ else for (let xx = x0; xx < x1; xx++) out[row + xx] = val;
3453
+ }
3454
+ }
3455
+ }
3456
+ if (!any) continue;
3457
+ const tile = finishTile(out, ch, def);
3458
+ if (tile) tiles.set(tileKey(tx, ty), tile);
3459
+ }
3460
+ return assemble(src, tiles);
3461
+ }
3462
+ var CELL = 4;
3463
+ var MAX_CELLS = 512;
3464
+ var MAX_SS = 8;
3465
+ function warpSurface(src, f, options = {}) {
3466
+ const interp = options.interpolation ?? "bicubic";
3467
+ const ch = src.channels;
3468
+ const def = src.defaultValue;
3469
+ const empty = emptySurface(ch, def);
3470
+ const sr = options.source ? intersect(options.source, options.source) : contentBounds(src);
3471
+ if (!sr) return empty;
3472
+ const step = normalizeStep(options.step);
3473
+ const nx = Math.min(MAX_CELLS, Math.max(1, Math.ceil(sr.width / CELL)));
3474
+ const ny = Math.min(MAX_CELLS, Math.max(1, Math.ceil(sr.height / CELL)));
3475
+ const w1 = nx + 1;
3476
+ const dvx = new Float64Array(w1 * (ny + 1));
3477
+ const dvy = new Float64Array(w1 * (ny + 1));
3478
+ const svx = new Float64Array(w1 * (ny + 1));
3479
+ const svy = new Float64Array(w1 * (ny + 1));
3480
+ let x0 = Infinity;
3481
+ let y0 = Infinity;
3482
+ let x1 = -Infinity;
3483
+ let y1 = -Infinity;
3484
+ for (let j = 0; j <= ny; j++) {
3485
+ for (let i = 0; i <= nx; i++) {
3486
+ const k = j * w1 + i;
3487
+ const sx = sr.x + sr.width * i / nx;
3488
+ const sy = sr.y + sr.height * j / ny;
3489
+ const d = f({ x: sx, y: sy });
3490
+ if (!d || !Number.isFinite(d.x) || !Number.isFinite(d.y)) return empty;
3491
+ dvx[k] = d.x;
3492
+ dvy[k] = d.y;
3493
+ svx[k] = sx;
3494
+ svy[k] = sy;
3495
+ if (d.x < x0) x0 = d.x;
3496
+ if (d.y < y0) y0 = d.y;
3497
+ if (d.x > x1) x1 = d.x;
3498
+ if (d.y > y1) y1 = d.y;
3499
+ }
3500
+ }
3501
+ const lim = 1 << 20;
3502
+ const bx0 = Math.floor(Math.max(-lim, x0)) - 1;
3503
+ const by0 = Math.floor(Math.max(-lim, y0)) - 1;
3504
+ const full = { x: bx0, y: by0, width: Math.ceil(Math.min(lim, x1)) + 1 - bx0, height: Math.ceil(Math.min(lim, y1)) + 1 - by0 };
3505
+ const dst = options.clip ? intersect(full, options.clip) : full;
3506
+ if (!dst) return empty;
3507
+ if (!options.clip) checkBudget(dst);
3508
+ const triCount = nx * ny * 2;
3509
+ const tris = new Int32Array(triCount * 3);
3510
+ for (let j = 0, t = 0; j < ny; j++) {
3511
+ for (let i = 0; i < nx; i++) {
3512
+ const a = j * w1 + i;
3513
+ tris[t++] = a;
3514
+ tris[t++] = a + 1;
3515
+ tris[t++] = a + w1 + 1;
3516
+ tris[t++] = a;
3517
+ tris[t++] = a + w1 + 1;
3518
+ tris[t++] = a + w1;
3519
+ }
3520
+ }
3521
+ const tx0 = Math.floor(dst.x / TILE);
3522
+ const ty0 = Math.floor(dst.y / TILE);
3523
+ const cols = Math.floor((dst.x + dst.width - 1) / TILE) - tx0 + 1;
3524
+ const rows = Math.floor((dst.y + dst.height - 1) / TILE) - ty0 + 1;
3525
+ const bins = Array.from({ length: cols * rows }, () => []);
3526
+ for (let k = 0; k < triCount; k++) {
3527
+ const a = tris[3 * k];
3528
+ const b = tris[3 * k + 1];
3529
+ const c = tris[3 * k + 2];
3530
+ const tminx = Math.floor(Math.min(dvx[a], dvx[b], dvx[c]));
3531
+ const tminy = Math.floor(Math.min(dvy[a], dvy[b], dvy[c]));
3532
+ const tmaxx = Math.ceil(Math.max(dvx[a], dvx[b], dvx[c]));
3533
+ const tmaxy = Math.ceil(Math.max(dvy[a], dvy[b], dvy[c]));
3534
+ const cx0 = Math.max(0, Math.floor(Math.max(tminx, dst.x) / TILE) - tx0);
3535
+ const cy0 = Math.max(0, Math.floor(Math.max(tminy, dst.y) / TILE) - ty0);
3536
+ const cx1 = Math.min(cols - 1, Math.floor(Math.min(tmaxx, dst.x + dst.width - 1) / TILE) - tx0);
3537
+ const cy1 = Math.min(rows - 1, Math.floor(Math.min(tmaxy, dst.y + dst.height - 1) / TILE) - ty0);
3538
+ for (let cy = cy0; cy <= cy1; cy++) for (let cx = cx0; cx <= cx1; cx++) bins[cy * cols + cx].push(k);
3539
+ }
3540
+ const tiles = /* @__PURE__ */ new Map();
3541
+ const acc = new Float64Array(4);
3542
+ const one = new Float64Array(4);
3543
+ const n = ch === 4 ? 4 : 2;
3544
+ const count = (jx, jy) => interp === "nearest" ? 1 : Math.min(MAX_SS, Math.max(1, Math.round(Math.hypot(jx, jy))));
3545
+ for (let cy = 0; cy < rows; cy++) {
3546
+ for (let cx = 0; cx < cols; cx++) {
3547
+ const list = bins[cy * cols + cx];
3548
+ if (!list.length) continue;
3549
+ const ttx = tx0 + cx;
3550
+ const tty = ty0 + cy;
3551
+ const t = intersect({ x: ttx * TILE, y: tty * TILE, width: TILE, height: TILE }, dst);
3552
+ if (!t) continue;
3553
+ const tw = t.width;
3554
+ const th = t.height;
3555
+ const us = new Float64Array(tw * th).fill(NaN);
3556
+ const vs = new Float64Array(tw * th);
3557
+ const owner = new Int32Array(tw * th);
3558
+ const jac = new Float64Array(list.length * 4);
3559
+ const ssx = new Int32Array(list.length);
3560
+ const ssy = new Int32Array(list.length);
3561
+ for (let li = 0; li < list.length; li++) {
3562
+ const k = list[li];
3563
+ const a = tris[3 * k];
3564
+ const b = tris[3 * k + 1];
3565
+ const c = tris[3 * k + 2];
3566
+ const p0x = dvx[a];
3567
+ const p0y = dvy[a];
3568
+ const p1x = dvx[b];
3569
+ const p1y = dvy[b];
3570
+ const p2x = dvx[c];
3571
+ const p2y = dvy[c];
3572
+ const det = (p1x - p0x) * (p2y - p0y) - (p2x - p0x) * (p1y - p0y);
3573
+ ssx[li] = 1;
3574
+ ssy[li] = 1;
3575
+ if (Math.abs(det) < 1e-12) continue;
3576
+ const d1x = svx[b] - svx[a];
3577
+ const d1y = svy[b] - svy[a];
3578
+ const d2x = svx[c] - svx[a];
3579
+ const d2y = svy[c] - svy[a];
3580
+ const l1x = (p2y - p0y) / det;
3581
+ const l1y = (p0x - p2x) / det;
3582
+ const l2x = (p0y - p1y) / det;
3583
+ const l2y = (p1x - p0x) / det;
3584
+ const cap = (vx, vy) => {
3585
+ const l = Math.hypot(vx, vy);
3586
+ return l > MAX_SS ? [vx * MAX_SS / l, vy * MAX_SS / l] : [vx, vy];
3587
+ };
3588
+ const jx = cap(l1x * d1x + l2x * d2x, l1x * d1y + l2x * d2y);
3589
+ const jy = cap(l1y * d1x + l2y * d2x, l1y * d1y + l2y * d2y);
3590
+ const cnx = count(jx[0], jx[1]);
3591
+ const cny = count(jy[0], jy[1]);
3592
+ if (cnx * cny > 1) {
3593
+ jac[li * 4] = jx[0];
3594
+ jac[li * 4 + 1] = jx[1];
3595
+ jac[li * 4 + 2] = jy[0];
3596
+ jac[li * 4 + 3] = jy[1];
3597
+ ssx[li] = cnx;
3598
+ ssy[li] = cny;
3599
+ }
3600
+ const rx0 = Math.max(t.x, Math.floor(Math.min(p0x, p1x, p2x) - 0.5));
3601
+ const ry0 = Math.max(t.y, Math.floor(Math.min(p0y, p1y, p2y) - 0.5));
3602
+ const rx1 = Math.min(t.x + tw, Math.ceil(Math.max(p0x, p1x, p2x) + 0.5));
3603
+ const ry1 = Math.min(t.y + th, Math.ceil(Math.max(p0y, p1y, p2y) + 0.5));
3604
+ const eps = -1e-9;
3605
+ const ay0 = step === 1 ? ry0 : ry0 + ((t.y - ry0) % step + step) % step;
3606
+ const ax0 = step === 1 ? rx0 : rx0 + ((t.x - rx0) % step + step) % step;
3607
+ for (let y = ay0; y < ry1; y += step) {
3608
+ const py = y + 0.5;
3609
+ for (let x = ax0; x < rx1; x += step) {
3610
+ const px = x + 0.5;
3611
+ const l1 = ((px - p0x) * (p2y - p0y) - (p2x - p0x) * (py - p0y)) / det;
3612
+ const l2 = ((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) / det;
3613
+ const l0 = 1 - l1 - l2;
3614
+ if (l0 < eps || l1 < eps || l2 < eps) continue;
3615
+ const o = (y - t.y) * tw + (x - t.x);
3616
+ us[o] = l0 * svx[a] + l1 * svx[b] + l2 * svx[c];
3617
+ vs[o] = l0 * svy[a] + l1 * svy[b] + l2 * svy[c];
3618
+ owner[o] = li;
3619
+ }
3620
+ }
3621
+ }
3622
+ let fx0 = Infinity;
3623
+ let fy0 = Infinity;
3624
+ let fx1 = -Infinity;
3625
+ let fy1 = -Infinity;
3626
+ for (let o = 0; o < us.length; o++) {
3627
+ const u = us[o];
3628
+ if (Number.isNaN(u)) continue;
3629
+ const li = owner[o];
3630
+ const ex = 0.5 * (Math.abs(jac[li * 4]) + Math.abs(jac[li * 4 + 2]));
3631
+ const ey = 0.5 * (Math.abs(jac[li * 4 + 1]) + Math.abs(jac[li * 4 + 3]));
3632
+ if (u - ex < fx0) fx0 = u - ex;
3633
+ if (vs[o] - ey < fy0) fy0 = vs[o] - ey;
3634
+ if (u + ex > fx1) fx1 = u + ex;
3635
+ if (vs[o] + ey > fy1) fy1 = vs[o] + ey;
3636
+ }
3637
+ if (fx0 > fx1) continue;
3638
+ const foot = intersect({ x: Math.floor(fx0) - 3, y: Math.floor(fy0) - 3, width: Math.ceil(fx1) - Math.floor(fx0) + 6, height: Math.ceil(fy1) - Math.floor(fy0) + 6 }, sr);
3639
+ if (!foot || !hasTilesIn(src, foot)) continue;
3640
+ const win = readWindow(src, sr, foot);
3641
+ const out = new Uint8ClampedArray(TILE * TILE * ch);
3642
+ if (def) out.fill(def);
3643
+ let any = false;
3644
+ const ox = ttx * TILE;
3645
+ const oy = tty * TILE;
3646
+ const pointAt = (o, into) => {
3647
+ const li = owner[o];
3648
+ const cnx = ssx[li];
3649
+ const cny = ssy[li];
3650
+ if (cnx * cny === 1) {
3651
+ sample(win, interp, us[o] - 0.5, vs[o] - 0.5, 1, 1, into);
3652
+ return;
3653
+ }
3654
+ for (let c = 0; c < n; c++) into[c] = 0;
3655
+ const j0 = jac[li * 4];
3656
+ const j1 = jac[li * 4 + 1];
3657
+ const j2 = jac[li * 4 + 2];
3658
+ const j3 = jac[li * 4 + 3];
3659
+ for (let sy = 0; sy < cny; sy++) {
3660
+ const tt = (sy + 0.5) / cny - 0.5;
3661
+ for (let sx = 0; sx < cnx; sx++) {
3662
+ const s = (sx + 0.5) / cnx - 0.5;
3663
+ sample(win, "bilinear", us[o] + s * j0 + tt * j2 - 0.5, vs[o] + s * j1 + tt * j3 - 0.5, 1, 1, one);
3664
+ for (let c = 0; c < n; c++) into[c] += one[c];
3665
+ }
3666
+ }
3667
+ const k = 1 / (cnx * cny);
3668
+ for (let c = 0; c < n; c++) into[c] *= k;
3669
+ };
3670
+ if (step === 1) {
3671
+ for (let o = 0; o < us.length; o++) {
3672
+ if (Number.isNaN(us[o])) continue;
3673
+ pointAt(o, acc);
3674
+ const x = t.x + o % tw;
3675
+ const y = t.y + Math.floor(o / tw);
3676
+ if (store(out, ((y - oy) * TILE + (x - ox)) * ch, acc, ch, def)) any = true;
3677
+ }
3678
+ } else {
3679
+ for (let by = 0; by < th; by += step) {
3680
+ for (let bx = 0; bx < tw; bx += step) {
3681
+ const o = by * tw + bx;
3682
+ if (Number.isNaN(us[o])) continue;
3683
+ pointAt(o, acc);
3684
+ const first = ((t.y + by - oy) * TILE + (t.x + bx - ox)) * ch;
3685
+ if (!store(out, first, acc, ch, def)) continue;
3686
+ any = true;
3687
+ for (let yy = by; yy < Math.min(th, by + step); yy++) {
3688
+ const row = ((t.y + yy - oy) * TILE + (t.x - ox)) * ch;
3689
+ for (let xx = bx; xx < Math.min(tw, bx + step); xx++) for (let c = 0; c < ch; c++) out[row + xx * ch + c] = out[first + c];
3690
+ }
3691
+ }
3692
+ }
3693
+ }
3694
+ if (!any) continue;
3695
+ const tile = finishTile(out, ch, def);
3696
+ if (tile) tiles.set(tileKey(ttx, tty), tile);
3697
+ }
3698
+ }
3699
+ return assemble(src, tiles);
3700
+ }
3701
+ function compositeSurfaceOver(base, top) {
3702
+ if (top.channels !== 4) throw new Error("compositeSurfaceOver: top must be RGBA");
3703
+ if (!top.tiles.size) return base;
3704
+ const ch = base.channels;
3705
+ const tiles = new Map(base.tiles);
3706
+ for (const [key, t] of top.tiles) {
3707
+ const under = base.tiles.get(key);
3708
+ if (!under && ch === 4) {
3709
+ tiles.set(key, t);
3710
+ continue;
3711
+ }
3712
+ const d = new Uint8ClampedArray(TILE * TILE * ch);
3713
+ if (under) d.set(under.data);
3714
+ else if (base.defaultValue) d.fill(base.defaultValue);
3715
+ const s = t.data;
3716
+ if (ch === 4) {
3717
+ for (let i = 0; i < d.length; i += 4) {
3718
+ const ta = s[i + 3] / 255;
3719
+ if (ta <= 0) continue;
3720
+ if (ta >= 1) {
3721
+ d[i] = s[i];
3722
+ d[i + 1] = s[i + 1];
3723
+ d[i + 2] = s[i + 2];
3724
+ d[i + 3] = 255;
3725
+ continue;
3726
+ }
3727
+ const da = d[i + 3] / 255;
3728
+ const oa = ta + da * (1 - ta);
3729
+ for (let c = 0; c < 3; c++) d[i + c] = (s[i + c] * ta + d[i + c] * da * (1 - ta)) / oa;
3730
+ d[i + 3] = oa * 255;
3731
+ }
3732
+ } else {
3733
+ for (let p = 0, i = 0; p < d.length; p++, i += 4) {
3734
+ const ta = s[i + 3] / 255;
3735
+ if (ta > 0) d[p] = s[i] * ta + d[p] * (1 - ta);
3736
+ }
3737
+ }
3738
+ const tile = finishTile(d, ch, base.defaultValue);
3739
+ if (tile) tiles.set(key, tile);
3740
+ else tiles.delete(key);
3741
+ }
3742
+ return new TiledSurface(ch, tiles, base.defaultValue);
3743
+ }
3744
+
2816
3745
  // src/raster/zlib.ts
2817
3746
  var LENGTH_BASE = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258];
2818
3747
  var LENGTH_EXTRA = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0];
@@ -3525,7 +4454,7 @@ function decodePng(bytes, options = {}) {
3525
4454
  lines[dst + x] = r & 255;
3526
4455
  }
3527
4456
  }
3528
- const sample = (y, i) => {
4457
+ const sample2 = (y, i) => {
3529
4458
  const row = y * stride;
3530
4459
  if (depth === 8) return lines[row + i];
3531
4460
  if (depth === 16) return lines[row + i * 2];
@@ -3544,36 +4473,36 @@ function decodePng(bytes, options = {}) {
3544
4473
  for (let x = 0; x < width; x++) {
3545
4474
  const d = (y * width + x) * channels;
3546
4475
  if (type === 0) {
3547
- const v = sample(y, x);
4476
+ const v = sample2(y, x);
3548
4477
  if (gray) out[d] = v;
3549
4478
  else {
3550
4479
  out[d] = out[d + 1] = out[d + 2] = v;
3551
4480
  out[d + 3] = v === trnsGray && trns ? 0 : 255;
3552
4481
  }
3553
4482
  } else if (type === 2) {
3554
- const r = sample(y, x * 3);
3555
- const g = sample(y, x * 3 + 1);
3556
- const b = sample(y, x * 3 + 2);
4483
+ const r = sample2(y, x * 3);
4484
+ const g = sample2(y, x * 3 + 1);
4485
+ const b = sample2(y, x * 3 + 2);
3557
4486
  out[d] = r;
3558
4487
  out[d + 1] = g;
3559
4488
  out[d + 2] = b;
3560
4489
  out[d + 3] = trnsRgb && r === trnsRgb[0] && g === trnsRgb[1] && b === trnsRgb[2] ? 0 : 255;
3561
4490
  } else if (type === 3) {
3562
- const i = sample(y, x);
4491
+ const i = sample2(y, x);
3563
4492
  if (!palette || i * 3 + 2 >= palette.length) throw new Error("PNG palette index out of range.");
3564
4493
  out[d] = palette[i * 3];
3565
4494
  out[d + 1] = palette[i * 3 + 1];
3566
4495
  out[d + 2] = palette[i * 3 + 2];
3567
4496
  out[d + 3] = trns && i < trns.length ? trns[i] : 255;
3568
4497
  } else if (type === 4) {
3569
- const v = sample(y, x * 2);
4498
+ const v = sample2(y, x * 2);
3570
4499
  out[d] = out[d + 1] = out[d + 2] = v;
3571
- out[d + 3] = sample(y, x * 2 + 1);
4500
+ out[d + 3] = sample2(y, x * 2 + 1);
3572
4501
  } else {
3573
- out[d] = sample(y, x * 4);
3574
- out[d + 1] = sample(y, x * 4 + 1);
3575
- out[d + 2] = sample(y, x * 4 + 2);
3576
- out[d + 3] = sample(y, x * 4 + 3);
4502
+ out[d] = sample2(y, x * 4);
4503
+ out[d + 1] = sample2(y, x * 4 + 1);
4504
+ out[d + 2] = sample2(y, x * 4 + 2);
4505
+ out[d + 3] = sample2(y, x * 4 + 3);
3577
4506
  }
3578
4507
  }
3579
4508
  }
@@ -3689,12 +4618,23 @@ export {
3689
4618
  activeEffectItems,
3690
4619
  effectsAreLocal,
3691
4620
  buildEffectMaps,
4621
+ patchEffectMaps,
3692
4622
  gradientT,
3693
4623
  compositeLayerEffects,
3694
4624
  renderLayerEffects,
3695
4625
  blendTile,
3696
4626
  RasterCompositor,
3697
4627
  changedTiles,
4628
+ INTERPOLATIONS,
4629
+ isInterpolation,
4630
+ MAX_TRANSFORM_PIXELS,
4631
+ TransformTooLargeError,
4632
+ keysWeight,
4633
+ reduceSurface,
4634
+ transformedBounds,
4635
+ transformSurface,
4636
+ warpSurface,
4637
+ compositeSurfaceOver,
3698
4638
  ZlibError,
3699
4639
  inflateRaw,
3700
4640
  adler32,
@@ -3709,4 +4649,4 @@ export {
3709
4649
  parseDataUrl,
3710
4650
  pngDataUrl
3711
4651
  };
3712
- //# sourceMappingURL=chunk-KFTQ2PKP.js.map
4652
+ //# sourceMappingURL=chunk-WIR377F6.js.map