@ -886,146 +886,164 @@ namespace Avalonia.Skia
switch ( gradientBrush )
switch ( gradientBrush )
{
{
case ILinearGradientBrush linearGradient :
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 =
var start = linearGradient . StartPoint . ToPixels ( targetRect ) . ToSKPoint ( ) ;
SKShader . CreateLinearGradient ( start , end , stopColors , stopOffsets , tileMode ) )
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
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 ( ) ) )
{
{
paintWrapper . Paint . Shader = shader ;
var transformOrigin = linearGradient . TransformOrigin . ToPixels ( targetRect ) ;
}
var offset = Matrix . CreateTranslation ( transformOrigin ) ;
}
var transform = ( - offset ) * linearGradient . Transform . Value * ( offset ) ;
break ;
using ( var shader =
}
SKShader . CreateLinearGradient ( start , end , stopColors , stopOffsets , tileMode , transform . ToSKMatrix ( ) ) )
case IRadialGradientBrush radialGradient :
{
{
paintWrapper . Paint . Shader = shader ;
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 ) ;
break ;
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 ;
}
}
case IRadialGradientBrush radialGradient :
if ( originPoint . Equals ( centerPoint ) )
{
{
// when the origin is the same as the center the Skia RadialGradient acts the same as D2D
var centerPoint = radialGradient . Center . ToPixels ( targetRect ) ;
using ( var shader =
var center = centerPoint . ToSKPoint ( ) ;
transform . HasValue
? SKShader . CreateRadialGradient ( center , ( float ) radiusX , stopColors , stopOffsets , tileMode ,
var radiusX = ( radialGradient . RadiusX . ToValue ( targetRect . Width ) ) ;
transform . Value . ToSKMatrix ( ) )
var radiusY = ( radialGradient . RadiusY . ToValue ( targetRect . Height ) ) ;
: SKShader . CreateRadialGradient ( center , ( float ) radiusX , stopColors , stopOffsets , tileMode )
)
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 )
if ( originPoint . Equals ( centerPoint ) )
// 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 + + )
{
{
reversedStops [ i ] = stopOffsets [ i ] ;
// when the origin is the same as the center the Skia RadialGradient acts the same as D2D
if ( reversedStops [ i ] > 0 & & reversedStops [ i ] < 1 )
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 ;
}
}
}
}
else
// 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 )
)
)
{
{
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 :
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
// 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.
// but we are matching CSS where the vertical point above the center is 0.
var angle = ( float ) ( conicGradient . Angle - 9 0 ) ;
var angle = ( float ) ( conicGradient . Angle - 9 0 ) ;
var rotation = SKMatrix . CreateRotationDegrees ( angle , center . X , center . Y ) ;
var rotation = SKMatrix . CreateRotationDegrees ( angle , center . X , center . Y ) ;
if ( conicGradient . Transform is { } )
if ( conicGradient . Transform is { } )
{
{
var transformOrigin = conicGradient . TransformOrigin . ToPixels ( targetRect ) ;
var offset = Matrix . CreateTranslation ( transformOrigin ) ;
var transform = ( - offset ) * conicGradient . Transform . Value * ( offset ) ;
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 =
rotation = rotation . PreConcat ( transform . ToSKMatrix ( ) ) ;
SKShader . CreateSweepGradient ( center , stopColors , stopOffsets , rotation ) )
}
{
paintWrapper . Paint . Shader = shader ;
}
break ;
using ( var shader =
}
SKShader . CreateSweepGradient ( center , stopColors , stopOffsets , rotation ) )
{
paintWrapper . Paint . Shader = shader ;
}
break ;
}
}
}
}
}