public static SKPath?GetSvgVisualElementClipPath(SvgVisualElement svgVisualElement, SKRect skBounds, HashSet <Uri> uris, CompositeDisposable disposable) { if (svgVisualElement == null || svgVisualElement.ClipPath == null) { return(null); } if (SvgExtensions.HasRecursiveReference(svgVisualElement, (e) => e.ClipPath, uris)) { return(null); } var svgClipPath = SvgExtensions.GetReference <SvgClipPath>(svgVisualElement, svgVisualElement.ClipPath); if (svgClipPath == null || svgClipPath.Children == null) { return(null); } return(GetClipPath(svgClipPath, skBounds, uris, disposable)); }
public static void CreateMarkers(this SvgMarkerElement svgMarkerElement, SKPath skPath, SKRect skOwnerBounds, ref List <Drawable>?markerDrawables, CompositeDisposable disposable) { var pathTypes = skPath.GetPathTypes(); var pathLength = pathTypes.Count; var markerStart = svgMarkerElement.MarkerStart; if (markerStart != null && pathLength > 0 && !SvgExtensions.HasRecursiveReference(svgMarkerElement, (e) => e.MarkerStart, new HashSet <Uri>())) { var marker = SvgExtensions.GetReference <SvgMarker>(svgMarkerElement, markerStart); if (marker != null) { var refPoint1 = pathTypes[0].Point; var index = 1; while (index < pathLength && pathTypes[index].Point == refPoint1) { ++index; } var refPoint2 = pathLength == 1 ? refPoint1 : pathTypes[index].Point; CreateMarker(marker, svgMarkerElement, refPoint1, refPoint1, refPoint2, true, skOwnerBounds, ref markerDrawables, disposable); } } var markerMid = svgMarkerElement.MarkerMid; if (markerMid != null && pathLength > 0 && !SvgExtensions.HasRecursiveReference(svgMarkerElement, (e) => e.MarkerMid, new HashSet <Uri>())) { var marker = SvgExtensions.GetReference <SvgMarker>(svgMarkerElement, markerMid); if (marker != null) { int bezierIndex = -1; for (int i = 1; i <= pathLength - 2; i++) { // for Bezier curves, the marker shall only been shown at the last point if ((pathTypes[i].Type & (byte)PathPointType.PathTypeMask) == (byte)PathPointType.Bezier) { bezierIndex = (bezierIndex + 1) % 3; } else { bezierIndex = -1; } if (bezierIndex == -1 || bezierIndex == 2) { CreateMarker(marker, svgMarkerElement, pathTypes[i].Point, pathTypes[i - 1].Point, pathTypes[i].Point, pathTypes[i + 1].Point, skOwnerBounds, ref markerDrawables, disposable); } } } } var markerEnd = svgMarkerElement.MarkerEnd; if (markerEnd != null && pathLength > 0 && !SvgExtensions.HasRecursiveReference(svgMarkerElement, (e) => e.MarkerEnd, new HashSet <Uri>())) { var marker = SvgExtensions.GetReference <SvgMarker>(svgMarkerElement, markerEnd); if (marker != null) { var index = pathLength - 1; var refPoint1 = pathTypes[index].Point; if (pathLength > 1) { --index; while (index > 0 && pathTypes[index].Point == refPoint1) { --index; } } var refPoint2 = pathLength == 1 ? refPoint1 : pathTypes[index].Point; CreateMarker(marker, svgMarkerElement, refPoint1, refPoint2, pathTypes[pathLength - 1].Point, false, skOwnerBounds, ref markerDrawables, disposable); } } }
public static SKPath?GetClipPath(SvgVisualElement svgVisualElement, SKRect skBounds, HashSet <Uri> uris, CompositeDisposable disposable) { if (!CanDraw(svgVisualElement, Attributes.None)) { return(null); } switch (svgVisualElement) { case SvgPath svgPath: { var fillRule = (svgPath.ClipRule == SvgClipRule.EvenOdd) ? SvgFillRule.EvenOdd : SvgFillRule.NonZero; var skPath = svgPath.PathData?.ToSKPath(fillRule, disposable); if (skPath != null) { var skMatrix = SvgTransformsExtensions.ToSKMatrix(svgPath.Transforms); skPath.Transform(skMatrix); var skPathClip = GetSvgVisualElementClipPath(svgPath, skPath.Bounds, uris, disposable); if (skPathClip != null) { var result = skPath.Op(skPathClip, SKPathOp.Intersect); disposable.Add(result); return(result); } return(skPath); } } break; case SvgRectangle svgRectangle: { var fillRule = (svgRectangle.ClipRule == SvgClipRule.EvenOdd) ? SvgFillRule.EvenOdd : SvgFillRule.NonZero; var skPath = svgRectangle.ToSKPath(fillRule, skBounds, disposable); if (skPath != null) { var skMatrix = SvgTransformsExtensions.ToSKMatrix(svgRectangle.Transforms); skPath.Transform(skMatrix); var skPathClip = GetSvgVisualElementClipPath(svgRectangle, skPath.Bounds, uris, disposable); if (skPathClip != null) { var result = skPath.Op(skPathClip, SKPathOp.Intersect); disposable.Add(result); return(result); } return(skPath); } } break; case SvgCircle svgCircle: { var fillRule = (svgCircle.ClipRule == SvgClipRule.EvenOdd) ? SvgFillRule.EvenOdd : SvgFillRule.NonZero; var skPath = svgCircle.ToSKPath(fillRule, skBounds, disposable); if (skPath != null) { var skMatrix = SvgTransformsExtensions.ToSKMatrix(svgCircle.Transforms); skPath.Transform(skMatrix); var skPathClip = GetSvgVisualElementClipPath(svgCircle, skPath.Bounds, uris, disposable); if (skPathClip != null) { var result = skPath.Op(skPathClip, SKPathOp.Intersect); disposable.Add(result); return(result); } return(skPath); } } break; case SvgEllipse svgEllipse: { var fillRule = (svgEllipse.ClipRule == SvgClipRule.EvenOdd) ? SvgFillRule.EvenOdd : SvgFillRule.NonZero; var skPath = svgEllipse.ToSKPath(fillRule, skBounds, disposable); if (skPath != null) { var skMatrix = SvgTransformsExtensions.ToSKMatrix(svgEllipse.Transforms); skPath.Transform(skMatrix); var skPathClip = GetSvgVisualElementClipPath(svgEllipse, skPath.Bounds, uris, disposable); if (skPathClip != null) { var result = skPath.Op(skPathClip, SKPathOp.Intersect); disposable.Add(result); return(result); } return(skPath); } } break; case SvgLine svgLine: { var fillRule = (svgLine.ClipRule == SvgClipRule.EvenOdd) ? SvgFillRule.EvenOdd : SvgFillRule.NonZero; var skPath = svgLine.ToSKPath(fillRule, skBounds, disposable); if (skPath != null) { var skMatrix = SvgTransformsExtensions.ToSKMatrix(svgLine.Transforms); skPath.Transform(skMatrix); var skPathClip = GetSvgVisualElementClipPath(svgLine, skPath.Bounds, uris, disposable); if (skPathClip != null) { var result = skPath.Op(skPathClip, SKPathOp.Intersect); disposable.Add(result); return(result); } return(skPath); } } break; case SvgPolyline svgPolyline: { var fillRule = (svgPolyline.ClipRule == SvgClipRule.EvenOdd) ? SvgFillRule.EvenOdd : SvgFillRule.NonZero; var skPath = svgPolyline.Points?.ToSKPath(fillRule, false, skBounds, disposable); if (skPath != null) { var skMatrix = SvgTransformsExtensions.ToSKMatrix(svgPolyline.Transforms); skPath.Transform(skMatrix); var skPathClip = GetSvgVisualElementClipPath(svgPolyline, skPath.Bounds, uris, disposable); if (skPathClip != null) { var result = skPath.Op(skPathClip, SKPathOp.Intersect); disposable.Add(result); return(result); } return(skPath); } } break; case SvgPolygon svgPolygon: { var fillRule = (svgPolygon.ClipRule == SvgClipRule.EvenOdd) ? SvgFillRule.EvenOdd : SvgFillRule.NonZero; var skPath = svgPolygon.Points?.ToSKPath(fillRule, true, skBounds, disposable); if (skPath != null) { var skMatrix = SvgTransformsExtensions.ToSKMatrix(svgPolygon.Transforms); skPath.Transform(skMatrix); var skPathClip = GetSvgVisualElementClipPath(svgPolygon, skPath.Bounds, uris, disposable); if (skPathClip != null) { var result = skPath.Op(skPathClip, SKPathOp.Intersect); disposable.Add(result); return(result); } return(skPath); } } break; case SvgUse svgUse: { if (SvgExtensions.HasRecursiveReference(svgUse, (e) => e.ReferencedElement, new HashSet <Uri>())) { break; } var svgReferencedVisualElement = SvgExtensions.GetReference <SvgVisualElement>(svgUse, svgUse.ReferencedElement); if (svgReferencedVisualElement == null || svgReferencedVisualElement is SvgSymbol) { break; } if (!CanDraw(svgReferencedVisualElement, Attributes.None)) { break; } var skPath = GetClipPath(svgReferencedVisualElement, skBounds, uris, disposable); if (skPath != null) { var skMatrix = SvgTransformsExtensions.ToSKMatrix(svgUse.Transforms); skPath.Transform(skMatrix); var skPathClip = GetSvgVisualElementClipPath(svgUse, skPath.Bounds, uris, disposable); if (skPathClip != null) { var result = skPath.Op(skPathClip, SKPathOp.Intersect); disposable.Add(result); return(result); } return(skPath); } } break; case SvgText svgText: { // TODO: Get path from SvgText. } break; default: break; } return(null); }
public UseDrawable(SvgUse svgUse, SKRect skOwnerBounds, Drawable?root, Drawable?parent, Attributes ignoreAttributes = Attributes.None) : base(svgUse, root, parent) { IgnoreAttributes = ignoreAttributes; IsDrawable = CanDraw(svgUse, IgnoreAttributes) && HasFeatures(svgUse, IgnoreAttributes); if (!IsDrawable) { return; } if (SvgExtensions.HasRecursiveReference(svgUse, (e) => e.ReferencedElement, new HashSet <Uri>())) { IsDrawable = false; return; } var svgReferencedElement = SvgExtensions.GetReference <SvgElement>(svgUse, svgUse.ReferencedElement); if (svgReferencedElement == null) { IsDrawable = false; return; } float x = svgUse.X.ToDeviceValue(UnitRenderingType.Horizontal, svgUse, skOwnerBounds); float y = svgUse.Y.ToDeviceValue(UnitRenderingType.Vertical, svgUse, skOwnerBounds); float width = svgUse.Width.ToDeviceValue(UnitRenderingType.Horizontal, svgUse, skOwnerBounds); float height = svgUse.Height.ToDeviceValue(UnitRenderingType.Vertical, svgUse, skOwnerBounds); if (width <= 0f) { width = new SvgUnit(SvgUnitType.Percentage, 100f).ToDeviceValue(UnitRenderingType.Horizontal, svgUse, skOwnerBounds); } if (height <= 0f) { height = new SvgUnit(SvgUnitType.Percentage, 100f).ToDeviceValue(UnitRenderingType.Vertical, svgUse, skOwnerBounds); } var originalReferencedElementParent = svgReferencedElement.Parent; var referencedElementParent = default(FieldInfo); try { referencedElementParent = svgReferencedElement.GetType().GetField("_parent", BindingFlags.NonPublic | BindingFlags.Instance); if (referencedElementParent != null) { referencedElementParent.SetValue(svgReferencedElement, svgUse); } } catch (Exception ex) { Debug.WriteLine(ex.Message); Debug.WriteLine(ex.StackTrace); } svgReferencedElement.InvalidateChildPaths(); if (svgReferencedElement is SvgSymbol svgSymbol) { ReferencedDrawable = new SymbolDrawable(svgSymbol, x, y, width, height, skOwnerBounds, root, this, ignoreAttributes); _disposable.Add(ReferencedDrawable); } else { var drawable = DrawableFactory.Create(svgReferencedElement, skOwnerBounds, root, this, ignoreAttributes); if (drawable != null) { ReferencedDrawable = drawable; _disposable.Add(ReferencedDrawable); } else { IsDrawable = false; return; } } IsAntialias = SvgPaintingExtensions.IsAntialias(svgUse); TransformedBounds = ReferencedDrawable.TransformedBounds; Transform = SvgTransformsExtensions.ToSKMatrix(svgUse.Transforms); if (!(svgReferencedElement is SvgSymbol)) { var skMatrixTranslateXY = SKMatrix.MakeTranslation(x, y); Transform = Transform.PreConcat(skMatrixTranslateXY); } Fill = null; Stroke = null; // TODO: Transform _skBounds using _skMatrix. TransformedBounds = Transform.MapRect(TransformedBounds); try { if (referencedElementParent != null) { referencedElementParent.SetValue(svgReferencedElement, originalReferencedElementParent); } } catch (Exception ex) { Debug.WriteLine(ex.Message); Debug.WriteLine(ex.StackTrace); } }