/// <summary> /// create the path points /// </summary> /// <param name="stroke">the stroke</param> /// <returns>the path points</returns> public static SKPoint[] CreatePathPoints(XInkStroke stroke) { if (stroke == null) { throw new ArgumentNullException(nameof(stroke)); } var inkPoints = stroke.GetInkPoints().ToArray(); if (inkPoints.Length < 2) { return(null); } var pointCount = inkPoints.Length * 2 + 2; var allPoints = new SKPoint[pointCount]; double angle = 0.0; for (var i = 0; i < inkPoints.Length; i++) { var point1 = inkPoints[i]; if (i < inkPoints.Length - 1) { var point2 = inkPoints[i + 1]; var x = point2.Position.X - point1.Position.X; var y = point2.Position.Y - point1.Position.Y; angle = Math.Atan2(y, x) - Math.PI / 2; } var h = stroke.DrawingAttributes.Size * 0.5; if (!stroke.DrawingAttributes.IgnorePressure) { h *= point1.Pressure; } var leftX = point1.Position.X + (Math.Cos(angle) * h); var leftY = point1.Position.Y + (Math.Sin(angle) * h); var rightX = point1.Position.X - (Math.Cos(angle) * h); var rightY = point1.Position.Y - (Math.Sin(angle) * h); allPoints[i + 1] = new Point(leftX, leftY).ToSKPoint(); allPoints[pointCount - i - 1] = new Point(rightX, rightY).ToSKPoint(); } allPoints[0] = GetModifiedFirstPoint(stroke, inkPoints).ToSKPoint(); allPoints[inkPoints.Length + 1] = GetModifiedLastPoint(stroke, inkPoints).ToSKPoint(); return(allPoints); }
/// <summary> /// Add a stroke /// </summary> /// <param name="inkStroke">the ink stroke to add</param> public void Add(XInkStroke inkStroke) { var newStroke = inkStroke.ToInkStroke(); if (newStroke == null) { return; } NativeStrokeContainer.AddStroke(newStroke); InkChanged?.Invoke(this, new EventArgs()); }
/// <summary> /// Create a variable thickness path /// </summary> /// <param name="stroke">the stroke</param> /// <returns>a variable thickness path</returns> public static SKPath CreateVariableThicknessPath(this XInkStroke stroke) { var pathPoints = CreatePathPoints(stroke); if (pathPoints == null || pathPoints.Length == 0) { return(null); } var newPath = new SKPath(); newPath.AddPoly(pathPoints); return(newPath); }
/// <summary> /// Remove an ink stroke /// </summary> /// <param name="item">the stroke to remove</param> public void Remove(XInkStroke item) { if (item == null) { throw new ArgumentNullException(nameof(item)); } foreach (var stroke in NativeStrokeContainer.GetStrokes()) { if (stroke.Id.ToString(CultureInfo.InvariantCulture) == item.Id) { stroke.Selected = true; } } NativeStrokeContainer.DeleteSelected(); InkChanged?.Invoke(this, new EventArgs()); }
/// <summary> /// create a path for the stroke /// </summary> /// <param name="stroke">the stroke</param> /// <returns>a new path</returns> public static SKPath CreatePath(this XInkStroke stroke) { if (stroke == null) { throw new ArgumentNullException(nameof(stroke)); } var path = new SKPath(); var inkPoints = stroke.GetInkPoints(); var points = from point in inkPoints let x = (float)point.Position.X let y = (float)point.Position.Y select new SKPoint(x, y); var count = points.Count(); //path.AddPoly(points.ToArray(), false); if (count > 1) { var firstPoints = new Point[count - 1]; var secondPoints = new Point[count - 1]; var controlPoints = (from item in inkPoints select item.Position).ToArray(); BezierSpline.GetCurveControlPoints(controlPoints, out firstPoints, out secondPoints); path.MoveTo(controlPoints.First().ToSKPoint()); for (var i = 1; i < count; i++) { path.CubicTo(firstPoints[i - 1].ToSKPoint(), secondPoints[i - 1].ToSKPoint(), points.ElementAt(i)); } } return(path); }
/// <summary> /// Convert a Xamarin Ink Stroke to a UWP InkStroke /// </summary> /// <param name="xInkStroke">a Xamarin Ink Stroke</param> /// <returns>a UWP Ink Stroke</returns> public static InkStroke ToInkStroke(this XInkStroke xInkStroke) { if (xInkStroke == null) { throw new ArgumentNullException(nameof(xInkStroke)); } var builder = new InkStrokeBuilder(); var inkPoints = from item in xInkStroke.GetInkPoints() select new InkPoint(new Point(item.Position.X, item.Position.Y), item.Pressure); if (!inkPoints.Any()) { return(null); } var stroke = builder.CreateStrokeFromInkPoints(inkPoints, Matrix3x2.Identity); stroke.DrawingAttributes = xInkStroke.DrawingAttributes.ToInkDrawingAttributes(); return(stroke); }
private static Point GetModifiedLastPoint(XInkStroke stroke, XInkPoint[] inkPoints) { var lastPoint = inkPoints[inkPoints.Length - 1]; var secondLastPoint = inkPoints[inkPoints.Length - 2]; var x1 = lastPoint.Position.X - secondLastPoint.Position.X; var y1 = lastPoint.Position.Y - secondLastPoint.Position.Y; var angle1 = Math.Atan2(y1, x1); var h1 = stroke.DrawingAttributes.Size * 0.5; if (!stroke.DrawingAttributes.IgnorePressure) { h1 *= lastPoint.Pressure; } var lastX = lastPoint.Position.X + (Math.Cos(angle1) * h1); var lastY = lastPoint.Position.Y + (Math.Sin(angle1) * h1); var modifiedLastPoint = new Point(lastX, lastY); return(modifiedLastPoint); }
public static Tuple <SKPaint, IEnumerable <IDisposable> > CreatePaint(this XInkStroke stroke, SKPaintStyle paintStyle = SKPaintStyle.Stroke, SKBlendMode blendMode = SKBlendMode.SrcATop) { if (stroke == null) { throw new ArgumentNullException(nameof(stroke)); } var disposables = new List <IDisposable>(); SKShader shader = null; if (stroke.DrawingAttributes.Kind == XInkDrawingAttributesKind.Pencil) { var perlin = SKShader.CreatePerlinNoiseFractalNoise(0.01f, 0.01f, 1, 1.0f); var color = SKShader.CreateColor(stroke.DrawingAttributes.Color.ToSKColor().WithAlpha(0x7F)); disposables.Add(perlin); disposables.Add(color); shader = SKShader.CreateCompose( perlin, color, blendMode); } Tuple <SKPaint, IEnumerable <IDisposable> > tuple = null; SKPaint paint = null; if (!stroke.DrawingAttributes.IgnorePressure) { paintStyle = SKPaintStyle.Fill; } try { paint = new SKPaint { Color = stroke.DrawingAttributes.Kind == XInkDrawingAttributesKind.Default ? stroke.DrawingAttributes.Color.ToSKColor() : new SKColor(), StrokeWidth = stroke.DrawingAttributes.IgnorePressure? stroke.DrawingAttributes.Size : 0.0f, Style = paintStyle, IsAntialias = true, StrokeCap = stroke.DrawingAttributes.PenTip == Inking.XPenTipShape.Circle ? SKStrokeCap.Round : SKStrokeCap.Butt, PathEffect = SKPathEffect.CreateCorner(100) }; if (shader != null) { paint.Shader = shader; } tuple = Tuple.Create(paint, disposables as IEnumerable <IDisposable>); paint = null; } finally { paint?.Dispose(); } return(tuple); }
/// <summary> /// Draw an ink stroke /// </summary> /// <param name="canvas">the canvas</param> /// <param name="stroke">the stroke</param> /// <param name="useCache">true to use cached data</param> public static void Draw(this SKCanvas canvas, XInkStroke stroke, bool useCache) { _ = canvas ?? throw new ArgumentNullException(nameof(canvas)); _ = stroke ?? throw new ArgumentNullException(nameof(stroke)); var inkPoints = stroke.GetInkPoints(); if (useCache) { if (inkPoints.Count == 1) { if (stroke.Paint == null) { var paintResult = stroke.CreatePaint(SKPaintStyle.Fill); stroke.Paint = paintResult.Item1; stroke.Resources = paintResult.Item2; } canvas.DrawCircle( stroke.GetInkPoints()[0].Position.ToSKPoint(), stroke.DrawingAttributes.Size / 4.0f, stroke.Paint); } else { stroke.Path ??= stroke.DrawingAttributes.IgnorePressure ? stroke.CreatePath() : stroke.CreateVariableThicknessPath(); if (stroke.Paint == null) { var paintResult = stroke.CreatePaint(); stroke.Resources = paintResult.Item2; stroke.Paint = paintResult.Item1; } if (stroke.Path != null) { canvas.DrawPath(stroke.Path, stroke.Paint); } } } else { if (inkPoints.Count == 1) { var paintResult = stroke.CreatePaint(SKPaintStyle.Fill); using (var paint = paintResult.Item1) { paint.IsStroke = false; var radius = stroke.DrawingAttributes.Size / 4.0f; //System.Diagnostics.Debug.WriteLine($"Drawing dot radius {radius}"); canvas.DrawCircle( inkPoints[0].Position.ToSKPoint(), radius, paint); } if (paintResult.Item2 != null) { foreach (var item in paintResult.Item2) { item.Dispose(); } } } else { using var path = stroke.DrawingAttributes.IgnorePressure ? stroke.CreatePath() : stroke.CreateVariableThicknessPath(); if (path == null) { return; } var paintResult = stroke.CreatePaint(); using (var paint = paintResult.Item1) { canvas.DrawPath(path, paint); } if (paintResult.Item2 != null) { foreach (var item in paintResult.Item2) { item.Dispose(); } } } } }
public void TestCreatePathPointsEmpty() { using var inkStroke = new XInkStroke(); InkRenderer.CreatePathPoints(inkStroke); }