Browse Source

Fix layers with opacity.

We were pushing the opacity twice.
pull/1306/head
Steven Kirk 9 years ago
parent
commit
1d1f31794e
  1. 28
      src/Avalonia.Visuals/Rendering/DeferredRenderer.cs
  2. 6
      src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs
  3. 10
      src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs
  4. 88
      tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

28
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@ -28,7 +28,6 @@ namespace Avalonia.Rendering
private bool _running;
private Scene _scene;
private IRenderTarget _renderTarget;
private DirtyVisuals _dirty;
private IRenderTargetBitmapImpl _overlay;
private bool _updateQueued;
@ -77,7 +76,7 @@ namespace Avalonia.Rendering
Contract.Requires<ArgumentNullException>(renderTarget != null);
_root = root;
_renderTarget = renderTarget;
RenderTarget = renderTarget;
_sceneBuilder = sceneBuilder ?? new SceneBuilder();
Layers = new RenderLayers();
}
@ -98,6 +97,11 @@ namespace Avalonia.Rendering
/// </summary>
internal RenderLayers Layers { get; }
/// <summary>
/// Gets the current render target.
/// </summary>
internal IRenderTarget RenderTarget { get; private set; }
/// <inheritdoc/>
public void AddDirty(IVisual visual)
{
@ -177,9 +181,9 @@ namespace Avalonia.Rendering
bool renderOverlay = DrawDirtyRects || DrawFps;
bool composite = false;
if (_renderTarget == null)
if (RenderTarget == null)
{
_renderTarget = ((IRenderRoot)_root).CreateRenderTarget();
RenderTarget = ((IRenderRoot)_root).CreateRenderTarget();
}
if (renderOverlay)
@ -195,7 +199,7 @@ namespace Avalonia.Rendering
if (scene.Generation != _lastSceneId)
{
context = _renderTarget.CreateDrawingContext(this);
context = RenderTarget.CreateDrawingContext(this);
Layers.Update(scene, context);
RenderToLayers(scene);
@ -212,13 +216,13 @@ namespace Avalonia.Rendering
if (renderOverlay)
{
context = context ?? _renderTarget.CreateDrawingContext(this);
context = context ?? RenderTarget.CreateDrawingContext(this);
RenderOverlay(scene, context);
RenderComposite(scene, context);
}
else if (composite)
{
context = context ?? _renderTarget.CreateDrawingContext(this);
context = context ?? RenderTarget.CreateDrawingContext(this);
RenderComposite(scene, context);
}
@ -228,8 +232,8 @@ namespace Avalonia.Rendering
catch (RenderTargetCorruptedException ex)
{
Logging.Logger.Information("Renderer", this, "Render target was corrupted. Exception: {0}", ex);
_renderTarget?.Dispose();
_renderTarget = null;
RenderTarget?.Dispose();
RenderTarget = null;
}
}
@ -241,7 +245,9 @@ namespace Avalonia.Rendering
if (!clipBounds.IsEmpty && node.Opacity > 0)
{
node.BeginRender(context);
var isLayerRoot = node.Visual == layer;
node.BeginRender(context, isLayerRoot);
foreach (var operation in node.DrawOperations)
{
@ -255,7 +261,7 @@ namespace Avalonia.Rendering
Render(context, (VisualNode)child, layer, clipBounds);
}
node.EndRender(context);
node.EndRender(context, isLayerRoot);
}
}
}

6
src/Avalonia.Visuals/Rendering/SceneGraph/IVisualNode.cs

@ -72,13 +72,15 @@ namespace Avalonia.Rendering.SceneGraph
/// Sets up the drawing context for rendering the node's geometry.
/// </summary>
/// <param name="context">The drawing context.</param>
void BeginRender(IDrawingContextImpl context);
/// <param name="skipOpacity">Whether to skip pushing the control's opacity.</param>
void BeginRender(IDrawingContextImpl context, bool skipOpacity);
/// <summary>
/// Resets the drawing context after rendering the node's geometry.
/// </summary>
/// <param name="context">The drawing context.</param>
void EndRender(IDrawingContextImpl context);
/// <param name="skipOpacity">Whether to skip popping the control's opacity.</param>
void EndRender(IDrawingContextImpl context, bool skipOpacity);
/// <summary>
/// Hit test the geometry in this node.

10
src/Avalonia.Visuals/Rendering/SceneGraph/VisualNode.cs

@ -218,15 +218,15 @@ namespace Avalonia.Rendering.SceneGraph
}
/// <inheritdoc/>
public void BeginRender(IDrawingContextImpl context)
public void BeginRender(IDrawingContextImpl context, bool skipOpacity)
{
if (ClipToBounds)
{
context.Transform = Matrix.Identity;
context.PushClip(ClipBounds);
}
if (Opacity != 1)
if (Opacity != 1 && !skipOpacity)
{
context.PushOpacity(Opacity);
}
@ -240,14 +240,14 @@ namespace Avalonia.Rendering.SceneGraph
}
/// <inheritdoc/>
public void EndRender(IDrawingContextImpl context)
public void EndRender(IDrawingContextImpl context, bool skipOpacity)
{
if (GeometryClip != null)
{
context.PopGeometryClip();
}
if (Opacity != 1)
if (Opacity != 1 && !skipOpacity)
{
context.PopOpacity();
}

88
tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

@ -201,6 +201,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
{
Background = Brushes.Green,
Child = new Canvas(),
Opacity = 0.9,
}
}
};
@ -225,6 +226,93 @@ namespace Avalonia.Visuals.UnitTests.Rendering
Assert.Equal(new[] { root }, target.Layers.Select(x => x.LayerRoot));
}
[Fact]
public void Should_Not_Create_Layer_For_Childless_Control_With_Animated_Opacity()
{
Border border;
var root = new TestRoot
{
Width = 100,
Height = 100,
Child = new Border
{
Background = Brushes.Red,
Child = border = new Border
{
Background = Brushes.Green,
}
}
};
var animation = new BehaviorSubject<double>(0.5);
border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
var loop = new Mock<IRenderLoop>();
var target = CreateTargetAndRunFrame(root, loop: loop);
Assert.Single(target.Layers);
}
[Fact]
public void Should_Not_Push_Opacity_For_Transparent_Layer_Root_Control()
{
Border border;
var root = new TestRoot
{
Width = 100,
Height = 100,
Child = border = new Border
{
Background = Brushes.Red,
Child = new Canvas(),
}
};
var animation = new BehaviorSubject<double>(0.5);
border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
var target = CreateTargetAndRunFrame(root);
var context = GetLayerContext(target, border);
context.Verify(x => x.PushOpacity(0.5), Times.Never);
context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once);
context.Verify(x => x.PopOpacity(), Times.Never);
}
[Fact]
public void Should_Draw_Transparent_Layer_With_Correct_Opacity()
{
Border border;
var root = new TestRoot
{
Width = 100,
Height = 100,
Child = border = new Border
{
Background = Brushes.Red,
Child = new Canvas(),
}
};
var animation = new BehaviorSubject<double>(0.5);
border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation);
root.Measure(Size.Infinity);
root.Arrange(new Rect(root.DesiredSize));
var target = CreateTargetAndRunFrame(root);
var context = Mock.Get(target.RenderTarget.CreateDrawingContext(null));
var borderLayer = target.Layers[border].Bitmap;
context.Verify(x => x.DrawImage(borderLayer, 0.5, It.IsAny<Rect>(), It.IsAny<Rect>()));
}
private DeferredRenderer CreateTargetAndRunFrame(
TestRoot root,
Mock<IRenderLoop> loop = null,

Loading…
Cancel
Save