using SkiaSharp; using System; using System.Linq; namespace Topten.RichTextKit { /// /// Desribes a gradient to apply to the text /// public class TextGradient { public SKPoint Center { get; set; } public RadialSizeMode SizeMode { get; set; } public GradientType GradientType { get; set; } public SKColor[] Colors { get; set; } public float[] Positions { get; set; } public float Angle { get; set; } public static TextGradient Linear( SKColor[] colors, float[] positions, float angle ) { return new TextGradient() { GradientType = GradientType.Linear, Colors = colors, Positions = positions, Angle = angle }; } public static TextGradient Radial( SKColor[] colors, float[] positions, float angle, SKPoint center, RadialSizeMode sizeMode ) { return new TextGradient() { GradientType = GradientType.Radial, Colors = colors, Positions = positions, Angle = angle, Center = center, SizeMode = sizeMode }; } internal SKShader CreateShader( float width, float height, float offsetx = 0 ) { var rotation = SKMatrix.CreateRotationDegrees( 180 + Angle, width * .5f, height * .5f ); var startPoint = new SKPoint( width * .5f, 0 ); var endPoint = new SKPoint( width * .5f, height ); startPoint = rotation.MapPoint( startPoint ); endPoint = rotation.MapPoint( endPoint ); var localMatrix = SKMatrix.CreateTranslation( offsetx, 0 ); if ( GradientType == GradientType.Linear ) { var sx = Math.Abs( endPoint.X - startPoint.X ); var sy = Math.Abs( endPoint.Y - startPoint.Y ); if ( sx == 0 ) sx = 1; if ( sy == 0 ) sy = 1; sx = width / sx; sy = height / sy; var localScale = SKMatrix.CreateScale( sx, sy, width * .5f, height * .5f ); localMatrix = SKMatrix.Concat( localMatrix, localScale ); return SKShader.CreateLinearGradient( startPoint, endPoint, Colors, Positions, SKShaderTileMode.Clamp, localMatrix ); } if ( GradientType == GradientType.Radial ) { var center = new SKPoint( width * Center.X, height * Center.Y ); var radius = Math.Max( width, height ); var scaleY = 1.0f; var scaleX = 1.0f; var top = center.Y; var bottom = height - center.Y; var left = center.X; var right = width - center.X; var tl = center; var tr = new SKPoint( width, 0 ) - center; var br = new SKPoint( width, height ) - center; var bl = new SKPoint( 0, height ) - center; switch ( SizeMode ) { case RadialSizeMode.Circle: case RadialSizeMode.FarthestSide: scaleX = Math.Max( left, right ) / width; scaleY = Math.Max( top, bottom ) / height; break; case RadialSizeMode.FarthestCorner: var furthestCorner = new SKPoint[] { tl, tr, br, bl }.OrderByDescending( x => x.Length ).First(); scaleX = furthestCorner.X / (width * .71f); scaleY = furthestCorner.Y / (height * .71f); break; case RadialSizeMode.ClosestSide: scaleX = Math.Min( left, right ) / width; scaleY = Math.Min( top, bottom ) / height; break; case RadialSizeMode.ClosestCorner: var closestCorner = new SKPoint[] { tl, tr, br, bl }.OrderBy( x => x.Length ).First(); scaleX = closestCorner.X / (width * .71f); scaleY = closestCorner.Y / (height * .71f); break; } if ( SizeMode != RadialSizeMode.Circle ) { scaleX *= width / radius; scaleY *= height / radius; } localMatrix = SKMatrix.Concat( localMatrix, SKMatrix.CreateScale( scaleX, scaleY, center.X, center.Y ) ); return SKShader.CreateRadialGradient( center, radius, Colors, Positions, SKShaderTileMode.Clamp, localMatrix ); } return null; } } public enum GradientType { Linear = 0, Radial = 1 } public enum RadialSizeMode { FarthestSide = 0, FarthestCorner = 1, ClosestSide = 2, ClosestCorner = 3, Circle = 4 } }