Browse Source

Make RadialGradientBrush work

With immediate renderer. Also added a render test and fixed some
problems with D2D implementation.
Steven Kirk 8 years ago
parent
commit
e4eaf729a9

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

@@ -73,10 +73,12 @@
     <Compile Include="Media\IImageBrush.cs" />
     <Compile Include="Media\ILinearGradientBrush.cs" />
     <Compile Include="Media\Immutable\ImmutableGradientBrush.cs" />
+    <Compile Include="Media\Immutable\ImmutableRadialGradientBrush.cs" />
     <Compile Include="Media\Immutable\ImmutableLinearGradientBrush.cs" />
     <Compile Include="Media\Immutable\ImmutableTileBrush.cs" />
     <Compile Include="Media\Immutable\ImmutableSolidColorBrush.cs" />
     <Compile Include="Media\IMutableBrush.cs" />
+    <Compile Include="Media\IRadialGradientBrush.cs" />
     <Compile Include="Media\ITileBrush.cs" />
     <Compile Include="Media\IVisualBrush.cs" />
     <Compile Include="Media\TextWrapping.cs" />

+ 23 - 0
src/Avalonia.Visuals/Media/IRadialGradientBrush.cs

@@ -0,0 +1,23 @@
+namespace Avalonia.Media
+{
+    /// <summary>
+    /// Paints an area with a radial gradient.
+    /// </summary>
+    public interface IRadialGradientBrush : IGradientBrush
+    {
+        /// <summary>
+        /// Gets the start point for the gradient.
+        /// </summary>
+        RelativePoint Center { get; }
+
+        /// <summary>
+        /// Gets the location of the two-dimensional focal point that defines the beginning of the gradient.
+        /// </summary>
+        RelativePoint GradientOrigin { get; }
+
+        /// <summary>
+        /// Gets the horizontal and vertical radius of the outermost circle of the radial gradient.
+        /// </summary>
+        double Radius { get; }
+    }
+}

+ 2 - 2
src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs

@@ -12,7 +12,7 @@ namespace Avalonia.Media.Immutable
     public class ImmutableLinearGradientBrush : ImmutableGradientBrush, ILinearGradientBrush
     {
         /// <summary>
-        /// Initializes a new instance of the <see cref="ImmutableGradientBrush"/> class.
+        /// Initializes a new instance of the <see cref="ImmutableLinearGradientBrush"/> class.
         /// </summary>
         /// <param name="gradientStops">The gradient stops.</param>
         /// <param name="opacity">The opacity of the brush.</param>
@@ -32,7 +32,7 @@ namespace Avalonia.Media.Immutable
         }
 
         /// <summary>
-        /// Initializes a new instance of the <see cref="ImmutableGradientBrush"/> class.
+        /// Initializes a new instance of the <see cref="ImmutableLinearGradientBrush"/> class.
         /// </summary>
         /// <param name="source">The brush from which this brush's properties should be copied.</param>
         public ImmutableLinearGradientBrush(ILinearGradientBrush source)

+ 59 - 0
src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs

@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+
+namespace Avalonia.Media.Immutable
+{
+    /// <summary>
+    /// A brush that draws with a radial gradient.
+    /// </summary>
+    public class ImmutableRadialGradientBrush : ImmutableGradientBrush, IRadialGradientBrush
+    {
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ImmutableRadialGradientBrush"/> class.
+        /// </summary>
+        /// <param name="gradientStops">The gradient stops.</param>
+        /// <param name="opacity">The opacity of the brush.</param>
+        /// <param name="spreadMethod">The spread method.</param>
+        /// <param name="center">The start point for the gradient.</param>
+        /// <param name="gradientOrigin">
+        /// The location of the two-dimensional focal point that defines the beginning of the gradient.
+        /// </param>
+        /// <param name="radius">
+        /// The horizontal and vertical radius of the outermost circle of the radial gradient.
+        /// </param>
+        public ImmutableRadialGradientBrush(
+            IReadOnlyList<GradientStop> gradientStops,
+            double opacity = 1,
+            GradientSpreadMethod spreadMethod = GradientSpreadMethod.Pad,
+            RelativePoint? center = null,
+            RelativePoint? gradientOrigin = null,
+            double radius = 0.5)
+            : base(gradientStops, opacity, spreadMethod)
+        {
+            Center = center ?? RelativePoint.Center;
+            GradientOrigin = gradientOrigin ?? RelativePoint.Center;
+            Radius = radius;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ImmutableRadialGradientBrush"/> class.
+        /// </summary>
+        /// <param name="source">The brush from which this brush's properties should be copied.</param>
+        public ImmutableRadialGradientBrush(IRadialGradientBrush source)
+            : base(source)
+        {
+            Center = source.Center;
+            GradientOrigin = source.GradientOrigin;
+            Radius = source.Radius;
+        }
+
+        /// <inheritdoc/>
+        public RelativePoint Center { get; }
+
+        /// <inheritdoc/>
+        public RelativePoint GradientOrigin { get; }
+
+        /// <inheritdoc/>
+        public double Radius { get; }
+    }
+}

+ 11 - 3
src/Avalonia.Visuals/Media/RadialGradientBrush.cs

@@ -1,13 +1,14 @@
 // 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;
+
 namespace Avalonia.Media
 {
     /// <summary>
-    /// Paints an area with a radial gradient. A focal point defines the beginning of the gradient, 
-    /// and a circle defines the end point of the gradient.
+    /// Paints an area with a radial gradient.
     /// </summary>
-    public sealed class RadialGradientBrush : GradientBrush
+    public sealed class RadialGradientBrush : GradientBrush, IRadialGradientBrush, IMutableBrush
     {
         /// <summary>
         /// Defines the <see cref="Center"/> property.
@@ -54,10 +55,17 @@ namespace Avalonia.Media
         /// <summary>
         /// Gets or sets the horizontal and vertical radius of the outermost circle of the radial gradient.
         /// </summary>
+        // TODO: This appears to always be relative so should use a RelativeSize struct or something.
         public double Radius
         {
             get { return GetValue(RadiusProperty); }
             set { SetValue(RadiusProperty, value); }
         }
+
+        /// <inheritdoc/>
+        IBrush IMutableBrush.ToImmutable()
+        {
+            return new Immutable.ImmutableRadialGradientBrush(this);
+        }
     }
 }

+ 1 - 1
src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs

@@ -320,7 +320,7 @@ namespace Avalonia.Direct2D1.Media
         {
             var solidColorBrush = brush as Avalonia.Media.ISolidColorBrush;
             var linearGradientBrush = brush as Avalonia.Media.ILinearGradientBrush;
-            var radialGradientBrush = brush as Avalonia.Media.RadialGradientBrush;
+            var radialGradientBrush = brush as Avalonia.Media.IRadialGradientBrush;
             var imageBrush = brush as Avalonia.Media.IImageBrush;
             var visualBrush = brush as Avalonia.Media.IVisualBrush;
 

+ 5 - 5
src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs

@@ -8,7 +8,7 @@ namespace Avalonia.Direct2D1.Media
     public class RadialGradientBrushImpl : BrushImpl
     {
         public RadialGradientBrushImpl(
-            Avalonia.Media.RadialGradientBrush brush,
+            Avalonia.Media.IRadialGradientBrush brush,
             SharpDX.Direct2D1.RenderTarget target,
             Size destinationSize)
         {
@@ -24,11 +24,11 @@ namespace Avalonia.Direct2D1.Media
             }).ToArray();
 
             var centerPoint = brush.Center.ToPixels(destinationSize);
-            var GradientOriginOffset = brush.GradientOrigin.ToPixels(destinationSize);
+            var gradientOrigin = brush.GradientOrigin.ToPixels(destinationSize) - centerPoint;
             
             // Note: Direct2D supports RadiusX and RadiusY but Cairo backend supports only Radius property
-            var radiusX = brush.Radius;
-            var radiusY = brush.Radius;
+            var radiusX = brush.Radius * destinationSize.Width;
+            var radiusY = brush.Radius * destinationSize.Height;
 
             using (var stops = new SharpDX.Direct2D1.GradientStopCollection(
                 target,
@@ -40,7 +40,7 @@ namespace Avalonia.Direct2D1.Media
                     new SharpDX.Direct2D1.RadialGradientBrushProperties
                     {
                         Center = centerPoint.ToSharpDX(),
-                        GradientOriginOffset = GradientOriginOffset.ToSharpDX(),
+                        GradientOriginOffset = gradientOrigin.ToSharpDX(),
                         RadiusX = (float)radiusX,
                         RadiusY = (float)radiusY
                     },

+ 3 - 0
tests/Avalonia.RenderTests/Avalonia.Cairo.RenderTests.v3.ncrunchproject

@@ -70,6 +70,9 @@
       <NamedTestSelector>
         <TestName>Avalonia.Cairo.RenderTests.Media.LinearGradientBrushTests.LinearGradientBrush_RedBlue_Horizontal_Fill</TestName>
       </NamedTestSelector>
+      <NamedTestSelector>
+        <TestName>Avalonia.Cairo.RenderTests.Media.RadialGradientBrushTests.RadialGradientBrush_RedBlue</TestName>
+      </NamedTestSelector>
     </IgnoredTests>
     <PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
   </Settings>

+ 1 - 0
tests/Avalonia.RenderTests/Avalonia.RenderTests.projitems

@@ -10,6 +10,7 @@
   </PropertyGroup>
   <ItemGroup>
     <Compile Include="$(MSBuildThisFileDirectory)GeometryClippingTests.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Media\RadialGradientBrushTests.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)OpacityMaskTests.cs" />
     <Compile Include="Media\FormattedTextImplTests.cs" />
     <Compile Include="Controls\ImageTests.cs" />

+ 56 - 0
tests/Avalonia.RenderTests/Media/RadialGradientBrushTests.cs

@@ -0,0 +1,56 @@
+// 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 Avalonia.Controls;
+using Avalonia.Media;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Xunit;
+
+#if AVALONIA_CAIRO
+namespace Avalonia.Cairo.RenderTests.Media
+#elif AVALONIA_SKIA
+namespace Avalonia.Skia.RenderTests
+#else
+namespace Avalonia.Direct2D1.RenderTests.Media
+#endif
+{
+    public class RadialGradientBrushTests : TestBase
+    {
+        public RadialGradientBrushTests() : base(@"Media\RadialGradientBrush")
+        {
+        }
+
+#if AVALONIA_SKIA_SKIP_FAIL
+        [Fact(Skip = "FIXME")]
+#else
+        [Fact]
+#endif
+        public async Task RadialGradientBrush_RedBlue()
+        {
+            Decorator target = new Decorator
+            {
+                Padding = new Thickness(8),
+                Width = 200,
+                Height = 200,
+                Child = new Border
+                {
+                    Background = new RadialGradientBrush
+                    {
+                        GradientStops =
+                        {
+                            new GradientStop { Color = Colors.Red, Offset = 0 },
+                            new GradientStop { Color = Colors.Blue, Offset = 1 }
+                        }
+                    }
+                }
+            };
+
+            await RenderToFile(target);
+            CompareImages();
+        }
+    }
+}

BIN
tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedBlue.expected.png