public static SKPoint GradientBrushPointToSkiaPoint(Point point, IGradientBrush gradientBrush, IUIElement uiElement) { if (gradientBrush.MappingMode == BrushMappingMode.RelativeToBoundingBox) { return(new SKPoint( (float)(point.X * uiElement.Width), (float)(point.Y * uiElement.Height))); } else { return(new SKPoint((float)point.X, (float)point.Y)); } }
public static SKShader ToSkiaShader(IGradientBrush gradientBrush, IShape shape) { SKShaderTileMode tileMode = gradientBrush.SpreadMethod switch { GradientSpreadMethod.Pad => SKShaderTileMode.Clamp, GradientSpreadMethod.Reflect => SKShaderTileMode.Mirror, GradientSpreadMethod.Repeat => SKShaderTileMode.Repeat, _ => throw new InvalidOperationException($"Unknown GradientSpreadmethod value {gradientBrush.SpreadMethod}") }; List <SKColor> skiaColors = new List <SKColor>(); List <float> skiaColorPositions = new List <float>(); foreach (IGradientStop gradientStop in gradientBrush.GradientStops) { skiaColors.Add(ToSkiaColor(gradientStop.Color)); skiaColorPositions.Add((float)gradientStop.Offset); } if (gradientBrush is ILinearGradientBrush linearGradientBrush) { SKPoint skiaStartPoint = new SKPoint( (float)(shape.Left + linearGradientBrush.StartPoint.X * shape.Width), (float)(shape.Top + linearGradientBrush.StartPoint.Y * shape.Height)); SKPoint skiaEndPoint = new SKPoint( (float)(shape.Left + linearGradientBrush.EndPoint.X * shape.Width), (float)(shape.Top + linearGradientBrush.EndPoint.Y * shape.Height)); return(SKShader.CreateLinearGradient(skiaStartPoint, skiaEndPoint, skiaColors.ToArray(), skiaColorPositions.ToArray(), tileMode)); } else if (gradientBrush is IRadialGradientBrush radialGradientBrush) { SKPoint skiaCenterPoint = new SKPoint( (float)(shape.Left + radialGradientBrush.Center.X * shape.Width), (float)(shape.Top + radialGradientBrush.Center.Y * shape.Height)); float radius = (float)(radialGradientBrush.RadiusX * shape.Width); return(SKShader.CreateRadialGradient(skiaCenterPoint, radius, skiaColors.ToArray(), skiaColorPositions.ToArray(), tileMode)); } else { throw new InvalidOperationException($"GradientBrush type {gradientBrush.GetType()} is unknown"); } }
internal static IGradientBrush ConvertSolidColorBrushToGradient(IGradientBrush gradientBrush, ISolidColorBrush solidColorBrush) { switch (gradientBrush) { case IRadialGradientBrush oldRadial: return(new ImmutableRadialGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldRadial.GradientStops), solidColorBrush.Opacity, oldRadial.SpreadMethod, oldRadial.Center, oldRadial.GradientOrigin, oldRadial.Radius)); case IConicGradientBrush oldConic: return(new ImmutableConicGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldConic.GradientStops), solidColorBrush.Opacity, oldConic.SpreadMethod, oldConic.Center, oldConic.Angle)); case ILinearGradientBrush oldLinear: return(new ImmutableLinearGradientBrush( CreateStopsFromSolidColorBrush(solidColorBrush, oldLinear.GradientStops), solidColorBrush.Opacity, oldLinear.SpreadMethod, oldLinear.StartPoint, oldLinear.EndPoint)); default: throw new NotSupportedException($"Gradient of type {gradientBrush?.GetType()} is not supported"); }
/// <summary> /// Configure paint wrapper for using gradient brush. /// </summary> /// <param name="paintWrapper">Paint wrapper.</param> /// <param name="targetSize">Target size.</param> /// <param name="gradientBrush">Gradient brush.</param> private void ConfigureGradientBrush(ref PaintWrapper paintWrapper, Size targetSize, IGradientBrush gradientBrush) { var tileMode = gradientBrush.SpreadMethod.ToSKShaderTileMode(); var stopColors = gradientBrush.GradientStops.Select(s => s.Color.ToSKColor()).ToArray(); var stopOffsets = gradientBrush.GradientStops.Select(s => (float)s.Offset).ToArray(); switch (gradientBrush) { case ILinearGradientBrush linearGradient: { var start = linearGradient.StartPoint.ToPixels(targetSize).ToSKPoint(); var end = linearGradient.EndPoint.ToPixels(targetSize).ToSKPoint(); // would be nice to cache these shaders possibly? using (var shader = SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode)) { paintWrapper.Paint.Shader = shader; } break; } case IRadialGradientBrush radialGradient: { var center = radialGradient.Center.ToPixels(targetSize).ToSKPoint(); var radius = (float)(radialGradient.Radius * targetSize.Width); // TODO: There is no SetAlpha in SkiaSharp //paint.setAlpha(128); // would be nice to cache these shaders possibly? using (var shader = SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode)) { paintWrapper.Paint.Shader = shader; } break; } } }
/// <summary> /// Configure paint wrapper for using gradient brush. /// </summary> /// <param name="paintWrapper">Paint wrapper.</param> /// <param name="targetSize">Target size.</param> /// <param name="gradientBrush">Gradient brush.</param> private void ConfigureGradientBrush(ref PaintWrapper paintWrapper, Size targetSize, IGradientBrush gradientBrush) { var tileMode = gradientBrush.SpreadMethod.ToSKShaderTileMode(); var stopColors = gradientBrush.GradientStops.Select(s => s.Color.ToSKColor()).ToArray(); var stopOffsets = gradientBrush.GradientStops.Select(s => (float)s.Offset).ToArray(); switch (gradientBrush) { case ILinearGradientBrush linearGradient: { var start = linearGradient.StartPoint.ToPixels(targetSize).ToSKPoint(); var end = linearGradient.EndPoint.ToPixels(targetSize).ToSKPoint(); // would be nice to cache these shaders possibly? using (var shader = SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode)) { paintWrapper.Paint.Shader = shader; } break; } case IRadialGradientBrush radialGradient: { var center = radialGradient.Center.ToPixels(targetSize).ToSKPoint(); var radius = (float)(radialGradient.Radius * targetSize.Width); var origin = radialGradient.GradientOrigin.ToPixels(targetSize).ToSKPoint(); if (origin.Equals(center)) { // when the origin is the same as the center the Skia RadialGradient acts the same as D2D using (var shader = SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode)) { paintWrapper.Paint.Shader = shader; } } else { // when the origin is different to the center use a two point ConicalGradient to match the behaviour of D2D // 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]; if (reversedStops[i] > 0 && reversedStops[i] < 1) { reversedStops[i] = Math.Abs(1 - stopOffsets[i]); } } // 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]), SKShader.CreateTwoPointConicalGradient(center, radius, origin, 0, reversedColors, reversedStops, tileMode) )) { paintWrapper.Paint.Shader = shader; } } break; } case IConicGradientBrush conicGradient: { var center = conicGradient.Center.ToPixels(targetSize).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); using (var shader = SKShader.CreateSweepGradient(center, stopColors, stopOffsets, rotation)) { paintWrapper.Paint.Shader = shader; } break; } } }
/// <summary> /// Initializes a new instance of the <see cref="ImmutableGradientBrush"/> class. /// </summary> /// <param name="source">The brush from which this brush's properties should be copied.</param> protected ImmutableGradientBrush(IGradientBrush source) : this(source.GradientStops.ToList(), source.Opacity, source.SpreadMethod) { }
public static IGradientBrush CreateRef(this IGradientBrush objectRef) => ((IGradientBrush)objectRef.CreateRef(typeof(IGradientBrush)));