Beispiel #1
0
        static SKMatrix Multiply(SKMatrix first, SKMatrix second)
        {
            SKMatrix target = SKMatrix.CreateIdentity();

            SKMatrix.Concat(ref target, first, second);
            return(target);
        }
Beispiel #2
0
        /// <summary>
        /// Configure paint wrapper for using tile brush.
        /// </summary>
        /// <param name="paintWrapper">Paint wrapper.</param>
        /// <param name="targetSize">Target size.</param>
        /// <param name="tileBrush">Tile brush to use.</param>
        /// <param name="tileBrushImage">Tile brush image.</param>
        private void ConfigureTileBrush(ref PaintWrapper paintWrapper, Size targetSize, ITileBrush tileBrush, IDrawableBitmapImpl tileBrushImage)
        {
            var calc         = new TileBrushCalculator(tileBrush, tileBrushImage.PixelSize.ToSizeWithDpi(_dpi), targetSize);
            var intermediate = CreateRenderTarget(calc.IntermediateSize);

            paintWrapper.AddDisposable(intermediate);

            using (var context = intermediate.CreateDrawingContext(null))
            {
                var sourceRect = new Rect(tileBrushImage.PixelSize.ToSizeWithDpi(96));
                var targetRect = new Rect(tileBrushImage.PixelSize.ToSizeWithDpi(_dpi));

                context.Clear(Colors.Transparent);
                context.PushClip(calc.IntermediateClip);
                context.Transform = calc.IntermediateTransform;
                context.DrawBitmap(
                    RefCountable.CreateUnownedNotClonable(tileBrushImage),
                    1,
                    sourceRect,
                    targetRect,
                    tileBrush.BitmapInterpolationMode);
                context.PopClip();
            }

            var tileTransform =
                tileBrush.TileMode != TileMode.None
                    ? SKMatrix.CreateTranslation(-(float)calc.DestinationRect.X, -(float)calc.DestinationRect.Y)
                    : SKMatrix.CreateIdentity();

            SKShaderTileMode tileX =
                tileBrush.TileMode == TileMode.None
                    ? SKShaderTileMode.Clamp
                    : tileBrush.TileMode == TileMode.FlipX || tileBrush.TileMode == TileMode.FlipXY
                        ? SKShaderTileMode.Mirror
                        : SKShaderTileMode.Repeat;

            SKShaderTileMode tileY =
                tileBrush.TileMode == TileMode.None
                    ? SKShaderTileMode.Clamp
                    : tileBrush.TileMode == TileMode.FlipY || tileBrush.TileMode == TileMode.FlipXY
                        ? SKShaderTileMode.Mirror
                        : SKShaderTileMode.Repeat;


            var image = intermediate.SnapshotImage();

            paintWrapper.AddDisposable(image);

            var paintTransform = default(SKMatrix);

            SKMatrix.Concat(
                ref paintTransform,
                tileTransform,
                SKMatrix.CreateScale((float)(96.0 / _dpi.X), (float)(96.0 / _dpi.Y)));

            using (var shader = image.ToShader(tileX, tileY, paintTransform))
            {
                paintWrapper.Paint.Shader = shader;
            }
        }
Beispiel #3
0
        /// <summary>
        /// Resets all zooming and dragging and makes the chart fit exactly it's
        /// bounds.
        /// </summary>
        /// <returns></returns>
        public SKMatrix FitScreen()
        {
            minScaleX = 1.0f;
            minScaleY = 1.0f;

            return(SKMatrix.CreateIdentity());
        }
Beispiel #4
0
        public static SKMatrix ToSkia(this Transform transform)
        {
            SKMatrix skMatrix = SKMatrix.CreateIdentity();

            if (transform == null)
            {
                return(skMatrix);
            }

            Matrix matrix = transform.Value;

            skMatrix.Values = new float[] {
                (float)matrix.M11,
                (float)matrix.M21,
                Forms.ConvertToScaledPixel(matrix.OffsetX),
                (float)matrix.M12,
                (float)matrix.M22,
                Forms.ConvertToScaledPixel(matrix.OffsetY),
                0,
                0,
                1
            };

            return(skMatrix);
        }
        private void Manipulate()
        {
            TouchManipulationInfo[] infos = new TouchManipulationInfo[touchDictionary.Count];
            touchDictionary.Values.CopyTo(infos, 0);
            SKMatrix touchMatrix = SKMatrix.CreateIdentity();

            if (infos.Length == 1 && Type != BitmapType.Main)
            {
                SKPoint prevPoint  = infos[0].PreviousPoint;
                SKPoint newPoint   = infos[0].NewPoint;
                SKPoint pivotPoint = Matrix.MapPoint(Bitmap.Width / 2, Bitmap.Height / 2);

                touchMatrix = TouchManager.OneFingerManipulate(prevPoint, newPoint, pivotPoint);
            }
            else if (infos.Length >= 2)
            {
                int     pivotIndex = infos[0].NewPoint == infos[0].PreviousPoint ? 0 : 1;
                SKPoint pivotPoint = infos[pivotIndex].NewPoint;
                SKPoint newPoint   = infos[1 - pivotIndex].NewPoint;
                SKPoint prevPoint  = infos[1 - pivotIndex].PreviousPoint;

                touchMatrix = TouchManager.TwoFingerManipulate(prevPoint, newPoint, pivotPoint);
            }

            SKMatrix matrix = Matrix;

            matrix.PostConcat(touchMatrix);
            Matrix = matrix;
        }
Beispiel #6
0
        public void Reset()
        {
            _rotatedDegrees = 0;

            Matrix = SKMatrix.CreateIdentity();

            CanvasView?.InvalidateSurface();
        }
        public DesignCanvasView()
        {
            InitializeComponent();

            Matrix  = SKMatrix.CreateIdentity();
            Touches = new Dictionary <long, SKPoint>();
            CanvasView.PaintSurface += OnCanvasViewPaintSurface;
        }
 internal TouchManipulationBitmap(SKBitmap bitmap, BitmapType type, string text, SKColor color = default)
 {
     Matrix = SKMatrix.CreateIdentity();
     Bitmap = bitmap;
     Type   = type;
     Color  = color;
     Text   = text;
 }
        public TouchManipulationBitmap(SKBitmap bitmap)
        {
            this.bitmap = bitmap;
            Matrix      = SKMatrix.CreateIdentity();

            TouchManager = new TouchManipulationManager
            {
                Mode = TouchManipulationMode.ScaleRotate
            };
        }
Beispiel #10
0
        public static SKMatrix RadianToMatrix(double radian)
        {
            SKMatrix newMatrix = SKMatrix.CreateIdentity();

            newMatrix.ScaleX = (float)Math.Cos(radian);
            newMatrix.ScaleY = newMatrix.ScaleX;
            newMatrix.SkewY  = (float)Math.Sin(radian);
            newMatrix.SkewX  = -newMatrix.SkewY;

            return(newMatrix);
        }
Beispiel #11
0
        SKMatrix CreateMatrix()
        {
            SKMatrix matrix = SKMatrix.CreateIdentity();

            SKRect drawableBounds  = _drawableBounds;
            float  halfStrokeWidth = _skPaint.StrokeWidth / 2;

            drawableBounds.Left   += halfStrokeWidth;
            drawableBounds.Top    += halfStrokeWidth;
            drawableBounds.Right  -= halfStrokeWidth;
            drawableBounds.Bottom -= halfStrokeWidth;

            float widthScale  = drawableBounds.Width / _pathFillBounds.Width;
            float heightScale = drawableBounds.Height / _pathFillBounds.Height;

            switch (_stretch)
            {
            case Stretch.None:
                drawableBounds = _drawableBounds;
                float adjustX = Math.Min(0, _pathStrokeBounds.Left);
                float adjustY = Math.Min(0, _pathStrokeBounds.Top);
                if (adjustX < 0 || adjustY < 0)
                {
                    matrix = SKMatrix.CreateTranslation(-adjustX, -adjustY);
                }
                break;

            case Stretch.Fill:
                matrix = SKMatrix.CreateScale(widthScale, heightScale);
                matrix = matrix.PostConcat(
                    SKMatrix.CreateTranslation(drawableBounds.Left - widthScale * _pathFillBounds.Left,
                                               drawableBounds.Top - heightScale * _pathFillBounds.Top));
                break;

            case Stretch.Uniform:
                float minScale = Math.Min(widthScale, heightScale);
                matrix = SKMatrix.CreateScale(minScale, minScale);
                matrix = matrix.PostConcat(
                    SKMatrix.CreateTranslation(drawableBounds.Left - (minScale * _pathFillBounds.Left) + (drawableBounds.Width - (minScale * _pathFillBounds.Width)) / 2,
                                               drawableBounds.Top - (minScale * _pathFillBounds.Top) + (drawableBounds.Height - (minScale * _pathFillBounds.Height)) / 2));
                break;

            case Stretch.UniformToFill:
                float maxScale = Math.Max(widthScale, heightScale);
                matrix = SKMatrix.CreateScale(maxScale, maxScale);
                matrix = matrix.PostConcat(
                    SKMatrix.CreateTranslation(drawableBounds.Left - (maxScale * _pathFillBounds.Left),
                                               drawableBounds.Top - (maxScale * _pathFillBounds.Top)));
                break;
            }

            return(matrix);
        }
        protected override void OnPaintSurface(SKPaintSurfaceEventArgs args)
        {
            base.OnPaintSurface(args);

            SKImageInfo info    = args.Info;
            SKSurface   surface = skSurface = args.Surface;
            SKCanvas    canvas  = surface.Canvas;

            canvas.Clear(SKColors.Gray);

            // Calculate rectangle for displaying bitmap
            float  scale      = Math.Min((float)info.Width / bitmap.Width, (float)info.Height / bitmap.Height);
            float  x          = (info.Width - scale * bitmap.Width) / 2;
            float  y          = (info.Height - scale * bitmap.Height) / 2;
            SKRect bitmapRect = new SKRect(x, y, x + scale * bitmap.Width, y + scale * bitmap.Height);

            canvas.DrawBitmap(bitmap, bitmapRect);

            // Calculate a matrix transform for displaying the cropping rectangle
            SKMatrix bitmapScaleMatrix = SKMatrix.CreateIdentity();

            bitmapScaleMatrix.SetScaleTranslate(scale, scale, x, y);

            // Display rectangle
            SKRect scaledCropRect = bitmapScaleMatrix.MapRect(croppingRect.Rect);

            canvas.DrawRect(scaledCropRect, edgeStroke);

            // Display heavier corners
            using (SKPath path = new SKPath())
            {
                path.MoveTo(scaledCropRect.Left, scaledCropRect.Top + CORNER);
                path.LineTo(scaledCropRect.Left, scaledCropRect.Top);
                path.LineTo(scaledCropRect.Left + CORNER, scaledCropRect.Top);

                path.MoveTo(scaledCropRect.Right - CORNER, scaledCropRect.Top);
                path.LineTo(scaledCropRect.Right, scaledCropRect.Top);
                path.LineTo(scaledCropRect.Right, scaledCropRect.Top + CORNER);

                path.MoveTo(scaledCropRect.Right, scaledCropRect.Bottom - CORNER);
                path.LineTo(scaledCropRect.Right, scaledCropRect.Bottom);
                path.LineTo(scaledCropRect.Right - CORNER, scaledCropRect.Bottom);

                path.MoveTo(scaledCropRect.Left + CORNER, scaledCropRect.Bottom);
                path.LineTo(scaledCropRect.Left, scaledCropRect.Bottom);
                path.LineTo(scaledCropRect.Left, scaledCropRect.Bottom - CORNER);

                canvas.DrawPath(path, cornerStroke);
            }

            // Invert the transform for touch tracking
            bitmapScaleMatrix.TryInvert(out inverseBitmapMatrix);
        }
Beispiel #13
0
        private SKMatrix ComputeMatrix(SKSize size, SKPoint ptUL, SKPoint ptUR, SKPoint ptLL, SKPoint?ptLR = null)
        {
            // Scale transform
            SKMatrix S = SKMatrix.CreateScale(1 / size.Width, 1 / size.Height);

            // Affine transform
            SKMatrix A = new SKMatrix
            {
                ScaleX = ptUR.X - ptUL.X,
                SkewY  = ptUR.Y - ptUL.Y,
                SkewX  = ptLL.X - ptUL.X,
                ScaleY = ptLL.Y - ptUL.Y,
                TransX = ptUL.X,
                TransY = ptUL.Y,
                Persp2 = 1
            };

            // Non-Affine transform
            SKMatrix N = SKMatrix.CreateIdentity();

            if (ptLR.HasValue)
            {
                SKMatrix inverseA;
                A.TryInvert(out inverseA);
                SKPoint abPoint = inverseA.MapPoint(ptLR.Value);
                float   a       = abPoint.X;
                float   b       = abPoint.Y;

                float scaleX = a / (a + b - 1);
                float scaleY = b / (a + b - 1);

                N = new SKMatrix
                {
                    ScaleX = scaleX,
                    ScaleY = scaleY,
                    Persp0 = scaleX - 1,
                    Persp1 = scaleY - 1,
                    Persp2 = 1
                };
            }

            // Multiply S * N * A
            SKMatrix result = SKMatrix.CreateIdentity();

            SKMatrix.PostConcat(ref result, S);
            SKMatrix.PostConcat(ref result, N);
            SKMatrix.PostConcat(ref result, A);

            return(result);
        }
Beispiel #14
0
        private SKMatrix GetScaleMatrix(SKPoint center, float radiusX, float radiusY)
        {
            if (radiusX > radiusY)
            {
                return(SKMatrix.CreateScale(radiusX / radiusY, 1f, center.X, center.Y));
            }

            if (radiusY > radiusX)
            {
                return(SKMatrix.CreateScale(1f, radiusY / radiusX, center.X, center.Y));
            }

            return(SKMatrix.CreateIdentity());
        }
Beispiel #15
0
        /// <summary>
        /// 单指操纵
        /// </summary>
        /// <param name="prevPoint"></param>
        /// <param name="newPoint"></param>
        /// <param name="pivotPoint"></param>
        /// <returns></returns>
        public SKMatrix OneFingerManipulate(SKPoint prevPoint, SKPoint newPoint, SKPoint pivotPoint)
        {
            if (Mode == TouchManipulationMode.None)
            {
                return(SKMatrix.CreateIdentity());
            }

            SKMatrix touchMatrix = SKMatrix.CreateIdentity();
            SKPoint  delta       = newPoint - prevPoint;

            if (Mode == TouchManipulationMode.ScaleDualRotate)  // One-finger rotation
            {
                SKPoint oldVector = prevPoint - pivotPoint;
                SKPoint newVector = newPoint - pivotPoint;

                float scale = Magnitude(newVector) / Magnitude(oldVector);


                // Avoid rotation if fingers are too close to center
                if (Magnitude(newVector) > 30 && Magnitude(oldVector) > 30)
                {
                    float prevAngle = (float)Math.Atan2(oldVector.Y, oldVector.X);
                    float newAngle  = (float)Math.Atan2(newVector.Y, newVector.X);

                    // Calculate rotation matrix
                    float angle = newAngle - prevAngle;
                    touchMatrix = SKMatrix.CreateRotation(angle, pivotPoint.X, pivotPoint.Y);

                    // Effectively rotate the old vector
                    float magnitudeRatio = Magnitude(oldVector) / Magnitude(newVector);
                    oldVector.X = magnitudeRatio * newVector.X;
                    oldVector.Y = magnitudeRatio * newVector.Y;

                    // Recalculate delta
                    delta = newVector - oldVector;
                }

                if (!float.IsNaN(scale) && !float.IsInfinity(scale))
                {
                    var tm = SKMatrix.CreateScale(scale, scale, pivotPoint.X, pivotPoint.Y);
                    touchMatrix.PostConcat(tm);
                }
            }

            // Multiply the rotation matrix by a translation matrix
            touchMatrix.PostConcat(SKMatrix.CreateTranslation(delta.X, delta.Y));

            return(touchMatrix);
        }
Beispiel #16
0
        public static MaskDrawable Create(SvgMask svgMask, SKRect skViewport, DrawableBase?parent, IAssetLoader assetLoader, HashSet <Uri>?references, DrawAttributes ignoreAttributes = DrawAttributes.None)
        {
            var drawable = new MaskDrawable(assetLoader, references)
            {
                Element          = svgMask,
                Parent           = parent,
                IgnoreAttributes = ignoreAttributes,
                IsDrawable       = true
            };

            if (!drawable.IsDrawable)
            {
                return(drawable);
            }

            var maskUnits        = svgMask.MaskUnits;
            var maskContentUnits = svgMask.MaskContentUnits;
            var xUnit            = svgMask.X;
            var yUnit            = svgMask.Y;
            var widthUnit        = svgMask.Width;
            var heightUnit       = svgMask.Height;

            // TODO: Pass correct skViewport
            var skRectTransformed = SvgExtensions.CalculateRect(xUnit, yUnit, widthUnit, heightUnit, maskUnits, skViewport, skViewport, svgMask);

            if (skRectTransformed is null)
            {
                drawable.IsDrawable = false;
                return(drawable);
            }

            var skMatrix = SKMatrix.CreateIdentity();

            if (maskContentUnits == SvgCoordinateUnits.ObjectBoundingBox)
            {
                var skBoundsTranslateTransform = SKMatrix.CreateTranslation(skViewport.Left, skViewport.Top);
                skMatrix = skMatrix.PreConcat(skBoundsTranslateTransform);

                var skBoundsScaleTransform = SKMatrix.CreateScale(skViewport.Width, skViewport.Height);
                skMatrix = skMatrix.PreConcat(skBoundsScaleTransform);
            }

            drawable.CreateChildren(svgMask, skViewport, drawable, assetLoader, references, ignoreAttributes);

            drawable.Initialize(skRectTransformed.Value, skMatrix);

            return(drawable);
        }
        /// <summary>
        /// 2本指での操作
        /// </summary>
        /// <param name="prevPoint"></param>
        /// <param name="newPoint"></param>
        /// <param name="pivotPoint"></param>
        /// <returns></returns>
        public SKMatrix TwoFingerManipulate(SKPoint prevPoint, SKPoint newPoint, SKPoint pivotPoint)
        {
            SKMatrix touchMatrix = SKMatrix.CreateIdentity();
            SKPoint  oldVector   = prevPoint - pivotPoint;
            SKPoint  newVector   = newPoint - pivotPoint;

            if (Mode == TouchManipulationMode.ScaleRotate ||
                Mode == TouchManipulationMode.ScaleDualRotate)
            {
                // Find angles from pivot point to touch points
                float oldAngle = (float)Math.Atan2(oldVector.Y, oldVector.X);
                float newAngle = (float)Math.Atan2(newVector.Y, newVector.X);

                // Calculate rotation matrix
                float angle = newAngle - oldAngle;
                touchMatrix = SKMatrix.CreateRotation(angle, pivotPoint.X, pivotPoint.Y);

                // Effectively rotate the old vector
                float magnitudeRatio = Magnitude(oldVector) / Magnitude(newVector);
                oldVector.X = magnitudeRatio * newVector.X;
                oldVector.Y = magnitudeRatio * newVector.Y;
            }

            float scaleX = 1;
            float scaleY = 1;

            if (Mode == TouchManipulationMode.AnisotropicScale)
            {
                scaleX = newVector.X / oldVector.X;
                scaleY = newVector.Y / oldVector.Y;
            }
            else if (Mode == TouchManipulationMode.IsotropicScale ||
                     Mode == TouchManipulationMode.ScaleRotate ||
                     Mode == TouchManipulationMode.ScaleDualRotate)
            {
                scaleX = scaleY = Magnitude(newVector) / Magnitude(oldVector);
            }

            if (!float.IsNaN(scaleX) && !float.IsInfinity(scaleX) &&
                !float.IsNaN(scaleY) && !float.IsInfinity(scaleY))
            {
                //SKMatrix.PostConcat(ref touchMatrix,
                //   SKMatrix.CreateScale(scaleX, scaleY, pivotPoint.X, pivotPoint.Y));
                touchMatrix = touchMatrix.PostConcat(SKMatrix.CreateScale(scaleX, scaleY, pivotPoint.X, pivotPoint.Y));
            }

            return(touchMatrix);
        }
Beispiel #18
0
        void DrawRotatedWithMatrices(SKCanvas canvas, SKPath path, SKPaint fill, SKPaint outline, ChartData item, float degrees, int cx, int cy)
        {
            var identity  = SKMatrix.CreateIdentity();
            var translate = SKMatrix.CreateTranslation(-cx, -cy);
            var rotate    = SKMatrix.CreateRotationDegrees(degrees);

            //angleBox.Text = degrees.ToString();
            var translate2 = SKMatrix.CreateTranslation(cx, cy);

            SKMatrix.PostConcat(ref identity, translate);
            SKMatrix.PostConcat(ref identity, rotate);
            SKMatrix.PostConcat(ref identity, translate2);


            path.Transform(identity);
            canvas.DrawPath(path, fill);
            canvas.DrawPath(path, outline);
        }
Beispiel #19
0
        public static SKMatrix CaculateOneFingerDraggedMatrix(SKPoint prevPoint, SKPoint curPoint, SKPoint pivotPoint, TouchManipulationMode mode)
        {
            if (mode == TouchManipulationMode.None)
            {
                return(SKMatrix.CreateIdentity());
            }

            SKMatrix touchMatrix = SKMatrix.CreateIdentity();
            SKPoint  delta       = curPoint - prevPoint;

            if (mode == TouchManipulationMode.ScaleDualRotate)  // One-finger rotation
            {
                SKPoint oldVector = prevPoint - pivotPoint;
                SKPoint newVector = curPoint - pivotPoint;

                // Avoid rotation if fingers are too close to center
                if (Magnitude(newVector) > 25 && Magnitude(oldVector) > 25)
                {
                    float prevAngle = (float)Math.Atan2(oldVector.Y, oldVector.X);
                    float newAngle  = (float)Math.Atan2(newVector.Y, newVector.X);

                    // Calculate rotation matrix
                    float angle = newAngle - prevAngle;
                    touchMatrix = SKMatrix.CreateRotation(angle, pivotPoint.X, pivotPoint.Y);

                    // Effectively rotate the old vector
                    float magnitudeRatio = Magnitude(oldVector) / Magnitude(newVector);
                    oldVector.X = magnitudeRatio * newVector.X;
                    oldVector.Y = magnitudeRatio * newVector.Y;

                    // Recalculate delta
                    delta = newVector - oldVector;
                }
            }

            // Multiply the rotation matrix by a translation matrix
            touchMatrix = touchMatrix.PostConcat(SKMatrix.CreateTranslation(delta.X, delta.Y));

            return(touchMatrix);
        }
Beispiel #20
0
        private void PanGestureRecognizer_PanUpdated(object sender, PanUpdatedEventArgs e)
        {
            Debug.WriteLine($"e.PanUpdate = {e.TotalX} - {e.TotalY}");
            switch (e.StatusType)
            {
            case GestureStatus.Started:
                isMoving    = true;
                startMatrix = currentmatrix;
                scaleMatrix = SKMatrix.CreateIdentity();
                break;

            case GestureStatus.Running:
                translationMatrix = SKMatrix.CreateTranslation((float)e.TotalX * translationSpeedUp, (float)e.TotalY * translationSpeedUp);
                canvasView.InvalidateSurface();
                break;

            case GestureStatus.Canceled:
            case GestureStatus.Completed:
                isMoving = false;
                break;
            }
        }
Beispiel #21
0
        private void Load(Stream stream)
        {
            var matrix = SKMatrix.CreateIdentity();
            Dictionary <int, SKMatrix> stack = new Dictionary <int, SKMatrix>();

            using (var reader = XmlReader.Create(stream))
            {
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.EndElement)
                    {
                        if (stack.TryGetValue(reader.Depth, out var stackMatrix))
                        {
                            stack.Remove(reader.Depth);
                            matrix = stackMatrix;
                        }
                    }
                    else if (reader.NodeType == XmlNodeType.Element)
                    {
                        if (string.Equals(reader.Name, "svg", StringComparison.Ordinal))
                        {
                            var preserveAspectRatio = reader["preserveAspectRatio"];
                            // get the SVG dimensions
                            var viewBoxA = reader["viewBox"] ?? reader["viewPort"];
                            if (viewBoxA != null)
                            {
                                ViewBox = ReadRectangle(viewBoxA);
                            }

                            var widthA  = reader["width"];
                            var heightA = reader["height"];
                            var width   = ReadNumber(widthA);
                            var height  = ReadNumber(heightA);
                            var size    = new SKSize(width, height);

                            if (widthA == null)
                            {
                                size.Width = ViewBox.Width;
                            }
                            else if (widthA.IndexOf('%') > -1)
                            {
                                size.Width *= ViewBox.Width;
                            }
                            if (heightA == null)
                            {
                                size.Height = ViewBox.Height;
                            }
                            else if (heightA != null && heightA.IndexOf('%') > -1)
                            {
                                size.Height *= ViewBox.Height;
                            }
                            CanvasSize = size;

                            if (!ViewBox.IsEmpty && (ViewBox.Width != CanvasSize.Width || ViewBox.Height != CanvasSize.Height))
                            {
                                if (preserveAspectRatio == "none")
                                {
                                    matrix = matrix.PostConcat(SKMatrix.CreateScale(CanvasSize.Width / ViewBox.Width, CanvasSize.Height / ViewBox.Height));
                                }
                                else
                                {
                                    // TODO: just center scale for now
                                    var scale    = Math.Min(CanvasSize.Width / ViewBox.Width, CanvasSize.Height / ViewBox.Height);
                                    var centered = SKRect.Create(CanvasSize).AspectFit(ViewBox.Size);
                                    matrix = matrix.PostConcat(SKMatrix.CreateTranslation(centered.Left, centered.Top))
                                             .PostConcat(SKMatrix.CreateScale(scale, scale));
                                }
                            }

                            // translate the canvas by the viewBox origin
                            matrix = matrix.PostConcat(SKMatrix.CreateTranslation(-ViewBox.Left, -ViewBox.Top));
                        }
                        else
                        {
                            var transform = reader["transform"];
                            if (transform != null)
                            {
                                stack[reader.Depth] = matrix;
                                var trMatrix = ReadTransform(transform);
                                matrix = matrix.PostConcat(trMatrix);
                            }

                            if (string.Equals(reader.Name, "path", StringComparison.Ordinal))
                            {
                                var pathData = reader["d"];
                                using (var path = SKPath.ParseSvgPathData(pathData))
                                    Path.AddPath(path, ref matrix);
                            }
                            else if (string.Equals(reader.Name, "polyline", StringComparison.Ordinal))
                            {
                                var pathData = "M" + reader["points"];
                                using (var path = SKPath.ParseSvgPathData(pathData))
                                    Path.AddPath(path, ref matrix);
                            }
                            else if (string.Equals(reader.Name, "polygon", StringComparison.Ordinal))
                            {
                                var pathData = "M" + reader["points"] + " Z";
                                using (var path = SKPath.ParseSvgPathData(pathData))
                                    Path.AddPath(path, ref matrix);
                            }
                            else if (string.Equals(reader.Name, "line", StringComparison.Ordinal))
                            {
                                var x1 = ReadNumber(reader["x1"]);
                                var x2 = ReadNumber(reader["x2"]);
                                var y1 = ReadNumber(reader["y1"]);
                                var y2 = ReadNumber(reader["y2"]);
                                using (var path = new SKPath())
                                {
                                    path.MoveTo(x1, y1);
                                    path.LineTo(x2, y2);
                                    Path.AddPath(path, ref matrix);
                                }
                            }
                            else if (string.Equals(reader.Name, "circle", StringComparison.Ordinal))
                            {
                                var cx = ReadNumber(reader["cx"]);
                                var cy = ReadNumber(reader["cy"]);
                                var rr = ReadNumber(reader["r"]);
                                using (var path = new SKPath())
                                {
                                    path.AddCircle(cx, cy, rr);
                                    Path.AddPath(path, ref matrix);
                                }
                            }
                            else if (string.Equals(reader.Name, "ellipse", StringComparison.Ordinal))
                            {
                                var cx = ReadNumber(reader["cx"]);
                                var cy = ReadNumber(reader["cy"]);
                                var rx = ReadNumber(reader["rx"]);
                                var ry = ReadNumber(reader["ry"]);
                                using (var path = new SKPath())
                                {
                                    path.AddOval(new SKRect(cx, cy, rx, ry));
                                    Path.AddPath(path, ref matrix);
                                }
                            }
                            else if (string.Equals(reader.Name, "rect", StringComparison.Ordinal))
                            {
                                var x      = ReadNumber(reader["x"]);
                                var y      = ReadNumber(reader["y"]);
                                var width  = ReadNumber(reader["width"]);
                                var height = ReadNumber(reader["height"]);
                                var rx     = ReadOptionalNumber(reader["rx"]);
                                var ry     = ReadOptionalNumber(reader["ry"]);
                                var rect   = SKRect.Create(x, y, width, height);
                                using (var path = new SKPath())
                                {
                                    if (rx != null)
                                    {
                                        path.AddRoundRect(rect, rx ?? 0, ry ?? 0);
                                    }
                                    else
                                    {
                                        path.AddRect(rect);
                                    }
                                    Path.AddPath(path, ref matrix);
                                }
                            }
                        }
                    }
                }
            }
        }
        protected override void OnPaintSurface(SKPaintSurfaceEventArgs args)
        {
            base.OnPaintSurface(args);

            SKImageInfo info    = args.Info;
            SKSurface   surface = args.Surface;
            SKCanvas    canvas  = surface.Canvas;

            canvas.Clear(SkiaHelper.backgroundColor);

            // 计算显示位图的矩形
            var rect = SkiaHelper.CalculateRectangle(new SKRect(0, 0, info.Width, info.Height), bitmap);

            canvas.DrawBitmap(bitmap, rect.rect);

            // 计算用于显示裁剪矩形的矩阵变换
            SKMatrix bitmapScaleMatrix = SKMatrix.CreateIdentity();

            SKMatrix.CreateScaleTranslation(rect.scaleX, rect.scaleX, rect.rect.Left, rect.rect.Top);

            // 显示矩形
            SKRect scaledCropRect = bitmapScaleMatrix.MapRect(croppingRect.Rect);


            using (SKPaint edgeStroke = new SKPaint())
            {
                edgeStroke.Style       = SKPaintStyle.Stroke;
                edgeStroke.Color       = SKColors.White;
                edgeStroke.StrokeWidth = 3;
                edgeStroke.IsAntialias = true;
                canvas.DrawRect(scaledCropRect, edgeStroke);
            }

            canvas.DrawSurrounding(rect.rect, scaledCropRect, SKColors.Gray.WithAlpha(190));

            // Display heavier corners
            using (SKPaint cornerStroke = new SKPaint())
                using (SKPath path = new SKPath())
                {
                    cornerStroke.Style       = SKPaintStyle.Stroke;
                    cornerStroke.Color       = SKColors.White;
                    cornerStroke.StrokeWidth = 7;

                    path.MoveTo(scaledCropRect.Left, scaledCropRect.Top + corner);
                    path.LineTo(scaledCropRect.Left, scaledCropRect.Top);
                    path.LineTo(scaledCropRect.Left + corner, scaledCropRect.Top);

                    path.MoveTo(scaledCropRect.Right - corner, scaledCropRect.Top);
                    path.LineTo(scaledCropRect.Right, scaledCropRect.Top);
                    path.LineTo(scaledCropRect.Right, scaledCropRect.Top + corner);

                    path.MoveTo(scaledCropRect.Right, scaledCropRect.Bottom - corner);
                    path.LineTo(scaledCropRect.Right, scaledCropRect.Bottom);
                    path.LineTo(scaledCropRect.Right - corner, scaledCropRect.Bottom);

                    path.MoveTo(scaledCropRect.Left + corner, scaledCropRect.Bottom);
                    path.LineTo(scaledCropRect.Left, scaledCropRect.Bottom);
                    path.LineTo(scaledCropRect.Left, scaledCropRect.Bottom - corner);

                    canvas.DrawPath(path, cornerStroke);
                }

            // 反转变换以进行触摸跟踪
            bitmapScaleMatrix.TryInvert(out inverseBitmapMatrix);
        }