1
0
Эх сурвалжийг харах

WIP: Getting resizing working.

Steven Kirk 9 жил өмнө
parent
commit
6d3ca92a4c

+ 1 - 1
samples/ControlCatalog/MainWindow.xaml.cs

@@ -10,7 +10,7 @@ namespace ControlCatalog
         {
             this.InitializeComponent();
             this.AttachDevTools();
-            Renderer.DrawDirtyRects = Renderer.DrawFps = true;
+            //Renderer.DrawDirtyRects = Renderer.DrawFps = true;
         }
 
         private void InitializeComponent()

+ 45 - 23
src/Avalonia.Visuals/Rendering/DeferredRenderer.cs

@@ -87,6 +87,28 @@ namespace Avalonia.Rendering
         {
         }
 
+        private void Render(Scene scene)
+        {
+            _rendering = true;
+            _totalFrames++;
+            _dirtyRectsDisplay.Tick();
+
+            if (scene.Size != Size.Empty)
+            {
+                if (scene.Id != _lastSceneId)
+                {
+                    _layers.RemoveUnused(scene);
+                    RenderToLayers(scene);
+                    _lastSceneId = scene.Id;
+                }
+
+                RenderOverlay(scene);
+                RenderComposite(scene);
+            }
+
+            _rendering = false;
+        }
+
         private void Render(IDrawingContextImpl context, VisualNode node, IVisual layer, Rect clipBounds)
         {
             if (node.LayerRoot == layer)
@@ -118,7 +140,7 @@ namespace Avalonia.Rendering
             {
                 foreach (var layer in scene.Layers)
                 {
-                    var renderTarget = GetRenderTargetForLayer(layer.LayerRoot);
+                    var renderTarget = GetRenderTargetForLayer(scene, layer.LayerRoot);
                     var node = (VisualNode)scene.FindNode(layer.LayerRoot);
 
                     using (var context = renderTarget.CreateDrawingContext())
@@ -140,11 +162,11 @@ namespace Avalonia.Rendering
             }
         }
 
-        private void RenderOverlay()
+        private void RenderOverlay(Scene scene)
         {
             if (DrawFps || DrawDirtyRects)
             {
-                var overlay = GetOverlay(_root.ClientSize);
+                var overlay = GetOverlay(scene.Size);
 
                 using (var context = overlay.CreateDrawingContext())
                 {
@@ -214,11 +236,11 @@ namespace Avalonia.Rendering
 
                 using (var context = _renderTarget.CreateDrawingContext())
                 {
-                    var clientRect = new Rect(_root.ClientSize);
+                    var clientRect = new Rect(scene.Size);
 
                     foreach (var layer in scene.Layers)
                     {
-                        var renderLayer = _layers.Get(layer.LayerRoot);
+                        var renderLayer = _layers[layer.LayerRoot];
                         context.DrawImage(renderLayer.Bitmap, layer.Opacity, clientRect, clientRect);
                     }
 
@@ -263,7 +285,7 @@ namespace Avalonia.Rendering
                 }
 
                 _dirty.Clear();
-                _root.Invalidate(new Rect(_root.ClientSize));
+                _root.Invalidate(new Rect(scene.Size));
             }
             finally
             {
@@ -284,10 +306,6 @@ namespace Avalonia.Rendering
                 _dispatcher.InvokeAsync(UpdateScene, DispatcherPriority.Render);
             }
 
-            _rendering = true;
-            _totalFrames++;
-            _dirtyRectsDisplay.Tick();
-
             Scene scene;
 
             lock (_scene)
@@ -295,17 +313,7 @@ namespace Avalonia.Rendering
                 scene = _scene;
             }
 
-            if (scene.Id != _lastSceneId)
-            {
-                _layers.RemoveUnused(scene);
-                RenderToLayers(scene);
-                _lastSceneId = scene.Id;
-            }
-
-            RenderOverlay();
-            RenderComposite(scene);
-
-            _rendering = false;
+            Render(scene);
         }
 
         private IRenderTargetBitmapImpl GetOverlay(Size size)
@@ -322,9 +330,23 @@ namespace Avalonia.Rendering
             return _overlay;
         }
 
-        private IRenderTargetBitmapImpl GetRenderTargetForLayer(IVisual layerRoot)
+        private IRenderTargetBitmapImpl GetRenderTargetForLayer(Scene scene, IVisual layerRoot)
         {
-            return (_layers.Get(layerRoot) ?? _layers.Add(layerRoot, _root.ClientSize)).Bitmap;
+            RenderLayer result;
+
+            if (_layers.TryGetValue(layerRoot, out result))
+            {
+                if (result.Size != scene.Size)
+                {
+                    result.ResizeBitmap(scene.Size);
+                }
+            }
+            else
+            {
+                _layers.Add(layerRoot, scene.Size);
+            }
+
+            return _layers[layerRoot].Bitmap;
         }
     }
 }

+ 2 - 2
src/Avalonia.Visuals/Rendering/DirtyRects.cs

@@ -21,7 +21,7 @@ namespace Avalonia.Rendering
                 {
                     var r = _rects[i];
 
-                    if (r.Inflate(1).Intersects(rect))
+                    if (r.Intersects(rect))
                     {
                         _rects[i] = r.Union(rect);
                         return;
@@ -36,7 +36,7 @@ namespace Avalonia.Rendering
         {
             for (var i = _rects.Count - 1; i >= 0; --i)
             {
-                var a = _rects[i].Inflate(1);
+                var a = _rects[i];
 
                 for (var j = 0; j < i; ++j)
                 {

+ 26 - 10
src/Avalonia.Visuals/Rendering/RenderLayer.cs

@@ -1,27 +1,48 @@
 using System;
+using Avalonia.Media;
 using Avalonia.Platform;
 using Avalonia.VisualTree;
 
 namespace Avalonia.Rendering
 {
-    public class RenderLayer : IComparable<RenderLayer>
+    public class RenderLayer
     {
+        private readonly IRenderLayerFactory _factory;
+
         public RenderLayer(
-            IRenderTargetBitmapImpl bitmap,
+            IRenderLayerFactory factory,
             Size size,
             IVisual layerRoot)
         {
-            Bitmap = bitmap;
+            _factory = factory;
+            Bitmap = factory.CreateLayer(layerRoot, size);
             Size = size;
             LayerRoot = layerRoot;
             Order = GetDistanceFromRenderRoot(layerRoot);
         }
 
-        public IRenderTargetBitmapImpl Bitmap { get; }
-        public Size Size { get; }
+        public IRenderTargetBitmapImpl Bitmap { get; private set; }
+        public Size Size { get; private set; }
         public IVisual LayerRoot { get; }
         public int Order { get; }
 
+        public void ResizeBitmap(Size size)
+        {
+            if (Size != size)
+            {
+                var resized = _factory.CreateLayer(LayerRoot, size);
+
+                using (var context = resized.CreateDrawingContext())
+                {
+                    context.Clear(Colors.Black);
+                    context.DrawImage(Bitmap, 1, new Rect(Size), new Rect(Size));
+                    Bitmap.Dispose();
+                    Bitmap = resized;
+                    Size = size;
+                }
+            }
+        }
+
         private static int GetDistanceFromRenderRoot(IVisual visual)
         {
             var root = visual as IRenderRoot;
@@ -42,10 +63,5 @@ namespace Avalonia.Rendering
 
             return result;
         }
-
-        public int CompareTo(RenderLayer other)
-        {
-            return Order - other.Order;
-        }
     }
 }

+ 10 - 8
src/Avalonia.Visuals/Rendering/RenderLayers.cs

@@ -18,14 +18,16 @@ namespace Avalonia.Rendering
             _factory = factory;
         }
 
+        public int Count => _inner.Count;
+        public RenderLayer this[IVisual layerRoot] => _index[layerRoot];
+
         public RenderLayer Add(IVisual layerRoot, Size size)
         {
             RenderLayer result;
 
             if (!_index.TryGetValue(layerRoot, out result))
             {
-                var bitmap = _factory.CreateLayer(layerRoot, size);
-                result = new RenderLayer(bitmap, size, layerRoot);
+                result = new RenderLayer(_factory, size, layerRoot);
                 _inner.Add(result);
                 _index.Add(layerRoot, result);
             }
@@ -33,12 +35,7 @@ namespace Avalonia.Rendering
             return result;
         }
 
-        public RenderLayer Get(IVisual layerRoot)
-        {
-            RenderLayer result;
-            _index.TryGetValue(layerRoot, out result);
-            return result;
-        }
+        public bool Exists(IVisual layerRoot) => _index.ContainsKey(layerRoot);
 
         public void RemoveUnused(Scene scene)
         {
@@ -55,6 +52,11 @@ namespace Avalonia.Rendering
             }
         }
 
+        public bool TryGetValue(IVisual layerRoot, out RenderLayer value)
+        {
+            return _index.TryGetValue(layerRoot, out value);
+        }
+
         public IEnumerator<RenderLayer> GetEnumerator() => _inner.GetEnumerator();
         IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
     }

+ 10 - 2
src/Avalonia.Visuals/Rendering/SceneGraph/Scene.cs

@@ -21,6 +21,8 @@ namespace Avalonia.Rendering.SceneGraph
         {
             Contract.Requires<ArgumentNullException>(root != null);
 
+            var renderRoot = root.Visual as IRenderRoot;
+
             _index = index;
             Root = root;
             Layers = layers;
@@ -31,6 +33,7 @@ namespace Avalonia.Rendering.SceneGraph
         public int Id { get; }
         public SceneLayers Layers { get; }
         public IVisualNode Root { get; }
+        public Size Size { get; set; }
 
         public void Add(IVisualNode node)
         {
@@ -42,8 +45,13 @@ namespace Avalonia.Rendering.SceneGraph
         public Scene Clone()
         {
             var index = new Dictionary<IVisual, IVisualNode>();
-            var root = (VisualNode)Clone((VisualNode)Root, null, index);
-            var result = new Scene(root, index, Layers.Clone(), Id + 1);
+            var root = Clone((VisualNode)Root, null, index);
+
+            var result = new Scene(root, index, Layers.Clone(), Id + 1)
+            {
+                Size = Size,
+            };
+
             return result;
         }
 

+ 37 - 0
src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs

@@ -16,6 +16,7 @@ namespace Avalonia.Rendering.SceneGraph
             Contract.Requires<ArgumentNullException>(scene != null);
             Dispatcher.UIThread.VerifyAccess();
 
+            scene.Size = (scene.Root.Visual as IRenderRoot)?.ClientSize ?? scene.Root.Visual.Bounds.Size;
             scene.Layers.GetOrAdd(scene.Root.Visual);
 
             using (var impl = new DeferredDrawingContextImpl(scene.Layers))
@@ -33,6 +34,11 @@ namespace Avalonia.Rendering.SceneGraph
 
             var node = (VisualNode)scene.FindNode(visual);
 
+            if (visual == scene.Root.Visual)
+            {
+                UpdateSize(scene);
+            }
+
             if (visual.VisualRoot != null)
             {
                 if (visual.IsVisible)
@@ -195,6 +201,37 @@ namespace Avalonia.Rendering.SceneGraph
             }
         }
 
+        private void UpdateSize(Scene scene)
+        {
+            var newSize = (scene.Root.Visual as IRenderRoot)?.ClientSize ?? scene.Root.Visual.Bounds.Size;
+
+            if (scene.Size != newSize)
+            {
+                var oldSize = scene.Size;
+
+                scene.Size = newSize;
+
+                Rect horizontalDirtyRect = Rect.Empty;
+                Rect verticalDirtyRect = Rect.Empty;
+
+                if (newSize.Width > oldSize.Width)
+                {
+                    horizontalDirtyRect = new Rect(oldSize.Width, 0, newSize.Width - oldSize.Width, oldSize.Height);
+                }
+
+                if (newSize.Height > oldSize.Height)
+                {
+                    verticalDirtyRect = new Rect(0, oldSize.Height, newSize.Width, newSize.Height - oldSize.Height);
+                }
+
+                foreach (var layer in scene.Layers)
+                {
+                    layer.Dirty.Add(horizontalDirtyRect);
+                    layer.Dirty.Add(verticalDirtyRect);
+                }
+            }
+        }
+
         private static VisualNode CreateNode(Scene scene, IVisual visual, VisualNode parent)
         {
             var node = new VisualNode(visual, parent);

+ 2 - 1
src/Windows/Avalonia.Direct2D1/Media/Imaging/BitmapImpl.cs

@@ -82,7 +82,8 @@ namespace Avalonia.Direct2D1.Media
         /// </summary>
         public Bitmap WicImpl
         {
-            get; }
+            get;
+        }
 
         /// <summary>
         /// Gets a Direct2D bitmap to use on the specified render target.

+ 1 - 0
src/Windows/Avalonia.Direct2D1/RenderTarget.cs

@@ -8,6 +8,7 @@ using Avalonia.Platform;
 using Avalonia.Win32.Interop;
 using SharpDX;
 using SharpDX.Direct2D1;
+using SharpDX.Mathematics.Interop;
 using DrawingContext = Avalonia.Media.DrawingContext;
 using DwFactory = SharpDX.DirectWrite.Factory;
 

+ 23 - 0
tests/Avalonia.Controls.UnitTests/TopLevelTests.cs

@@ -163,6 +163,29 @@ namespace Avalonia.Controls.UnitTests
             }
         }
 
+        [Fact]
+        public void Window_Resize_Notification_Should_Notify_Renderer_Dirty()
+        {
+            var renderer = new Mock<IRenderer>();
+            var services = TestServices.StyledWindow.With(renderer: (_, __) => renderer.Object);
+
+            using (UnitTestApplication.Start(services))
+            {
+                var impl = new Mock<ITopLevelImpl>();
+                impl.SetupAllProperties();
+                impl.Setup(x => x.ClientSize).Returns(new Size(123, 456));
+                impl.Setup(x => x.Scaling).Returns(1);
+
+                var target = new TestTopLevel(impl.Object);
+                target.Measure(new Size(123, 456));
+                Assert.True(target.IsMeasureValid);
+
+                impl.Object.Resized(new Size(100, 200));
+
+                renderer.Verify(x => x.AddDirty(target));
+            }
+        }
+
         [Fact]
         public void Activate_Should_Call_Impl_Activate()
         {

+ 1 - 1
tests/Avalonia.UnitTests/TestRoot.cs

@@ -42,7 +42,7 @@ namespace Avalonia.UnitTests
 
         public int NameScopeUnregisteredSubscribers { get; private set; }
 
-        public Size ClientSize => new Size(100, 100);
+        public Size ClientSize { get; set;} = new Size(100, 100);
 
         public Size MaxClientSize => Size.Infinity;
 

+ 1 - 0
tests/Avalonia.Visuals.UnitTests/Rendering/DeferredRendererTests.cs

@@ -118,6 +118,7 @@ namespace Avalonia.Visuals.UnitTests.Rendering
             sceneBuilder.Setup(x => x.UpdateAll(It.IsAny<Scene>()))
                 .Callback<Scene>(scene =>
                 {
+                    scene.Size = root.ClientSize;
                     scene.Layers.Add(root).Dirty.Add(new Rect(root.ClientSize));
                 });
 

+ 45 - 0
tests/Avalonia.Visuals.UnitTests/Rendering/SceneGraph/SceneBuilderTests.cs

@@ -510,6 +510,51 @@ namespace Avalonia.Visuals.UnitTests.Rendering.SceneGraph
             }
         }
 
+        [Fact]
+        public void Resizing_Scene_Should_Add_DirtyRects()
+        {
+            using (TestApplication())
+            {
+                Decorator decorator;
+                Border border;
+                Canvas canvas;
+                var tree = new TestRoot
+                {
+                    Child = decorator = new Decorator
+                    {
+                        Margin = new Thickness(0, 10, 0, 0),
+                        Child = border = new Border
+                        {
+                            Opacity = 0.5,
+                            Background = Brushes.Red,
+                            Child = canvas = new Canvas(),
+                        }
+                    }
+                };
+
+                var scene = new Scene(tree);
+                var sceneBuilder = new SceneBuilder();
+                sceneBuilder.UpdateAll(scene);
+
+                Assert.Equal(new Size(100, 100), scene.Size);
+
+                tree.ClientSize = new Size(110, 120);
+                scene = scene.Clone();
+                sceneBuilder.Update(scene, tree);
+
+                Assert.Equal(new Size(110, 120), scene.Size);
+
+                var expected = new[]
+                {
+                    new Rect(100, 0, 10, 100),
+                    new Rect(0, 100, 110, 20),
+                };
+
+                Assert.Equal(expected, scene.Layers[tree].Dirty.ToArray());
+                Assert.Equal(expected, scene.Layers[border].Dirty.ToArray());
+            }
+        }
+
         private IDisposable TestApplication()
         {
             return UnitTestApplication.Start(