Skip to content

Commit 0b46143

Browse files
committed
Skia - Only reverse radial gradient stops if we reach cover the whole content bounds
1 parent 9a94a62 commit 0b46143

File tree

1 file changed

+124
-118
lines changed

1 file changed

+124
-118
lines changed

src/Skia/Avalonia.Skia/DrawingContextImpl.cs

Lines changed: 124 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -886,151 +886,157 @@ private static void ConfigureGradientBrush(ref PaintWrapper paintWrapper, Rect t
886886
switch (gradientBrush)
887887
{
888888
case ILinearGradientBrush linearGradient:
889-
{
890-
var start = linearGradient.StartPoint.ToPixels(targetRect).ToSKPoint();
891-
var end = linearGradient.EndPoint.ToPixels(targetRect).ToSKPoint();
892-
893-
// would be nice to cache these shaders possibly?
894-
if (linearGradient.Transform is null)
895889
{
896-
using (var shader =
897-
SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode))
890+
var start = linearGradient.StartPoint.ToPixels(targetRect).ToSKPoint();
891+
var end = linearGradient.EndPoint.ToPixels(targetRect).ToSKPoint();
892+
893+
// would be nice to cache these shaders possibly?
894+
if (linearGradient.Transform is null)
898895
{
899-
paintWrapper.Paint.Shader = shader;
896+
using (var shader =
897+
SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode))
898+
{
899+
paintWrapper.Paint.Shader = shader;
900+
}
900901
}
901-
}
902-
else
903-
{
904-
var transformOrigin = linearGradient.TransformOrigin.ToPixels(targetRect);
905-
var offset = Matrix.CreateTranslation(transformOrigin);
906-
var transform = (-offset) * linearGradient.Transform.Value * (offset);
907-
908-
using (var shader =
909-
SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode, transform.ToSKMatrix()))
902+
else
910903
{
911-
paintWrapper.Paint.Shader = shader;
912-
}
913-
}
914-
915-
break;
916-
}
917-
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));
904+
var transformOrigin = linearGradient.TransformOrigin.ToPixels(targetRect);
905+
var offset = Matrix.CreateTranslation(transformOrigin);
906+
var transform = (-offset) * linearGradient.Transform.Value * (offset);
924907

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)
937-
{
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-
)
953-
{
954-
paintWrapper.Paint.Shader = shader;
908+
using (var shader =
909+
SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode, transform.ToSKMatrix()))
910+
{
911+
paintWrapper.Paint.Shader = shader;
912+
}
955913
}
914+
915+
break;
956916
}
957-
else
917+
case IRadialGradientBrush radialGradient:
958918
{
959-
// when the origin is different to the center use a two point ConicalGradient to match the behaviour of D2D
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;
960928

961929
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();
930+
transform =
931+
Matrix.CreateTranslation(-centerPoint)
932+
* Matrix.CreateScale(1, radiusY / radiusX)
933+
* Matrix.CreateTranslation(centerPoint);
967934

968-
var endOffset = 0.0;
969935

970-
// and then reverse the reference point of the stops
971-
var reversedStops = new float[stopOffsets.Length];
972-
for (var i = 0; i < stopOffsets.Length; i++)
936+
if (radialGradient.Transform != null)
973937
{
974-
var offset = stopOffsets[i];
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+
}
975943

976-
if (endOffset < offset)
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+
)
977953
{
978-
endOffset = offset;
954+
paintWrapper.Paint.Shader = shader;
979955
}
980-
981-
reversedStops[i] = offset;
982-
983-
if (reversedStops[i] > 0 && reversedStops[i] < 1)
956+
}
957+
else
958+
{
959+
// when the origin is different to the center use a two point ConicalGradient to match the behaviour of D2D
960+
if (radiusX != radiusY)
961+
// Adjust the origin point for radiusX/Y transformation by reversing it
962+
originPoint = originPoint.WithY(
963+
(originPoint.Y - centerPoint.Y) * radiusX / radiusY + centerPoint.Y);
964+
var origin = originPoint.ToSKPoint();
965+
var endOffset = 0.0;
966+
// and then reverse the reference point of the stops
967+
var reversedStops = new float[stopOffsets.Length];
968+
for (var i = 0; i < stopOffsets.Length; i++)
984969
{
985-
reversedStops[i] = Math.Abs(1 - offset);
970+
var offset = stopOffsets[i];
971+
if (endOffset < offset)
972+
{
973+
endOffset = offset;
974+
}
975+
reversedStops[i] = offset;
976+
if (reversedStops[i] > 0 && reversedStops[i] < 1)
977+
{
978+
reversedStops[i] = Math.Abs(1 - offset);
979+
}
986980
}
987-
}
988-
989-
// compose with a background colour of the final stop to match D2D's behaviour of filling with the final color
990-
using (var shader = SKShader.CreateCompose(
991-
SKShader.CreateColor(stopColors[0]),
992-
transform.HasValue
993-
? SKShader.CreateTwoPointConicalGradient(start, radiusStart, end, radiusEnd,
994-
stopColors, stopOffsets, tileMode, transform.Value.ToSKMatrix())
995-
: SKShader.CreateTwoPointConicalGradient(start, radiusStart, end, radiusEnd,
996-
stopColors, stopOffsets, tileMode)
997-
981+
var start = origin;
982+
var radiusStart = 0f;
983+
var end = center;
984+
var radiusEnd = (float)radiusX;
985+
var reverse = MathUtilities.AreClose(1, endOffset);
986+
if (reverse)
987+
{
988+
(start, radiusStart, end, radiusEnd) = (end, radiusEnd, start, radiusStart);
989+
// reverse the order of the stops to match D2D
990+
var reversedColors = new SKColor[stopColors.Length];
991+
Array.Copy(stopColors, reversedColors, stopColors.Length);
992+
Array.Reverse(reversedColors);
993+
stopColors = reversedColors;
994+
stopOffsets = reversedStops;
995+
}
996+
// compose with a background colour of the final stop to match D2D's behaviour of filling with the final color
997+
using (var shader = SKShader.CreateCompose(
998+
SKShader.CreateColor(stopColors[0]),
999+
transform.HasValue
1000+
? SKShader.CreateTwoPointConicalGradient(start, radiusStart, end, radiusEnd,
1001+
stopColors, stopOffsets, tileMode, transform.Value.ToSKMatrix())
1002+
: SKShader.CreateTwoPointConicalGradient(start, radiusStart, end, radiusEnd,
1003+
stopColors, stopOffsets, tileMode)
9981004
)
999-
)
1000-
{
1001-
paintWrapper.Paint.Shader = shader;
1005+
)
1006+
{
1007+
paintWrapper.Paint.Shader = shader;
1008+
}
10021009
}
1003-
}
10041010

1005-
break;
1006-
}
1011+
break;
1012+
}
10071013
case IConicGradientBrush conicGradient:
1008-
{
1009-
var center = conicGradient.Center.ToPixels(targetRect).ToSKPoint();
1014+
{
1015+
var center = conicGradient.Center.ToPixels(targetRect).ToSKPoint();
10101016

1011-
// Skia's default is that angle 0 is from the right hand side of the center point
1012-
// but we are matching CSS where the vertical point above the center is 0.
1013-
var angle = (float)(conicGradient.Angle - 90);
1014-
var rotation = SKMatrix.CreateRotationDegrees(angle, center.X, center.Y);
1017+
// Skia's default is that angle 0 is from the right hand side of the center point
1018+
// but we are matching CSS where the vertical point above the center is 0.
1019+
var angle = (float)(conicGradient.Angle - 90);
1020+
var rotation = SKMatrix.CreateRotationDegrees(angle, center.X, center.Y);
10151021

1016-
if (conicGradient.Transform is { })
1017-
{
1018-
1019-
var transformOrigin = conicGradient.TransformOrigin.ToPixels(targetRect);
1020-
var offset = Matrix.CreateTranslation(transformOrigin);
1021-
var transform = (-offset) * conicGradient.Transform.Value * (offset);
1022+
if (conicGradient.Transform is { })
1023+
{
10221024

1023-
rotation = rotation.PreConcat(transform.ToSKMatrix());
1024-
}
1025+
var transformOrigin = conicGradient.TransformOrigin.ToPixels(targetRect);
1026+
var offset = Matrix.CreateTranslation(transformOrigin);
1027+
var transform = (-offset) * conicGradient.Transform.Value * (offset);
10251028

1026-
using (var shader =
1027-
SKShader.CreateSweepGradient(center, stopColors, stopOffsets, rotation))
1028-
{
1029-
paintWrapper.Paint.Shader = shader;
1030-
}
1029+
rotation = rotation.PreConcat(transform.ToSKMatrix());
1030+
}
10311031

1032-
break;
1033-
}
1032+
using (var shader =
1033+
SKShader.CreateSweepGradient(center, stopColors, stopOffsets, rotation))
1034+
{
1035+
paintWrapper.Paint.Shader = shader;
1036+
}
1037+
1038+
break;
1039+
}
10341040
}
10351041
}
10361042

0 commit comments

Comments
 (0)