File: src\Graphics\src\Graphics\Platforms\Windows\PlatformCanvas.cs
Web Access
Project: src\src\Graphics\src\Graphics.Win2D\Graphics.Win2D.csproj (Microsoft.Maui.Graphics.Win2D.WinUI.Desktop)
using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.Brushes;
using Microsoft.Graphics.Canvas.Effects;
using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.Text;
using Microsoft.Maui.Graphics.Text;
using System;
using System.Numerics;
using WRect = Windows.Foundation.Rect;
#if NETFX_CORE
using WColors = global::Windows.UI.Colors;
#else
using WColors = global::Microsoft.UI.Colors;
#endif
 
#if MAUI_GRAPHICS_WIN2D
namespace Microsoft.Maui.Graphics.Win2D
#else
namespace Microsoft.Maui.Graphics.Platform
#endif
{
	/// <summary>
	/// A Windows platform implementation of <see cref="AbstractCanvas{T}"/>.
	/// </summary>
#if MAUI_GRAPHICS_WIN2D
	public class W2DCanvas
#else
	public class PlatformCanvas
#endif
		: AbstractCanvas<PlatformCanvasState>, IBlurrableCanvas
	{
		private CanvasDrawingSession _session;
		private CanvasRenderTarget _patternContext;
		private CanvasRenderTarget _effectContext;
		private ShadowEffect _shadowEffect;
		private GaussianBlurEffect _blurEffect;
		private global::Windows.Foundation.Size _canvasSize;
		private Vector2 _point1;
		private Vector2 _point2;
		private Vector2 _linearGradientStartPoint;
		private Vector2 _linearGradientEndPoint;
		private Vector2 _radialGradientCenter;
		private float _radialGradientRadius;
 
		/* Unmerged change from project 'Microsoft.Maui.Graphics.Win2D.WinUI.Desktop'
		Before:
				private Rect _rect;
		After:
				private global::Windows.Foundation.Rect _rect;
		*/
		private WRect _rect;
		private global::Windows.Foundation.Size _size;
 
		private bool _bitmapPatternFills;
 
#if MAUI_GRAPHICS_WIN2D
		public W2DCanvas()
#else
		public PlatformCanvas()
#endif
			: base(new PlatformCanvasStateService(), new PlatformStringSizeService())
		{
		}
 
		public CanvasDrawingSession Session
		{
			get => _session;
			set => _session = value;
		}
 
		public global::Windows.Foundation.Size CanvasSize
		{
			get => _canvasSize;
			set => _canvasSize = value;
		}
 
		public bool BitmapPatternFills
		{
			get => _bitmapPatternFills;
			set => _bitmapPatternFills = value;
		}
 
		public override float MiterLimit
		{
			set => CurrentState.MiterLimit = value;
		}
 
		public override Color StrokeColor
		{
			set => CurrentState.StrokeColor = value;
		}
 
		public override LineCap StrokeLineCap
		{
			set => CurrentState.StrokeLineCap = value;
		}
 
		public override LineJoin StrokeLineJoin
		{
			set => CurrentState.StrokeLineJoin = value;
		}
 
		protected override void PlatformSetStrokeDashPattern(float[] strokePattern, float strokeDashOffset, float strokeSize)
		{
			CurrentState.SetStrokeDashPattern(strokePattern, strokeDashOffset, strokeSize);
		}
 
		public override Color FillColor
		{
			set => CurrentState.FillColor = value;
		}
 
		public override Color FontColor
		{
			set => CurrentState.FontColor = value;
		}
 
		public override IFont Font
		{
			set => CurrentState.Font = value;
		}
 
		public override float FontSize
		{
			set => CurrentState.FontSize = value;
		}
 
		public override float Alpha
		{
			set => CurrentState.Alpha = value;
		}
 
		public override bool Antialias
		{
			set => _session.Antialiasing = value ? CanvasAntialiasing.Antialiased : CanvasAntialiasing.Aliased;
		}
 
		public override BlendMode BlendMode
		{
			set { }
		}
 
		public override void FillPath(PathF path, WindingMode windingMode)
		{
			var geometry = GetPath(path, windingMode == WindingMode.NonZero ? CanvasFilledRegionDetermination.Winding : CanvasFilledRegionDetermination.Alternate);
			Draw(s => s.FillGeometry(geometry, CurrentState.PlatformFillBrush));
		}
 
 
		public override void SubtractFromClip(float x, float y, float width, float height)
		{
			CurrentState.SubtractFromClip(x, y, width, height);
		}
 
		public override void ClipPath(PathF path, WindingMode windingMode = WindingMode.NonZero)
		{
			CurrentState.ClipPath(path, windingMode);
		}
 
		public override void ClipRectangle(float x, float y, float width, float height)
		{
			CurrentState.ClipRectangle(x, y, width, height);
		}
 
		public override void FillArc(float x, float y, float width, float height, float startAngle, float endAngle, bool clockwise)
		{
			while (startAngle < 0)
			{
				startAngle += 360;
			}
 
			while (endAngle < 0)
			{
				endAngle += 360;
			}
 
			var rotation = GeometryUtil.GetSweep(startAngle, endAngle, clockwise);
			var absRotation = Math.Abs(rotation);
 
			float strokeWidth = CurrentState.StrokeSize;
			SetRect(x, y, width, height);
 
			_size.Width = _rect.Width / 2;
			_size.Height = _rect.Height / 2;
 
			var startPoint = GeometryUtil.EllipseAngleToPoint((float)_rect.X, (float)_rect.Y, (float)_rect.Width, (float)_rect.Height, -startAngle);
			var endPoint = GeometryUtil.EllipseAngleToPoint((float)_rect.X, (float)_rect.Y, (float)_rect.Width, (float)_rect.Height, -endAngle);
 
			_point1.X = startPoint.X;
			_point1.Y = startPoint.Y;
 
			_point2.X = endPoint.X;
			_point2.Y = endPoint.Y;
 
			var builder = new CanvasPathBuilder(_session);
			builder.BeginFigure(_point1, CanvasFigureFill.Default);
			builder.AddArc(
				new Vector2(_point2.X, _point2.Y),
				(float)_size.Width,
				(float)_size.Height,
				0,
				clockwise ? CanvasSweepDirection.Clockwise : CanvasSweepDirection.CounterClockwise,
				absRotation >= 180 ? CanvasArcSize.Large : CanvasArcSize.Small);
 
			builder.EndFigure(CanvasFigureLoop.Closed);
			var geometry = CanvasGeometry.CreatePath(builder);
 
			Draw(ctx => ctx.FillGeometry(geometry, CurrentState.PlatformFillBrush));
		}
 
		public override void FillRectangle(float x, float y, float width, float height)
		{
			Draw(s => s.FillRectangle(x, y, width, height, CurrentState.PlatformFillBrush));
		}
 
		public override void FillRoundedRectangle(float x, float y, float width, float height, float cornerRadius)
		{
			Draw(s => s.FillRoundedRectangle(x, y, width, height, cornerRadius, cornerRadius, CurrentState.PlatformFillBrush));
		}
 
		public override void FillEllipse(float x, float y, float width, float height)
		{
			float radiusX;
			float radiusY;
 
			if (width > 0 || width < 0)
			{
				_point1.X = x + width / 2;
				radiusX = width / 2;
			}
			else
			{
				_point1.X = x;
				radiusX = 0;
			}
 
			if (height > 0 || height < 0)
			{
				_point1.Y = y + height / 2;
				radiusY = height / 2;
			}
			else
			{
				_point1.Y = x;
				radiusY = 0;
			}
 
			Draw(s => s.FillEllipse(_point1, radiusX, radiusY, CurrentState.PlatformFillBrush));
		}
 
		public override void DrawString(string value, float x, float y, HorizontalAlignment horizontalAlignment)
		{
			// Initialize a TextFormat
#if DEBUG
			try
			{
#endif
				var textFormat = (CurrentState.Font ?? Graphics.Font.Default).ToCanvasTextFormat(CurrentState.FontSize);
				textFormat.VerticalAlignment = CanvasVerticalAlignment.Top;
 
				switch (horizontalAlignment)
				{
					case HorizontalAlignment.Left:
						_rect.X = x;
						_rect.Width = CanvasSize.Width;
						textFormat.HorizontalAlignment = CanvasHorizontalAlignment.Left;
						break;
					case HorizontalAlignment.Right:
						_rect.X = x - CanvasSize.Width;
						_rect.Width = CanvasSize.Width;
						textFormat.HorizontalAlignment = CanvasHorizontalAlignment.Right;
						break;
					default:
						_rect.X = x - _canvasSize.Width;
						_rect.Width = _canvasSize.Width * 2;
						textFormat.HorizontalAlignment = CanvasHorizontalAlignment.Center;
						break;
				}
 
				_rect.Y = y - CurrentState.FontSize;
				_rect.Height = CurrentState.FontSize * 2;
 
				_point1.X = (float)_rect.X;
				_point1.Y = (float)_rect.Y;
 
				var textLayout = new CanvasTextLayout(
					_session,
					value,
					textFormat,
					(float)_rect.Width,
					(float)_rect.Height);
 
				Draw(ctx => ctx.DrawTextLayout(textLayout, _point1, CurrentState.PlatformFontBrush));
 
#if DEBUG
 
			}
			catch (Exception exc)
			{
				System.Diagnostics.Debug.WriteLine(exc);
			}
#endif
		}
 
		public override void DrawString(
			string value,
			float x,
			float y,
			float width,
			float height,
			HorizontalAlignment horizontalAlignment,
			VerticalAlignment verticalAlignment,
			TextFlow textFlow = TextFlow.ClipBounds,
			float lineAdjustment = 0)
		{
			var textFormat = (CurrentState.Font ?? Graphics.Font.Default).ToCanvasTextFormat(CurrentState.FontSize);
 
			switch (horizontalAlignment)
			{
				case HorizontalAlignment.Left:
					textFormat.HorizontalAlignment = CanvasHorizontalAlignment.Left;
					break;
				case HorizontalAlignment.Center:
					textFormat.HorizontalAlignment = CanvasHorizontalAlignment.Center;
					break;
				case HorizontalAlignment.Right:
					textFormat.HorizontalAlignment = CanvasHorizontalAlignment.Right;
					break;
				case HorizontalAlignment.Justified:
					textFormat.HorizontalAlignment = CanvasHorizontalAlignment.Justified;
					break;
			}
 
			switch (verticalAlignment)
			{
				case VerticalAlignment.Top:
					textFormat.VerticalAlignment = CanvasVerticalAlignment.Top;
					break;
				case VerticalAlignment.Center:
					textFormat.VerticalAlignment = CanvasVerticalAlignment.Center;
					break;
				case VerticalAlignment.Bottom:
					textFormat.VerticalAlignment = CanvasVerticalAlignment.Bottom;
					break;
			}
 
			// Initialize a TextLayout
			var textLayout = new CanvasTextLayout(
				_session,
				value,
				textFormat,
				width,
				height)
			{
				Options = textFlow == TextFlow.OverflowBounds
					? CanvasDrawTextOptions.Default
					: CanvasDrawTextOptions.Clip
			};
 
 
			_point1.X = x;
			_point1.Y = y;
 
			Draw(ctx => ctx.DrawTextLayout(textLayout, _point1, CurrentState.PlatformFontBrush));
		}
 
		public override void DrawText(IAttributedText value, float x, float y, float width, float height)
		{
			throw new NotImplementedException();
		}
 
		public override void SetShadow(SizeF offset, float blur, Color color)
		{
			CurrentState.SetShadow(offset, blur, color);
		}
 
		public override void SetFillPaint(Paint paint, RectF rectangle)
		{
			if (paint == null)
			{
				CurrentState.FillColor = Colors.White;
				return;
			}
 
			if (paint is SolidPaint solidPaint)
			{
				CurrentState.FillColor = solidPaint.Color;
				return;
			}
 
			if (paint is ImagePaint imagePaint)
			{
				if (imagePaint.Image is PlatformImage image)
				{
					var bitmapBrush = new CanvasImageBrush(_session, image.PlatformRepresentation)
					{
						ExtendX = CanvasEdgeBehavior.Wrap,
						ExtendY = CanvasEdgeBehavior.Wrap
					};
					CurrentState.SetBitmapBrush(bitmapBrush);
				}
				else
				{
					CurrentState.FillColor = Colors.White;
				}
				return;
			}
 
			if (paint is PatternPaint patternPaint)
			{
				var pattern = patternPaint.Pattern;
				if (pattern == null)
				{
					CurrentState.FillColor = Colors.White;
					return;
				}
 
				if (_bitmapPatternFills)
				{
					var bitmap = CreatePatternBitmap(pattern);
					if (bitmap != null)
					{
						var bitmapBrush = new CanvasImageBrush(_session, bitmap)
						{
							ExtendX = CanvasEdgeBehavior.Wrap,
							ExtendY = CanvasEdgeBehavior.Wrap
						};
						CurrentState.SetBitmapBrush(bitmapBrush);
					}
					else
					{
						CurrentState.FillColor = Colors.White;
					}
				}
				else
				{
					var commandList = CreatePatternCommandList(pattern);
					if (commandList != null)
					{
						var imageBrush = new CanvasImageBrush(_session, commandList)
						{
							ExtendX = CanvasEdgeBehavior.Wrap,
							ExtendY = CanvasEdgeBehavior.Wrap,
 
							/* Unmerged change from project 'Microsoft.Maui.Graphics.Win2D.WinUI.Desktop'
							Before:
														SourceRectangle = new Rect(
							After:
														SourceRectangle = new global::Windows.Foundation.Rect(
							*/
							SourceRectangle = new WRect(
									(pattern.Width - pattern.StepX) / 2,
									(pattern.Height - pattern.StepY) / 2,
									pattern.StepX,
									pattern.StepY)
						};
						CurrentState.SetBitmapBrush(imageBrush);
					}
					else
					{
						CurrentState.FillColor = Colors.White;
					}
				}
				return;
			}
 
			if (paint is LinearGradientPaint linearGradientPaint)
			{
				float x1 = (float)(linearGradientPaint.StartPoint.X * rectangle.Width) + rectangle.X;
				float y1 = (float)(linearGradientPaint.StartPoint.Y * rectangle.Height) + rectangle.Y;
 
				float x2 = (float)(linearGradientPaint.EndPoint.X * rectangle.Width) + rectangle.X;
				float y2 = (float)(linearGradientPaint.EndPoint.Y * rectangle.Height) + rectangle.Y;
 
				_linearGradientStartPoint.X = x1;
				_linearGradientStartPoint.Y = y1;
				_linearGradientEndPoint.X = x2;
				_linearGradientEndPoint.Y = y2;
 
				CurrentState.SetLinearGradient(paint, _linearGradientStartPoint, _linearGradientEndPoint);
			}
			else if (paint is RadialGradientPaint radialGradientPaint)
			{
				float centerX = (float)(radialGradientPaint.Center.X * rectangle.Width) + rectangle.X;
				float centerY = (float)(radialGradientPaint.Center.Y * rectangle.Height) + rectangle.Y;
				float radius = (float)radialGradientPaint.Radius * Math.Max(rectangle.Height, rectangle.Width);
 
				_radialGradientCenter.X = centerX;
				_radialGradientCenter.Y = centerY;
				_radialGradientRadius = radius;
 
				CurrentState.SetRadialGradient(paint, _radialGradientCenter, _radialGradientRadius);
			}
		}
 
		private CanvasCommandList CreatePatternCommandList(IPattern pattern)
		{
			var commandList = new CanvasCommandList(_session);
			using (var patternSession = commandList.CreateDrawingSession())
			{
				var canvas = new PlatformCanvas { Session = patternSession };
				pattern?.Draw(canvas);
			}
 
			return commandList;
		}
 
 
		private CanvasBitmap CreatePatternBitmap(IPattern pattern)
		{
			var context = GetOrCreatePatternContext(new global::Windows.Foundation.Size(pattern.Width, pattern.Height));
			if (context != null)
			{
				using (var imageSession = context.CreateDrawingSession())
				{
					imageSession.Clear(WColors.Transparent);
					var canvas = new PlatformCanvas { Session = imageSession };
					pattern.Draw(canvas);
				}
 
				return context;
			}
 
			return null;
		}
 
		public override void DrawImage(IImage image, float x, float y, float width, float height)
		{
			if (image.ToPlatformImage() is Platform.PlatformImage platformImage)
			{
				SetRect(x, y, width, height);
 
				/* Unmerged change from project 'Microsoft.Maui.Graphics.Win2D.WinUI.Desktop'
				Before:
								Draw(s => s.DrawImage(platformImage.PlatformImage, _rect, Rect.Empty, CurrentState.Alpha, CanvasImageInterpolation.Linear));
				After:
								Draw(s => s.DrawImage(platformImage.PlatformImage, _rect, global::Windows.Foundation.Rect.Empty, CurrentState.Alpha, CanvasImageInterpolation.Linear));
				*/
				Draw(s => s.DrawImage(platformImage.PlatformRepresentation, _rect, WRect.Empty, CurrentState.Alpha, CanvasImageInterpolation.Linear));
			}
		}
 
		protected override float PlatformStrokeSize
		{
			set { }
		}
 
		protected override void PlatformDrawLine(float x1, float y1, float x2, float y2)
		{
			Draw(s => s.DrawLine(x1, y1, x2, y2, CurrentState.PlatformStrokeBrush, CurrentState.StrokeSize, CurrentState.PlatformStrokeStyle));
		}
 
		protected override void PlatformDrawArc(float x, float y, float width, float height, float startAngle, float endAngle, bool clockwise, bool closed)
		{
			while (startAngle < 0)
			{
				startAngle += 360;
			}
 
			while (endAngle < 0)
			{
				endAngle += 360;
			}
 
			var rotation = GeometryUtil.GetSweep(startAngle, endAngle, clockwise);
			var absRotation = Math.Abs(rotation);
 
			float strokeWidth = CurrentState.StrokeSize;
			SetRect(x, y, width, height);
 
			_size.Width = _rect.Width / 2;
			_size.Height = _rect.Height / 2;
 
			var startPoint = GeometryUtil.EllipseAngleToPoint((float)_rect.X, (float)_rect.Y, (float)_rect.Width, (float)_rect.Height, -startAngle);
			var endPoint = GeometryUtil.EllipseAngleToPoint((float)_rect.X, (float)_rect.Y, (float)_rect.Width, (float)_rect.Height, -endAngle);
 
			_point1.X = startPoint.X;
			_point1.Y = startPoint.Y;
 
			_point2.X = endPoint.X;
			_point2.Y = endPoint.Y;
 
			var builder = new CanvasPathBuilder(_session);
			builder.BeginFigure(_point1, CanvasFigureFill.Default);
			builder.AddArc(
				new Vector2(_point2.X, _point2.Y),
				(float)_size.Width,
				(float)_size.Height,
				0,
				clockwise ? CanvasSweepDirection.Clockwise : CanvasSweepDirection.CounterClockwise,
				absRotation >= 180 ? CanvasArcSize.Large : CanvasArcSize.Small);
 
			builder.EndFigure(CanvasFigureLoop.Open);
			var geometry = CanvasGeometry.CreatePath(builder);
 
			Draw(ctx => ctx.DrawGeometry(geometry, CurrentState.PlatformStrokeBrush, strokeWidth, CurrentState.PlatformStrokeStyle));
		}
 
		protected override void PlatformDrawRectangle(float x, float y, float width, float height)
		{
			float strokeWidth = CurrentState.StrokeSize;
			SetRect(x, y, width, height);
 
			Draw(s => s.DrawRectangle(_rect, CurrentState.PlatformStrokeBrush, CurrentState.StrokeSize, CurrentState.PlatformStrokeStyle));
		}
 
		protected override void PlatformDrawRoundedRectangle(float x, float y, float width, float height, float cornerRadius)
		{
			float strokeWidth = CurrentState.StrokeSize;
			SetRect(x, y, width, height);
 
			if (cornerRadius > _rect.Width / 2)
			{
				cornerRadius = (float)_rect.Width / 2;
			}
 
			if (cornerRadius > _rect.Height / 2)
			{
				cornerRadius = (float)_rect.Height / 2;
			}
 
			Draw(s => s.DrawRoundedRectangle(_rect, cornerRadius, cornerRadius, CurrentState.PlatformStrokeBrush, CurrentState.StrokeSize, CurrentState.PlatformStrokeStyle));
		}
 
		protected override void PlatformDrawEllipse(float x, float y, float width, float height)
		{
			float radiusX;
			float radiusY;
			float px;
			float py;
			float strokeWidth = CurrentState.StrokeSize;
 
			if (width > 0 || width < 0)
			{
				px = x + width / 2;
				radiusX = width / 2;
			}
			else
			{
				px = x;
				radiusX = 0;
			}
 
			if (height > 0 || height < 0)
			{
				py = y + height / 2;
				radiusY = height / 2;
			}
			else
			{
				py = x;
				radiusY = 0;
			}
 
			Draw(s => s.DrawEllipse(px, py, radiusX, radiusY, CurrentState.PlatformStrokeBrush, CurrentState.StrokeSize, CurrentState.PlatformStrokeStyle));
		}
 
		private CanvasGeometry GetPath(PathF path, CanvasFilledRegionDetermination fillMode = CanvasFilledRegionDetermination.Winding)
		{
			var geometry = path.PlatformPath as CanvasGeometry;
 
			if (geometry == null)
			{
				geometry = path.AsPath(_session, fillMode);
				path.PlatformPath = geometry;
			}
 
			return geometry;
		}
 
		protected override void PlatformDrawPath(PathF path)
		{
			if (path == null)
				return;
 
			var geometry = GetPath(path);
 
			Draw(s =>
			{
				// ReSharper disable AccessToDisposedClosure
				float strokeWidth = CurrentState.StrokeSize;
				s.DrawGeometry(geometry, CurrentState.PlatformStrokeBrush, strokeWidth, CurrentState.PlatformStrokeStyle);
			});
		}
 
		protected override void PlatformRotate(float degrees, float radians, float x, float y)
		{
			_session.Transform = CurrentState.AppendRotate(degrees, x, y);
		}
 
		protected override void PlatformRotate(float degrees, float radians)
		{
			_session.Transform = CurrentState.AppendRotate(degrees);
		}
 
		protected override void PlatformScale(float sx, float sy)
		{
			_session.Transform = CurrentState.AppendScale(sx, sy);
		}
 
		protected override void PlatformTranslate(float tx, float ty)
		{
			_session.Transform = CurrentState.AppendTranslate(tx, ty);
		}
 
		protected override void PlatformConcatenateTransform(Matrix3x2 transform)
		{
			_session.Transform = CurrentState.AppendConcatenateTransform(transform);
		}
 
		public void SetBlur(float blurRadius)
		{
			CurrentState.SetBlur(blurRadius);
		}
 
		public override void SaveState()
		{
			CurrentState.SaveRenderTargetState();
			base.SaveState();
		}
 
		protected override void StateRestored(PlatformCanvasState state)
		{
			if (_session != null)
			{
				state?.RestoreRenderTargetState();
			}
		}
 
		private void Draw(Action<CanvasDrawingSession> drawingAction)
		{
			if (CurrentState.IsShadowed)
			{
				DrawShadow(drawingAction);
			}
 
			if (CurrentState.IsBlurred)
			{
				DrawBlurred(drawingAction);
			}
			else
			{
				drawingAction(_session);
			}
		}
 
		private CanvasRenderTarget GetOrCreatePatternContext(global::Windows.Foundation.Size patternSize)
		{
			if (_patternContext != null)
			{
				// If the effect bitmap size does not equal the size of our render target, then dispose of it
				// and create a new one.
				if (_patternContext.Size != patternSize)
				{
					_patternContext.Dispose();
					_patternContext = null;
				}
				else
				{
					return _patternContext;
				}
			}
 
			_patternContext = new CanvasRenderTarget(_session, _canvasSize);
			return _patternContext;
		}
 
		private CanvasRenderTarget GetOrCreateEffectContext()
		{
			if (_effectContext != null)
			{
				// If the effect bitmap size does not equal the size of our render target, then dispose of it
				// and create a new one.
				if (_effectContext.Size != CanvasSize)
				{
					_effectContext.Dispose();
					_effectContext = null;
 
					if (_shadowEffect != null)
					{
						_shadowEffect.Dispose();
						_shadowEffect = null;
					}
 
					if (_blurEffect != null)
					{
						_blurEffect.Dispose();
						_blurEffect = null;
					}
				}
				else
				{
					return _effectContext;
				}
			}
 
			_effectContext = new CanvasRenderTarget(_session, _canvasSize);
			return _effectContext;
		}
 
		private void DrawShadow(Action<CanvasDrawingSession> drawingAction)
		{
			var context = GetOrCreateEffectContext();
			if (context != null)
			{
				using (var imageSession = context.CreateDrawingSession())
				{
					imageSession.Clear(WColors.Transparent);
					imageSession.Transform = CurrentState.Matrix.Translate(CurrentState.ShadowOffset.X, CurrentState.ShadowOffset.Y);
					drawingAction(imageSession);
				}
 
				if (_shadowEffect == null)
					_shadowEffect = new ShadowEffect();
 
				_shadowEffect.Source = context;
				_shadowEffect.BlurAmount = CurrentState.ActualShadowBlur / 3;
				_shadowEffect.ShadowColor = CurrentState.ShadowColor;
 
				_session.Transform = Matrix3x2.Identity;
				_session.DrawImage(_shadowEffect, 0, 0);
				_session.Transform = CurrentState.Matrix;
			}
		}
 
		private void DrawBlurred(Action<CanvasDrawingSession> drawingAction)
		{
			var context = GetOrCreateEffectContext();
			if (context != null)
			{
				using (var imageSession = context.CreateDrawingSession())
				{
					imageSession.Clear(WColors.Transparent);
					imageSession.Transform = CurrentState.Matrix;
					drawingAction(imageSession);
				}
 
				if (_blurEffect == null)
					_blurEffect = new GaussianBlurEffect();
 
				_blurEffect.Source = context;
				_blurEffect.BorderMode = EffectBorderMode.Soft;
				_blurEffect.Optimization = EffectOptimization.Speed;
 
				_session.Transform = Matrix3x2.Identity;
				_session.DrawImage(_blurEffect, 0, 0);
				_session.Transform = CurrentState.Matrix;
			}
		}
 
		private void SetRect(float x, float y, float width, float height)
		{
			_rect.X = Math.Min(x, x + width);
			_rect.Y = Math.Min(y, y + height);
			_rect.Width = Math.Abs(width);
			_rect.Height = Math.Abs(height);
		}
	}
}