diff --git a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs index 98528a128a..05a8daaec8 100644 --- a/src/Skia/Avalonia.Skia/DrawingContextImpl.cs +++ b/src/Skia/Avalonia.Skia/DrawingContextImpl.cs @@ -583,14 +583,45 @@ namespace Avalonia.Skia var center = radialGradient.Center.ToPixels(targetSize).ToSKPoint(); var radius = (float)(radialGradient.Radius * targetSize.Width); - // TODO: There is no SetAlpha in SkiaSharp - //paint.setAlpha(128); + var origin = radialGradient.GradientOrigin.ToPixels(targetSize).ToSKPoint(); - // would be nice to cache these shaders possibly? - using (var shader = - SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode)) + if (origin.Equals(center)) { - paintWrapper.Paint.Shader = shader; + // when the origin is the same as the center the Skia RadialGradient acts the same as D2D + using (var shader = + SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode)) + { + paintWrapper.Paint.Shader = shader; + } + } + else + { + // when the origin is different to the center use a two point ConicalGradient to match the behaviour of D2D + + // reverse the order of the stops to match D2D + var reversedColors = new SKColor[stopColors.Length]; + Array.Copy(stopColors, reversedColors, stopColors.Length); + Array.Reverse(reversedColors); + + // and then reverse the reference point of the stops + var reversedStops = new float[stopOffsets.Length]; + for (var i = 0; i < stopOffsets.Length; i++) + { + reversedStops[i] = stopOffsets[i]; + if (reversedStops[i] > 0 && reversedStops[i] < 1) + { + reversedStops[i] = Math.Abs(1 - stopOffsets[i]); + } + } + + // compose with a background colour of the final stop to match D2D's behaviour of filling with the final color + using (var shader = SKShader.CreateCompose( + SKShader.CreateColor(reversedColors[0]), + SKShader.CreateTwoPointConicalGradient(center, radius, origin, 0, reversedColors, reversedStops, tileMode) + )) + { + paintWrapper.Paint.Shader = shader; + } } break; diff --git a/tests/Avalonia.RenderTests/Media/RadialGradientBrushTests.cs b/tests/Avalonia.RenderTests/Media/RadialGradientBrushTests.cs index 9beb860bee..2941b8dc34 100644 --- a/tests/Avalonia.RenderTests/Media/RadialGradientBrushTests.cs +++ b/tests/Avalonia.RenderTests/Media/RadialGradientBrushTests.cs @@ -18,7 +18,7 @@ namespace Avalonia.Direct2D1.RenderTests.Media public RadialGradientBrushTests() : base(@"Media\RadialGradientBrush") { } - + [Fact] public async Task RadialGradientBrush_RedBlue() { @@ -43,5 +43,127 @@ namespace Avalonia.Direct2D1.RenderTests.Media await RenderToFile(target); CompareImages(); } + + /// + /// Tests using a GradientOrigin that falls inside of the circle described by Center/Radius. + /// + [Fact] + public async Task RadialGradientBrush_RedBlue_Offset_Inside() + { + 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 } + }, + GradientOrigin = new RelativePoint(0.25, 0.25, RelativeUnit.Relative) + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + + /// + /// Tests using a GradientOrigin that falls outside of the circle described by Center/Radius. + /// + [Fact] + public async Task RadialGradientBrush_RedBlue_Offset_Outside() + { + 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 } + }, + GradientOrigin = new RelativePoint(0.1, 0.1, RelativeUnit.Relative) + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + + /// + /// Tests using a GradientOrigin that falls inside of the circle described by Center/Radius. + /// + [Fact] + public async Task RadialGradientBrush_RedGreenBlue_Offset_Inside() + { + 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.Green, Offset = 0.5 }, + new GradientStop { Color = Colors.Blue, Offset = 1 } + }, + GradientOrigin = new RelativePoint(0.25, 0.25, RelativeUnit.Relative), + Center = new RelativePoint(0.5, 0.5, RelativeUnit.Relative), + Radius = 0.5 + } + } + }; + + await RenderToFile(target); + CompareImages(); + } + + /// + /// Tests using a GradientOrigin that falls outside of the circle described by Center/Radius. + /// + [Fact] + public async Task RadialGradientBrush_RedGreenBlue_Offset_Outside() + { + 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.Green, Offset = 0.25 }, + new GradientStop { Color = Colors.Blue, Offset = 1 } + }, + GradientOrigin = new RelativePoint(0.1, 0.1, RelativeUnit.Relative), + Center = new RelativePoint(0.5, 0.5, RelativeUnit.Relative), + Radius = 0.5 + } + } + }; + + await RenderToFile(target); + CompareImages(); + } } } diff --git a/tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedBlue_Offset_Inside.expected.png b/tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedBlue_Offset_Inside.expected.png new file mode 100644 index 0000000000..4d690abee5 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedBlue_Offset_Inside.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedBlue_Offset_Outside.expected.png b/tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedBlue_Offset_Outside.expected.png new file mode 100644 index 0000000000..667aa9151e Binary files /dev/null and b/tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedBlue_Offset_Outside.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedGreenBlue_Offset_Inside.expected.png b/tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedGreenBlue_Offset_Inside.expected.png new file mode 100644 index 0000000000..1d660f79d1 Binary files /dev/null and b/tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedGreenBlue_Offset_Inside.expected.png differ diff --git a/tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedGreenBlue_Offset_Outside.expected.png b/tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedGreenBlue_Offset_Outside.expected.png new file mode 100644 index 0000000000..ea1009b54a Binary files /dev/null and b/tests/TestFiles/Direct2D1/Media/RadialGradientBrush/RadialGradientBrush_RedGreenBlue_Offset_Outside.expected.png differ diff --git a/tests/TestFiles/Skia/Media/RadialGradientBrush/RadialGradientBrush_RedBlue_Offset_Inside.expected.png b/tests/TestFiles/Skia/Media/RadialGradientBrush/RadialGradientBrush_RedBlue_Offset_Inside.expected.png new file mode 100644 index 0000000000..4d690abee5 Binary files /dev/null and b/tests/TestFiles/Skia/Media/RadialGradientBrush/RadialGradientBrush_RedBlue_Offset_Inside.expected.png differ diff --git a/tests/TestFiles/Skia/Media/RadialGradientBrush/RadialGradientBrush_RedBlue_Offset_Outside.expected.png b/tests/TestFiles/Skia/Media/RadialGradientBrush/RadialGradientBrush_RedBlue_Offset_Outside.expected.png new file mode 100644 index 0000000000..667aa9151e Binary files /dev/null and b/tests/TestFiles/Skia/Media/RadialGradientBrush/RadialGradientBrush_RedBlue_Offset_Outside.expected.png differ diff --git a/tests/TestFiles/Skia/Media/RadialGradientBrush/RadialGradientBrush_RedGreenBlue_Offset_Inside.expected.png b/tests/TestFiles/Skia/Media/RadialGradientBrush/RadialGradientBrush_RedGreenBlue_Offset_Inside.expected.png new file mode 100644 index 0000000000..1d660f79d1 Binary files /dev/null and b/tests/TestFiles/Skia/Media/RadialGradientBrush/RadialGradientBrush_RedGreenBlue_Offset_Inside.expected.png differ diff --git a/tests/TestFiles/Skia/Media/RadialGradientBrush/RadialGradientBrush_RedGreenBlue_Offset_Outside.expected.png b/tests/TestFiles/Skia/Media/RadialGradientBrush/RadialGradientBrush_RedGreenBlue_Offset_Outside.expected.png new file mode 100644 index 0000000000..ea1009b54a Binary files /dev/null and b/tests/TestFiles/Skia/Media/RadialGradientBrush/RadialGradientBrush_RedGreenBlue_Offset_Outside.expected.png differ