diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj index a167cbe753..41e8857e6f 100644 --- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj +++ b/src/Avalonia.Visuals/Avalonia.Visuals.csproj @@ -73,10 +73,12 @@ + + diff --git a/src/Avalonia.Visuals/Media/IRadialGradientBrush.cs b/src/Avalonia.Visuals/Media/IRadialGradientBrush.cs new file mode 100644 index 0000000000..a9d902dabe --- /dev/null +++ b/src/Avalonia.Visuals/Media/IRadialGradientBrush.cs @@ -0,0 +1,23 @@ +namespace Avalonia.Media +{ + /// + /// Paints an area with a radial gradient. + /// + public interface IRadialGradientBrush : IGradientBrush + { + /// + /// Gets the start point for the gradient. + /// + RelativePoint Center { get; } + + /// + /// Gets the location of the two-dimensional focal point that defines the beginning of the gradient. + /// + RelativePoint GradientOrigin { get; } + + /// + /// Gets the horizontal and vertical radius of the outermost circle of the radial gradient. + /// + double Radius { get; } + } +} \ No newline at end of file diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs index 985e535b22..b46ee951f7 100644 --- a/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableLinearGradientBrush.cs @@ -12,7 +12,7 @@ namespace Avalonia.Media.Immutable public class ImmutableLinearGradientBrush : ImmutableGradientBrush, ILinearGradientBrush { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The gradient stops. /// The opacity of the brush. @@ -32,7 +32,7 @@ namespace Avalonia.Media.Immutable } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The brush from which this brush's properties should be copied. public ImmutableLinearGradientBrush(ILinearGradientBrush source) diff --git a/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs b/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs new file mode 100644 index 0000000000..cc2c7b3697 --- /dev/null +++ b/src/Avalonia.Visuals/Media/Immutable/ImmutableRadialGradientBrush.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; + +namespace Avalonia.Media.Immutable +{ + /// + /// A brush that draws with a radial gradient. + /// + public class ImmutableRadialGradientBrush : ImmutableGradientBrush, IRadialGradientBrush + { + /// + /// Initializes a new instance of the class. + /// + /// The gradient stops. + /// The opacity of the brush. + /// The spread method. + /// The start point for the gradient. + /// + /// The location of the two-dimensional focal point that defines the beginning of the gradient. + /// + /// + /// The horizontal and vertical radius of the outermost circle of the radial gradient. + /// + public ImmutableRadialGradientBrush( + IReadOnlyList 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; + } + + /// + /// Initializes a new instance of the class. + /// + /// The brush from which this brush's properties should be copied. + public ImmutableRadialGradientBrush(IRadialGradientBrush source) + : base(source) + { + Center = source.Center; + GradientOrigin = source.GradientOrigin; + Radius = source.Radius; + } + + /// + public RelativePoint Center { get; } + + /// + public RelativePoint GradientOrigin { get; } + + /// + public double Radius { get; } + } +} diff --git a/src/Avalonia.Visuals/Media/RadialGradientBrush.cs b/src/Avalonia.Visuals/Media/RadialGradientBrush.cs index 70657abe88..a78cd86afe 100644 --- a/src/Avalonia.Visuals/Media/RadialGradientBrush.cs +++ b/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 { /// - /// 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. /// - public sealed class RadialGradientBrush : GradientBrush + public sealed class RadialGradientBrush : GradientBrush, IRadialGradientBrush, IMutableBrush { /// /// Defines the property. @@ -54,10 +55,17 @@ namespace Avalonia.Media /// /// Gets or sets the horizontal and vertical radius of the outermost circle of the radial gradient. /// + // 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); } } + + /// + IBrush IMutableBrush.ToImmutable() + { + return new Immutable.ImmutableRadialGradientBrush(this); + } } } \ No newline at end of file diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index 20b5f54784..623f18ea0c 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/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; diff --git a/src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs index 72779096ff..be972c0173 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/RadialGradientBrushImpl.cs +++ b/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 }, diff --git a/tests/Avalonia.RenderTests/Avalonia.Cairo.RenderTests.v3.ncrunchproject b/tests/Avalonia.RenderTests/Avalonia.Cairo.RenderTests.v3.ncrunchproject index bc70262c50..5c2560547a 100644 --- a/tests/Avalonia.RenderTests/Avalonia.Cairo.RenderTests.v3.ncrunchproject +++ b/tests/Avalonia.RenderTests/Avalonia.Cairo.RenderTests.v3.ncrunchproject @@ -70,6 +70,9 @@ Avalonia.Cairo.RenderTests.Media.LinearGradientBrushTests.LinearGradientBrush_RedBlue_Horizontal_Fill + + Avalonia.Cairo.RenderTests.Media.RadialGradientBrushTests.RadialGradientBrush_RedBlue + True diff --git a/tests/Avalonia.RenderTests/Avalonia.RenderTests.projitems b/tests/Avalonia.RenderTests/Avalonia.RenderTests.projitems index ad3b182bdf..cf86e80d92 100644 --- a/tests/Avalonia.RenderTests/Avalonia.RenderTests.projitems +++ b/tests/Avalonia.RenderTests/Avalonia.RenderTests.projitems @@ -10,6 +10,7 @@ + diff --git a/tests/Avalonia.RenderTests/Media/RadialGradientBrushTests.cs b/tests/Avalonia.RenderTests/Media/RadialGradientBrushTests.cs new file mode 100644 index 0000000000..af4c17b328 --- /dev/null +++ b/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(); + } + } +} diff --git a/tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedBlue.expected.png b/tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedBlue.expected.png new file mode 100644 index 0000000000..fce116d98d Binary files /dev/null and b/tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedBlue.expected.png differ