@koda-sl/baker-cli 0.481.0 → 0.482.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 (32) hide show
  1. package/dist/{canvas-4T64K6PQ.js → canvas-A4XKEBN7.js} +10 -10
  2. package/dist/{catalog-UPF4SOBW.js → catalog-SJBGNNY4.js} +2 -2
  3. package/dist/{chunk-O6OCL3LH.js → chunk-2K3NCVDI.js} +38 -28
  4. package/dist/{chunk-O6OCL3LH.js.map → chunk-2K3NCVDI.js.map} +1 -1
  5. package/dist/{chunk-PJSIVGHU.js → chunk-5YKC2WWG.js} +2 -2
  6. package/dist/{chunk-LULQRFGD.js → chunk-A2FKWRKO.js} +2 -2
  7. package/dist/{chunk-LJNYZLOH.js → chunk-GQ6JS5MR.js} +295 -105
  8. package/dist/chunk-GQ6JS5MR.js.map +1 -0
  9. package/dist/cli.js +1 -1
  10. package/dist/engine/index.js +1 -1
  11. package/dist/{rerun-GFMBT6NJ.js → rerun-R3DNEV5U.js} +4 -4
  12. package/dist/{run-AIBAPD4R.js → run-QYIUO4NB.js} +4 -4
  13. package/dist/{scaffold-ad-7L6D6PKO.js → scaffold-ad-NIVJSO73.js} +3 -3
  14. package/dist/{scaffold-static-ad-V2H7TOG6.js → scaffold-static-ad-QX2NUXFR.js} +5 -5
  15. package/dist/{scaffold-video-A3KAT6QO.js → scaffold-video-NXBM54K3.js} +3 -3
  16. package/dist/{set-prompt-VLGJG5WO.js → set-prompt-3MVZW3L6.js} +2 -2
  17. package/dist/{sync-definition-VSR72E7E.js → sync-definition-6XPX6VJU.js} +3 -3
  18. package/dist/{validate-2NRSRRMM.js → validate-AYPLNS7A.js} +3 -3
  19. package/package.json +1 -1
  20. package/dist/chunk-LJNYZLOH.js.map +0 -1
  21. /package/dist/{canvas-4T64K6PQ.js.map → canvas-A4XKEBN7.js.map} +0 -0
  22. /package/dist/{catalog-UPF4SOBW.js.map → catalog-SJBGNNY4.js.map} +0 -0
  23. /package/dist/{chunk-PJSIVGHU.js.map → chunk-5YKC2WWG.js.map} +0 -0
  24. /package/dist/{chunk-LULQRFGD.js.map → chunk-A2FKWRKO.js.map} +0 -0
  25. /package/dist/{rerun-GFMBT6NJ.js.map → rerun-R3DNEV5U.js.map} +0 -0
  26. /package/dist/{run-AIBAPD4R.js.map → run-QYIUO4NB.js.map} +0 -0
  27. /package/dist/{scaffold-ad-7L6D6PKO.js.map → scaffold-ad-NIVJSO73.js.map} +0 -0
  28. /package/dist/{scaffold-static-ad-V2H7TOG6.js.map → scaffold-static-ad-QX2NUXFR.js.map} +0 -0
  29. /package/dist/{scaffold-video-A3KAT6QO.js.map → scaffold-video-NXBM54K3.js.map} +0 -0
  30. /package/dist/{set-prompt-VLGJG5WO.js.map → set-prompt-3MVZW3L6.js.map} +0 -0
  31. /package/dist/{sync-definition-VSR72E7E.js.map → sync-definition-6XPX6VJU.js.map} +0 -0
  32. /package/dist/{validate-2NRSRRMM.js.map → validate-AYPLNS7A.js.map} +0 -0
@@ -3283,6 +3283,29 @@ function renderRect(plate, rect, format, ratios) {
3283
3283
  return best ?? { ...rect, ratio: format };
3284
3284
  }
3285
3285
 
3286
+ // src/engine/nodes/local/lib/picture-memo.ts
3287
+ var ids = /* @__PURE__ */ new WeakMap();
3288
+ var nextId = 0;
3289
+ function pictureId(picture) {
3290
+ const known = ids.get(picture);
3291
+ if (known !== void 0) return known;
3292
+ const id = nextId++;
3293
+ ids.set(picture, id);
3294
+ return id;
3295
+ }
3296
+ function remembered() {
3297
+ const readings = /* @__PURE__ */ new WeakMap();
3298
+ return (picture, what, read) => {
3299
+ const known = readings.get(picture) ?? /* @__PURE__ */ new Map();
3300
+ readings.set(picture, known);
3301
+ const kept = known.get(what);
3302
+ if (kept !== void 0) return kept;
3303
+ const value = read();
3304
+ known.set(what, value);
3305
+ return value;
3306
+ };
3307
+ }
3308
+
3286
3309
  // src/engine/nodes/local/lib/words-band.ts
3287
3310
  var GROUND_REACH = 0.06;
3288
3311
  var LAID = 24;
@@ -3485,7 +3508,9 @@ function moveBand(g, move) {
3485
3508
  const dx = Math.round(move.dx * width);
3486
3509
  const dy = Math.round(move.dy * height);
3487
3510
  const data = Buffer.alloc(width * height * 3);
3488
- for (let i = 0; i < width * height; i++) g.out.data.copy(data, i * 3, i * g.out.channels, i * g.out.channels + 3);
3511
+ if (g.out.channels === 3) g.out.data.copy(data, 0, 0, width * height * 3);
3512
+ else
3513
+ for (let i = 0; i < width * height; i++) g.out.data.copy(data, i * 3, i * g.out.channels, i * g.out.channels + 3);
3489
3514
  const shape = shapeOf(g, r);
3490
3515
  const edge = edgeLeft(r, dx, dy, width, height);
3491
3516
  const tone = edge ? toneOf(
@@ -3626,6 +3651,25 @@ async function fitPicture(image, fit) {
3626
3651
 
3627
3652
  // src/engine/nodes/local/lib/words-layer.ts
3628
3653
  var inSpans = (spans, fx, fy) => spans.some((s) => fx >= s.x0 && fx <= s.x1 && fy >= s.y0 && fy <= s.y1);
3654
+ function centresIn(lo, hi, size) {
3655
+ const inside = new Uint8Array(size);
3656
+ for (let t = 0; t < size; t++) {
3657
+ const f = (t + 0.5) / size;
3658
+ inside[t] = f >= lo && f <= hi ? 1 : 0;
3659
+ }
3660
+ return inside;
3661
+ }
3662
+ function spanMask(spans, width, height) {
3663
+ const mask = new Uint8Array(width * height);
3664
+ for (const s of spans) {
3665
+ const across2 = centresIn(s.x0, s.x1, width);
3666
+ const down = centresIn(s.y0, s.y1, height);
3667
+ for (let y = 0; y < height; y++) {
3668
+ if (down[y]) for (let x = 0; x < width; x++) mask[y * width + x] ||= across2[x] ?? 0;
3669
+ }
3670
+ }
3671
+ return mask;
3672
+ }
3629
3673
  function taps(u, v, width, height) {
3630
3674
  const x = Math.min(width - 1, Math.max(0, u));
3631
3675
  const y = Math.min(height - 1, Math.max(0, v));
@@ -3663,7 +3707,10 @@ function shrink(g, edge) {
3663
3707
  const x = i % width * f;
3664
3708
  const y = Math.floor(i / width) * f;
3665
3709
  let sum = 0;
3666
- for (let k = 0; k < f * f; k++) sum += g.data[(y + Math.floor(k / f)) * g.width + x + k % f] ?? 0;
3710
+ for (let j = 0; j < f; j++) {
3711
+ const row = (y + j) * g.width + x;
3712
+ for (let k = 0; k < f; k++) sum += g.data[row + k] ?? 0;
3713
+ }
3667
3714
  data[i] = sum / (f * f);
3668
3715
  }
3669
3716
  return { data, width, height };
@@ -3681,19 +3728,34 @@ function judgedPixels(g, exclude) {
3681
3728
  if (x < mx || y < my || x >= g.width - mx || y >= g.height - my) continue;
3682
3729
  if (!inSpans(exclude, x / g.width, y / g.height)) out.push(i);
3683
3730
  }
3684
- return Int32Array.from(out);
3731
+ const rows = [];
3732
+ const starts = [];
3733
+ out.forEach((i, j) => {
3734
+ const y = Math.floor(i / g.width);
3735
+ if (rows[rows.length - 1] === y) return;
3736
+ rows.push(y);
3737
+ starts.push(j);
3738
+ });
3739
+ starts.push(out.length);
3740
+ return {
3741
+ at: Int32Array.from(out),
3742
+ xs: Int32Array.from(out, (i) => i % g.width),
3743
+ rows: Int32Array.from(rows),
3744
+ starts: Int32Array.from(starts)
3745
+ };
3685
3746
  }
3686
- function grayAt(g, u, v) {
3687
- const x = Math.min(g.width - 1.001, Math.max(0, u));
3688
- const y = Math.min(g.height - 1.001, Math.max(0, v));
3689
- const x0 = x | 0;
3690
- const y0 = y | 0;
3691
- const fx = x - x0;
3692
- const fy = y - y0;
3693
- const i = y0 * g.width + x0;
3694
- const top = (g.data[i] ?? 0) * (1 - fx) + (g.data[i + 1] ?? 0) * fx;
3695
- const bottom = (g.data[i + g.width] ?? 0) * (1 - fx) + (g.data[i + g.width + 1] ?? 0) * fx;
3696
- return top * (1 - fy) + bottom * fy;
3747
+ function axisOf(origin, scale, count, size) {
3748
+ const near = new Int32Array(count);
3749
+ const at2 = new Int32Array(count);
3750
+ const frac = new Float64Array(count);
3751
+ for (let t = 0; t < count; t++) {
3752
+ const p = origin + scale * t;
3753
+ near[t] = Math.min(size - 1, Math.max(0, Math.round(p)));
3754
+ const q = Math.min(size - 1.001, Math.max(0, p));
3755
+ at2[t] = q | 0;
3756
+ frac[t] = q - (at2[t] ?? 0);
3757
+ }
3758
+ return { near, at: at2, frac };
3697
3759
  }
3698
3760
  var MISFIT_CAP = 40;
3699
3761
  var LINEUP_FLAT = 1.5;
@@ -3714,26 +3776,42 @@ function withFlat(g) {
3714
3776
  }
3715
3777
  return { ...g, flat };
3716
3778
  }
3717
- function misfit(cut, render, judged, k, c, cap = MISFIT_CAP) {
3779
+ function misfit(cut, render, judged, k, c, cap = MISFIT_CAP, beat = Number.POSITIVE_INFINITY, above = Number.POSITIVE_INFINITY) {
3718
3780
  const cx = cut.width / 2;
3719
3781
  const cy = cut.height / 2;
3720
3782
  const ox = cx - c.scale * cx + c.dx * k;
3721
3783
  const oy = cy - c.scale * cy + c.dy * k;
3722
- const most = cap * cap;
3723
- let sum = 0;
3724
- let n = 0;
3725
- for (const i of judged) {
3726
- const x = i % cut.width;
3727
- const y = (i - x) / cut.width;
3728
- const u = ox + c.scale * x;
3729
- const v = oy + c.scale * y;
3730
- const at2 = Math.min(render.height - 1, Math.max(0, Math.round(v))) * render.width + Math.min(render.width - 1, Math.max(0, Math.round(u)));
3731
- if (render.flat?.[at2]) continue;
3732
- const d = (cut.data[i] ?? 0) - grayAt(render, u, v);
3733
- sum += Math.min(d * d, most);
3734
- n++;
3784
+ const cols = axisOf(ox, c.scale, cut.width, render.width);
3785
+ const rows = axisOf(oy, c.scale, cut.height, render.height);
3786
+ const look = { cut, render, judged, cols, most: cap * cap };
3787
+ const count = judged.at.length;
3788
+ const acc = { sum: 0, n: 0 };
3789
+ for (let r = 0; r < judged.rows.length; r++) {
3790
+ const from = judged.starts[r] ?? 0;
3791
+ const least = acc.sum / (acc.n + count - from);
3792
+ if (least >= beat || least > above) return Number.POSITIVE_INFINITY;
3793
+ rowMisfit(look, rows, judged.rows[r] ?? 0, from, judged.starts[r + 1] ?? count, acc);
3794
+ }
3795
+ return acc.n > 0 ? acc.sum / acc.n : Number.POSITIVE_INFINITY;
3796
+ }
3797
+ function bilinear(data, i, width, fx, fy) {
3798
+ const top = (data[i] ?? 0) * (1 - fx) + (data[i + 1] ?? 0) * fx;
3799
+ const bottom = (data[i + width] ?? 0) * (1 - fx) + (data[i + width + 1] ?? 0) * fx;
3800
+ return top * (1 - fy) + bottom * fy;
3801
+ }
3802
+ function rowMisfit(s, rows, y, from, to, acc) {
3803
+ const { width, data, flat } = s.render;
3804
+ const { near, at: at2, frac } = s.cols;
3805
+ const flatRow = (rows.near[y] ?? 0) * width;
3806
+ const row = (rows.at[y] ?? 0) * width;
3807
+ const fy = rows.frac[y] ?? 0;
3808
+ for (let j = from; j < to; j++) {
3809
+ const x = s.judged.xs[j] ?? 0;
3810
+ if (flat?.[flatRow + (near[x] ?? 0)]) continue;
3811
+ const d = (s.cut.data[s.judged.at[j] ?? 0] ?? 0) - bilinear(data, row + (at2[x] ?? 0), width, frac[x] ?? 0, fy);
3812
+ acc.sum += Math.min(d * d, s.most);
3813
+ acc.n++;
3735
3814
  }
3736
- return n > 0 ? sum / n : Number.POSITIVE_INFINITY;
3737
3815
  }
3738
3816
  var MAX_SHIFT = 0.03;
3739
3817
  var MAX_SCALE_OFF = 0.03;
@@ -3747,13 +3825,14 @@ function searchLevel(cut, render, k, level, centre, exclude) {
3747
3825
  const reach = level.reach ?? Math.ceil(MAX_SHIFT * Math.max(cut.width, cut.height));
3748
3826
  let best = centre;
3749
3827
  let score = Number.POSITIVE_INFINITY;
3828
+ const atCentre = misfit(cut, render, judged, k, centre);
3750
3829
  const side = 2 * reach + 1;
3751
3830
  for (const ds of level.scales) {
3752
3831
  const scale = centre.scale + ds;
3753
3832
  if (Math.abs(scale - 1) > MAX_SCALE_OFF + 1e-9) continue;
3754
3833
  for (let s = 0; s < side * side; s++) {
3755
3834
  const c = { scale, dx: centre.dx + (s % side - reach) / k, dy: centre.dy + (Math.floor(s / side) - reach) / k };
3756
- const m = misfit(cut, render, judged, k, c);
3835
+ const m = misfit(cut, render, judged, k, c, MISFIT_CAP, score, atCentre);
3757
3836
  if (m < score) {
3758
3837
  score = m;
3759
3838
  best = c;
@@ -3763,6 +3842,11 @@ function searchLevel(cut, render, k, level, centre, exclude) {
3763
3842
  return { best, score };
3764
3843
  }
3765
3844
  function registerToSource(render, cut, exclude) {
3845
+ return lineUps(render, `${pictureId(cut)} ${JSON.stringify(exclude)}`, () => lineUp(render, cut, exclude));
3846
+ }
3847
+ var lineUps = remembered();
3848
+ var warps = remembered();
3849
+ function lineUp(render, cut, exclude) {
3766
3850
  const full = { cut: grayOf(cut), render: grayOf(render) };
3767
3851
  let best = { scale: 1, dx: 0, dy: 0 };
3768
3852
  let finest = { cut: full.cut, render: full.render, k: 1 };
@@ -3777,19 +3861,54 @@ function registerToSource(render, cut, exclude) {
3777
3861
  const residual = misfit(finest.cut, finest.render, judged, finest.k, best, Number.POSITIVE_INFINITY);
3778
3862
  return { ...best, residual: Math.sqrt(residual) };
3779
3863
  }
3864
+ function bilinearAxis(centre, scale, shift, size) {
3865
+ const lo = new Int32Array(size);
3866
+ const hi = new Int32Array(size);
3867
+ const frac = new Float64Array(size);
3868
+ for (let t = 0; t < size; t++) {
3869
+ const p = Math.min(size - 1, Math.max(0, centre + scale * (t - centre) + shift));
3870
+ lo[t] = Math.floor(p);
3871
+ hi[t] = Math.min(size - 1, (lo[t] ?? 0) + 1);
3872
+ frac[t] = p - (lo[t] ?? 0);
3873
+ }
3874
+ return { lo, hi, frac };
3875
+ }
3780
3876
  function warpToCut(render, fit) {
3781
- const { width, height, channels } = render;
3877
+ return warps(render, `${fit.scale} ${fit.dx} ${fit.dy}`, () => warped(render, fit));
3878
+ }
3879
+ function warped(render, fit) {
3880
+ const { width, height } = render;
3782
3881
  const out = Buffer.alloc(width * height * 3);
3783
- const cx = width / 2;
3784
- const cy = height / 2;
3785
- for (let i = 0; i < width * height; i++) {
3786
- const x = i % width;
3787
- const y = Math.floor(i / width);
3788
- const t = taps(cx + fit.scale * (x - cx) + fit.dx, cy + fit.scale * (y - cy) + fit.dy, width, height);
3789
- for (let c = 0; c < 3; c++) out[i * 3 + c] = Math.round(tapped(render.data, t, channels, c));
3882
+ const cols = bilinearAxis(width / 2, fit.scale, fit.dx, width);
3883
+ const rows = bilinearAxis(height / 2, fit.scale, fit.dy, height);
3884
+ for (let y = 0; y < height; y++) {
3885
+ const row = { y, top: (rows.lo[y] ?? 0) * width, bottom: (rows.hi[y] ?? 0) * width, fy: rows.frac[y] ?? 0 };
3886
+ warpRow(render, out, cols, row);
3790
3887
  }
3791
3888
  return { data: out, width, height, channels: 3 };
3792
3889
  }
3890
+ function warpRow(render, out, cols, row) {
3891
+ const { width, channels, data } = render;
3892
+ const { fy } = row;
3893
+ for (let x = 0; x < width; x++) {
3894
+ const fx = cols.frac[x] ?? 0;
3895
+ const w = [(1 - fx) * (1 - fy), fx * (1 - fy), (1 - fx) * fy, fx * fy];
3896
+ const lo = cols.lo[x] ?? 0;
3897
+ const hi = cols.hi[x] ?? 0;
3898
+ const a = [
3899
+ (row.top + lo) * channels,
3900
+ (row.top + hi) * channels,
3901
+ (row.bottom + lo) * channels,
3902
+ (row.bottom + hi) * channels
3903
+ ];
3904
+ const o = (row.y * width + x) * 3;
3905
+ for (let c = 0; c < 3; c++) {
3906
+ out[o + c] = Math.round(
3907
+ w[0] * (data[a[0] + c] ?? 0) + w[1] * (data[a[1] + c] ?? 0) + w[2] * (data[a[2] + c] ?? 0) + w[3] * (data[a[3] + c] ?? 0)
3908
+ );
3909
+ }
3910
+ }
3911
+ }
3793
3912
  function emptyLayer(width, height) {
3794
3913
  return {
3795
3914
  width,
@@ -3869,7 +3988,7 @@ function cellStats(aligned, cut, words, grid) {
3869
3988
  const x = i % cut.width;
3870
3989
  const y = Math.floor(i / cut.width);
3871
3990
  const g = grid.cell(x, y);
3872
- const tone = !inSpans(words, (x + 0.5) / cut.width, (y + 0.5) / cut.height);
3991
+ const tone = !words[i];
3873
3992
  const d = addPixel(s, aligned, cut, i, g, tone);
3874
3993
  s.pixelDiff[i] = d;
3875
3994
  s.diff[g] = (s.diff[g] ?? 0) + d;
@@ -3960,7 +4079,7 @@ function toneMisfit(aligned, cut, gains, words, grid) {
3960
4079
  for (let i = 0; i < cut.width * cut.height; i++) {
3961
4080
  const x = i % cut.width;
3962
4081
  const y = (i - x) / cut.width;
3963
- if (inSpans(words, (x + 0.5) / cut.width, (y + 0.5) / cut.height)) continue;
4082
+ if (words[i]) continue;
3964
4083
  const g = grid.cell(x, y);
3965
4084
  let d = 0;
3966
4085
  for (let c = 0; c < 3; c++) {
@@ -3972,19 +4091,19 @@ function toneMisfit(aligned, cut, gains, words, grid) {
3972
4091
  }
3973
4092
  return sum.map((v, g) => count[g] ? v / (count[g] ?? 1) : 0);
3974
4093
  }
3975
- function atCell(values, grid, x, y, stride = 1, c = 0) {
3976
- const t = taps((x + 0.5) / CELL - 0.5, (y + 0.5) / CELL - 0.5, grid.gw, grid.gh);
3977
- return tapped(values, t, stride, c, 1);
4094
+ function cellTaps(grid, x, y) {
4095
+ return taps((x + 0.5) / CELL - 0.5, (y + 0.5) / CELL - 0.5, grid.gw, grid.gh);
3978
4096
  }
3979
4097
  function wordsLayer(aligned, cut, designed, opts = {}) {
3980
4098
  const { width, height } = cut;
3981
4099
  const grid = gridOf(width, height);
3982
4100
  const boxes = designed.map((b) => padded(b, width, height));
3983
4101
  const tight = designed.map((b) => ({ ...b }));
3984
- const stats = cellStats(aligned, cut, boxes, grid);
4102
+ const inBoxes2 = spanMask(boxes, width, height);
4103
+ const stats = cellStats(aligned, cut, inBoxes2, grid);
3985
4104
  const kept = keptChange(stats.diff, boxes, grid, TOUCH * width / height);
3986
4105
  const gains = smoothGains(toneGains(stats, grid), grid);
3987
- const misfit2 = opts.boxesOnly ? null : toneMisfit(aligned, cut, gains, boxes, grid);
4106
+ const misfit2 = opts.boxesOnly ? null : toneMisfit(aligned, cut, gains, inBoxes2, grid);
3988
4107
  const tone = kept.tone.map((t, g) => t > 0 && misfit2 && (misfit2[g] ?? 0) < STRUCTURAL ? 1 : 0);
3989
4108
  const dropped = kept.tone.reduce((n, t, g) => n + (t > 0 && !tone[g] ? 1 : 0), 0);
3990
4109
  const reverted = kept.reverted + dropped / kept.tone.length;
@@ -3992,12 +4111,12 @@ function wordsLayer(aligned, cut, designed, opts = {}) {
3992
4111
  aligned,
3993
4112
  stats,
3994
4113
  grid,
3995
- boxes,
4114
+ boxes: inBoxes2,
3996
4115
  tone,
3997
4116
  gains,
3998
- tight,
3999
- subjects: opts.subjects ?? [],
4000
- keepOut: opts.keepOut ?? []
4117
+ tight: spanMask(tight, width, height),
4118
+ subjects: spanMask(opts.subjects ?? [], width, height),
4119
+ keepOut: spanMask(opts.keepOut ?? [], width, height)
4001
4120
  };
4002
4121
  const layer = emptyLayer(width, height);
4003
4122
  for (let i = 0; i < width * height; i++) setLayerPixel(layer, look, i);
@@ -4025,21 +4144,21 @@ function keptChange(diff, boxes, grid, touchY) {
4025
4144
  function setLayerPixel(layer, look, i) {
4026
4145
  const x = i % layer.width;
4027
4146
  const y = Math.floor(i / layer.width);
4028
- const fx = (x + 0.5) / layer.width;
4029
- const fy = (y + 0.5) / layer.height;
4030
- const onSubject = look.subjects.length > 0 && inSpans(look.subjects, fx, fy);
4031
- const inBox = inSpans(onSubject ? look.tight : look.boxes, fx, fy);
4032
- if (!inBox && inSpans(look.keepOut, fx, fy)) return;
4033
- const toned = look.tone[look.grid.cell(x, y)] ? atCell(look.tone, look.grid, x, y) : 0;
4147
+ const onSubject = look.subjects[i] === 1;
4148
+ const inBox = (onSubject ? look.tight : look.boxes)[i] === 1;
4149
+ if (!inBox && look.keepOut[i]) return;
4150
+ const near = look.tone[look.grid.cell(x, y)] ? cellTaps(look.grid, x, y) : void 0;
4151
+ const toned = near ? tapped(look.tone, near, 1, 0, 1) : 0;
4034
4152
  const changed = ((look.stats.pixelDiff[i] ?? 0) - MATTE_LO) / (MATTE_HI - MATTE_LO);
4035
4153
  const matte = inBox ? Math.min(1, Math.max(0, changed)) : 0;
4036
4154
  const a = Math.max(matte, toned);
4037
4155
  if (a <= 0) return;
4038
4156
  layer.alpha[i] = a;
4039
4157
  layer.ink[i] = inBox ? 1 : 0;
4158
+ const cells = near ?? cellTaps(look.grid, x, y);
4040
4159
  for (let c = 0; c < 3; c++) {
4041
4160
  layer.pixels[i * 3 + c] = look.aligned.data[i * look.aligned.channels + c] ?? 0;
4042
- layer.gain[i * 3 + c] = atCell(look.gains, look.grid, x, y, 3, c);
4161
+ layer.gain[i * 3 + c] = tapped(look.gains, cells, 3, c, 1);
4043
4162
  }
4044
4163
  }
4045
4164
  function protectFaces(layer, faces) {
@@ -4236,17 +4355,33 @@ function owners(layer, moves) {
4236
4355
  const { width } = layer;
4237
4356
  const own = new Int16Array(width * layer.height).fill(-1);
4238
4357
  for (let i = 0; i < own.length; i++) if ((layer.alpha[i] ?? 0) > 0) own[i] = blockAt(layer, moves, i);
4239
- const stack = [...own.keys()].filter((i) => (own[i] ?? -1) >= 0);
4240
- const free = (n) => n >= 0 && n < own.length && (own[n] ?? -1) < 0 && (layer.alpha[n] ?? 0) > 0;
4241
- while (stack.length > 0) {
4242
- const i = stack.pop() ?? 0;
4243
- const x = i % width;
4244
- const open = [x > 0 ? i - 1 : -1, x < width - 1 ? i + 1 : -1, i - width, i + width].filter(free);
4245
- for (const n of open) own[n] = own[i] ?? -1;
4246
- stack.push(...open);
4247
- }
4358
+ spreadOwners(own, layer.alpha, width);
4248
4359
  return own;
4249
4360
  }
4361
+ function spreadOwners(own, alpha, width) {
4362
+ const { stack, seeds } = seedsOf(own);
4363
+ let top = seeds;
4364
+ const free = (n) => n >= 0 && n < own.length && (own[n] ?? -1) < 0 && (alpha[n] ?? 0) > 0;
4365
+ while (top > 0) {
4366
+ const i = stack[--top] ?? 0;
4367
+ const block = own[i] ?? -1;
4368
+ for (const n of touching(i, width)) {
4369
+ if (!free(n)) continue;
4370
+ own[n] = block;
4371
+ stack[top++] = n;
4372
+ }
4373
+ }
4374
+ }
4375
+ function seedsOf(own) {
4376
+ const stack = new Int32Array(own.length);
4377
+ let seeds = 0;
4378
+ for (let i = 0; i < own.length; i++) if ((own[i] ?? -1) >= 0) stack[seeds++] = i;
4379
+ return { stack, seeds };
4380
+ }
4381
+ function touching(i, width) {
4382
+ const x = i % width;
4383
+ return [x > 0 ? i - 1 : -1, x < width - 1 ? i + 1 : -1, i - width, i + width];
4384
+ }
4250
4385
  function takePixel(out, to, layer, t) {
4251
4386
  const a = tapped(layer.alpha, t);
4252
4387
  if (a <= (out.alpha[to] ?? 0)) return;
@@ -4311,12 +4446,29 @@ function blurLine(src, out, start, step, n, r) {
4311
4446
  acc += (src[enter >= n - 1 ? last : start + enter * step] ?? 0) - (src[leave <= 0 ? start : start + leave * step] ?? 0);
4312
4447
  }
4313
4448
  }
4449
+ function blurColumns(src, out, width, height, r) {
4450
+ const acc = new Float64Array(width);
4451
+ const row = (k) => (k <= 0 ? 0 : k >= height - 1 ? height - 1 : k) * width;
4452
+ for (let k = -r; k <= r; k++) {
4453
+ const from = row(k);
4454
+ for (let x = 0; x < width; x++) acc[x] = (acc[x] ?? 0) + (src[from + x] ?? 0);
4455
+ }
4456
+ for (let k = 0; k < height; k++) {
4457
+ const to = k * width;
4458
+ const enter = row(k + r + 1);
4459
+ const leave = row(k - r);
4460
+ for (let x = 0; x < width; x++) {
4461
+ out[to + x] = (acc[x] ?? 0) / (2 * r + 1);
4462
+ acc[x] = (acc[x] ?? 0) + ((src[enter + x] ?? 0) - (src[leave + x] ?? 0));
4463
+ }
4464
+ }
4465
+ }
4314
4466
  function boxBlur(src, width, height, r) {
4315
4467
  if (r < 1) return Float32Array.from(src);
4316
4468
  const along = new Float32Array(width * height);
4317
4469
  for (let y = 0; y < height; y++) blurLine(src, along, y * width, 1, width, r);
4318
4470
  const out = new Float32Array(width * height);
4319
- for (let x = 0; x < width; x++) blurLine(along, out, x, width, height, r);
4471
+ blurColumns(along, out, width, height, r);
4320
4472
  return out;
4321
4473
  }
4322
4474
  var soften = (src, width, height, r) => boxBlur(boxBlur(src, width, height, r), width, height, r);
@@ -4337,14 +4489,16 @@ function flatOf2(img) {
4337
4489
  const o = i * img.channels;
4338
4490
  grey[i] = 0.299 * (img.data[o] ?? 0) + 0.587 * (img.data[o + 1] ?? 0) + 0.114 * (img.data[o + 2] ?? 0);
4339
4491
  }
4492
+ const squares = new Float32Array(grey.length);
4493
+ for (let i = 0; i < grey.length; i++) squares[i] = (grey[i] ?? 0) * (grey[i] ?? 0);
4340
4494
  const mean = boxBlur(grey, width, height, 2);
4341
- const square = boxBlur(
4342
- grey.map((v) => v * v),
4343
- width,
4344
- height,
4345
- 2
4346
- );
4347
- return Uint8Array.from(mean, (m, i) => Math.sqrt(Math.max(0, (square[i] ?? 0) - m * m)) < FLAT2 ? 1 : 0);
4495
+ const square = boxBlur(squares, width, height, 2);
4496
+ const flat = new Uint8Array(mean.length);
4497
+ for (let i = 0; i < mean.length; i++) {
4498
+ const m = mean[i] ?? 0;
4499
+ flat[i] = Math.sqrt(Math.max(0, (square[i] ?? 0) - m * m)) < FLAT2 ? 1 : 0;
4500
+ }
4501
+ return flat;
4348
4502
  }
4349
4503
  var gridOver = (width, height, size) => ({
4350
4504
  size,
@@ -4402,7 +4556,15 @@ function flood2(seeds, open, grid) {
4402
4556
  function surfaceOf(flat, structure, width, height, words) {
4403
4557
  const grid = gridOver(width, height, SURFACE_CELL);
4404
4558
  const reached = flood2(wordCells(words, width, height, grid), laidCells(flat, structure, width, grid), grid);
4405
- return Uint8Array.from(structure, (d, i) => reached[cellOf(grid, i, width)] && d > MOVED ? 1 : 0);
4559
+ const surface = new Uint8Array(structure.length);
4560
+ for (let y = 0; y < height; y++) {
4561
+ const row = Math.floor(y / grid.size) * grid.gw;
4562
+ for (let x = 0; x < width; x++) {
4563
+ const i = y * width + x;
4564
+ surface[i] = reached[row + Math.floor(x / grid.size)] && (structure[i] ?? 0) > MOVED ? 1 : 0;
4565
+ }
4566
+ }
4567
+ return surface;
4406
4568
  }
4407
4569
  var GROUND_LINE = 3;
4408
4570
  var CARD_TONE = 16;
@@ -4492,6 +4654,8 @@ function trustOf(width, height, words) {
4492
4654
  }
4493
4655
  return trust;
4494
4656
  }
4657
+ var cutPlanes = remembered();
4658
+ var shrunkCuts = remembered();
4495
4659
  function planesOf(img, r1) {
4496
4660
  const own = [0, 1, 2].map((c) => channelOf(img, c));
4497
4661
  return {
@@ -4504,13 +4668,11 @@ var LIGHT_CELL = 0.015;
4504
4668
  var LIGHT_REACH = 3;
4505
4669
  function cellSums(a, b, open, width, grid) {
4506
4670
  const sums = new Float32Array(grid.gw * grid.gh * 7);
4671
+ const own = [...a.own, ...b.own];
4507
4672
  for (let i = 0; i < open.length; i++) {
4508
4673
  if (!open[i]) continue;
4509
4674
  const g = cellOf(grid, i, width) * 7;
4510
- for (let c = 0; c < 3; c++) {
4511
- sums[g + c] = (sums[g + c] ?? 0) + (a.own[c]?.[i] ?? 0);
4512
- sums[g + 3 + c] = (sums[g + 3 + c] ?? 0) + (b.own[c]?.[i] ?? 0);
4513
- }
4675
+ for (let c = 0; c < 6; c++) sums[g + c] = (sums[g + c] ?? 0) + (own[c]?.[i] ?? 0);
4514
4676
  sums[g + 6] = (sums[g + 6] ?? 0) + 1;
4515
4677
  }
4516
4678
  return sums;
@@ -4527,14 +4689,24 @@ function cellRatios(a, b, open, width, grid) {
4527
4689
  }
4528
4690
  return ratio;
4529
4691
  }
4530
- function medianNear(values, grid, g, c, reach) {
4531
- const near = cellsNear(grid, g, reach).map((n) => values[n * 3 + c] ?? Number.NaN).filter((v) => !Number.isNaN(v)).sort((p, q) => p - q);
4532
- return near.length > 0 ? near[Math.floor(near.length / 2)] ?? 1 : 1;
4692
+ function medianNear(values, near, c, scratch) {
4693
+ let known = 0;
4694
+ for (const n of near) {
4695
+ const v = values[n * 3 + c] ?? Number.NaN;
4696
+ if (!Number.isNaN(v)) scratch[known++] = v;
4697
+ }
4698
+ if (known === 0) return 1;
4699
+ const sorted = scratch.subarray(0, known).sort();
4700
+ return sorted[Math.floor(known / 2)] ?? 1;
4533
4701
  }
4534
4702
  function medianLight(ratio, grid) {
4535
4703
  const cells = grid.gw * grid.gh;
4536
4704
  const median = new Float32Array(cells * 3);
4537
- for (let k = 0; k < cells * 3; k++) median[k] = medianNear(ratio, grid, Math.floor(k / 3), k % 3, LIGHT_REACH);
4705
+ const scratch = new Float64Array((2 * LIGHT_REACH + 1) ** 2);
4706
+ for (let g = 0; g < cells; g++) {
4707
+ const near = cellsNear(grid, g, LIGHT_REACH);
4708
+ for (let c = 0; c < 3; c++) median[g * 3 + c] = medianNear(ratio, near, c, scratch);
4709
+ }
4538
4710
  const smooth = new Float32Array(cells * 3);
4539
4711
  for (let k = 0; k < cells * 3; k++) {
4540
4712
  const near = cellsNear(grid, Math.floor(k / 3), 1);
@@ -4546,16 +4718,19 @@ function lightOf(a, b, open, width, height) {
4546
4718
  const grid = gridOver(width, height, Math.max(8, Math.round(width * LIGHT_CELL)));
4547
4719
  const light = medianLight(cellRatios(a, b, open, width, grid), grid);
4548
4720
  const gain = enlarge(light, { width: grid.gw, height: grid.gh }, { width, height }, 3);
4549
- return gain.map((g) => Math.min(GAIN_MAX2, Math.max(1 / GAIN_MAX2, g)));
4721
+ const kept = new Float32Array(gain.length);
4722
+ for (let i = 0; i < gain.length; i++) kept[i] = Math.min(GAIN_MAX2, Math.max(1 / GAIN_MAX2, gain[i] ?? 0));
4723
+ return kept;
4550
4724
  }
4551
4725
  function compared(a, b, gain, n) {
4552
4726
  const structure = new Float32Array(n);
4553
4727
  const detail = new Float32Array(n);
4554
4728
  for (let c = 0; c < 3; c++) {
4729
+ const [aLow, bLow, aFine, bFine] = [a.low[c], b.low[c], a.fine[c], b.fine[c]];
4555
4730
  for (let i = 0; i < n; i++) {
4556
4731
  const g = gain[i * 3 + c] ?? 1;
4557
- structure[i] = Math.max(structure[i] ?? 0, Math.abs((a.low[c]?.[i] ?? 0) - g * (b.low[c]?.[i] ?? 0)));
4558
- detail[i] = Math.max(detail[i] ?? 0, Math.abs((a.fine[c]?.[i] ?? 0) - g * (b.fine[c]?.[i] ?? 0)));
4732
+ structure[i] = Math.max(structure[i] ?? 0, Math.abs((aLow?.[i] ?? 0) - g * (bLow?.[i] ?? 0)));
4733
+ detail[i] = Math.max(detail[i] ?? 0, Math.abs((aFine?.[i] ?? 0) - g * (bFine?.[i] ?? 0)));
4559
4734
  }
4560
4735
  }
4561
4736
  return { structure, detail };
@@ -4564,7 +4739,7 @@ function renderDiff(aligned, cut, words) {
4564
4739
  const { width, height } = cut;
4565
4740
  const r1 = Math.max(2, Math.round(width * STRUCTURE_RADIUS));
4566
4741
  const a = planesOf(aligned, r1);
4567
- const b = planesOf(cut, r1);
4742
+ const b = cutPlanes(cut, String(r1), () => planesOf(cut, r1));
4568
4743
  const gain = lightOf(a, b, trustOf(width, height, words), width, height);
4569
4744
  const { structure, detail } = compared(a, b, gain, width * height);
4570
4745
  const flat = flatOf2(aligned);
@@ -4633,9 +4808,11 @@ function greyOf(img, f) {
4633
4808
  const x = i % width * f;
4634
4809
  const y = Math.floor(i / width) * f;
4635
4810
  let sum = 0;
4636
- for (let k = 0; k < f * f; k++) {
4637
- const o = ((y + Math.floor(k / f)) * img.width + x + k % f) * img.channels;
4638
- sum += 0.299 * (img.data[o] ?? 0) + 0.587 * (img.data[o + 1] ?? 0) + 0.114 * (img.data[o + 2] ?? 0);
4811
+ for (let j = 0; j < f; j++) {
4812
+ for (let k = 0; k < f; k++) {
4813
+ const o = ((y + j) * img.width + x + k) * img.channels;
4814
+ sum += 0.299 * (img.data[o] ?? 0) + 0.587 * (img.data[o + 1] ?? 0) + 0.114 * (img.data[o + 2] ?? 0);
4815
+ }
4639
4816
  }
4640
4817
  data[i] = sum / (f * f);
4641
4818
  }
@@ -4810,10 +4987,15 @@ function keptShare(diff, words) {
4810
4987
  }
4811
4988
  function cropRaw(img, rect) {
4812
4989
  const data = Buffer.alloc(rect.width * rect.height * 3);
4813
- for (let i = 0; i < rect.width * rect.height; i++) {
4814
- const x = i % rect.width;
4815
- const from = ((rect.y + (i - x) / rect.width) * img.width + rect.x + x) * img.channels;
4816
- img.data.copy(data, i * 3, from, from + 3);
4990
+ for (let y = 0; y < rect.height; y++) {
4991
+ const from = ((rect.y + y) * img.width + rect.x) * img.channels;
4992
+ if (img.channels === 3) {
4993
+ img.data.copy(data, y * rect.width * 3, from, from + rect.width * 3);
4994
+ continue;
4995
+ }
4996
+ for (let x = 0; x < rect.width; x++) {
4997
+ for (let c = 0; c < 3; c++) data[(y * rect.width + x) * 3 + c] = img.data[from + x * img.channels + c] ?? 0;
4998
+ }
4817
4999
  }
4818
5000
  return { data, width: rect.width, height: rect.height, channels: 3 };
4819
5001
  }
@@ -4843,8 +5025,10 @@ function shrinkRaw(img, f) {
4843
5025
  const y = Math.floor(i / width) * f;
4844
5026
  for (let c = 0; c < 3; c++) {
4845
5027
  let sum = 0;
4846
- for (let k = 0; k < f * f; k++)
4847
- sum += img.data[((y + Math.floor(k / f)) * img.width + x + k % f) * img.channels + c] ?? 0;
5028
+ for (let j = 0; j < f; j++) {
5029
+ const row = ((y + j) * img.width + x) * img.channels + c;
5030
+ for (let k = 0; k < f; k++) sum += img.data[row + k * img.channels] ?? 0;
5031
+ }
4848
5032
  data[i * 3 + c] = Math.round(sum / (f * f));
4849
5033
  }
4850
5034
  }
@@ -4863,15 +5047,16 @@ function axisTaps(from, to) {
4863
5047
  return { near, far, t };
4864
5048
  }
4865
5049
  function enlargeRow(values, out, xs, row, stride) {
4866
- const at2 = (i) => values[i] ?? 0;
5050
+ const { top, bottom, fy } = row;
4867
5051
  for (let x = 0; x < row.width; x++) {
4868
5052
  const left = xs.near[x] ?? 0;
4869
5053
  const right = xs.far[x] ?? 0;
4870
5054
  const fx = xs.t[x] ?? 0;
5055
+ const o = (row.y * row.width + x) * stride;
4871
5056
  for (let c = 0; c < stride; c++) {
4872
- const upper = at2((row.top + left) * stride + c) * (1 - fx) + at2((row.top + right) * stride + c) * fx;
4873
- const lower = at2((row.bottom + left) * stride + c) * (1 - fx) + at2((row.bottom + right) * stride + c) * fx;
4874
- out[(row.y * row.width + x) * stride + c] = upper * (1 - row.fy) + lower * row.fy;
5057
+ const upper = (values[(top + left) * stride + c] ?? 0) * (1 - fx) + (values[(top + right) * stride + c] ?? 0) * fx;
5058
+ const lower = (values[(bottom + left) * stride + c] ?? 0) * (1 - fx) + (values[(bottom + right) * stride + c] ?? 0) * fx;
5059
+ out[o + c] = upper * (1 - fy) + lower * fy;
4875
5060
  }
4876
5061
  }
4877
5062
  }
@@ -5057,7 +5242,7 @@ function takeWhole(input) {
5057
5242
  aligned,
5058
5243
  cut,
5059
5244
  out,
5060
- gain,
5245
+ gain: gain(),
5061
5246
  judged: judging.judged,
5062
5247
  frame,
5063
5248
  words: input.words,
@@ -5075,7 +5260,7 @@ function judge(input, read, fit, aligned) {
5075
5260
  const { width, height } = cut;
5076
5261
  const words = read.designed.map((b) => padded(b, width, height));
5077
5262
  const shrink2 = Math.max(1, Math.round(width / JUDGED_WIDTH));
5078
- const judged = shrink2 > 1 ? { aligned: shrinkRaw(aligned, shrink2), cut: shrinkRaw(cut, shrink2) } : { aligned, cut };
5263
+ const judged = shrink2 > 1 ? { aligned: shrinkRaw(aligned, shrink2), cut: shrunkCuts(cut, String(shrink2), () => shrinkRaw(cut, shrink2)) } : { aligned, cut };
5079
5264
  const diff = renderDiff(judged.aligned, judged.cut, words);
5080
5265
  const drifts = input.subjects.map((s) => ({ subject: s, ...subjectDrift(diff, s, words) }));
5081
5266
  const inFrame = (b) => ({
@@ -5130,8 +5315,12 @@ function putBack(input, read, aligned, j) {
5130
5315
  const back = input.subjects.filter((s) => restored.includes(s.label));
5131
5316
  const zones = [...back, ...read.inScene.map((b) => padded(b, full.width, full.height))];
5132
5317
  const covered = j.drifts.filter((d) => (d.subject.kind === "product" || d.subject.kind === "animal") && d.shown < SHOWN_MIN).map((d) => d.subject.label);
5133
- const gain = enlarge(j.diff.gain, j.diff, full, 3);
5134
- let out = zones.length === 0 ? aligned : restoreSubjects(aligned, cut, gain, enlarge(restoreMask(j.diff, zones, j.words), j.diff, full, 1));
5318
+ let enlarged;
5319
+ const gain = () => {
5320
+ enlarged ??= enlarge(j.diff.gain, j.diff, full, 3);
5321
+ return enlarged;
5322
+ };
5323
+ let out = zones.length === 0 ? aligned : restoreSubjects(aligned, cut, gain(), enlarge(restoreMask(j.diff, zones, j.words), j.diff, full, 1));
5135
5324
  for (const r of j.relocated) out = putMoved(out, cut, r, j);
5136
5325
  const moved = j.relocated.map((r) => r.subject.label);
5137
5326
  return { out, gain, taken: { restored: [.../* @__PURE__ */ new Set([...restored, ...moved])], covered } };
@@ -5164,7 +5353,8 @@ function movedBox(box, place) {
5164
5353
  function cutMoved(cut, box, place) {
5165
5354
  const { width, height } = cut;
5166
5355
  const out = Buffer.alloc(width * height * 3);
5167
- for (let i = 0; i < width * height; i++) cut.data.copy(out, i * 3, i * cut.channels, i * cut.channels + 3);
5356
+ if (cut.channels === 3) cut.data.copy(out, 0, 0, width * height * 3);
5357
+ else for (let i = 0; i < width * height; i++) cut.data.copy(out, i * 3, i * cut.channels, i * cut.channels + 3);
5168
5358
  const to = movedBox(box, place);
5169
5359
  const margin = (ZONE_MARGIN + FEATHER) * Math.max((to.x1 - to.x0) * width, (to.y1 - to.y0) * height) + 4;
5170
5360
  const cx = (box.x0 + box.x1) / 2 * width;
@@ -8658,4 +8848,4 @@ export {
8658
8848
  defaultRegistry,
8659
8849
  createEngineFromEnv
8660
8850
  };
8661
- //# sourceMappingURL=chunk-LJNYZLOH.js.map
8851
+ //# sourceMappingURL=chunk-GQ6JS5MR.js.map