Web source: container == content — element grid + selection box sized to the cropped frame's canvas extent

- FindContentBounds already trimmed the bitmap; the grid/box still rendered at the uncropped user
  size, so Fill stretched the tight widget into a bigger frame = dead-between-content-and-border.
- ContentCanvasSize maps the crop rect to canvas units (1920/pixW per axis, DPI-correct) and the
  preview event now carries (bitmap, canvasW, canvasH).
- OnWebView2PreviewBitmapChanged sets the element Width/Height to that size; the grid and the
  SelectionOverlay (both bound to the element's Width/Height) now equal the widget exactly.
- IsPreviewDragging (set in MainWindow drag/resize) suppresses auto-fit mid-gesture.
- Tests 10/10 (alpha bounds, canvas-size mapping incl. DPI, control size); 0 warnings.
This commit is contained in:
2026-08-28 12:08:04 -07:00
parent bccdb4892c
commit ab29ec801d
7 changed files with 103 additions and 51 deletions
+11 -2
View File
@@ -20,7 +20,7 @@ public sealed class WebView2Manager : IDisposable
private readonly Dispatcher _dispatcher;
private readonly Dictionary<string, WebSourceSession> _sessions = new();
public event Action<string, WriteableBitmap>? PreviewBitmapChanged;
public event Action<string, WriteableBitmap, double, double>? PreviewBitmapChanged;
private sealed class WebSourceSession
{
@@ -182,6 +182,14 @@ public sealed class WebView2Manager : IDisposable
return (minX, minY, maxX - minX + 1, maxY - minY + 1);
}
// The cropped pixel extent, expressed in MASTER CANVAS units (the WebView2 is
// 1920×1080 DIPs and captures at its physical size, so 1920/pixW maps capture
// pixels back to canvas units). The element grid + selection box are sized to
// this, so the container matches the content exactly instead of Fill-stretching
// a cropped bitmap into a larger frame.
internal static (double W, double H) ContentCanvasSize(int cropW, int cropH, int pixW, int pixH)
=> (cropW * (1920.0 / pixW), cropH * (1080.0 / pixH));
private async Task CaptureFrame(string sourceId)
{
if (!_sessions.TryGetValue(sourceId, out var session) || session.Disposed) return;
@@ -232,11 +240,12 @@ public sealed class WebView2Manager : IDisposable
wb.WritePixels(new Int32Rect(0, 0, cropW, cropH), outPixels, cropW * 4, 0);
wb.Freeze();
var (canvasW, canvasH) = ContentCanvasSize(cropW, cropH, pixW, pixH);
session.LatestFrame = new VideoFrame(cropW, cropH, outPixels);
_ = _dispatcher.BeginInvoke(() =>
{
PreviewBitmapChanged?.Invoke(sourceId, wb);
PreviewBitmapChanged?.Invoke(sourceId, wb, canvasW, canvasH);
});
}
catch (ObjectDisposedException) { }