feat(blur): universal — the blur covers every layer, webcam included

The creator found the use case that reframes the feature: a moment of privacy while the webcam is
on. A blur that can only cover the desktop cannot do that.

- SceneCompositor applies the blur group AFTER every element is composited (still before the
  flash / social bar / ticker / zoom overlays), so it covers webcam, chat, alerts — anything. Its
  position in the layer list no longer affects the output, so it is added at the top and the
  reorder clamp is gone.
- Supersedes the earlier "blur sits below the brand" rule: a universal blur does obscure the logo
  when it covers it. Called out in frame-pipeline.md so it is not re-litigated.
- Preview mirrors it exactly: the blur host moved OUT of CanvasGrid to a sibling above it, so the
  VisualBrush samples every layer (and cannot sample itself). It used to sample only the backdrop,
  which is why a circle over the webcam would have shown sharp pixels in the editor and blurred
  ones in the stream.
This commit is contained in:
2026-11-01 13:29:15 -08:00
parent bb80ec8370
commit bb51164f2f
5 changed files with 29 additions and 73 deletions
+8 -14
View File
@@ -174,25 +174,13 @@ public sealed class SceneCompositor
}
// Composite the above-split layers (dynamic + any static above them) per frame.
var blurApplied = false;
for (var i = Math.Max(0, split); i < scene.Elements.Count; i++)
{
var element = scene.Elements[i];
if (!element.IsVisible) continue;
// Privacy blurs are handled as ONE group the first time one is reached: overlapping
// boxes must resolve to the strongest single blur, never to a blurred blur. The group
// runs at the lowest blur's z-position, which is where the creator put it — directly
// above the background and below the brand logo.
if (element is Source { Type: SourceType.Blur })
{
if (!blurApplied)
{
blurApplied = true;
ApplyBlurLayers(scene, buffer, cropW, cropH, options, timings);
}
continue;
}
// Blurs are applied once, after every layer (see below) — they are universal.
if (element is Source { Type: SourceType.Blur }) continue;
switch (element)
{
@@ -207,6 +195,12 @@ public sealed class SceneCompositor
BlitBakedElement(buffer, cropW, cropH, options, element, frame, timings, $"#{i} {element.Name}");
}
// ⛔ Privacy blur is UNIVERSAL (creator ruling 2026-11-01): it is applied after every
// layer has been composited, so it can cover the webcam, chat, alerts, anything — the
// creator's "moment of privacy" case. It is the only layer that ignores its z-position;
// overlapping blurs still resolve to the strongest single one.
ApplyBlurLayers(scene, buffer, cropW, cropH, options, timings);
if (flashFrame != null)
{
var tFlash = System.Diagnostics.Stopwatch.GetTimestamp();