File: Graphics\PaintExtensions.iOS.cs
Web Access
Project: src\src\Core\src\Core.csproj (Microsoft.Maui)
using System;
using System.Collections.Generic;
using System.Linq;
using CoreAnimation;
using CoreGraphics;
using Foundation;
 
namespace Microsoft.Maui.Graphics
{
	public static partial class PaintExtensions
	{
		public static CALayer? ToCALayer(this Paint paint, CGRect frame = default)
		{
			if (paint is SolidPaint solidPaint)
				return solidPaint.CreateCALayer(frame);
 
			if (paint is LinearGradientPaint linearGradientPaint)
				return linearGradientPaint.CreateCALayer(frame);
 
			if (paint is RadialGradientPaint radialGradientPaint)
				return radialGradientPaint.CreateCALayer(frame);
 
			if (paint is ImagePaint imagePaint)
				return imagePaint.CreateCALayer(frame);
 
			if (paint is PatternPaint patternPaint)
				return patternPaint.CreateCALayer(frame);
 
			return null;
		}
 
		public static CALayer? CreateCALayer(this SolidPaint solidPaint, CGRect frame = default)
		{
			var solidColorLayer = new StaticCALayer
			{
				ContentsGravity = CALayer.GravityResizeAspectFill,
				Frame = frame,
				BackgroundColor = solidPaint.Color.ToCGColor()
			};
 
			return solidColorLayer;
		}
 
		public static CALayer? CreateCALayer(this GradientPaint gradientPaint, CGRect frame = default)
		{
			if (gradientPaint is LinearGradientPaint linearGradientPaint)
				return linearGradientPaint.CreateCALayer(frame);
 
			if (gradientPaint is RadialGradientPaint radialGradientPaint)
				return radialGradientPaint.CreateCALayer(frame);
 
			return null;
		}
 
		public static CALayer? CreateCALayer(this LinearGradientPaint linearGradientPaint, CGRect frame = default)
		{
			var p1 = linearGradientPaint.StartPoint;
			var p2 = linearGradientPaint.EndPoint;
 
			var linearGradientLayer = new StaticCAGradientLayer
			{
				ContentsGravity = CALayer.GravityResizeAspectFill,
				Frame = frame,
				LayerType = CAGradientLayerType.Axial,
				StartPoint = new CGPoint(p1.X, p1.Y),
				EndPoint = new CGPoint(p2.X, p2.Y)
			};
 
			if (linearGradientPaint.GradientStops != null && linearGradientPaint.GradientStops.Length > 0)
			{
				var orderedStops = linearGradientPaint.GradientStops.OrderBy(x => x.Offset).ToList();
				linearGradientLayer.Colors = GetCAGradientLayerColors(orderedStops);
				linearGradientLayer.Locations = GetCAGradientLayerLocations(orderedStops);
			}
 
			return linearGradientLayer;
		}
 
		public static CALayer? CreateCALayer(this RadialGradientPaint radialGradientPaint, CGRect frame = default)
		{
			var center = radialGradientPaint.Center;
			var radius = radialGradientPaint.Radius;
 
			var radialGradientLayer = new StaticCAGradientLayer
			{
				ContentsGravity = CALayer.GravityResizeAspectFill,
				Frame = frame,
#pragma warning disable CA1416 // TODO: 'CAGradientLayerType.Radial' is only supported on: 'ios' 12.0 and later
				LayerType = CAGradientLayerType.Radial,
#pragma warning restore CA1416
				StartPoint = new CGPoint(center.X, center.Y),
				EndPoint = GetRadialGradientPaintEndPoint(center, radius),
				CornerRadius = (float)radius
			};
 
			if (radialGradientPaint.GradientStops != null && radialGradientPaint.GradientStops.Length > 0)
			{
				var orderedStops = radialGradientPaint.GradientStops.OrderBy(x => x.Offset).ToList();
				radialGradientLayer.Colors = GetCAGradientLayerColors(orderedStops);
				radialGradientLayer.Locations = GetCAGradientLayerLocations(orderedStops);
			}
 
			return radialGradientLayer;
		}
 
		public static CALayer? CreateCALayer(this ImagePaint imagePaint, CGRect frame = default)
		{
			throw new NotImplementedException();
		}
 
		public static CALayer? CreateCALayer(this PatternPaint patternPaint, CGRect frame = default)
		{
			throw new NotImplementedException();
		}
 
		static CGPoint GetRadialGradientPaintEndPoint(Point startPoint, double radius)
		{
			double x = startPoint.X == 1 ? (startPoint.X - radius) : (startPoint.X + radius);
 
			if (x < 0)
				x = 0;
 
			if (x > 1)
				x = 1;
 
			double y = startPoint.Y == 1 ? (startPoint.Y - radius) : (startPoint.Y + radius);
 
			if (y < 0)
				y = 0;
 
			if (y > 1)
				y = 1;
 
			return new CGPoint(x, y);
		}
 
		static NSNumber[] GetCAGradientLayerLocations(List<PaintGradientStop> gradientStops)
		{
			if (gradientStops == null || gradientStops.Count == 0)
				return Array.Empty<NSNumber>();
 
			if (gradientStops.Count > 1 && gradientStops.Any(gt => gt.Offset != 0))
				return gradientStops.Select(x => new NSNumber(x.Offset)).ToArray();
			else
			{
				int itemCount = gradientStops.Count;
				int index = 0;
				float step = 1.0f / itemCount;
 
				NSNumber[] locations = new NSNumber[itemCount];
 
				foreach (var gradientStop in gradientStops)
				{
					float location = step * index;
					bool setLocation = !gradientStops.Any(gt => gt.Offset > location);
 
					if (gradientStop.Offset == 0 && setLocation)
						locations[index] = new NSNumber(location);
					else
						locations[index] = new NSNumber(gradientStop.Offset);
 
					index++;
				}
 
				return locations;
			}
		}
 
		static CGColor[] GetCAGradientLayerColors(List<PaintGradientStop> gradientStops)
		{
			if (gradientStops == null || gradientStops.Count == 0)
				return Array.Empty<CGColor>();
 
			CGColor[] colors = new CGColor[gradientStops.Count];
 
			int index = 0;
			foreach (var gradientStop in gradientStops)
			{
				if (gradientStop.Color == Colors.Transparent)
				{
					var color = gradientStops[index == 0 ? index + 1 : index - 1].Color;
					CGColor nativeColor = color.ToPlatform().ColorWithAlpha(0.0f).CGColor;
					colors[index] = nativeColor;
				}
				else
					colors[index] = gradientStop.Color.ToCGColor();
 
				index++;
			}
 
			return colors;
		}
	}
}