Skip to content

Commit e29a256

Browse files
committed
Skia - Only reverse radial gradient stops if we reach cover the whole content bounds
1 parent 03f91a2 commit e29a256

File tree

4 files changed

+126
-73
lines changed

4 files changed

+126
-73
lines changed

src/Skia/Avalonia.Skia/DrawingContextImpl.cs

Lines changed: 100 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -915,90 +915,117 @@ private static void ConfigureGradientBrush(ref PaintWrapper paintWrapper, Rect t
915915
break;
916916
}
917917
case IRadialGradientBrush radialGradient:
918-
{
919-
var centerPoint = radialGradient.Center.ToPixels(targetRect);
920-
var center = centerPoint.ToSKPoint();
921-
922-
var radiusX = (radialGradient.RadiusX.ToValue(targetRect.Width));
923-
var radiusY = (radialGradient.RadiusY.ToValue(targetRect.Height));
924-
925-
var originPoint = radialGradient.GradientOrigin.ToPixels(targetRect);
926-
927-
Matrix? transform = null;
928-
929-
if (radiusX != radiusY)
930-
transform =
931-
Matrix.CreateTranslation(-centerPoint)
932-
* Matrix.CreateScale(1, radiusY / radiusX)
933-
* Matrix.CreateTranslation(centerPoint);
934-
935-
936-
if (radialGradient.Transform != null)
937918
{
938-
var transformOrigin = radialGradient.TransformOrigin.ToPixels(targetRect);
939-
var offset = Matrix.CreateTranslation(transformOrigin);
940-
var brushTransform = (-offset) * radialGradient.Transform.Value * (offset);
941-
transform = transform.HasValue ? transform * brushTransform : brushTransform;
942-
}
943-
944-
if (originPoint.Equals(centerPoint))
945-
{
946-
// when the origin is the same as the center the Skia RadialGradient acts the same as D2D
947-
using (var shader =
948-
transform.HasValue
949-
? SKShader.CreateRadialGradient(center, (float)radiusX, stopColors, stopOffsets, tileMode,
950-
transform.Value.ToSKMatrix())
951-
: SKShader.CreateRadialGradient(center, (float)radiusX, stopColors, stopOffsets, tileMode)
952-
)
919+
var centerPoint = radialGradient.Center.ToPixels(targetRect);
920+
var center = centerPoint.ToSKPoint();
921+
922+
var radiusX = (radialGradient.RadiusX.ToValue(targetRect.Width));
923+
var radiusY = (radialGradient.RadiusY.ToValue(targetRect.Height));
924+
925+
var originPoint = radialGradient.GradientOrigin.ToPixels(targetRect);
926+
927+
Matrix? transform = null;
928+
929+
if (radiusX != radiusY)
930+
transform =
931+
Matrix.CreateTranslation(-centerPoint)
932+
* Matrix.CreateScale(1, radiusY / radiusX)
933+
* Matrix.CreateTranslation(centerPoint);
934+
935+
936+
if (radialGradient.Transform != null)
953937
{
954-
paintWrapper.Paint.Shader = shader;
938+
var transformOrigin = radialGradient.TransformOrigin.ToPixels(targetRect);
939+
var offset = Matrix.CreateTranslation(transformOrigin);
940+
var brushTransform = (-offset) * radialGradient.Transform.Value * (offset);
941+
transform = transform.HasValue ? transform * brushTransform : brushTransform;
955942
}
956-
}
957-
else
958-
{
959-
// when the origin is different to the center use a two point ConicalGradient to match the behaviour of D2D
960943

961-
if (radiusX != radiusY)
962-
// Adjust the origin point for radiusX/Y transformation by reversing it
963-
originPoint = originPoint.WithY(
964-
(originPoint.Y - centerPoint.Y) * radiusX / radiusY + centerPoint.Y);
965-
966-
var origin = originPoint.ToSKPoint();
967-
968-
// reverse the order of the stops to match D2D
969-
var reversedColors = new SKColor[stopColors.Length];
970-
Array.Copy(stopColors, reversedColors, stopColors.Length);
971-
Array.Reverse(reversedColors);
972-
973-
// and then reverse the reference point of the stops
974-
var reversedStops = new float[stopOffsets.Length];
975-
for (var i = 0; i < stopOffsets.Length; i++)
944+
if (originPoint.Equals(centerPoint))
976945
{
977-
reversedStops[i] = stopOffsets[i];
978-
if (reversedStops[i] > 0 && reversedStops[i] < 1)
946+
// when the origin is the same as the center the Skia RadialGradient acts the same as D2D
947+
using (var shader =
948+
transform.HasValue
949+
? SKShader.CreateRadialGradient(center, (float)radiusX, stopColors, stopOffsets, tileMode,
950+
transform.Value.ToSKMatrix())
951+
: SKShader.CreateRadialGradient(center, (float)radiusX, stopColors, stopOffsets, tileMode)
952+
)
979953
{
980-
reversedStops[i] = Math.Abs(1 - stopOffsets[i]);
954+
paintWrapper.Paint.Shader = shader;
981955
}
982956
}
983-
984-
// compose with a background colour of the final stop to match D2D's behaviour of filling with the final color
985-
using (var shader = SKShader.CreateCompose(
986-
SKShader.CreateColor(reversedColors[0]),
987-
transform.HasValue
988-
? SKShader.CreateTwoPointConicalGradient(center, (float)radiusX, origin, 0,
989-
reversedColors, reversedStops, tileMode, transform.Value.ToSKMatrix())
990-
: SKShader.CreateTwoPointConicalGradient(center, (float)radiusX, origin, 0,
991-
reversedColors, reversedStops, tileMode)
992-
993-
)
994-
)
957+
else
995958
{
996-
paintWrapper.Paint.Shader = shader;
959+
// when the origin is different to the center use a two point ConicalGradient to match the behaviour of D2D
960+
961+
if (radiusX != radiusY)
962+
// Adjust the origin point for radiusX/Y transformation by reversing it
963+
originPoint = originPoint.WithY(
964+
(originPoint.Y - centerPoint.Y) * radiusX / radiusY + centerPoint.Y);
965+
966+
var origin = originPoint.ToSKPoint();
967+
968+
var endOffset = 0.0;
969+
970+
// and then reverse the reference point of the stops
971+
var reversedStops = new float[stopOffsets.Length];
972+
973+
for (var i = 0; i < stopOffsets.Length; i++)
974+
{
975+
var offset = stopOffsets[i];
976+
977+
if (endOffset < offset)
978+
{
979+
endOffset = offset;
980+
}
981+
982+
reversedStops[i] = offset;
983+
984+
if (reversedStops[i] > 0 && reversedStops[i] < 1)
985+
{
986+
reversedStops[i] = Math.Abs(1 - offset);
987+
}
988+
}
989+
990+
var start = origin;
991+
var radiusStart = 0f;
992+
993+
var end = center;
994+
var radiusEnd = (float)radiusX;
995+
996+
var reverse = MathUtilities.AreClose(1, endOffset);
997+
998+
if (reverse)
999+
{
1000+
(start, radiusStart, end, radiusEnd) = (end, radiusEnd, start, radiusStart);
1001+
1002+
// reverse the order of the stops to match D2D
1003+
var reversedColors = new SKColor[stopColors.Length];
1004+
Array.Copy(stopColors, reversedColors, stopColors.Length);
1005+
Array.Reverse(reversedColors);
1006+
1007+
stopColors = reversedColors;
1008+
stopOffsets = reversedStops;
1009+
}
1010+
1011+
// compose with a background colour of the final stop to match D2D's behaviour of filling with the final color
1012+
using (var shader = SKShader.CreateCompose(
1013+
SKShader.CreateColor(stopColors[0]),
1014+
transform.HasValue
1015+
? SKShader.CreateTwoPointConicalGradient(start, radiusStart, end, radiusEnd,
1016+
stopColors, stopOffsets, tileMode, transform.Value.ToSKMatrix())
1017+
: SKShader.CreateTwoPointConicalGradient(start, radiusStart, end, radiusEnd,
1018+
stopColors, stopOffsets, tileMode)
1019+
1020+
)
1021+
)
1022+
{
1023+
paintWrapper.Paint.Shader = shader;
1024+
}
9971025
}
998-
}
9991026

1000-
break;
1001-
}
1027+
break;
1028+
}
10021029
case IConicGradientBrush conicGradient:
10031030
{
10041031
var center = conicGradient.Center.ToPixels(targetRect).ToSKPoint();

tests/Avalonia.RenderTests/Media/RadialGradientBrushTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,32 @@ public RadialGradientBrushTests() : base(@"Media\RadialGradientBrush")
1919
{
2020
}
2121

22+
[Fact]
23+
public async Task RadialGradientBrush_Partial_Cover()
24+
{
25+
Decorator target = new Decorator
26+
{
27+
Padding = new Thickness(8),
28+
Width = 200,
29+
Height = 200,
30+
Child = new Border
31+
{
32+
Background = new RadialGradientBrush
33+
{
34+
GradientStops =
35+
{
36+
new GradientStop { Color = Colors.White, Offset = 0 },
37+
new GradientStop { Color = Color.Parse("#00DD00"), Offset = 0.7 }
38+
},
39+
GradientOrigin = new RelativePoint(0.7, 0.15, RelativeUnit.Relative)
40+
}
41+
}
42+
};
43+
44+
await RenderToFile(target);
45+
CompareImages();
46+
}
47+
2248
[Fact]
2349
public async Task RadialGradientBrush_RedBlue()
2450
{
Loading

0 commit comments

Comments
 (0)