File: ScalingCanvas.cs
Web Access
Project: src\src\Graphics\src\Graphics\Graphics.csproj (Microsoft.Maui.Graphics)
using System;
using System.Collections.Generic;
using System.Numerics;
 
using Microsoft.Maui.Graphics.Text;
 
namespace Microsoft.Maui.Graphics
{
	public class ScalingCanvas : ICanvas, IBlurrableCanvas
	{
		private readonly ICanvas _canvas;
		private readonly IBlurrableCanvas _blurrableCanvas;
		private readonly Stack<float> _scaleXStack = new Stack<float>();
		private readonly Stack<float> _scaleYStack = new Stack<float>();
		private float _scaleX = 1f;
		private float _scaleY = 1f;
 
		public ScalingCanvas(ICanvas wrapped)
		{
			_canvas = wrapped;
			_blurrableCanvas = _canvas as IBlurrableCanvas;
		}
 
		public float DisplayScale
		{
			get => _canvas.DisplayScale;
			set => _canvas.DisplayScale = value;
		}
 
		public object Wrapped => _canvas;
 
		public ICanvas ParentCanvas => _canvas;
 
		public float StrokeSize
		{
			set => _canvas.StrokeSize = value;
		}
 
		public float MiterLimit
		{
			set => _canvas.MiterLimit = value;
		}
 
		public Color StrokeColor
		{
			set => _canvas.StrokeColor = value;
		}
 
		public LineCap StrokeLineCap
		{
			set => _canvas.StrokeLineCap = value;
		}
 
		public float Alpha
		{
			set => _canvas.Alpha = value;
		}
 
		public LineJoin StrokeLineJoin
		{
			set => _canvas.StrokeLineJoin = value;
		}
 
		public float[] StrokeDashPattern
		{
			set => _canvas.StrokeDashPattern = value;
		}
 
		public float StrokeDashOffset
		{
			set => _canvas.StrokeDashOffset = value;
		}
 
		public Color FillColor
		{
			set => _canvas.FillColor = value;
		}
 
		public Color FontColor
		{
			set => _canvas.FontColor = value;
		}
 
		public IFont Font
		{
			set => _canvas.Font = value;
		}
 
		public float FontSize
		{
			set => _canvas.FontSize = value;
		}
 
		public BlendMode BlendMode
		{
			set => _canvas.BlendMode = value;
		}
 
		public bool Antialias
		{
			set => _canvas.Antialias = value;
		}
 
		public void SubtractFromClip(float x1, float y1, float x2, float y2)
		{
			_canvas.SubtractFromClip(x1 * _scaleX, y1 * _scaleY, x2 * _scaleX, y2 * _scaleY);
		}
 
		public void DrawLine(float x1, float y1, float x2, float y2)
		{
			_canvas.DrawLine(x1 * _scaleX, y1 * _scaleY, x2 * _scaleX, y2 * _scaleY);
		}
 
		public void DrawArc(float x, float y, float width, float height, float startAngle, float endAngle, bool clockwise, bool closed)
		{
			_canvas.DrawArc(x * _scaleX, y * _scaleY, width * _scaleX, height * _scaleY, startAngle, endAngle, clockwise, closed);
		}
 
		public void FillArc(float x, float y, float width, float height, float startAngle, float endAngle, bool clockwise)
		{
			_canvas.FillArc(x * _scaleX, y * _scaleY, width * _scaleX, height * _scaleY, startAngle, endAngle, clockwise);
		}
 
		public void DrawEllipse(float x, float y, float width, float height)
		{
			_canvas.DrawEllipse(x * _scaleX, y * _scaleY, width * _scaleX, height * _scaleY);
		}
 
		public void DrawImage(IImage image, float x, float y, float width, float height)
		{
			_canvas.DrawImage(image, x * _scaleX, y * _scaleY, width * _scaleX, height * _scaleY);
		}
 
		public void DrawRectangle(float x, float y, float width, float height)
		{
			_canvas.DrawRectangle(x * _scaleX, y * _scaleY, width * _scaleX, height * _scaleY);
		}
 
		public void DrawRoundedRectangle(float x, float y, float width, float height, float cornerRadius)
		{
			_canvas.DrawRoundedRectangle(x * _scaleX, y * _scaleY, width * _scaleX, height * _scaleY, cornerRadius * _scaleX);
		}
 
		public void DrawString(string value, float x, float y, HorizontalAlignment horizontalAlignment)
		{
			_canvas.DrawString(value, x * _scaleX, y * _scaleY, horizontalAlignment);
		}
 
		public void DrawString(string value, float x, float y, float width, float height, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment,
			TextFlow textFlow = TextFlow.ClipBounds, float lineSpacingAdjustment = 0)
		{
			_canvas.DrawString(value, x * _scaleX, y * _scaleY, width * _scaleX, height * _scaleY, horizontalAlignment, verticalAlignment, textFlow);
		}
 
		public void DrawText(IAttributedText value, float x, float y, float width, float height)
		{
			_canvas.DrawText(value, x * _scaleX, y * _scaleY, width * _scaleX, height * _scaleY);
		}
 
		public void FillEllipse(float x, float y, float width, float height)
		{
			_canvas.FillEllipse(x * _scaleX, y * _scaleY, width * _scaleX, height * _scaleY);
		}
 
		public void FillRectangle(float x, float y, float width, float height)
		{
			_canvas.FillRectangle(x * _scaleX, y * _scaleY, width * _scaleX, height * _scaleY);
		}
 
		public void FillRoundedRectangle(float x, float y, float width, float height, float cornerRadius)
		{
			_canvas.FillRoundedRectangle(x * _scaleX, y * _scaleY, width * _scaleX, height * _scaleY, cornerRadius * _scaleX);
		}
 
		public void DrawPath(PathF path)
		{
			var scaledPath = path.AsScaledPath(_scaleX, _scaleY);
			_canvas.DrawPath(scaledPath);
		}
 
		public void FillPath(PathF path, WindingMode windingMode)
		{
			var scaledPath = path.AsScaledPath(_scaleX, _scaleY);
			_canvas.FillPath(scaledPath, windingMode);
		}
 
		public void ClipPath(PathF path, WindingMode windingMode = WindingMode.NonZero)
		{
			var scaledPath = path.AsScaledPath(_scaleX, _scaleY);
			_canvas.ClipPath(scaledPath, windingMode);
		}
 
		public void ClipRectangle(float x, float y, float width, float height)
		{
			_canvas.ClipRectangle(x * _scaleX, y * _scaleY, width * _scaleX, height * _scaleY);
		}
 
		public void Rotate(float degrees, float x, float y)
		{
			_canvas.Rotate(degrees, x * _scaleX, y * _scaleY);
		}
 
		public void SetFillPaint(Paint paint, RectF rectangle)
		{
			_canvas.SetFillPaint(paint, new RectF(rectangle.X * _scaleX, rectangle.Y * _scaleY, rectangle.Width * _scaleX, rectangle.Height * _scaleY));
		}
 
		public void Rotate(float degrees)
		{
			_canvas.Rotate(degrees);
		}
 
		public void Scale(float sx, float sy)
		{
			_scaleX *= Math.Abs(sx);
			_scaleY *= Math.Abs(sy);
			_canvas.Scale(sx, sy);
		}
 
		public void Translate(float tx, float ty)
		{
			_canvas.Translate(tx, ty);
		}
 
		public void ConcatenateTransform(Matrix3x2 transform)
		{
			transform.DeconstructScales(out _, out var sx, out var sy);
			_scaleX *= sx;
			_scaleY *= sy;
			_canvas.ConcatenateTransform(transform);
		}
 
		public void SaveState()
		{
			_canvas.SaveState();
			_scaleXStack.Push(_scaleX);
			_scaleYStack.Push(_scaleY);
		}
 
		public void ResetState()
		{
			_canvas.ResetState();
			_scaleXStack.Clear();
			_scaleYStack.Clear();
			_scaleX = 1;
			_scaleY = 1;
		}
 
		public bool RestoreState()
		{
			var restored = _canvas.RestoreState();
			if (_scaleXStack.Count > 0)
			{
				_scaleX = _scaleXStack.Pop();
				_scaleY = _scaleYStack.Pop();
			}
			else
			{
				_scaleX = 1;
				_scaleY = 1;
			}
 
			return restored;
		}
 
		public float GetScale()
		{
			return _scaleX;
		}
 
		public void SetShadow(SizeF offset, float blur, Color color)
		{
			_canvas.SetShadow(offset, blur, color);
		}
 
		public void SetBlur(float blurRadius)
		{
			_blurrableCanvas?.SetBlur(blurRadius);
		}
 
		public SizeF GetStringSize(string value, IFont font, float fontSize)
			=> _canvas.GetStringSize(value, font, fontSize);
 
		public SizeF GetStringSize(string value, IFont font, float fontSize, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment)
			=> _canvas.GetStringSize(value, font, fontSize, horizontalAlignment, verticalAlignment);
	}
}