Jelajahi Sumber

WIP: More work on VisualBrush

Laying the foundations for getting `VisualBrush` working with
`DeferredRenderer`.
Steven Kirk 8 tahun lalu
induk
melakukan
5261ab303f

+ 35 - 1
src/Avalonia.Controls/Control.cs

@@ -17,7 +17,9 @@ using Avalonia.Input;
 using Avalonia.Interactivity;
 using Avalonia.Logging;
 using Avalonia.LogicalTree;
+using Avalonia.Rendering;
 using Avalonia.Styling;
+using Avalonia.VisualTree;
 
 namespace Avalonia.Controls
 {
@@ -33,7 +35,7 @@ namespace Avalonia.Controls
     /// - Implements <see cref="IStyleable"/> to allow styling to work on the control.
     /// - Implements <see cref="ILogical"/> to form part of a logical tree.
     /// </remarks>
-    public class Control : InputElement, IControl, INamed, ISetInheritanceParent, ISetLogicalParent, ISupportInitialize
+    public class Control : InputElement, IControl, INamed, ISetInheritanceParent, ISetLogicalParent, ISupportInitialize, IVisualBrushInitialize
     {
         /// <summary>
         /// Defines the <see cref="DataContext"/> property.
@@ -460,6 +462,38 @@ namespace Avalonia.Controls
             InheritanceParent = parent;
         }
 
+        /// <inheritdoc/>
+        void IVisualBrushInitialize.EnsureInitialized()
+        {
+            if (VisualRoot == null)
+            {
+                if (!IsInitialized)
+                {
+                    foreach (var i in this.GetSelfAndVisualDescendents())
+                    {
+                        var c = i as IControl;
+
+                        if (c?.IsInitialized == false)
+                        {
+                            var init = c as ISupportInitialize;
+
+                            if (init != null)
+                            {
+                                init.BeginInit();
+                                init.EndInit();
+                            }
+                        }
+                    }
+                }
+
+                if (!IsArrangeValid)
+                {
+                    Measure(Size.Infinity);
+                    Arrange(new Rect(DesiredSize));
+                }
+            }
+        }
+
         /// <summary>
         /// Adds a pseudo-class to be set when a property is true.
         /// </summary>

+ 1 - 0
src/Avalonia.Visuals/Avalonia.Visuals.csproj

@@ -119,6 +119,7 @@
     <Compile Include="Rendering\DefaultRenderLoop.cs" />
     <Compile Include="Rendering\IRenderLayerFactory.cs" />
     <Compile Include="Rendering\IVisualBrushRenderer.cs" />
+    <Compile Include="Rendering\IVisualBrushInitialize.cs" />
     <Compile Include="Rendering\RendererBase.cs" />
     <Compile Include="Rendering\RenderLayer.cs" />
     <Compile Include="Rendering\RenderLayers.cs" />

+ 20 - 0
src/Avalonia.Visuals/Rendering/IVisualBrushInitialize.cs

@@ -0,0 +1,20 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using Avalonia.Media;
+
+namespace Avalonia.Rendering
+{
+    /// <summary>
+    /// Internal interface for initializing controls that are to be used as the viusal in a
+    /// <see cref="VisualBrush"/>.
+    /// </summary>
+    public interface IVisualBrushInitialize
+    {
+        /// <summary>
+        /// Ensures that the control is ready to use as the visual in a visual brush.
+        /// </summary>
+        void EnsureInitialized();
+    }
+}

+ 12 - 1
src/Avalonia.Visuals/Rendering/IVisualBrushRenderer.cs

@@ -1,4 +1,7 @@
-using System;
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
 using Avalonia.Media;
 
 namespace Avalonia.Rendering
@@ -8,6 +11,14 @@ namespace Avalonia.Rendering
     /// </summary>
     public interface IVisualBrushRenderer
     {
+        /// <summary>
+        /// Gets the size of the intermediate render target to which the visual brush should be
+        /// drawn.
+        /// </summary>
+        /// <param name="brush">The visual brush.</param>
+        /// <returns>The size of the intermediate render target to create.</returns>
+        Size GetRenderTargetSize(VisualBrush brush);
+
         /// <summary>
         /// Renders a visual brush to a bitmap.
         /// </summary>

+ 6 - 0
src/Avalonia.Visuals/Rendering/ImmediateRenderer.cs

@@ -79,6 +79,12 @@ namespace Avalonia.Rendering
         {
         }
 
+        Size IVisualBrushRenderer.GetRenderTargetSize(VisualBrush brush)
+        {
+            (brush.Visual as IVisualBrushInitialize)?.EnsureInitialized();
+            return brush.Visual?.Bounds.Size ?? Size.Empty;
+        }
+
         void IVisualBrushRenderer.RenderVisualBrush(IDrawingContextImpl context, VisualBrush brush)
         {
             var visual = brush.Visual;

+ 19 - 18
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@@ -348,24 +348,27 @@ namespace Avalonia.Direct2D1.Media
             {
                 if (_visualBrushRenderer != null)
                 {
-                    TileBrushImplHelper.EnsureInitialized(visualBrush.Visual);
+                    var intermediateSize = _visualBrushRenderer.GetRenderTargetSize(visualBrush);
 
-                    using (var intermediate = new BitmapRenderTarget(
-                        _renderTarget,
-                        CompatibleRenderTargetOptions.None,
-                        visualBrush.Visual.Bounds.Size.ToSharpDX()))
+                    if (intermediateSize.Width >= 1 && intermediateSize.Height >= 1)
                     {
-                        using (var ctx = new RenderTarget(intermediate).CreateDrawingContext(_visualBrushRenderer))
+                        using (var intermediate = new BitmapRenderTarget(
+                            _renderTarget,
+                            CompatibleRenderTargetOptions.None,
+                            intermediateSize.ToSharpDX()))
                         {
-                            intermediate.Clear(null);
-                            _visualBrushRenderer.RenderVisualBrush(ctx, visualBrush);
+                            using (var ctx = new RenderTarget(intermediate).CreateDrawingContext(_visualBrushRenderer))
+                            {
+                                intermediate.Clear(null);
+                                _visualBrushRenderer.RenderVisualBrush(ctx, visualBrush);
+                            }
+
+                            return new ImageBrushImpl(
+                                visualBrush,
+                                _renderTarget,
+                                new D2DBitmapImpl(intermediate.Bitmap),
+                                destinationSize);
                         }
-
-                        return new ImageBrushImpl(
-                            visualBrush,
-                            _renderTarget,
-                            new D2DBitmapImpl(intermediate.Bitmap),
-                            destinationSize);
                     }
                 }
                 else
@@ -373,10 +376,8 @@ namespace Avalonia.Direct2D1.Media
                     throw new NotSupportedException("No IVisualBrushRenderer was supplied to DrawingContextImpl.");
                 }
             }
-            else
-            {
-                return new SolidColorBrushImpl((Avalonia.Media.SolidColorBrush)null, _renderTarget);
-            }
+
+            return new SolidColorBrushImpl(null, _renderTarget);
         }
 
         public void PushGeometryClip(Avalonia.Media.Geometry clip)