fix(web): feed full canvas to compositor — transparent margins reveal layers beneath (take-21)
This commit is contained in:
+15
-11
@@ -30,13 +30,13 @@ public sealed class WebView2Manager : IDisposable
|
|||||||
public bool Disposed;
|
public bool Disposed;
|
||||||
public bool Initialized;
|
public bool Initialized;
|
||||||
|
|
||||||
// Buffer reuse (2026-09-04, same lesson as the capture/camera rings): every
|
// Buffer reuse (2026-09-04): every capture tick used to mint a fresh
|
||||||
// capture tick used to mint a fresh 8.3MB canvas array + a fresh crop array
|
// 8.3MB canvas array + a fresh crop array (~83MB/s of LOH at 10Hz →
|
||||||
// (~83MB/s of LOH at 10Hz → gen2 pauses surfaced as the pump's 35-40ms
|
// gen2 pauses surfaced as the pump's 35-40ms "worst render" spikes).
|
||||||
// "worst render" spikes). Canvas scratch is reused while the size holds;
|
// CanvasScratch is reused while the size holds. The frame handed to the
|
||||||
// the frame handed to the compositor rotates an 8-deep ring with a monotonic
|
// compositor is always the FULL canvas (transparent margins reveal layers
|
||||||
// Epoch (identity-keyed consumers must see each generation). The preview
|
// beneath); the crop is only for the preview WriteableBitmap. Epoch is
|
||||||
// WriteableBitmap is recreated only when the cropped size changes.
|
// monotonic per canvas generation (identity-keyed consumers see each frame).
|
||||||
public byte[]? CanvasScratch;
|
public byte[]? CanvasScratch;
|
||||||
public readonly byte[]?[] OutRing = new byte[8][];
|
public readonly byte[]?[] OutRing = new byte[8][];
|
||||||
public int OutRingNext;
|
public int OutRingNext;
|
||||||
@@ -192,9 +192,13 @@ public sealed class WebView2Manager : IDisposable
|
|||||||
// bounding box of non-transparent pixels (the background is injected
|
// bounding box of non-transparent pixels (the background is injected
|
||||||
// transparent, so the alpha channel IS the widget). This is immune to
|
// transparent, so the alpha channel IS the widget). This is immune to
|
||||||
// DOM timing (fonts/images/animations), needs no JS round-trip, and
|
// DOM timing (fonts/images/animations), needs no JS round-trip, and
|
||||||
// CANNOT break rendering: if the widget fills the canvas the box equals
|
// CANNOT break the render: if the widget fills the canvas the box equals
|
||||||
// the full frame (identical to the confirmed-good uncropped display); if
|
// the full frame; if it floats in transparent margin the box is the
|
||||||
// it floats in transparent margin, the crop maps it flush under the box.
|
// measured content extent — used ONLY for the preview bitmap / selection.
|
||||||
|
// The frame handed to the compositor is always the FULL canvas, so the
|
||||||
|
// page's transparent margins keep revealing the layers beneath (the
|
||||||
|
// "black square over webcam" was the crop fed to the compositor:
|
||||||
|
// UniformToFill zoomed the opaque content box to cover the whole element).
|
||||||
internal static (int X, int Y, int W, int H) FindContentBounds(byte[] pixels, int pixW, int pixH, int stride)
|
internal static (int X, int Y, int W, int H) FindContentBounds(byte[] pixels, int pixW, int pixH, int stride)
|
||||||
{
|
{
|
||||||
int minX = pixW, minY = pixH, maxX = -1, maxY = -1;
|
int minX = pixW, minY = pixH, maxX = -1, maxY = -1;
|
||||||
@@ -266,7 +270,7 @@ public sealed class WebView2Manager : IDisposable
|
|||||||
session.PreviewBitmap = new WriteableBitmap(cropW, cropH, 96, 96, PixelFormats.Bgra32, null);
|
session.PreviewBitmap = new WriteableBitmap(cropW, cropH, 96, 96, PixelFormats.Bgra32, null);
|
||||||
session.PreviewBitmap.WritePixels(new Int32Rect(0, 0, cropW, cropH), outPixels, cropW * 4, 0);
|
session.PreviewBitmap.WritePixels(new Int32Rect(0, 0, cropW, cropH), outPixels, cropW * 4, 0);
|
||||||
|
|
||||||
session.LatestFrame = new VideoFrame(cropW, cropH, outPixels) { Epoch = ++session.Epoch };
|
session.LatestFrame = new VideoFrame(pixW, pixH, pixels) { Epoch = ++session.Epoch };
|
||||||
|
|
||||||
// The preview bitmap is now MUTABLE and reused (CameraManager precedent):
|
// The preview bitmap is now MUTABLE and reused (CameraManager precedent):
|
||||||
// WPF re-renders it after WritePixels; handlers must not Freeze it.
|
// WPF re-renders it after WritePixels; handlers must not Freeze it.
|
||||||
|
|||||||
Reference in New Issue
Block a user