コード例 #1
0
        private Path CreateWaterPath(RectF waterRect, float progress)
        {
            Path path = new Path();

            path.MoveTo(waterRect.Left, waterRect.Top);

            //Similar to the way draw the bottle's Bottom sides
            float radius            = (waterRect.Width() - mStrokeWidth) / 2.0f;
            float CenterY           = waterRect.Bottom - 0.86f * radius;
            float bottleBottomWidth = waterRect.Width() / 2.0f;
            RectF bodyRect          = new RectF(waterRect.Left, CenterY - radius, waterRect.Right, CenterY + radius);

            path.AddArc(bodyRect, 187.5f, -67.5f);
            path.LineTo(waterRect.CenterX() - bottleBottomWidth / 2.0f, waterRect.Bottom);
            path.LineTo(waterRect.CenterX() + bottleBottomWidth / 2.0f, waterRect.Bottom);
            path.AddArc(bodyRect, 60, -67.5f);

            //draw the water waves
            float cubicXChangeSize = waterRect.Width() * 0.35f * progress;
            float cubicYChangeSize = waterRect.Height() * 1.2f * progress;

            path.CubicTo(waterRect.Left + waterRect.Width() * 0.80f - cubicXChangeSize, waterRect.Top - waterRect.Height() * 1.2f + cubicYChangeSize, waterRect.Left + waterRect.Width() * 0.55f - cubicXChangeSize, waterRect.Top - cubicYChangeSize, waterRect.Left, waterRect.Top - mStrokeWidth / 2.0f);

            path.LineTo(waterRect.Left, waterRect.Top);

            return(path);
        }
コード例 #2
0
        private Path CreateWaitPath(RectF bounds)
        {
            Path path = new Path();

            //create circle
            path.MoveTo(bounds.CenterX() + mWaitCircleRadius, bounds.CenterY());

            //create w
            path.CubicTo(bounds.CenterX() + mWaitCircleRadius, bounds.CenterY() - mWaitCircleRadius * 0.5f, bounds.CenterX() + mWaitCircleRadius * 0.3f, bounds.CenterY() - mWaitCircleRadius, bounds.CenterX() - mWaitCircleRadius * 0.35f, bounds.CenterY() + mWaitCircleRadius * 0.5f);
            path.QuadTo(bounds.CenterX() + mWaitCircleRadius, bounds.CenterY() - mWaitCircleRadius, bounds.CenterX() + mWaitCircleRadius * 0.05f, bounds.CenterY() + mWaitCircleRadius * 0.5f);
            path.LineTo(bounds.CenterX() + mWaitCircleRadius * 0.75f, bounds.CenterY() - mWaitCircleRadius * 0.2f);

            path.CubicTo(bounds.CenterX(), bounds.CenterY() + mWaitCircleRadius * 1f, bounds.CenterX() + mWaitCircleRadius, bounds.CenterY() + mWaitCircleRadius * 0.4f, bounds.CenterX() + mWaitCircleRadius, bounds.CenterY());

            //create arc
            path.ArcTo(new RectF(bounds.CenterX() - mWaitCircleRadius, bounds.CenterY() - mWaitCircleRadius, bounds.CenterX() + mWaitCircleRadius, bounds.CenterY() + mWaitCircleRadius), 0, -359);
            path.ArcTo(new RectF(bounds.CenterX() - mWaitCircleRadius, bounds.CenterY() - mWaitCircleRadius, bounds.CenterX() + mWaitCircleRadius, bounds.CenterY() + mWaitCircleRadius), 1, -359);
            path.ArcTo(new RectF(bounds.CenterX() - mWaitCircleRadius, bounds.CenterY() - mWaitCircleRadius, bounds.CenterX() + mWaitCircleRadius, bounds.CenterY() + mWaitCircleRadius), 2, -2);
            //create w
            path.CubicTo(bounds.CenterX() + mWaitCircleRadius, bounds.CenterY() - mWaitCircleRadius * 0.5f, bounds.CenterX() + mWaitCircleRadius * 0.3f, bounds.CenterY() - mWaitCircleRadius, bounds.CenterX() - mWaitCircleRadius * 0.35f, bounds.CenterY() + mWaitCircleRadius * 0.5f);
            path.QuadTo(bounds.CenterX() + mWaitCircleRadius, bounds.CenterY() - mWaitCircleRadius, bounds.CenterX() + mWaitCircleRadius * 0.05f, bounds.CenterY() + mWaitCircleRadius * 0.5f);
            path.LineTo(bounds.CenterX() + mWaitCircleRadius * 0.75f, bounds.CenterY() - mWaitCircleRadius * 0.2f);

            path.CubicTo(bounds.CenterX(), bounds.CenterY() + mWaitCircleRadius * 1f, bounds.CenterX() + mWaitCircleRadius, bounds.CenterY() + mWaitCircleRadius * 0.4f, bounds.CenterX() + mWaitCircleRadius, bounds.CenterY());

            return(path);
        }
コード例 #3
0
        private Path CreateFishPath(float fishCenterX, float fishCenterY)
        {
            Path path = new Path();

            float fishHeadX = fishCenterX;
            float fishHeadY = fishCenterY - mFishHeight / 2.0f;

            //the head of the fish
            path.MoveTo(fishHeadX, fishHeadY);
            //the left body of the fish
            path.QuadTo(fishHeadX - mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.222f, fishHeadX - mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.444f);
            path.LineTo(fishHeadX - mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.666f);
            path.LineTo(fishHeadX - mFishWidth * 0.5f, fishHeadY + mFishHeight * 0.8f);
            path.LineTo(fishHeadX - mFishWidth * 0.5f, fishHeadY + mFishHeight);

            //the tail of the fish
            path.LineTo(fishHeadX, fishHeadY + mFishHeight * 0.9f);

            //the right body of the fish
            path.LineTo(fishHeadX + mFishWidth * 0.5f, fishHeadY + mFishHeight);
            path.LineTo(fishHeadX + mFishWidth * 0.5f, fishHeadY + mFishHeight * 0.8f);
            path.LineTo(fishHeadX + mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.666f);
            path.LineTo(fishHeadX + mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.444f);
            path.QuadTo(fishHeadX + mFishWidth * 0.333f, fishHeadY + mFishHeight * 0.222f, fishHeadX, fishHeadY);

            path.Close();

            return(path);
        }
コード例 #4
0
        void UpdateShape()
        {
            var path = new APath();

            path.MoveTo(_x1, _y1);
            path.LineTo(_x2, _y2);
            UpdateShape(path);
        }
コード例 #5
0
ファイル: Line.Android.cs プロジェクト: jokm1/uno-2
        protected override Android.Graphics.Path GetPath(Size availableSize)
        {
            var output = new Android.Graphics.Path();

            output.MoveTo(ViewHelper.LogicalToPhysicalPixels(X1), ViewHelper.LogicalToPhysicalPixels(Y1));
            output.LineTo(ViewHelper.LogicalToPhysicalPixels(X2), ViewHelper.LogicalToPhysicalPixels(Y2));

            return(output);
        }
コード例 #6
0
ファイル: Line.Android.cs プロジェクト: Robert-Louis/Uno
        private Android.Graphics.Path GetPath()
        {
            var output = new Android.Graphics.Path();

            output.MoveTo((float)X1, (float)Y1);
            output.LineTo((float)X2, (float)Y2);

            return(output);
        }
コード例 #7
0
        private Android.Graphics.Path GetPath()
        {
            var coords = Points;

            if (coords == null || coords.Count <= 1)
            {
                return(null);
            }

            var output        = new Android.Graphics.Path();
            var startingPoint = coords[0];

            output.MoveTo((float)startingPoint.X, (float)startingPoint.Y);
            for (var i = 1; i < coords.Count; i++)
            {
                output.LineTo((float)coords[i].X, (float)coords[i].Y);
            }
            output.LineTo((float)startingPoint.X, (float)startingPoint.Y);

            return(output);
        }
コード例 #8
0
		void UpdateShape()
		{
			if (_points != null && _points.Count > 1)
			{
				var path = new APath();
				path.SetFillType(_fillMode ? FillType.Winding : FillType.EvenOdd);

				path.MoveTo(_density * (float)_points[0].X, _density * (float)_points[0].Y);

				for (int index = 1; index < _points.Count; index++)
					path.LineTo(_density * (float)_points[index].X, _density * (float)_points[index].Y);

				UpdateShape(path);
			}
		}
コード例 #9
0
        private Path CreateBottlePath(RectF bottleRect)
        {
            float bottleneckWidth            = bottleRect.Width() * 0.3f;
            float bottleneckHeight           = bottleRect.Height() * 0.415f;
            float bottleneckDecorationWidth  = bottleneckWidth * 1.1f;
            float bottleneckDecorationHeight = bottleneckHeight * 0.167f;

            Path path = new Path();

            //draw the Left side of the bottleneck decoration
            path.MoveTo(bottleRect.CenterX() - bottleneckDecorationWidth * 0.5f, bottleRect.Top);
            path.QuadTo(bottleRect.CenterX() - bottleneckDecorationWidth * 0.5f - bottleneckWidth * 0.15f, bottleRect.Top + bottleneckDecorationHeight * 0.5f, bottleRect.CenterX() - bottleneckWidth * 0.5f, bottleRect.Top + bottleneckDecorationHeight);
            path.LineTo(bottleRect.CenterX() - bottleneckWidth * 0.5f, bottleRect.Top + bottleneckHeight);

            //draw the Left side of the bottle's body
            float radius   = (bottleRect.Width() - mStrokeWidth) / 2.0f;
            float CenterY  = bottleRect.Bottom - 0.86f * radius;
            RectF bodyRect = new RectF(bottleRect.Left, CenterY - radius, bottleRect.Right, CenterY + radius);

            path.AddArc(bodyRect, 255, -135);

            //draw the Bottom of the bottle
            float bottleBottomWidth = bottleRect.Width() / 2.0f;

            path.LineTo(bottleRect.CenterX() - bottleBottomWidth / 2.0f, bottleRect.Bottom);
            path.LineTo(bottleRect.CenterX() + bottleBottomWidth / 2.0f, bottleRect.Bottom);

            //draw the Right side of the bottle's body
            path.AddArc(bodyRect, 60, -135);

            //draw the Right side of the bottleneck decoration
            path.LineTo(bottleRect.CenterX() + bottleneckWidth * 0.5f, bottleRect.Top + bottleneckDecorationHeight);
            path.QuadTo(bottleRect.CenterX() + bottleneckDecorationWidth * 0.5f + bottleneckWidth * 0.15f, bottleRect.Top + bottleneckDecorationHeight * 0.5f, bottleRect.CenterX() + bottleneckDecorationWidth * 0.5f, bottleRect.Top);

            return(path);
        }
コード例 #10
0
        public void FillPolygon(Brush brush, Point[] points)
        {
            APaint.Color = brush.Color.AColor();
            APaint.Flags = (Android.Graphics.PaintFlags) 0;
            APaint.Flags = Flags;
            APaint.SetStyle(Android.Graphics.Paint.Style.Fill);
            APaint.StrokeWidth = LineWidth;
            var p = new Android.Graphics.Path();

            p.MoveTo(points[0].X, points[0].Y);
            foreach (var pt in points)
            {
                p.LineTo(pt.X, pt.Y);
            }
            ACanvas.DrawPath(p, APaint);
            p.Dispose();
        }
コード例 #11
0
        private bool AddStrokeInternal(IEnumerable <NativePoint> points, NativeColor color, float width)
        {
            var strokePoints = points?.ToList();

            if (strokePoints == null || strokePoints.Count == 0)
            {
                return(false);
            }

            var newpath = new NativePath();

            newpath.MoveTo(strokePoints[0].X, strokePoints[0].Y);
            foreach (var point in strokePoints.Skip(1))
            {
                newpath.LineTo(point.X, point.Y);
            }

            paths.Add(new InkStroke(newpath, strokePoints, color, width));

            return(true);
        }
コード例 #12
0
        public override Carto.Graphics.Bitmap OnDrawPopup(PopupDrawInfo popupDrawInfo)
        {
            PopupStyle style = popupDrawInfo.Popup.Style;

            // Calculate scaled dimensions
            float DPToPX = popupDrawInfo.DPToPX;
            float PXTODP = 1 / DPToPX;

            if (style.ScaleWithDPI)
            {
                DPToPX = 1;
            }
            else
            {
                PXTODP = 1;
            }

            float screenWidth  = popupDrawInfo.ScreenBounds.GetWidth() * PXTODP;
            float screenHeight = popupDrawInfo.ScreenBounds.GetHeight() * PXTODP;

            // Update sizes based on scale (uses extension method, cf. Shared/Extensions
            int fontSize = FontSize.Update(DPToPX);

            int triangleWidth  = TriangleSize.Update(DPToPX);
            int triangleHeight = TriangleSize.Update(DPToPX);

            int strokeWidth   = StrokeWidth.Update(DPToPX);
            int screenPadding = ScreenPadding.Update(DPToPX);

            // Set font
            var font = Android.Graphics.Typeface.Create("HelveticaNeue-Light", Android.Graphics.TypefaceStyle.Normal);

            // Calculate the maximum popup size, adjust with dpi
            int maxPopupWidth = (int)(Math.Min(screenWidth, screenHeight));

            float halfStrokeWidth = strokeWidth * 0.5f;
            int   maxTextWidth    = maxPopupWidth - (2 * screenPadding + strokeWidth);

            // Measure text
            TextPaint textPaint = new TextPaint {
                Color = TextColor, TextSize = fontSize
            };

            textPaint.SetTypeface(font);

            var textLayout = new StaticLayout(text, textPaint, maxTextWidth, Layout.Alignment.AlignNormal, 1, 0, false);

            int textX = (int)Math.Min(textPaint.MeasureText(text), textLayout.Width);
            int textY = textLayout.Height;

            int popupWidth  = textX + (2 * PopupPadding + strokeWidth + triangleWidth);
            int popupHeight = textY + (2 * PopupPadding + strokeWidth);

            var bitmap = Android.Graphics.Bitmap.CreateBitmap(popupWidth, popupHeight, Android.Graphics.Bitmap.Config.Argb8888);
            var canvas = new Android.Graphics.Canvas(bitmap);

            var trianglePath = new Android.Graphics.Path();

            trianglePath.MoveTo(triangleWidth, 0);
            trianglePath.LineTo(halfStrokeWidth, triangleHeight * 0.5f);
            trianglePath.LineTo(triangleWidth, triangleHeight);
            trianglePath.Close();

            int triangleOffsetX = 0;
            int triangleOffsetY = (popupHeight - triangleHeight) / 2;

            // Create paint object
            var paint = new Android.Graphics.Paint();

            paint.AntiAlias = true;
            paint.SetStyle(Android.Graphics.Paint.Style.Stroke);
            paint.StrokeWidth = strokeWidth;
            paint.Color       = StrokeColor;

            // Stroke background
            var background = new Android.Graphics.RectF();

            background.Left   = triangleWidth;
            background.Top    = halfStrokeWidth;
            background.Right  = popupWidth - strokeWidth;
            background.Bottom = popupHeight - strokeWidth;
            canvas.DrawRect(background, paint);

            // Stroke triangle
            canvas.Save();
            canvas.Translate(triangleOffsetX, triangleOffsetY);
            canvas.DrawPath(trianglePath, paint);
            canvas.Restore();

            // Fill background
            paint.SetStyle(Android.Graphics.Paint.Style.Fill);
            paint.Color = BackgroundColor;
            canvas.DrawRect(background, paint);

            // Fill triangle
            canvas.Save();
            canvas.Translate(triangleOffsetX, triangleOffsetY);
            canvas.DrawPath(trianglePath, paint);
            canvas.Restore();

            if (textLayout != null)
            {
                // Draw text
                canvas.Save();
                canvas.Translate(halfStrokeWidth + triangleWidth + PopupPadding, halfStrokeWidth + PopupPadding);
                textLayout.Draw(canvas);
                canvas.Restore();
            }

            return(BitmapUtils.CreateBitmapFromAndroidBitmap(bitmap));
        }
コード例 #13
0
        public override Carto.Graphics.Bitmap OnDrawPopup(PopupDrawInfo popupDrawInfo)
        {
            PopupStyle style = popupDrawInfo.Popup.Style;

            // Calculate scaled dimensions
            float DPToPX = popupDrawInfo.DPToPX;
            float PXTODP = 1 / DPToPX;

            if (style.ScaleWithDPI)
            {
                DPToPX = 1;
            }
            else
            {
                PXTODP = 1;
            }

            float screenWidth = popupDrawInfo.ScreenBounds.GetWidth() * PXTODP;
            float screenHeight = popupDrawInfo.ScreenBounds.GetHeight() * PXTODP;

            // Update sizes based on scale (uses extension method, cf. Shared/Extensions
            int fontSize = FontSize.Update(DPToPX);

            int triangleWidth = TriangleSize.Update(DPToPX);
            int triangleHeight = TriangleSize.Update(DPToPX);

            int strokeWidth = StrokeWidth.Update(DPToPX);
            int screenPadding = ScreenPadding.Update(DPToPX);

            // Set font
            var font = Android.Graphics.Typeface.Create("HelveticaNeue-Light", Android.Graphics.TypefaceStyle.Normal);

            // Calculate the maximum popup size, adjust with dpi
            int maxPopupWidth = (int)(Math.Min(screenWidth, screenHeight));

            float halfStrokeWidth = strokeWidth * 0.5f;
            int maxTextWidth = maxPopupWidth - (2 * screenPadding + strokeWidth);

            // Measure text
            TextPaint textPaint = new TextPaint { Color = TextColor, TextSize = fontSize };
            textPaint.SetTypeface(font);

            var textLayout = new StaticLayout(text, textPaint, maxTextWidth, Layout.Alignment.AlignNormal, 1, 0, false);

            int textX = (int)Math.Min(textPaint.MeasureText(text), textLayout.Width);
            int textY = textLayout.Height;

            int popupWidth = textX + (2 * PopupPadding + strokeWidth + triangleWidth);
            int popupHeight = textY + (2 * PopupPadding + strokeWidth);

            var bitmap = Android.Graphics.Bitmap.CreateBitmap(popupWidth, popupHeight, Android.Graphics.Bitmap.Config.Argb8888);
            var canvas = new Android.Graphics.Canvas(bitmap);

            var trianglePath = new Android.Graphics.Path();
            trianglePath.MoveTo(triangleWidth, 0);
            trianglePath.LineTo(halfStrokeWidth, triangleHeight * 0.5f);
            trianglePath.LineTo(triangleWidth, triangleHeight);
            trianglePath.Close();

            int triangleOffsetX = 0;
            int triangleOffsetY = (popupHeight - triangleHeight) / 2;

            // Create paint object
            var paint = new Android.Graphics.Paint();
            paint.AntiAlias = true;
            paint.SetStyle(Android.Graphics.Paint.Style.Stroke);
            paint.StrokeWidth = strokeWidth;
            paint.Color = StrokeColor;

            // Stroke background
            var background = new Android.Graphics.RectF();
            background.Left = triangleWidth;
            background.Top = halfStrokeWidth;
            background.Right = popupWidth - strokeWidth;
            background.Bottom = popupHeight - strokeWidth;
            canvas.DrawRect(background, paint);

            // Stroke triangle
            canvas.Save();
            canvas.Translate(triangleOffsetX, triangleOffsetY);
            canvas.DrawPath(trianglePath, paint);
            canvas.Restore();

            // Fill background
            paint.SetStyle(Android.Graphics.Paint.Style.Fill);
            paint.Color = BackgroundColor;
            canvas.DrawRect(background, paint);

            // Fill triangle
            canvas.Save();
            canvas.Translate(triangleOffsetX, triangleOffsetY);
            canvas.DrawPath(trianglePath, paint);
            canvas.Restore();

            if (textLayout != null)
            {
                // Draw text
                canvas.Save();
                canvas.Translate(halfStrokeWidth + triangleWidth + PopupPadding, halfStrokeWidth + PopupPadding);
                textLayout.Draw(canvas);
                canvas.Restore();
            }

            return BitmapUtils.CreateBitmapFromAndroidBitmap(bitmap);
        }
コード例 #14
0
        public static void SmoothedPathWithGranularity(List <NativePoint> currentPoints, int granularity, out NativePath smoothedPath, out List <NativePoint> smoothedPoints)
        {
            // not enough points to smooth effectively, so return the original path and points.
            if (currentPoints.Count < 4)
            {
                smoothedPath   = null;
                smoothedPoints = null;
                return;
            }

            // create a new bezier path to hold the smoothed path.
            smoothedPath   = new NativePath();
            smoothedPoints = new List <NativePoint> ();

            // duplicate the first and last points as control points.
            currentPoints.Insert(0, currentPoints[0]);
            currentPoints.Add(currentPoints[currentPoints.Count - 1]);

            // add the first point
            smoothedPath.MoveTo(currentPoints[0].X, currentPoints[0].Y);
            smoothedPoints.Add(currentPoints[0]);

            for (var index = 1; index < currentPoints.Count - 2; index++)
            {
                var p0 = currentPoints[index - 1];
                var p1 = currentPoints[index];
                var p2 = currentPoints[index + 1];
                var p3 = currentPoints[index + 2];

                // add n points starting at p1 + dx/dy up until p2 using Catmull-Rom splines
                for (var i = 1; i < granularity; i++)
                {
                    var t   = (float)i * (1f / (float)granularity);
                    var tt  = t * t;
                    var ttt = tt * t;

                    // intermediate point
                    var mid = new NativePoint
                    {
                        X = 0.5f * (2f * p1.X + (p2.X - p0.X) * t +
                                    (2f * p0.X - 5f * p1.X + 4f * p2.X - p3.X) * tt +
                                    (3f * p1.X - p0.X - 3f * p2.X + p3.X) * ttt),

                        Y = 0.5f * (2 * p1.Y + (p2.Y - p0.Y) * t +
                                    (2 * p0.Y - 5 * p1.Y + 4 * p2.Y - p3.Y) * tt +
                                    (3 * p1.Y - p0.Y - 3 * p2.Y + p3.Y) * ttt)
                    };
                    smoothedPath.LineTo(mid.X, mid.Y);
                    smoothedPoints.Add(mid);
                }

                // add p2
                smoothedPath.LineTo(p2.X, p2.Y);
                smoothedPoints.Add(p2);
            }

            // add the last point
            var last = currentPoints[currentPoints.Count - 1];

            smoothedPath.LineTo(last.X, last.Y);
            smoothedPoints.Add(last);
        }
コード例 #15
0
ファイル: Graphics.cs プロジェクト: heocon8319/AndroidDrawing
 public void FillPolygon(Brush brush, Point[] points)
 {
     APaint.Color = brush.Color.AColor();
     APaint.Flags = (Android.Graphics.PaintFlags)0;
     APaint.Flags = Flags;
     APaint.SetStyle(Android.Graphics.Paint.Style.Fill);
     APaint.StrokeWidth = LineWidth;
     var p = new Android.Graphics.Path();
     p.MoveTo(points[0].X, points[0].Y);
     foreach(var pt in points)
         p.LineTo(pt.X, pt.Y);
     ACanvas.DrawPath(p, APaint);
     p.Dispose();
 }
コード例 #16
0
        /// <summary>
        /// Obtain a smoothed path with the specified granularity from the current path using Catmull-Rom spline.
        /// Also outputs a List of the points corresponding to the smoothed path.
        /// </summary>
        /// <remarks>
        /// Implemented using a modified version of the code in the solution at
        /// http://stackoverflow.com/questions/8702696/drawing-smooth-curves-methods-needed
        /// </remarks>
        public static InkStroke SmoothedPathWithGranularity(InkStroke currentPath, int granularity)
        {
            var currentPoints = currentPath.GetPoints().ToList();

            // not enough points to smooth effectively, so return the original path and points.
            if (currentPoints.Count < 4)
            {
                return(currentPath);
            }

            // create a new bezier path to hold the smoothed path.
            var smoothedPath   = new NativePath();
            var smoothedPoints = new List <NativePoint> ();

            // duplicate the first and last points as control points.
            currentPoints.Insert(0, currentPoints[0]);
            currentPoints.Add(currentPoints[currentPoints.Count - 1]);

            // add the first point
            smoothedPath.MoveTo(currentPoints[0].X, currentPoints[0].Y);
            smoothedPoints.Add(currentPoints[0]);

            for (var index = 1; index < currentPoints.Count - 2; index++)
            {
                var p0 = currentPoints[index - 1];
                var p1 = currentPoints[index];
                var p2 = currentPoints[index + 1];
                var p3 = currentPoints[index + 2];

                // add n points starting at p1 + dx/dy up until p2 using Catmull-Rom splines
                for (var i = 1; i < granularity; i++)
                {
                    var t   = (float)i * (1f / (float)granularity);
                    var tt  = t * t;
                    var ttt = tt * t;

                    // intermediate point
                    var mid = new NativePoint
                    {
                        X = 0.5f * (2f * p1.X + (p2.X - p0.X) * t +
                                    (2f * p0.X - 5f * p1.X + 4f * p2.X - p3.X) * tt +
                                    (3f * p1.X - p0.X - 3f * p2.X + p3.X) * ttt),

                        Y = 0.5f * (2 * p1.Y + (p2.Y - p0.Y) * t +
                                    (2 * p0.Y - 5 * p1.Y + 4 * p2.Y - p3.Y) * tt +
                                    (3 * p1.Y - p0.Y - 3 * p2.Y + p3.Y) * ttt)
                    };
                    smoothedPath.LineTo(mid.X, mid.Y);
                    smoothedPoints.Add(mid);
                }

                // add p2
                smoothedPath.LineTo(p2.X, p2.Y);
                smoothedPoints.Add(p2);
            }

            // add the last point
            var last = currentPoints[currentPoints.Count - 1];

            smoothedPath.LineTo(last.X, last.Y);
            smoothedPoints.Add(last);

            // create the new path with the old attributes
#if __ANDROID__ || __IOS__
            return(new InkStroke(smoothedPath, smoothedPoints.ToList(), currentPath.Color, currentPath.Width));
#elif WINDOWS_PHONE
            var da = currentPath.DrawingAttributes;
            smoothedPath.DrawingAttributes = new DrawingAttributes
            {
                Color        = da.Color,
                OutlineColor = da.OutlineColor,
                Width        = da.Width,
                Height       = da.Height,
            };
            return(smoothedPath);
#elif WINDOWS_UWP
            var da      = currentPath.DrawingAttributes;
            var builder = new InkStrokeBuilder();
            builder.SetDefaultDrawingAttributes(new InkDrawingAttributes
            {
                Color = da.Color,
                Size  = da.Size
            });
            return(builder.CreateStroke(smoothedPath));
#endif
        }