|
|
|
@ -1,7 +1,9 @@ |
|
|
|
using System; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Linq; |
|
|
|
using System.Reactive.Subjects; |
|
|
|
using Avalonia.Controls; |
|
|
|
using Avalonia.Data; |
|
|
|
using Avalonia.Media; |
|
|
|
using Avalonia.Platform; |
|
|
|
using Avalonia.Rendering; |
|
|
|
@ -19,28 +21,18 @@ namespace Avalonia.Visuals.UnitTests.Rendering |
|
|
|
[Fact] |
|
|
|
public void First_Frame_Calls_UpdateScene_On_Dispatcher() |
|
|
|
{ |
|
|
|
var loop = new Mock<IRenderLoop>(); |
|
|
|
var root = new TestRoot(); |
|
|
|
|
|
|
|
var dispatcher = new Mock<IDispatcher>(); |
|
|
|
dispatcher.Setup(x => x.InvokeAsync(It.IsAny<Action>(), DispatcherPriority.Render)) |
|
|
|
.Callback<Action, DispatcherPriority>((a, p) => a()); |
|
|
|
|
|
|
|
var target = new DeferredRenderer( |
|
|
|
root, |
|
|
|
loop.Object, |
|
|
|
sceneBuilder: MockSceneBuilder(root).Object, |
|
|
|
dispatcher: dispatcher.Object); |
|
|
|
CreateTargetAndRunFrame(root, dispatcher: dispatcher.Object); |
|
|
|
|
|
|
|
target.Start(); |
|
|
|
RunFrame(loop); |
|
|
|
|
|
|
|
#if !NETCOREAPP1_1 // Delegate.Method is not available in netcoreapp1.1
|
|
|
|
dispatcher.Verify(x => |
|
|
|
x.InvokeAsync( |
|
|
|
It.Is<Action>(a => a.Method.Name == "UpdateScene"), |
|
|
|
DispatcherPriority.Render)); |
|
|
|
#endif
|
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
@ -49,15 +41,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering |
|
|
|
var loop = new Mock<IRenderLoop>(); |
|
|
|
var root = new TestRoot(); |
|
|
|
var sceneBuilder = MockSceneBuilder(root); |
|
|
|
var dispatcher = new ImmediateDispatcher(); |
|
|
|
var target = new DeferredRenderer( |
|
|
|
root, |
|
|
|
loop.Object, |
|
|
|
sceneBuilder: sceneBuilder.Object, |
|
|
|
dispatcher: dispatcher); |
|
|
|
|
|
|
|
target.Start(); |
|
|
|
RunFrame(loop); |
|
|
|
CreateTargetAndRunFrame(root, sceneBuilder: sceneBuilder.Object); |
|
|
|
|
|
|
|
sceneBuilder.Verify(x => x.UpdateAll(It.IsAny<Scene>())); |
|
|
|
} |
|
|
|
@ -68,12 +53,10 @@ namespace Avalonia.Visuals.UnitTests.Rendering |
|
|
|
var loop = new Mock<IRenderLoop>(); |
|
|
|
var root = new TestRoot(); |
|
|
|
var sceneBuilder = MockSceneBuilder(root); |
|
|
|
var dispatcher = new ImmediateDispatcher(); |
|
|
|
var target = new DeferredRenderer( |
|
|
|
root, |
|
|
|
loop.Object, |
|
|
|
sceneBuilder: sceneBuilder.Object, |
|
|
|
dispatcher: dispatcher); |
|
|
|
sceneBuilder: sceneBuilder.Object); |
|
|
|
|
|
|
|
target.Start(); |
|
|
|
IgnoreFirstFrame(loop, sceneBuilder); |
|
|
|
@ -127,12 +110,93 @@ namespace Avalonia.Visuals.UnitTests.Rendering |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Frame_Should_Create_Layer_For_Root() |
|
|
|
public void Should_Push_Opacity_For_Controls_With_Less_Than_1_Opacity() |
|
|
|
{ |
|
|
|
var root = new TestRoot |
|
|
|
{ |
|
|
|
Width = 100, |
|
|
|
Height = 100, |
|
|
|
Child = new Border |
|
|
|
{ |
|
|
|
Background = Brushes.Red, |
|
|
|
Opacity = 0.5, |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
root.Measure(Size.Infinity); |
|
|
|
root.Arrange(new Rect(root.DesiredSize)); |
|
|
|
|
|
|
|
var target = CreateTargetAndRunFrame(root); |
|
|
|
var context = GetLayerContext(target, root); |
|
|
|
var animation = new BehaviorSubject<double>(0.5); |
|
|
|
|
|
|
|
context.Verify(x => x.PushOpacity(0.5), Times.Once); |
|
|
|
context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once); |
|
|
|
context.Verify(x => x.PopOpacity(), Times.Once); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Should_Not_Draw_Controls_With_0_Opacity() |
|
|
|
{ |
|
|
|
var root = new TestRoot |
|
|
|
{ |
|
|
|
Width = 100, |
|
|
|
Height = 100, |
|
|
|
Child = new Border |
|
|
|
{ |
|
|
|
Background = Brushes.Red, |
|
|
|
Opacity = 0, |
|
|
|
Child = new Border |
|
|
|
{ |
|
|
|
Background = Brushes.Green, |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
root.Measure(Size.Infinity); |
|
|
|
root.Arrange(new Rect(root.DesiredSize)); |
|
|
|
|
|
|
|
var target = CreateTargetAndRunFrame(root); |
|
|
|
var context = GetLayerContext(target, root); |
|
|
|
var animation = new BehaviorSubject<double>(0.5); |
|
|
|
|
|
|
|
context.Verify(x => x.PushOpacity(0.5), Times.Never); |
|
|
|
context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Never); |
|
|
|
context.Verify(x => x.PopOpacity(), Times.Never); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Should_Push_Opacity_Mask() |
|
|
|
{ |
|
|
|
var root = new TestRoot |
|
|
|
{ |
|
|
|
Width = 100, |
|
|
|
Height = 100, |
|
|
|
Child = new Border |
|
|
|
{ |
|
|
|
Background = Brushes.Red, |
|
|
|
OpacityMask = Brushes.Green, |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
root.Measure(Size.Infinity); |
|
|
|
root.Arrange(new Rect(root.DesiredSize)); |
|
|
|
|
|
|
|
var target = CreateTargetAndRunFrame(root); |
|
|
|
var context = GetLayerContext(target, root); |
|
|
|
var animation = new BehaviorSubject<double>(0.5); |
|
|
|
|
|
|
|
context.Verify(x => x.PushOpacityMask(Brushes.Green, new Rect(0, 0, 100, 100)), Times.Once); |
|
|
|
context.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once); |
|
|
|
context.Verify(x => x.PopOpacityMask(), Times.Once); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Should_Create_Layer_For_Root() |
|
|
|
{ |
|
|
|
var loop = new Mock<IRenderLoop>(); |
|
|
|
var root = new TestRoot(); |
|
|
|
var rootLayer = new Mock<IRenderTargetBitmapImpl>(); |
|
|
|
var dispatcher = new ImmediateDispatcher(); |
|
|
|
|
|
|
|
var sceneBuilder = new Mock<ISceneBuilder>(); |
|
|
|
sceneBuilder.Setup(x => x.UpdateAll(It.IsAny<Scene>())) |
|
|
|
@ -143,23 +207,53 @@ namespace Avalonia.Visuals.UnitTests.Rendering |
|
|
|
}); |
|
|
|
|
|
|
|
var renderInterface = new Mock<IPlatformRenderInterface>(); |
|
|
|
var target = CreateTargetAndRunFrame(root, sceneBuilder: sceneBuilder.Object); |
|
|
|
|
|
|
|
var target = new DeferredRenderer( |
|
|
|
root, |
|
|
|
loop.Object, |
|
|
|
sceneBuilder: sceneBuilder.Object, |
|
|
|
//layerFactory: layers.Object,
|
|
|
|
dispatcher: dispatcher); |
|
|
|
Assert.Single(target.Layers); |
|
|
|
} |
|
|
|
|
|
|
|
target.Start(); |
|
|
|
[Fact] |
|
|
|
public void Should_Create_And_Delete_Layers_For_Controls_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, |
|
|
|
Child = new Canvas(), |
|
|
|
Opacity = 0.9, |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
root.Measure(Size.Infinity); |
|
|
|
root.Arrange(new Rect(root.DesiredSize)); |
|
|
|
|
|
|
|
var loop = new Mock<IRenderLoop>(); |
|
|
|
var target = CreateTargetAndRunFrame(root, loop: loop); |
|
|
|
|
|
|
|
Assert.Equal(new[] { root }, target.Layers.Select(x => x.LayerRoot)); |
|
|
|
|
|
|
|
var animation = new BehaviorSubject<double>(0.5); |
|
|
|
border.Bind(Border.OpacityProperty, animation, BindingPriority.Animation); |
|
|
|
RunFrame(loop); |
|
|
|
|
|
|
|
var context = Mock.Get(root.CreateRenderTarget().CreateDrawingContext(null)); |
|
|
|
context.Verify(x => x.CreateLayer(root.ClientSize)); |
|
|
|
Assert.Equal(new IVisual[] { root, border }, target.Layers.Select(x => x.LayerRoot)); |
|
|
|
|
|
|
|
animation.OnCompleted(); |
|
|
|
RunFrame(loop); |
|
|
|
|
|
|
|
Assert.Equal(new[] { root }, target.Layers.Select(x => x.LayerRoot)); |
|
|
|
} |
|
|
|
|
|
|
|
[Fact] |
|
|
|
public void Should_Create_And_Delete_Layers_For_Transparent_Controls() |
|
|
|
public void Should_Not_Create_Layer_For_Childless_Control_With_Animated_Opacity() |
|
|
|
{ |
|
|
|
Border border; |
|
|
|
var root = new TestRoot |
|
|
|
@ -176,51 +270,96 @@ namespace Avalonia.Visuals.UnitTests.Rendering |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
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 rootLayer = CreateLayer(); |
|
|
|
var borderLayer = CreateLayer(); |
|
|
|
var renderTargetContext = Mock.Get(root.CreateRenderTarget().CreateDrawingContext(null)); |
|
|
|
renderTargetContext.SetupSequence(x => x.CreateLayer(It.IsAny<Size>())) |
|
|
|
.Returns(rootLayer) |
|
|
|
.Returns(borderLayer); |
|
|
|
|
|
|
|
var loop = new Mock<IRenderLoop>(); |
|
|
|
var target = new DeferredRenderer( |
|
|
|
root, |
|
|
|
loop.Object, |
|
|
|
dispatcher: new ImmediateDispatcher()); |
|
|
|
root.Renderer = target; |
|
|
|
var target = CreateTargetAndRunFrame(root, loop: loop); |
|
|
|
|
|
|
|
target.Start(); |
|
|
|
RunFrame(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); |
|
|
|
|
|
|
|
var rootContext = Mock.Get(rootLayer.CreateDrawingContext(null)); |
|
|
|
var borderContext = Mock.Get(borderLayer.CreateDrawingContext(null)); |
|
|
|
root.Measure(Size.Infinity); |
|
|
|
root.Arrange(new Rect(root.DesiredSize)); |
|
|
|
|
|
|
|
rootContext.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once); |
|
|
|
rootContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Once); |
|
|
|
borderContext.Verify(x => x.FillRectangle(It.IsAny<IBrush>(), It.IsAny<Rect>(), It.IsAny<float>()), Times.Never); |
|
|
|
var target = CreateTargetAndRunFrame(root); |
|
|
|
var context = GetLayerContext(target, border); |
|
|
|
|
|
|
|
rootContext.ResetCalls(); |
|
|
|
borderContext.ResetCalls(); |
|
|
|
border.Opacity = 0.5; |
|
|
|
RunFrame(loop); |
|
|
|
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)); |
|
|
|
|
|
|
|
rootContext.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once); |
|
|
|
rootContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Never); |
|
|
|
borderContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Once); |
|
|
|
var target = CreateTargetAndRunFrame(root); |
|
|
|
var context = Mock.Get(target.RenderTarget.CreateDrawingContext(null)); |
|
|
|
var borderLayer = target.Layers[border].Bitmap; |
|
|
|
|
|
|
|
rootContext.ResetCalls(); |
|
|
|
borderContext.ResetCalls(); |
|
|
|
border.Opacity = 1; |
|
|
|
context.Verify(x => x.DrawImage(borderLayer, 0.5, It.IsAny<Rect>(), It.IsAny<Rect>())); |
|
|
|
} |
|
|
|
|
|
|
|
private DeferredRenderer CreateTargetAndRunFrame( |
|
|
|
TestRoot root, |
|
|
|
Mock<IRenderLoop> loop = null, |
|
|
|
ISceneBuilder sceneBuilder = null, |
|
|
|
IDispatcher dispatcher = null) |
|
|
|
{ |
|
|
|
loop = loop ?? new Mock<IRenderLoop>(); |
|
|
|
var target = new DeferredRenderer( |
|
|
|
root, |
|
|
|
loop.Object, |
|
|
|
sceneBuilder: sceneBuilder, |
|
|
|
dispatcher: dispatcher ?? new ImmediateDispatcher()); |
|
|
|
root.Renderer = target; |
|
|
|
target.Start(); |
|
|
|
RunFrame(loop); |
|
|
|
return target; |
|
|
|
} |
|
|
|
|
|
|
|
Mock.Get(borderLayer).Verify(x => x.Dispose()); |
|
|
|
rootContext.Verify(x => x.FillRectangle(Brushes.Red, new Rect(0, 0, 100, 100), 0), Times.Once); |
|
|
|
rootContext.Verify(x => x.FillRectangle(Brushes.Green, new Rect(0, 0, 100, 100), 0), Times.Once); |
|
|
|
borderContext.Verify(x => x.FillRectangle(It.IsAny<IBrush>(), It.IsAny<Rect>(), It.IsAny<float>()), Times.Never); |
|
|
|
private Mock<IDrawingContextImpl> GetLayerContext(DeferredRenderer renderer, IControl layerRoot) |
|
|
|
{ |
|
|
|
return Mock.Get(renderer.Layers[layerRoot].Bitmap.CreateDrawingContext(null)); |
|
|
|
} |
|
|
|
|
|
|
|
private void IgnoreFirstFrame(Mock<IRenderLoop> loop, Mock<ISceneBuilder> sceneBuilder) |
|
|
|
|