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:
@@ -81,18 +81,10 @@ public partial class MainViewModel
|
||||
: sourceType == SourceType.Blur ? 160 : 0,
|
||||
};
|
||||
|
||||
if (sourceType == SourceType.Blur)
|
||||
{
|
||||
// ⛔ Z-order is policy, not preference: the blur sits directly above the background and
|
||||
// BELOW the brand logo, which can never be obscured (creator ruling 2026-11-01).
|
||||
_sceneGraph.AddElement(scene, source);
|
||||
var blurIndex = scene.Elements.IndexOf(source);
|
||||
if (blurIndex >= 0) scene.Elements.Move(blurIndex, BlurInsertIndex(scene));
|
||||
}
|
||||
else
|
||||
{
|
||||
_sceneGraph.AddElement(scene, source);
|
||||
}
|
||||
// The blur is universal (creator ruling 2026-11-01): the compositor applies it over every
|
||||
// layer, so its position in the list does not affect the output. It is added at the top so
|
||||
// the layer list reads "this covers everything", and no reorder clamp is needed.
|
||||
_sceneGraph.AddElement(scene, source);
|
||||
|
||||
if (sourceType == SourceType.WebSource)
|
||||
{
|
||||
@@ -133,38 +125,10 @@ public partial class MainViewModel
|
||||
/// X/Y/W/H) in the same Source rows.</summary>
|
||||
public void OnSceneElementsReordered()
|
||||
{
|
||||
// A blur may never end up above the brand logo (creator ruling 2026-11-01). Only offending
|
||||
// blurs move — a legitimate reorder inside the layer list is left exactly as dropped.
|
||||
if (StagedScene is { } scene)
|
||||
{
|
||||
var brand = scene.Elements.OfType<Source>().FirstOrDefault(s => s.IsBrandLogo);
|
||||
if (brand != null)
|
||||
{
|
||||
foreach (var blur in scene.Elements.OfType<Source>().Where(s => s.IsBlur).ToList())
|
||||
{
|
||||
var brandIndex = scene.Elements.IndexOf(brand);
|
||||
var blurIndex = scene.Elements.IndexOf(blur);
|
||||
if (blurIndex > brandIndex) scene.Elements.Move(blurIndex, brandIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (StagedScene != null) _sceneGraph.InvalidateBake(StagedScene);
|
||||
SaveLayoutNow();
|
||||
}
|
||||
|
||||
/// <summary>The slot a privacy blur belongs in: immediately after the background, which puts it
|
||||
/// below the brand logo (the logo is inserted directly above the background too, so the blur
|
||||
/// must land before it).</summary>
|
||||
private static int BlurInsertIndex(Scene scene)
|
||||
{
|
||||
var background = scene.Elements.OfType<Source>().FirstOrDefault(s => s.IsBackground);
|
||||
if (background != null) return scene.Elements.IndexOf(background) + 1;
|
||||
|
||||
var brand = scene.Elements.OfType<Source>().FirstOrDefault(s => s.IsBrandLogo);
|
||||
return brand != null ? scene.Elements.IndexOf(brand) : 0;
|
||||
}
|
||||
|
||||
// Duplicate resource names get an incrementing suffix with no space: Image,
|
||||
// Image2, Image3… The next free number is derived from the names actually in
|
||||
// the scene, so deleting a middle resource never collides with a survivor.
|
||||
|
||||
Reference in New Issue
Block a user