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
+5 -4
View File
@@ -788,15 +788,16 @@ Full suite 290/291 passing, the sole failure the pre-existing compositor pixel t
bottom) in master space. `MainViewModel` owns the frame (`_socialBarFrame`, rebuilt by `RenderSocialBarFrame`
on load/save/`NotifySocialsChanged`).
- **Privacy blur layer (2026-11-01):** `SourceType.Blur` is a Live-scene element that blurs the
composed-so-far buffer inside a rect or ellipse — it hides part of the creator's desktop without
hiding the brand. `SceneCompositor` treats all visible blurs as ONE group the first time one is
composed frame inside a rect or ellipse. **It is universal**: `SceneCompositor` applies the blur
group AFTER every layer has been composited (before the flash/social/ticker overlays), so it can
cover the webcam, chat, alerts — anything — which is the creator's "moment of privacy" case. Its
position in the layer list is cosmetic for that reason (it is added at the top), and the earlier
"never above the brand" rule is superseded for blurs: a blur over the logo does obscure it. `SceneCompositor` treats all visible blurs as ONE group the first time one is
reached in the z-order (`ApplyBlurLayers`), sorts them **strongest first** and passes a shared
coverage map to `RegionBlur.ApplyInPlace`, so an overlap resolves to the strongest single blur
rather than a blurred blur. `RegionBlur` shares the webcam's radius curve and constants
(`Downscale 4`, `MinRadius 1`, `MaxRadius 6`, `RadiusFor(strength)`), so a strength means the
same thing in both features; the webcam's `BackgroundEffectProcessor` delegates to it.
⛔ Z-order is policy: `AddSource` inserts the blur directly above the background (below the brand
logo), and `OnSceneElementsReordered` moves any blur that ends up above the brand back down.
Blur is `ElementKind.Dynamic`, so it is never baked into the static base and every render path
(pump, preview, thumbnails, scene dump) gets it from the one compositor.
- Known consideration: the pump reads the active scene on a background thread while the UI can still edit