public static SKPicture?ToPicture(SvgFragment svgFragment) { var skSize = SvgExtensions.GetDimensions(svgFragment); var skBounds = SKRect.Create(skSize); using var drawable = DrawableFactory.Create(svgFragment, skBounds, null, null, Attributes.None); if (drawable == null) { return(null); } drawable.PostProcess(); if (skBounds.IsEmpty) { skBounds = GetBounds(drawable); } using var skPictureRecorder = new SKPictureRecorder(); using var skCanvas = skPictureRecorder.BeginRecording(skBounds); #if USE_EXPERIMENTAL_LINEAR_RGB // TODO: using var skPaint = new SKPaint(); using var skColorFilter = SKColorFilter.CreateTable(null, SvgPaintingExtensions.s_LinearRGBtoSRGB, SvgPaintingExtensions.s_LinearRGBtoSRGB, SvgPaintingExtensions.s_LinearRGBtoSRGB); using var skImageFilter = SKImageFilter.CreateColorFilter(skColorFilter); skPaint.ImageFilter = skImageFilter; skCanvas.SaveLayer(skPaint); #endif drawable?.Draw(skCanvas, 0f, 0f); #if USE_EXPERIMENTAL_LINEAR_RGB // TODO: skCanvas.Restore(); #endif return(skPictureRecorder.EndRecording()); }
public static void Draw(SKCanvas skCanvas, SvgFragment svgFragment) { var skSize = SvgExtensions.GetDimensions(svgFragment); var skBounds = SKRect.Create(skSize); using var drawable = DrawableFactory.Create(svgFragment, skBounds, null, null, Attributes.None); drawable?.PostProcess(); drawable?.Draw(skCanvas, 0f, 0f); }
public static Drawable?ToDrawable(SvgFragment svgFragment) { var skSize = SvgExtensions.GetDimensions(svgFragment); var skBounds = SKRect.Create(skSize); var drawable = DrawableFactory.Create(svgFragment, skBounds, null, null, Attributes.None); drawable?.PostProcess(); return(drawable); }
public static void Draw(SkiaSharp.SKCanvas skCanvas, SvgFragment svgFragment) { var references = new HashSet <Uri> { svgFragment.OwnerDocument.BaseUri }; var size = SvgExtensions.GetDimensions(svgFragment); var bounds = SKRect.Create(size); var drawable = DrawableFactory.Create(svgFragment, bounds, null, s_assetLoader, references); if (drawable is { })
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 SKPicture?ToPicture(SvgFragment svgFragment, out Drawable?drawable) { var skSize = SvgExtensions.GetDimensions(svgFragment); var skBounds = SKRect.Create(skSize); drawable = DrawableFactory.Create(svgFragment, skBounds, null, null, Attributes.None); if (drawable == null) { return(null); } drawable.PostProcess(); if (skBounds.IsEmpty) { skBounds = GetBounds(drawable); } using var skPictureRecorder = new SKPictureRecorder(); using var skCanvas = skPictureRecorder.BeginRecording(skBounds); drawable?.Draw(skCanvas, 0f, 0f); return(skPictureRecorder.EndRecording()); }
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 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 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); } }
public ImageDrawable(SvgImage svgImage, SKRect skOwnerBounds, Drawable?root, Drawable?parent, Attributes ignoreAttributes = Attributes.None) : base(svgImage, root, parent) { IgnoreAttributes = ignoreAttributes; IsDrawable = CanDraw(svgImage, IgnoreAttributes) && HasFeatures(svgImage, IgnoreAttributes); if (!IsDrawable) { return; } float width = svgImage.Width.ToDeviceValue(UnitRenderingType.Horizontal, svgImage, skOwnerBounds); float height = svgImage.Height.ToDeviceValue(UnitRenderingType.Vertical, svgImage, skOwnerBounds); float x = svgImage.Location.X.ToDeviceValue(UnitRenderingType.Horizontal, svgImage, skOwnerBounds); float y = svgImage.Location.Y.ToDeviceValue(UnitRenderingType.Vertical, svgImage, skOwnerBounds); var location = new SKPoint(x, y); if (width <= 0f || height <= 0f || svgImage.Href == null) { IsDrawable = false; return; } // TODO: Check for image recursive references. //if (SkiaUtil.HasRecursiveReference(svgImage, (e) => e.Href)) //{ // _canDraw = false; // return; //} var image = SvgImageExtensions.GetImage(svgImage.Href, svgImage.OwnerDocument); var skImage = image as SKImage; var svgFragment = image as SvgFragment; if (skImage == null && svgFragment == null) { IsDrawable = false; return; } if (skImage != null) { _disposable.Add(skImage); } SrcRect = default; if (skImage != null) { SrcRect = SKRect.Create(0f, 0f, skImage.Width, skImage.Height); } if (svgFragment != null) { var skSize = SvgExtensions.GetDimensions(svgFragment); SrcRect = SKRect.Create(0f, 0f, skSize.Width, skSize.Height); } var destClip = SKRect.Create(location.X, location.Y, width, height); var aspectRatio = svgImage.AspectRatio; if (aspectRatio.Align != SvgPreserveAspectRatio.none) { var fScaleX = destClip.Width / SrcRect.Width; var fScaleY = destClip.Height / SrcRect.Height; var xOffset = 0f; var yOffset = 0f; if (aspectRatio.Slice) { fScaleX = Math.Max(fScaleX, fScaleY); fScaleY = Math.Max(fScaleX, fScaleY); } else { fScaleX = Math.Min(fScaleX, fScaleY); fScaleY = Math.Min(fScaleX, fScaleY); } switch (aspectRatio.Align) { case SvgPreserveAspectRatio.xMinYMin: break; case SvgPreserveAspectRatio.xMidYMin: xOffset = (destClip.Width - SrcRect.Width * fScaleX) / 2; break; case SvgPreserveAspectRatio.xMaxYMin: xOffset = (destClip.Width - SrcRect.Width * fScaleX); break; case SvgPreserveAspectRatio.xMinYMid: yOffset = (destClip.Height - SrcRect.Height * fScaleY) / 2; break; case SvgPreserveAspectRatio.xMidYMid: xOffset = (destClip.Width - SrcRect.Width * fScaleX) / 2; yOffset = (destClip.Height - SrcRect.Height * fScaleY) / 2; break; case SvgPreserveAspectRatio.xMaxYMid: xOffset = (destClip.Width - SrcRect.Width * fScaleX); yOffset = (destClip.Height - SrcRect.Height * fScaleY) / 2; break; case SvgPreserveAspectRatio.xMinYMax: yOffset = (destClip.Height - SrcRect.Height * fScaleY); break; case SvgPreserveAspectRatio.xMidYMax: xOffset = (destClip.Width - SrcRect.Width * fScaleX) / 2; yOffset = (destClip.Height - SrcRect.Height * fScaleY); break; case SvgPreserveAspectRatio.xMaxYMax: xOffset = (destClip.Width - SrcRect.Width * fScaleX); yOffset = (destClip.Height - SrcRect.Height * fScaleY); break; } DestRect = SKRect.Create( destClip.Left + xOffset, destClip.Top + yOffset, SrcRect.Width * fScaleX, SrcRect.Height * fScaleY); } else { DestRect = destClip; } Clip = destClip; var skClipRect = SvgClippingExtensions.GetClipRect(svgImage, destClip); if (skClipRect != null) { Clip = skClipRect; } if (skImage != null) { Image = skImage; } if (svgFragment != null) { FragmentDrawable = new FragmentDrawable(svgFragment, skOwnerBounds, root, this, ignoreAttributes); _disposable.Add(FragmentDrawable); } IsAntialias = SvgPaintingExtensions.IsAntialias(svgImage); if (Image != null) { TransformedBounds = DestRect; } if (FragmentDrawable != null) { //_skBounds = _fragmentDrawable._skBounds; TransformedBounds = DestRect; } Transform = SvgTransformsExtensions.ToSKMatrix(svgImage.Transforms); FragmentTransform = SKMatrix.MakeIdentity(); if (FragmentDrawable != null) { float dx = DestRect.Left; float dy = DestRect.Top; float sx = DestRect.Width / SrcRect.Width; float sy = DestRect.Height / SrcRect.Height; var skTranslationMatrix = SKMatrix.MakeTranslation(dx, dy); var skScaleMatrix = SKMatrix.MakeScale(sx, sy); FragmentTransform = FragmentTransform.PreConcat(skTranslationMatrix); FragmentTransform = FragmentTransform.PreConcat(skScaleMatrix); } Fill = null; Stroke = null; // TODO: Transform _skBounds using _skMatrix. TransformedBounds = Transform.MapRect(TransformedBounds); }
public FragmentDrawable(SvgFragment svgFragment, SKRect skOwnerBounds, Drawable?root, Drawable?parent, Attributes ignoreAttributes = Attributes.None) : base(svgFragment, root, parent) { IgnoreAttributes = ignoreAttributes; IsDrawable = HasFeatures(svgFragment, IgnoreAttributes); if (!IsDrawable) { return; } var svgFragmentParent = svgFragment.Parent; float x = svgFragmentParent == null ? 0f : svgFragment.X.ToDeviceValue(UnitRenderingType.Horizontal, svgFragment, skOwnerBounds); float y = svgFragmentParent == null ? 0f : svgFragment.Y.ToDeviceValue(UnitRenderingType.Vertical, svgFragment, skOwnerBounds); var skSize = SvgExtensions.GetDimensions(svgFragment); if (skOwnerBounds.IsEmpty) { skOwnerBounds = SKRect.Create(x, y, skSize.Width, skSize.Height); } CreateChildren(svgFragment, skOwnerBounds, this, this, ignoreAttributes); IsAntialias = SvgPaintingExtensions.IsAntialias(svgFragment); TransformedBounds = skOwnerBounds; CreateTransformedBounds(); Transform = SvgTransformsExtensions.ToSKMatrix(svgFragment.Transforms); var skMatrixViewBox = SvgTransformsExtensions.ToSKMatrix(svgFragment.ViewBox, svgFragment.AspectRatio, x, y, skSize.Width, skSize.Height); SKMatrix.PreConcat(ref Transform, ref skMatrixViewBox); switch (svgFragment.Overflow) { case SvgOverflow.Auto: case SvgOverflow.Visible: case SvgOverflow.Inherit: break; default: if (skSize.IsEmpty) { Overflow = SKRect.Create( x, y, Math.Abs(TransformedBounds.Left) + TransformedBounds.Width, Math.Abs(TransformedBounds.Top) + TransformedBounds.Height); } else { Overflow = SKRect.Create(x, y, skSize.Width, skSize.Height); } break; } var clipPathUris = new HashSet <Uri>(); var svgClipPath = svgFragment.GetUriElementReference <SvgClipPath>("clip-path", clipPathUris); if (svgClipPath != null && svgClipPath.Children != null) { ClipPath = IgnoreAttributes.HasFlag(Attributes.ClipPath) ? null : SvgClippingExtensions.GetClipPath(svgClipPath, TransformedBounds, clipPathUris, _disposable); } else { ClipPath = null; } Fill = null; Stroke = null; // TODO: Transform _skBounds using _skMatrix. SKMatrix.MapRect(ref Transform, out TransformedBounds, ref TransformedBounds); }
public static SkiaSharp.SKPicture?ToPicture(SvgFragment svgFragment) { var picture = SvgExtensions.ToModel(svgFragment, s_assetLoader, out _, out _); return(picture?.ToSKPicture()); }