|
|
@@ -886,146 +886,164 @@ namespace Avalonia.Skia
|
|
|
switch (gradientBrush)
|
|
|
{
|
|
|
case ILinearGradientBrush linearGradient:
|
|
|
- {
|
|
|
- var start = linearGradient.StartPoint.ToPixels(targetRect).ToSKPoint();
|
|
|
- var end = linearGradient.EndPoint.ToPixels(targetRect).ToSKPoint();
|
|
|
-
|
|
|
- // would be nice to cache these shaders possibly?
|
|
|
- if (linearGradient.Transform is null)
|
|
|
{
|
|
|
- using (var shader =
|
|
|
- SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode))
|
|
|
+ var start = linearGradient.StartPoint.ToPixels(targetRect).ToSKPoint();
|
|
|
+ var end = linearGradient.EndPoint.ToPixels(targetRect).ToSKPoint();
|
|
|
+
|
|
|
+ // would be nice to cache these shaders possibly?
|
|
|
+ if (linearGradient.Transform is null)
|
|
|
{
|
|
|
- paintWrapper.Paint.Shader = shader;
|
|
|
+ using (var shader =
|
|
|
+ SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode))
|
|
|
+ {
|
|
|
+ paintWrapper.Paint.Shader = shader;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- var transformOrigin = linearGradient.TransformOrigin.ToPixels(targetRect);
|
|
|
- var offset = Matrix.CreateTranslation(transformOrigin);
|
|
|
- var transform = (-offset) * linearGradient.Transform.Value * (offset);
|
|
|
-
|
|
|
- using (var shader =
|
|
|
- SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode, transform.ToSKMatrix()))
|
|
|
+ else
|
|
|
{
|
|
|
- paintWrapper.Paint.Shader = shader;
|
|
|
- }
|
|
|
- }
|
|
|
+ var transformOrigin = linearGradient.TransformOrigin.ToPixels(targetRect);
|
|
|
+ var offset = Matrix.CreateTranslation(transformOrigin);
|
|
|
+ var transform = (-offset) * linearGradient.Transform.Value * (offset);
|
|
|
|
|
|
- break;
|
|
|
- }
|
|
|
- case IRadialGradientBrush radialGradient:
|
|
|
- {
|
|
|
- var centerPoint = radialGradient.Center.ToPixels(targetRect);
|
|
|
- var center = centerPoint.ToSKPoint();
|
|
|
-
|
|
|
- var radiusX = (radialGradient.RadiusX.ToValue(targetRect.Width));
|
|
|
- var radiusY = (radialGradient.RadiusY.ToValue(targetRect.Height));
|
|
|
+ using (var shader =
|
|
|
+ SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode, transform.ToSKMatrix()))
|
|
|
+ {
|
|
|
+ paintWrapper.Paint.Shader = shader;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- var originPoint = radialGradient.GradientOrigin.ToPixels(targetRect);
|
|
|
-
|
|
|
- Matrix? transform = null;
|
|
|
-
|
|
|
- if (radiusX != radiusY)
|
|
|
- transform =
|
|
|
- Matrix.CreateTranslation(-centerPoint)
|
|
|
- * Matrix.CreateScale(1, radiusY / radiusX)
|
|
|
- * Matrix.CreateTranslation(centerPoint);
|
|
|
-
|
|
|
-
|
|
|
- if (radialGradient.Transform != null)
|
|
|
- {
|
|
|
- var transformOrigin = radialGradient.TransformOrigin.ToPixels(targetRect);
|
|
|
- var offset = Matrix.CreateTranslation(transformOrigin);
|
|
|
- var brushTransform = (-offset) * radialGradient.Transform.Value * (offset);
|
|
|
- transform = transform.HasValue ? transform * brushTransform : brushTransform;
|
|
|
+ break;
|
|
|
}
|
|
|
-
|
|
|
- if (originPoint.Equals(centerPoint))
|
|
|
+ case IRadialGradientBrush radialGradient:
|
|
|
{
|
|
|
- // when the origin is the same as the center the Skia RadialGradient acts the same as D2D
|
|
|
- using (var shader =
|
|
|
- transform.HasValue
|
|
|
- ? SKShader.CreateRadialGradient(center, (float)radiusX, stopColors, stopOffsets, tileMode,
|
|
|
- transform.Value.ToSKMatrix())
|
|
|
- : SKShader.CreateRadialGradient(center, (float)radiusX, stopColors, stopOffsets, tileMode)
|
|
|
- )
|
|
|
+ var centerPoint = radialGradient.Center.ToPixels(targetRect);
|
|
|
+ var center = centerPoint.ToSKPoint();
|
|
|
+
|
|
|
+ var radiusX = (radialGradient.RadiusX.ToValue(targetRect.Width));
|
|
|
+ var radiusY = (radialGradient.RadiusY.ToValue(targetRect.Height));
|
|
|
+
|
|
|
+ var originPoint = radialGradient.GradientOrigin.ToPixels(targetRect);
|
|
|
+
|
|
|
+ Matrix? transform = null;
|
|
|
+
|
|
|
+ if (radiusX != radiusY)
|
|
|
+ transform =
|
|
|
+ Matrix.CreateTranslation(-centerPoint)
|
|
|
+ * Matrix.CreateScale(1, radiusY / radiusX)
|
|
|
+ * Matrix.CreateTranslation(centerPoint);
|
|
|
+
|
|
|
+
|
|
|
+ if (radialGradient.Transform != null)
|
|
|
{
|
|
|
- paintWrapper.Paint.Shader = shader;
|
|
|
+ var transformOrigin = radialGradient.TransformOrigin.ToPixels(targetRect);
|
|
|
+ var offset = Matrix.CreateTranslation(transformOrigin);
|
|
|
+ var brushTransform = (-offset) * radialGradient.Transform.Value * (offset);
|
|
|
+ transform = transform.HasValue ? transform * brushTransform : brushTransform;
|
|
|
}
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- // when the origin is different to the center use a two point ConicalGradient to match the behaviour of D2D
|
|
|
|
|
|
- if (radiusX != radiusY)
|
|
|
- // Adjust the origin point for radiusX/Y transformation by reversing it
|
|
|
- originPoint = originPoint.WithY(
|
|
|
- (originPoint.Y - centerPoint.Y) * radiusX / radiusY + centerPoint.Y);
|
|
|
-
|
|
|
- var origin = originPoint.ToSKPoint();
|
|
|
-
|
|
|
- // 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++)
|
|
|
+ if (originPoint.Equals(centerPoint))
|
|
|
{
|
|
|
- reversedStops[i] = stopOffsets[i];
|
|
|
- if (reversedStops[i] > 0 && reversedStops[i] < 1)
|
|
|
+ // when the origin is the same as the center the Skia RadialGradient acts the same as D2D
|
|
|
+ using (var shader =
|
|
|
+ transform.HasValue
|
|
|
+ ? SKShader.CreateRadialGradient(center, (float)radiusX, stopColors, stopOffsets, tileMode,
|
|
|
+ transform.Value.ToSKMatrix())
|
|
|
+ : SKShader.CreateRadialGradient(center, (float)radiusX, stopColors, stopOffsets, tileMode)
|
|
|
+ )
|
|
|
{
|
|
|
- reversedStops[i] = Math.Abs(1 - stopOffsets[i]);
|
|
|
+ paintWrapper.Paint.Shader = shader;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- // 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]),
|
|
|
- transform.HasValue
|
|
|
- ? SKShader.CreateTwoPointConicalGradient(center, (float)radiusX, origin, 0,
|
|
|
- reversedColors, reversedStops, tileMode, transform.Value.ToSKMatrix())
|
|
|
- : SKShader.CreateTwoPointConicalGradient(center, (float)radiusX, origin, 0,
|
|
|
- reversedColors, reversedStops, tileMode)
|
|
|
-
|
|
|
- )
|
|
|
- )
|
|
|
+ else
|
|
|
{
|
|
|
- paintWrapper.Paint.Shader = shader;
|
|
|
+ // when the origin is different to the center use a two point ConicalGradient to match the behaviour of D2D
|
|
|
+ if (radiusX != radiusY)
|
|
|
+ // Adjust the origin point for radiusX/Y transformation by reversing it
|
|
|
+ originPoint = originPoint.WithY(
|
|
|
+ (originPoint.Y - centerPoint.Y) * radiusX / radiusY + centerPoint.Y);
|
|
|
+
|
|
|
+ var origin = originPoint.ToSKPoint();
|
|
|
+ var endOffset = 0.0;
|
|
|
+
|
|
|
+ // and then reverse the reference point of the stops
|
|
|
+ var reversedStops = new float[stopOffsets.Length];
|
|
|
+
|
|
|
+ for (var i = 0; i < stopOffsets.Length; i++)
|
|
|
+ {
|
|
|
+ var offset = stopOffsets[i];
|
|
|
+ if (endOffset < offset)
|
|
|
+ {
|
|
|
+ endOffset = offset;
|
|
|
+ }
|
|
|
+ reversedStops[i] = offset;
|
|
|
+ if (reversedStops[i] > 0 && reversedStops[i] < 1)
|
|
|
+ {
|
|
|
+ reversedStops[i] = Math.Abs(1 - offset);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var start = origin;
|
|
|
+ var radiusStart = 0f;
|
|
|
+ var end = center;
|
|
|
+ var radiusEnd = (float)radiusX;
|
|
|
+ var reverse = MathUtilities.AreClose(1, endOffset);
|
|
|
+
|
|
|
+ if (reverse)
|
|
|
+ {
|
|
|
+ (start, radiusStart, end, radiusEnd) = (end, radiusEnd, start, radiusStart);
|
|
|
+
|
|
|
+ // reverse the order of the stops to match D2D
|
|
|
+ var reversedColors = new SKColor[stopColors.Length];
|
|
|
+ Array.Copy(stopColors, reversedColors, stopColors.Length);
|
|
|
+ Array.Reverse(reversedColors);
|
|
|
+ stopColors = reversedColors;
|
|
|
+ stopOffsets = reversedStops;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 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(stopColors[0]),
|
|
|
+ transform.HasValue
|
|
|
+ ? SKShader.CreateTwoPointConicalGradient(start, radiusStart, end, radiusEnd,
|
|
|
+ stopColors, stopOffsets, tileMode, transform.Value.ToSKMatrix())
|
|
|
+ : SKShader.CreateTwoPointConicalGradient(start, radiusStart, end, radiusEnd,
|
|
|
+ stopColors, stopOffsets, tileMode)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ {
|
|
|
+ paintWrapper.Paint.Shader = shader;
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- break;
|
|
|
- }
|
|
|
+ break;
|
|
|
+ }
|
|
|
case IConicGradientBrush conicGradient:
|
|
|
- {
|
|
|
- var center = conicGradient.Center.ToPixels(targetRect).ToSKPoint();
|
|
|
+ {
|
|
|
+ var center = conicGradient.Center.ToPixels(targetRect).ToSKPoint();
|
|
|
|
|
|
- // Skia's default is that angle 0 is from the right hand side of the center point
|
|
|
- // but we are matching CSS where the vertical point above the center is 0.
|
|
|
- var angle = (float)(conicGradient.Angle - 90);
|
|
|
- var rotation = SKMatrix.CreateRotationDegrees(angle, center.X, center.Y);
|
|
|
+ // Skia's default is that angle 0 is from the right hand side of the center point
|
|
|
+ // but we are matching CSS where the vertical point above the center is 0.
|
|
|
+ var angle = (float)(conicGradient.Angle - 90);
|
|
|
+ var rotation = SKMatrix.CreateRotationDegrees(angle, center.X, center.Y);
|
|
|
|
|
|
- if (conicGradient.Transform is { })
|
|
|
- {
|
|
|
-
|
|
|
- var transformOrigin = conicGradient.TransformOrigin.ToPixels(targetRect);
|
|
|
- var offset = Matrix.CreateTranslation(transformOrigin);
|
|
|
- var transform = (-offset) * conicGradient.Transform.Value * (offset);
|
|
|
+ if (conicGradient.Transform is { })
|
|
|
+ {
|
|
|
|
|
|
- rotation = rotation.PreConcat(transform.ToSKMatrix());
|
|
|
- }
|
|
|
+ var transformOrigin = conicGradient.TransformOrigin.ToPixels(targetRect);
|
|
|
+ var offset = Matrix.CreateTranslation(transformOrigin);
|
|
|
+ var transform = (-offset) * conicGradient.Transform.Value * (offset);
|
|
|
|
|
|
- using (var shader =
|
|
|
- SKShader.CreateSweepGradient(center, stopColors, stopOffsets, rotation))
|
|
|
- {
|
|
|
- paintWrapper.Paint.Shader = shader;
|
|
|
- }
|
|
|
+ rotation = rotation.PreConcat(transform.ToSKMatrix());
|
|
|
+ }
|
|
|
|
|
|
- break;
|
|
|
- }
|
|
|
+ using (var shader =
|
|
|
+ SKShader.CreateSweepGradient(center, stopColors, stopOffsets, rotation))
|
|
|
+ {
|
|
|
+ paintWrapper.Paint.Shader = shader;
|
|
|
+ }
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|