Zoom: anchor the capture, magnify the full composite, draw on top (Unit: zoom rework per creator correction)

Creator corrected the "loupe" zoom on 2026-10-08: the click anchors a fixed
capture region (box/2 centred on the click) and the box is a live 2x view of
that region — dragging the box never changes its content; it magnifies the
fully-composited frame (every layer, incl. webcam) and draws LAST (n+1).
Rectangle shape is 4:3 ("more of a 640x480 aspect").

- MainViewModel.Zoom.cs rewritten: ActivateZoom anchors CaptureX/CaptureY
  (independently clamped); UpdateZoomMove relocates the box only; the per-frame
  BuildZoomFrame/ZoomFrameBuilder path is gone.
- SceneCompositor: skips the active ZoomSceneElement in every layer loop and
  BlitZooms it last via the new pure ZoomMagnifier (2x bilinear, temp snapshot
  via ArrayPool, round clip).
- Preview: ZoomBox hoisted above CanvasGrid as a sibling; its brush samples the
  whole composed preview at the fixed capture (2x).
- Tests: ZoomMagnifierTests (box-over-blue shows green capture at 2x incl. an
  extra red "layer" — the loupe/backdrop-only regressions both fail it) +
  round-clip; ZoomBoxInteractionTests rewritten (anchored capture, move-doesn't-
  change-capture, 4:3/9:16/square). 404 pass, 2 known environment flakes.
This commit is contained in:
2026-10-08 08:26:29 -07:00
parent 4b3d82ff0f
commit 6bff21b234
15 changed files with 467 additions and 330 deletions
+19
View File
@@ -1526,3 +1526,22 @@ A one-constant difference (`W/4` vs nothing) is invisible in a real scene but fa
gradient test — which is exactly why the magnification proof exists: 4 vertical bands, a box whose
1x region straddles two of them, assert ALL output pixels are the center-band color. If you change
the preview window, change `ZoomFrameBuilder` in the same edit or the preview lies about the output.
## 2026-10-08 — The zoom box is NOT a loupe: anchored capture, all-layer content, drawn LAST
I shipped the whole first zoom layer (commit `4b3d82f`) with a "loupe" mental model — the box shows
what's *under it* in the output — and the creator corrected it in one message: the click fixes a
**capture region** and the box is a live, always-on 2x view of THAT region; moving the box changes
NOTHING about its content; it magnifies the fully-composited frame (every layer, including webcam),
and it must draw **on top of everything** (layer n+1), not slot at z-index 1. Three wrongs, all made
"elegantly": a BoxFollowing frame builder, a Backdrop+first-layer-only sample, and a z=1 layering.
The corrected test that would have caught all three in one shot (`ZoomMagnifierTests`): a banded
buffer (blue left / green right) with the box's left quarter OVER a blue region while the capture
sits wholly in green — assert **no box pixel is blue**. A loupe-style sampler fails it, a backdrop-
only sampler fails the "red webcam square appears magnified" assert, and `SceneCompositor` skips the
active zoom in every layer loop so nothing else can cover it. Lessons: (1) ask the *triggering
semantics* — anchored-capture vs follow-the-box — before building any "zoom" UI; (2) prove content
captures ALL layers with an extra layer in the fixture and assert it appears; (3) "draw above
everything" is a compositor ordering property (skip in the layer loops + blit last), not a z-index
of the element among the others.