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:
+44
-19
@@ -1,4 +1,4 @@
|
||||
# HANDOFF — 2026-10-08
|
||||
# HANDOFF — 2026-10-08 (zoom rework)
|
||||
|
||||
## Branch
|
||||
- main (no feature branch)
|
||||
@@ -6,29 +6,54 @@
|
||||
## Dirty files (NOT mine — the in-flight "pinned chat" work unit; do NOT commit or co-edit)
|
||||
- `Models/YouTube.cs`
|
||||
- `ViewModels/MainViewModel.Chat.cs`
|
||||
- `Controls/LeftPanel.xaml` (pinned-message UI; restored via `git stash pop` after this commit)
|
||||
- `Controls/LeftPanel.xaml` (pinned-message UI)
|
||||
- Untracked: `Helpers/RelayCommandGeneric.cs`
|
||||
|
||||
## What just happened (2026-10-08 zoom build)
|
||||
- **TASK 51 booked (webcam background Remove + Blur as a v1 requirement):** `TASKS/task-51-webcam-bg-effects.md` created; TASKS.md row 51 + the two prior "background removal excluded" rulings (`~line 180` webcam cadence + "Out of product — permanently") reversed 2026-10-08. NVIDIA VFX SDK stays ruled out. Implementation queued after the zoom build.
|
||||
- **Zoom rebuilt from "loupe + output crop" into a REAL composited layer:**
|
||||
- New `Models/ZoomSceneElement.cs` (z-index 1 above the background, below every other layer; `IsZoomActive` flips `Kind` Static→Dynamic; never persisted, never shown in the layers list or element template). `SceneElement.IsZoomLayer` virtual added.
|
||||
- New `ViewModels/MainViewModel.Zoom.cs`: `ActivateZoom` (all shapes CENTERED on the click, clamped to the master), move/dismiss, `HealZoomLayers`/`EnsureZoomLayer` (load + `AddScene`), `BuildZoomFrame` (resolves live-capture-under-art exactly like `SceneCompositor.Render`, single-slot cache keyed on frame identity+Epoch+box ints so the per-tick resolver allocates nothing idle), and the pure static `ZoomFrameBuilder.Build` (2x bilinear sample of the box/2 window centered on the box centre).
|
||||
- `BuildCompositorOptions` now ALWAYS returns the tier crop (internal = test seam); zoom-as-crop branch + `RebuildDimRectsForZoom` old version removed. `ResolveOutputFrameCore` gained the `ZoomSceneElement` case; output composites the zoom like any layer.
|
||||
- Preview: backdrop images wrapped in `BackdropLayer`; `ZoomBox` moved INSIDE `OverlayCanvas` under the element Layers (renders below layers = honest preview); `ZoomVisualBrush` now samples `BackdropLayer` at `(X+W/4, Y+H/4, W/2, H/2)` + `Stretch=Fill` (2x); element template + LeftPanel list collapse `IsZoomLayer`.
|
||||
- Tests: `ZoomBoxInteractionTests` rewritten (center-anchor for all shapes, placeholder at Elements[1], Kind flips, tier crop asserted, dismiss resets); new `ZoomFrameBuilderTests` = the genuine-magnification proof (4-band backdrop, box straddling two bands, all output pixels must be the center band → a 1x or mis-positioned window fails hard). `LayerReorderPersistenceTests` updated: cross-type reload compares persisted elements only (the zoom placeholder never persists), and the real-mouse drag targets rows by ITEM (the collapsed zoom row shifts container indices — the handlers hit-test visually so real dragging is unaffected).
|
||||
- Full suite: **405 passed / 0 failed / 0 warnings**.
|
||||
## What just happened (2026-10-08, second zoom pass — creator correction)
|
||||
The zoom box shipped in `4b3d82f` was a **loupe** (box follows its own content, built from the
|
||||
backdrop only, slotted at z-index 1). The creator corrected it; the model is now:
|
||||
|
||||
- **Anchored capture, not a loupe.** Right-clicking the live preview fixes a **capture window** —
|
||||
the box/2 region centred on the click (`CaptureX/Y`, clamped to the master) — and parks a movable
|
||||
**2x magnifier** box. The box is a **live, always-on 2x view of that captured region**; dragging the
|
||||
box relocates it, **`UpdateZoomMove` never touches the capture** (creator ruling).
|
||||
- **Content = every layer.** The zoom pass runs on the **already-composited frame** (backdrop + all
|
||||
elements), so the magnified webcam/square/layers all show in the box. `SceneCompositor` skips the
|
||||
active `ZoomSceneElement` in every layer loop and `BlitZoom`s it **LAST (layer n+1, on top of
|
||||
everything)** via the pure static `ZoomMagnifier` (2x bilinear, 8.8 fixed-point, temp snapshot via
|
||||
`ArrayPool` so a box overlapping its own source never recurses, optional round clip).
|
||||
- **Rectangle = 4:3** ("more of a 640x480 aspect ratio"); Circle = square, Phone = 9:16 (kept).
|
||||
|
||||
Changes in this pass: new `Services/Compositor/ZoomMagnifier.cs`; `ZoomSceneElement`/`ZoomState`
|
||||
gained `CaptureX`/`CaptureY`; `MainViewModel.Zoom.cs` rewritten (`BuildZoomFrame`/`ZoomFrameBuilder`
|
||||
and the zoom-as-crop resolver case removed — no per-frame builder/cache anymore);
|
||||
`PreviewPane.xaml`: `ZoomBox` hoisted OUT of `OverlayCanvas` to be a **sibling of `CanvasGrid`**
|
||||
(renders above everything in the preview); `PreviewPane.xaml.cs` `UpdateZoomBox` samples `CanvasGrid`
|
||||
(the whole composed preview) at the fixed capture, `Stretch=Fill`. `ZoomFrameBuilderTests.cs` deleted
|
||||
(superseded); new `ZoomMagnifierTests.cs` (the "box over blue shows green capture, at 2x, all layers"
|
||||
proof + round-clip); `ZoomBoxInteractionTests.cs` rewritten (per-shape 4:3/9:16/square + capture
|
||||
anchor centred on the click + move-moves-only-the-box assertion + corner-clamp).
|
||||
|
||||
## Zoom feature state
|
||||
- Models: `ZoomState.cs`, `ZoomShape.cs` (Circle/VerticalRectangle/HorizontalRectangle), `ZoomSceneElement.cs` (TASK 51 build).
|
||||
- ViewModel (all in `MainViewModel.Zoom.cs`): activation/move/dismiss + layer healing + `BuildZoomFrame`; `MainViewModel.cs` core no longer holds zoom methods.
|
||||
- Output: zoom layer composited at z-index 1; output crop = tier; dim rects show the box frame while active.
|
||||
- Preview: `ZoomBox` samples `BackdropLayer` at 2x, circle→ellipse clip, rect/square outline per shape; `LeftPanel` hides the placeholder row via `IsZoomLayer`.
|
||||
- Models: `ZoomState.cs`, `ZoomShape.cs` (Circle/VerticalRectangle/HorizontalRectangle), `ZoomSceneElement.cs`.
|
||||
- ViewModel (`MainViewModel.Zoom.cs`): `ActivateZoom`/`ToggleZoom`/`DismissZoom`/`StartZoomMove`/`UpdateZoomMove`/`EndZoomMove` + `RebuildDimRectsForZoom`; transient element healed/mirrored by `HealZoomLayers`/`EnsureZoomLayer`.
|
||||
- Output: box composited last via `BlitZoom`/`ZoomMagnifier`; output crop = tier (box never the crop).
|
||||
- Preview: `ZoomBox` = sibling above `CanvasGrid`, samples the full composite at the fixed capture, 2x; circle→ellipse clip, rect/square outline per shape; `LeftPanel` hides the placeholder row via `IsZoomLayer`.
|
||||
|
||||
## Tests
|
||||
- Full suite: **404 passed / 2 failed** (both = known pre-existing environment flakes, unrelated to
|
||||
zoom): `AudioPipelineTests.Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback` (audio timing)
|
||||
and `LayerReorderPersistenceTests.RealMouseDrag_OnTheLayerList_PersistsTheReorder` (needs
|
||||
interactive desktop). Zoom tests 3/3 green, 0 build warnings.
|
||||
|
||||
## Known issues / follow-ups
|
||||
- **Background "Use default background" toggle still not working** (carried from 2026-10-07): binding exists (`UseDefaultBackground`), VM handler invalidates bake; investigate Source background logic when time permits.
|
||||
- `AudioPipelineTests.Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback` flaked once in a full-suite run (passed in isolation 27/27 and in the final full run) — treat as machine-contention flake, not a zoom regression.
|
||||
- `RealMouseDrag_OnTheLayerList_PersistsTheReorder` needs an interactive desktop (documented) — it passes when the window is live; don't read a failure there as a code bug without re-running.
|
||||
- **Background "Use default background" toggle still not working** (carried from 2026-10-07):
|
||||
binding exists (`UseDefaultBackground`), VM handler invalidates bake; investigate when time permits.
|
||||
- Preview shows editing chrome (dim rects, selection) inside the zoom box — accepted preview
|
||||
artefact; the output only shows composed content.
|
||||
|
||||
## Next step
|
||||
- User runs the app live/test to eyeball the zoom: right-click preview → centered box per shape, output contains a genuine 2x magnified backdrop under the other layers, dimming + outline correct, layers list unchanged. Then TASK 51 (webcam background Remove + Blur) implementation per `TASKS/task-51-webcam-bg-effects.md`, and the background-toggle investigation if still open.
|
||||
- User runs the app live to eyeball the corrected zoom: right-click preview → anchored capture stays
|
||||
fixed while the box is dragged; the box shows ALL layers at 2x on top of everything; rectangle box
|
||||
is 4:3. Then TASK 51 (webcam background Remove + Blur) per `TASKS/task-51-webcam-bg-effects.md`,
|
||||
and the background-toggle investigation if still open.
|
||||
Reference in New Issue
Block a user