internal void DrawMarker(SvgMarker svgMarker, SvgVisualElement pOwner, SKPoint pMarkerPoint, float fAngle) { var markerElement = GetMarkerElement(svgMarker); if (markerElement == null) { return; } var skMarkerMatrix = SKMatrix.MakeIdentity(); var skMatrixMarkerPoint = SKMatrix.MakeTranslation(pMarkerPoint.X, pMarkerPoint.Y); SKMatrix.PreConcat(ref skMarkerMatrix, ref skMatrixMarkerPoint); var skMatrixAngle = SKMatrix.MakeRotationDegrees(svgMarker.Orient.IsAuto ? fAngle : svgMarker.Orient.Angle); SKMatrix.PreConcat(ref skMarkerMatrix, ref skMatrixAngle); var strokeWidth = pOwner.StrokeWidth.ToDeviceValue(null, UnitRenderingType.Other, svgMarker); var refX = svgMarker.RefX.ToDeviceValue(null, UnitRenderingType.Horizontal, svgMarker); var refY = svgMarker.RefY.ToDeviceValue(null, UnitRenderingType.Horizontal, svgMarker); float markerWidth = svgMarker.MarkerWidth; float markerHeight = svgMarker.MarkerHeight; float viewBoxToMarkerUnitsScaleX = 1f; float viewBoxToMarkerUnitsScaleY = 1f; switch (svgMarker.MarkerUnits) { case SvgMarkerUnits.StrokeWidth: { var skMatrixStrokeWidth = SKMatrix.MakeScale(strokeWidth, strokeWidth); SKMatrix.PreConcat(ref skMarkerMatrix, ref skMatrixStrokeWidth); var viewBoxWidth = svgMarker.ViewBox.Width; var viewBoxHeight = svgMarker.ViewBox.Height; var scaleFactorWidth = (viewBoxWidth <= 0) ? 1 : (markerWidth / viewBoxWidth); var scaleFactorHeight = (viewBoxHeight <= 0) ? 1 : (markerHeight / viewBoxHeight); viewBoxToMarkerUnitsScaleX = Math.Min(scaleFactorWidth, scaleFactorHeight); viewBoxToMarkerUnitsScaleY = Math.Min(scaleFactorWidth, scaleFactorHeight); var skMatrixTranslateRefXY = SKMatrix.MakeTranslation(-refX * viewBoxToMarkerUnitsScaleX, -refY * viewBoxToMarkerUnitsScaleY); SKMatrix.PreConcat(ref skMarkerMatrix, ref skMatrixTranslateRefXY); var skMatrixScaleXY = SKMatrix.MakeScale(viewBoxToMarkerUnitsScaleX, viewBoxToMarkerUnitsScaleY); SKMatrix.PreConcat(ref skMarkerMatrix, ref skMatrixScaleXY); } break; case SvgMarkerUnits.UserSpaceOnUse: { var skMatrixTranslateRefXY = SKMatrix.MakeTranslation(-refX, -refY); SKMatrix.PreConcat(ref skMarkerMatrix, ref skMatrixTranslateRefXY); } break; } _skCanvas.Save(); var skMatrix = SkiaUtil.GetSKMatrix(svgMarker.Transforms); SKMatrix.PreConcat(ref skMatrix, ref skMarkerMatrix); SetTransform(skMatrix); SetClipPath(svgMarker, _disposable); var skPaintOpacity = SetOpacity(svgMarker, _disposable); var skPaintFilter = SetFilter(svgMarker, _disposable); switch (svgMarker.Overflow) { case SvgOverflow.Auto: case SvgOverflow.Visible: case SvgOverflow.Inherit: break; default: var skClipRect = SKRect.Create( svgMarker.ViewBox.MinX, svgMarker.ViewBox.MinY, markerWidth / viewBoxToMarkerUnitsScaleX, markerHeight / viewBoxToMarkerUnitsScaleY); _skCanvas.ClipRect(skClipRect, SKClipOperation.Intersect); break; } Draw(markerElement, true); if (skPaintFilter != null) { _skCanvas.Restore(); } if (skPaintOpacity != null) { _skCanvas.Restore(); } _skCanvas.Restore(); }
public void DrawImage(SvgImage svgImage, bool ignoreDisplay) { if (!CanDraw(svgImage, ignoreDisplay)) { return; } float width = svgImage.Width.ToDeviceValue(null, UnitRenderingType.Horizontal, svgImage); float height = svgImage.Height.ToDeviceValue(null, UnitRenderingType.Vertical, svgImage); var location = svgImage.Location.ToDeviceValue(null, svgImage); if (width <= 0f || height <= 0f || svgImage.Href == null) { return; } var image = SkiaUtil.GetImage(svgImage, svgImage.Href); var skImage = image as SKImage; var svgFragment = image as SvgFragment; if (skImage == null && svgFragment == null) { return; } if (skImage != null) { _disposable.Add(skImage); } SKRect srcRect = default; if (skImage != null) { srcRect = SKRect.Create(0f, 0f, skImage.Width, skImage.Height); } if (svgFragment != null) { var skSize = SkiaUtil.GetDimensions(svgFragment); srcRect = SKRect.Create(0f, 0f, skSize.Width, skSize.Height); } var destClip = SKRect.Create(location.X, location.Y, width, height); var destRect = destClip; 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); } _skCanvas.Save(); var skMatrix = SkiaUtil.GetSKMatrix(svgImage.Transforms); SetTransform(skMatrix); SetClipPath(svgImage, _disposable); var skPaintOpacity = SetOpacity(svgImage, _disposable); var skPaintFilter = SetFilter(svgImage, _disposable); _skCanvas.ClipRect(destClip, SKClipOperation.Intersect); SetClip(svgImage, destClip); if (skImage != null) { _skCanvas.DrawImage(skImage, srcRect, destRect); } if (svgFragment != null) { _skCanvas.Save(); 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); SKMatrix.PreConcat(ref skTranslationMatrix, ref skScaleMatrix); SetTransform(skTranslationMatrix); DrawFragment(svgFragment, ignoreDisplay); _skCanvas.Restore(); } if (skPaintFilter != null) { _skCanvas.Restore(); } if (skPaintOpacity != null) { _skCanvas.Restore(); } _skCanvas.Restore(); }
private PaintWrapper CreatePaint(IBrush brush, Size targetSize) { SKPaint paint = new SKPaint(); var rv = new PaintWrapper(paint); paint.IsStroke = false; // TODO: SkiaSharp does not contain alpha yet! double opacity = brush.Opacity * _currentOpacity; //paint.SetAlpha(paint.GetAlpha() * opacity); paint.IsAntialias = true; SKColor color = new SKColor(255, 255, 255, 255); var solid = brush as SolidColorBrush; if (solid != null) { color = solid.Color.ToSKColor(); } paint.Color = (new SKColor(color.Red, color.Green, color.Blue, (byte)(color.Alpha * opacity))); if (solid != null) { return(rv); } var gradient = brush as GradientBrush; if (gradient != null) { var tileMode = gradient.SpreadMethod.ToSKShaderTileMode(); var stopColors = gradient.GradientStops.Select(s => s.Color.ToSKColor()).ToArray(); var stopOffsets = gradient.GradientStops.Select(s => (float)s.Offset).ToArray(); var linearGradient = brush as LinearGradientBrush; if (linearGradient != null) { var start = linearGradient.StartPoint.ToPixels(targetSize).ToSKPoint(); var end = linearGradient.EndPoint.ToPixels(targetSize).ToSKPoint(); // would be nice to cache these shaders possibly? var shader = SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode); paint.Shader = shader; shader.Dispose(); } else { var radialGradient = brush as RadialGradientBrush; if (radialGradient != null) { var center = radialGradient.Center.ToPixels(targetSize).ToSKPoint(); var radius = (float)radialGradient.Radius; // TODO: There is no SetAlpha in SkiaSharp //paint.setAlpha(128); // would be nice to cache these shaders possibly? var shader = SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode); paint.Shader = shader; shader.Dispose(); } } return(rv); } var tileBrush = brush as TileBrush; if (tileBrush != null) { var helper = new TileBrushImplHelper(tileBrush, targetSize); var bitmap = new BitmapImpl((int)helper.IntermediateSize.Width, (int)helper.IntermediateSize.Height); rv.AddDisposable(bitmap); using (var ctx = bitmap.CreateDrawingContext()) helper.DrawIntermediate(ctx); SKMatrix translation = SKMatrix.MakeTranslation(-(float)helper.DestinationRect.X, -(float)helper.DestinationRect.Y); 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; paint.Shader = SKShader.CreateBitmap(bitmap.Bitmap, tileX, tileY, translation); paint.Shader.Dispose(); } return(rv); }
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); SKMatrix.PreConcat(ref FragmentTransform, ref skTranslationMatrix); SKMatrix.PreConcat(ref FragmentTransform, ref skScaleMatrix); } Fill = null; Stroke = null; // TODO: Transform _skBounds using _skMatrix. SKMatrix.MapRect(ref Transform, out TransformedBounds, ref TransformedBounds); }
public static SKMatrix Translate(this SKMatrix m, float dx, float dy) { return(m.Concat(SKMatrix.MakeTranslation(dx, dy))); }
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args) { SKImageInfo info = args.Info; SKSurface surface = args.Surface; SKCanvas canvas = surface.Canvas; canvas.Clear(); float x = (info.Width - monkeyBitmap.Width) / 2; float y = info.Height - monkeyBitmap.Height; // Draw monkey bitmap if (step >= 1) { canvas.DrawBitmap(monkeyBitmap, x, y); } // Draw matte to exclude monkey's surroundings if (step >= 2) { using (SKPaint paint = new SKPaint()) { paint.BlendMode = SKBlendMode.DstIn; canvas.DrawBitmap(matteBitmap, x, y, paint); } } const float sidewalkHeight = 80; SKRect rect = new SKRect(info.Rect.Left, info.Rect.Bottom - sidewalkHeight, info.Rect.Right, info.Rect.Bottom); // Draw gravel sidewalk for monkey to sit on if (step >= 3) { using (SKPaint paint = new SKPaint()) { paint.Shader = SKShader.CreateCompose( SKShader.CreateColor(SKColors.SandyBrown), SKShader.CreatePerlinNoiseTurbulence(0.1f, 0.3f, 1, 9)); paint.BlendMode = SKBlendMode.DstOver; canvas.DrawRect(rect, paint); } } // Draw bitmap tiled brick wall behind monkey if (step >= 4) { using (SKPaint paint = new SKPaint()) { SKBitmap bitmap = BrickWallTile; float yAdjust = (info.Height - sidewalkHeight) % bitmap.Height; paint.Shader = SKShader.CreateBitmap(bitmap, SKShaderTileMode.Repeat, SKShaderTileMode.Repeat, SKMatrix.MakeTranslation(0, yAdjust)); paint.BlendMode = SKBlendMode.DstOver; canvas.DrawRect(info.Rect, paint); } } }
internal PaintWrapper CreatePaint(IBrush brush, Size targetSize) { SKPaint paint = new SKPaint(); var rv = new PaintWrapper(paint); paint.IsStroke = false; double opacity = brush.Opacity * _currentOpacity; paint.IsAntialias = true; var solid = brush as ISolidColorBrush; if (solid != null) { paint.Color = new SKColor(solid.Color.R, solid.Color.G, solid.Color.B, (byte)(solid.Color.A * opacity)); return(rv); } paint.Color = (new SKColor(255, 255, 255, (byte)(255 * opacity))); var gradient = brush as IGradientBrush; if (gradient != null) { var tileMode = gradient.SpreadMethod.ToSKShaderTileMode(); var stopColors = gradient.GradientStops.Select(s => s.Color.ToSKColor()).ToArray(); var stopOffsets = gradient.GradientStops.Select(s => (float)s.Offset).ToArray(); var linearGradient = brush as ILinearGradientBrush; if (linearGradient != null) { var start = linearGradient.StartPoint.ToPixels(targetSize).ToSKPoint(); var end = linearGradient.EndPoint.ToPixels(targetSize).ToSKPoint(); // would be nice to cache these shaders possibly? using (var shader = SKShader.CreateLinearGradient(start, end, stopColors, stopOffsets, tileMode)) paint.Shader = shader; } else { var radialGradient = brush as IRadialGradientBrush; if (radialGradient != null) { var center = radialGradient.Center.ToPixels(targetSize).ToSKPoint(); var radius = (float)radialGradient.Radius; // TODO: There is no SetAlpha in SkiaSharp //paint.setAlpha(128); // would be nice to cache these shaders possibly? using (var shader = SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode)) paint.Shader = shader; } } return(rv); } var tileBrush = brush as ITileBrush; var visualBrush = brush as IVisualBrush; var tileBrushImage = default(BitmapImpl); if (visualBrush != null) { if (_visualBrushRenderer != null) { var intermediateSize = _visualBrushRenderer.GetRenderTargetSize(visualBrush); if (intermediateSize.Width >= 1 && intermediateSize.Height >= 1) { var intermediate = new BitmapImpl((int)intermediateSize.Width, (int)intermediateSize.Height, _dpi); using (var ctx = intermediate.CreateDrawingContext(_visualBrushRenderer)) { ctx.Clear(Colors.Transparent); _visualBrushRenderer.RenderVisualBrush(ctx, visualBrush); } rv.AddDisposable(tileBrushImage); tileBrushImage = intermediate; } } else { throw new NotSupportedException("No IVisualBrushRenderer was supplied to DrawingContextImpl."); } } else { tileBrushImage = (BitmapImpl)((tileBrush as IImageBrush)?.Source?.PlatformImpl); } if (tileBrush != null && tileBrushImage != null) { var calc = new TileBrushCalculator(tileBrush, new Size(tileBrushImage.PixelWidth, tileBrushImage.PixelHeight), targetSize); var bitmap = new BitmapImpl((int)calc.IntermediateSize.Width, (int)calc.IntermediateSize.Height, _dpi); rv.AddDisposable(bitmap); using (var context = bitmap.CreateDrawingContext(null)) { var rect = new Rect(0, 0, tileBrushImage.PixelWidth, tileBrushImage.PixelHeight); context.Clear(Colors.Transparent); context.PushClip(calc.IntermediateClip); context.Transform = calc.IntermediateTransform; context.DrawImage(tileBrushImage, 1, rect, rect); context.PopClip(); } SKMatrix translation = SKMatrix.MakeTranslation(-(float)calc.DestinationRect.X, -(float)calc.DestinationRect.Y); 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; using (var shader = SKShader.CreateBitmap(bitmap.Bitmap, tileX, tileY, translation)) paint.Shader = shader; } return(rv); }
private void ReadElement(XElement e, SKCanvas canvas, SKPaint stroke, SKPaint fill) { if (e.Attribute("display")?.Value == "none") { return; } // transform matrix var transform = ReadTransform(e.Attribute("transform")?.Value ?? string.Empty); canvas.Save(); canvas.Concat(ref transform); // clip-path var clipPath = ReadClipPath(e.Attribute("clip-path")?.Value ?? string.Empty); if (clipPath != null) { canvas.ClipPath(clipPath); } // SVG element var elementName = e.Name.LocalName; var isGroup = elementName == "g"; // read style var style = ReadPaints(e, ref stroke, ref fill, isGroup); // parse elements switch (elementName) { case "image": { var image = ReadImage(e); if (image.Bytes != null) { using (var bitmap = SKBitmap.Decode(image.Bytes)) { if (bitmap != null) { canvas.DrawBitmap(bitmap, image.Rect); } } } break; } case "text": if (stroke != null || fill != null) { var spans = ReadText(e, stroke?.Clone(), fill?.Clone()); if (spans.Any()) { canvas.DrawText(spans); } } break; case "rect": case "ellipse": case "circle": case "path": case "polygon": case "polyline": case "line": if (stroke != null || fill != null) { var elementPath = ReadElement(e); if (elementPath != null) { if (fill != null) { canvas.DrawPath(elementPath, fill); } if (stroke != null) { canvas.DrawPath(elementPath, stroke); } } } break; case "g": if (e.HasElements) { // get current group opacity float groupOpacity = ReadOpacity(style); if (groupOpacity != 1.0f) { var opacity = (byte)(255 * groupOpacity); var opacityPaint = new SKPaint { Color = SKColors.Black.WithAlpha(opacity) }; // apply the opacity canvas.SaveLayer(opacityPaint); } foreach (var gElement in e.Elements()) { ReadElement(gElement, canvas, stroke?.Clone(), fill?.Clone()); } // restore state if (groupOpacity != 1.0f) { canvas.Restore(); } } break; case "use": if (e.HasAttributes) { var href = ReadHref(e); if (href != null) { // TODO: copy/process other attributes var x = ReadNumber(e.Attribute("x")); var y = ReadNumber(e.Attribute("y")); var useTransform = SKMatrix.MakeTranslation(x, y); canvas.Save(); canvas.Concat(ref useTransform); ReadElement(href, canvas, stroke?.Clone(), fill?.Clone()); canvas.Restore(); } } break; case "switch": if (e.HasElements) { foreach (var ee in e.Elements()) { var requiredFeatures = ee.Attribute("requiredFeatures"); var requiredExtensions = ee.Attribute("requiredExtensions"); var systemLanguage = ee.Attribute("systemLanguage"); // TODO: evaluate requiredFeatures, requiredExtensions and systemLanguage var isVisible = requiredFeatures == null && requiredExtensions == null && systemLanguage == null; if (isVisible) { ReadElement(ee, canvas, stroke?.Clone(), fill?.Clone()); } } } break; case "defs": case "title": case "desc": case "description": // already read earlier break; default: LogOrThrow($"SVG element '{elementName}' is not supported"); break; } // restore matrix canvas.Restore(); }
public MarkerDrawable(SvgMarker svgMarker, SvgVisualElement pOwner, SKPoint pMarkerPoint, float fAngle, SKRect skOwnerBounds, Drawable?root, Drawable?parent, Attributes ignoreAttributes = Attributes.None) : base(svgMarker, root, parent) { IgnoreAttributes = Attributes.Display | ignoreAttributes; IsDrawable = true; if (!IsDrawable) { return; } var markerElement = GetMarkerElement(svgMarker); if (markerElement == null) { IsDrawable = false; return; } var skMarkerMatrix = SKMatrix.MakeIdentity(); var skMatrixMarkerPoint = SKMatrix.MakeTranslation(pMarkerPoint.X, pMarkerPoint.Y); SKMatrix.PreConcat(ref skMarkerMatrix, ref skMatrixMarkerPoint); var skMatrixAngle = SKMatrix.MakeRotationDegrees(svgMarker.Orient.IsAuto ? fAngle : svgMarker.Orient.Angle); SKMatrix.PreConcat(ref skMarkerMatrix, ref skMatrixAngle); var strokeWidth = pOwner.StrokeWidth.ToDeviceValue(UnitRenderingType.Other, svgMarker, skOwnerBounds); var refX = svgMarker.RefX.ToDeviceValue(UnitRenderingType.Horizontal, svgMarker, skOwnerBounds); var refY = svgMarker.RefY.ToDeviceValue(UnitRenderingType.Vertical, svgMarker, skOwnerBounds); float markerWidth = svgMarker.MarkerWidth.ToDeviceValue(UnitRenderingType.Other, svgMarker, skOwnerBounds); float markerHeight = svgMarker.MarkerHeight.ToDeviceValue(UnitRenderingType.Other, svgMarker, skOwnerBounds); float viewBoxToMarkerUnitsScaleX = 1f; float viewBoxToMarkerUnitsScaleY = 1f; switch (svgMarker.MarkerUnits) { case SvgMarkerUnits.StrokeWidth: { var skMatrixStrokeWidth = SKMatrix.MakeScale(strokeWidth, strokeWidth); SKMatrix.PreConcat(ref skMarkerMatrix, ref skMatrixStrokeWidth); var viewBoxWidth = svgMarker.ViewBox.Width; var viewBoxHeight = svgMarker.ViewBox.Height; var scaleFactorWidth = (viewBoxWidth <= 0) ? 1 : (markerWidth / viewBoxWidth); var scaleFactorHeight = (viewBoxHeight <= 0) ? 1 : (markerHeight / viewBoxHeight); viewBoxToMarkerUnitsScaleX = Math.Min(scaleFactorWidth, scaleFactorHeight); viewBoxToMarkerUnitsScaleY = Math.Min(scaleFactorWidth, scaleFactorHeight); var skMatrixTranslateRefXY = SKMatrix.MakeTranslation(-refX * viewBoxToMarkerUnitsScaleX, -refY * viewBoxToMarkerUnitsScaleY); SKMatrix.PreConcat(ref skMarkerMatrix, ref skMatrixTranslateRefXY); var skMatrixScaleXY = SKMatrix.MakeScale(viewBoxToMarkerUnitsScaleX, viewBoxToMarkerUnitsScaleY); SKMatrix.PreConcat(ref skMarkerMatrix, ref skMatrixScaleXY); } break; case SvgMarkerUnits.UserSpaceOnUse: { var skMatrixTranslateRefXY = SKMatrix.MakeTranslation(-refX, -refY); SKMatrix.PreConcat(ref skMarkerMatrix, ref skMatrixTranslateRefXY); } break; } switch (svgMarker.Overflow) { case SvgOverflow.Auto: case SvgOverflow.Visible: case SvgOverflow.Inherit: break; default: MarkerClipRect = SKRect.Create( svgMarker.ViewBox.MinX, svgMarker.ViewBox.MinY, markerWidth / viewBoxToMarkerUnitsScaleX, markerHeight / viewBoxToMarkerUnitsScaleY); break; } var drawable = DrawableFactory.Create(markerElement, skOwnerBounds, root, this, Attributes.Display); if (drawable != null) { MarkerElementDrawable = drawable; _disposable.Add(MarkerElementDrawable); } else { IsDrawable = false; return; } IsAntialias = SvgPaintingExtensions.IsAntialias(svgMarker); TransformedBounds = MarkerElementDrawable.TransformedBounds; Transform = SvgTransformsExtensions.ToSKMatrix(svgMarker.Transforms); SKMatrix.PreConcat(ref Transform, ref skMarkerMatrix); Fill = null; Stroke = null; // TODO: Transform _skBounds using _skMatrix. SKMatrix.MapRect(ref Transform, out TransformedBounds, ref TransformedBounds); }
void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args) { SKImageInfo info = args.Info; SKSurface surface = args.Surface; SKCanvas canvas = surface.Canvas; canvas.Clear(); string strName = Apartment.Name; string strRating = Apartment.RatesAverage?.ToString(); // Create an SKPaint object to display the text SKPaint textPaint = new SKPaint { Typeface = SKTypeface.FromFamilyName("Brandon_reg"), Color = SKColors.Black }; // Adjust TextSize property so text is 90% of screen width textPaint.TextSize = 0.05f * info.Width; // Find the text bounds SKRect textBounds = new SKRect(); textPaint.MeasureText(strName, ref textBounds); // Calculate offsets to center the text on the screen float xTextName = 0.1f * info.Width; float yTextName = info.Height / 2 - textBounds.MidY; float yTextRating = yTextName + 1.2f * textBounds.Height; // And draw the text float xCenter = info.Width / 2; float yCenter = info.Height / 2; // Translate center to origin SKMatrix matrix = SKMatrix.MakeTranslation(-xCenter, -yCenter); // Use 3D matrix for 3D rotations and perspective SKMatrix44 matrix44 = SKMatrix44.CreateIdentity(); matrix44.PostConcat(SKMatrix44.CreateRotationDegrees(1, 0, 0, angle)); SKMatrix44 perspectiveMatrix = SKMatrix44.CreateIdentity(); perspectiveMatrix[3, 2] = -1 / (float)2000; matrix44.PostConcat(perspectiveMatrix); // Concatenate with 2D matrix SKMatrix.PostConcat(ref matrix, matrix44.Matrix); // Translate back to center SKMatrix.PostConcat(ref matrix, SKMatrix.MakeTranslation(xCenter, yCenter)); // Set the matrix and display the bitmap canvas.SetMatrix(matrix); // Create a new SKRect object for the frame around the text SKRect frameRect = SKRect.Create(0.05f * info.Width, 0, 0.9f * info.Width, info.Height); // Create an SKPaint object to display the frame SKPaint framePaint = new SKPaint { Style = SKPaintStyle.Stroke, StrokeWidth = 5, Color = SKColors.Blue }; SKPaint rectanglePaint = new SKPaint { Color = SKColors.Beige }; // Draw one frame canvas.DrawRoundRect(frameRect, 20, 20, rectanglePaint); canvas.DrawText(strName, xTextName, yTextName, textPaint); canvas.DrawText($"{strRating}", xTextName, yTextRating, textPaint); if (bitmap != null) { var pictureFrame = SKRect.Create(info.Width * (float)0.7, 0, info.Width * (float)0.25, info.Height); float xBitmap = (float)0.8 * info.Width; float yBitmap = 0; canvas.DrawBitmap(bitmap, pictureFrame); } }
private void OnTouchEffectAction(object sender, TouchTracking.TouchActionEventArgs args) { Point pt = args.Location; SKPoint point = new SKPoint((float)(view.CanvasSize.Width * pt.X / view.Width), (float)(view.CanvasSize.Height * pt.Y / view.Height)); switch (args.Type) { case TouchActionType.Pressed: if (!touchDictionary.ContainsKey(args.Id)) { touchDictionary.Add(args.Id, point); } //SetPixelsValue(args, view); break; case TouchActionType.Moved: if (touchDictionary.ContainsKey(args.Id)) { if (touchDictionary.Count == 1) { long[] keys = new long[touchDictionary.Count]; touchDictionary.Keys.CopyTo(keys, 0); // Find index of non-moving (pivot) finger int pivotIndex = (keys[0] == args.Id) ? 1 : 0; // Get the three points involved in the transform // SKPoint pivotPoint = touchDictionary[keys[pivotIndex]]; SKPoint prevPoint = touchDictionary[args.Id]; SKPoint newPoint = point; SKPoint delta = newPoint - prevPoint; // new_p = newPoint; old_p = prevPoint; SKMatrix.PostConcat(ref matrix, SKMatrix.MakeTranslation(delta.X, delta.Y)); touchDictionary[args.Id] = point; } else if (touchDictionary.Count >= 2) { // Copy two dictionary keys into array long[] keys = new long[touchDictionary.Count]; touchDictionary.Keys.CopyTo(keys, 0); int pivotIndex = (keys[0] == args.Id) ? 1 : 0; SKPoint pivotPoint = touchDictionary[keys[pivotIndex]]; SKPoint prevPoint = touchDictionary[args.Id]; SKPoint newPoint = point; double distance = Math.Sqrt(Math.Pow(Math.Abs(pivotPoint.X - newPoint.X), 2) + Math.Pow(Math.Abs(pivotPoint.Y - newPoint.Y), 2)); double oldDistance = Math.Sqrt(Math.Pow(Math.Abs(pivotPoint.X - prevPoint.X), 2) + Math.Pow(Math.Abs(pivotPoint.Y - prevPoint.Y), 2)); double differentDistance = distance / oldDistance; SKPoint centerPoint = new SKPoint() { X = (newPoint.X + pivotPoint.X) / 2, Y = (newPoint.Y + pivotPoint.Y) / 2 }; SKPoint oldCenterPoint = new SKPoint() { X = (prevPoint.X + pivotPoint.X) / 2, Y = (prevPoint.Y + pivotPoint.Y) / 2 }; SKMatrix translationMatrix = SKMatrix.CreateTranslation((centerPoint.X - oldCenterPoint.X) /*/matrix.ScaleX*/, (centerPoint.Y - oldCenterPoint.Y) /*/ matrix.ScaleX*/); SKMatrix scaleMatrix = SKMatrix.Identity; if (!(matrix.ScaleX > 8 && differentDistance > 1)) { scaleMatrix = SKMatrix.CreateScale((float)differentDistance, (float)differentDistance, centerPoint.X, centerPoint.Y /* pivotPoint.X, pivotPoint.Y*/); } SKMatrix.PostConcat(ref matrix, scaleMatrix); // NEW "PostContact() DOES NOT WORKING CORRECTLY!!!" SKMatrix.PostConcat(ref matrix, translationMatrix); touchDictionary[args.Id] = point; } } break; case TouchActionType.Released: case TouchActionType.Cancelled: if (touchDictionary.ContainsKey(args.Id)) { touchDictionary.Remove(args.Id); } break; } this.view.InvalidateSurface(); }
public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo) { if (_disposed) { throw new ObjectDisposedException("Folder"); } if (Path == null || !Enabled || !Children.Any(c => c.Enabled)) { return; } // No need to render if at the end of the timeline if (TimelinePosition > TimelineLength) { return; } if (_folderBitmap == null) { _folderBitmap = new SKBitmap(new SKImageInfo((int)Path.Bounds.Width, (int)Path.Bounds.Height)); } else if (_folderBitmap.Info.Width != (int)Path.Bounds.Width || _folderBitmap.Info.Height != (int)Path.Bounds.Height) { _folderBitmap.Dispose(); _folderBitmap = new SKBitmap(new SKImageInfo((int)Path.Bounds.Width, (int)Path.Bounds.Height)); } using SKPath folderPath = new SKPath(Path); using SKCanvas folderCanvas = new SKCanvas(_folderBitmap); using SKPaint folderPaint = new SKPaint(); folderCanvas.Clear(); folderPath.Transform(SKMatrix.MakeTranslation(folderPath.Bounds.Left * -1, folderPath.Bounds.Top * -1)); SKPoint targetLocation = Path.Bounds.Location; if (Parent is Folder parentFolder) { targetLocation -= parentFolder.Path.Bounds.Location; } canvas.Save(); using SKPath clipPath = new SKPath(folderPath); clipPath.Transform(SKMatrix.MakeTranslation(targetLocation.X, targetLocation.Y)); canvas.ClipPath(clipPath); foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled)) { baseLayerEffect.PreProcess(folderCanvas, _folderBitmap.Info, folderPath, folderPaint); } // No point rendering if the alpha was set to zero by one of the effects if (folderPaint.Color.Alpha == 0) { return; } // Iterate the children in reverse because the first layer must be rendered last to end up on top for (int index = Children.Count - 1; index > -1; index--) { folderCanvas.Save(); ProfileElement profileElement = Children[index]; profileElement.Render(deltaTime, folderCanvas, _folderBitmap.Info); folderCanvas.Restore(); } // If required, apply the opacity override of the module to the root folder if (IsRootFolder && Profile.Module.OpacityOverride < 1) { double multiplier = Easings.SineEaseInOut(Profile.Module.OpacityOverride); folderPaint.Color = folderPaint.Color.WithAlpha((byte)(folderPaint.Color.Alpha * multiplier)); } foreach (BaseLayerEffect baseLayerEffect in LayerEffects.Where(e => e.Enabled)) { baseLayerEffect.PostProcess(canvas, canvasInfo, folderPath, folderPaint); } canvas.DrawBitmap(_folderBitmap, targetLocation, folderPaint); canvas.Restore(); }
private void FolderCanvas_PaintSurface(object sender, SkiaSharp.Views.Forms.SKPaintSurfaceEventArgs e) { SKImageInfo info = e.Info; SKSurface surface = e.Surface; SKCanvas canvas = surface.Canvas; canvas.Clear(); float scaleFactor = 0.67f; float density = scaleFactor * info.Size.Height / (float)this.Height; canvas.Save(); using (SKPath backPath = SKPath.ParseSvgPathData( "M87.909,9.917V4.02A4.032,4.032,0,0,0,83.889,0H23.72A4.032,4.032,0,0,0,19.7,4.02v5.9H13.338a4.529,4.529,0,0,0-3.822,4.02l.27,112.7a3.954,3.954,0,0,0,3.951,4.024H167.344a3.963,3.963,0,0,0,3.97-4.011l-.6-112.71a4.092,4.092,0,0,0-4.07-4.02Z")) { backPath.Transform(SKMatrix.MakeScale(density, density)); backPath.GetTightBounds(out var backPathTightBounds); var translateXBackPath = info.Width * 0.5f - backPathTightBounds.MidX; var translateYbackPath = info.Height - backPathTightBounds.Bottom - 20f; canvas.Translate(translateXBackPath, translateYbackPath); fillPaint.Shader = SKShader.CreateLinearGradient( new SKPoint(info.Width * 0.5f, 0), new SKPoint(info.Width * 0.5f, info.Height), new SKColor[] { SKColor.Parse(Color1), SKColor.Parse(Color2), SKColor.Parse(Color2) }, new float[] { 0, 0.37f, 1 }, SKShaderTileMode.Clamp ); canvas.DrawPath(backPath, fillPaint); } canvas.Restore(); canvas.Save(); using (SKPath backFilePath = SKPath.ParseSvgPathData("M174.079,150.908H29.155l-.4-114.144H174.481Z")) { backFilePath.Transform(SKMatrix.MakeScale(density, density)); backFilePath.GetTightBounds(out var backFilePathTightBounds); var translateXBackFilePath = info.Width * 0.5f - backFilePathTightBounds.MidX; var translateYbackFilePath = info.Height - backFilePathTightBounds.Bottom - 20f; canvas.Translate(translateXBackFilePath, translateYbackFilePath); fillPaint.Shader = SKShader.CreateLinearGradient( new SKPoint(info.Width * 0.5f, 0), new SKPoint(info.Width * 0.5f, info.Height), new SKColor[] { SKColor.Parse("#e6e6e6"), SKColor.Parse("#e8e8e8"), SKColor.Parse("#f0f0f0"), SKColor.Parse("#f2f2f2") }, new float[] { 0, 0.633f, 0.949f, 1 }, SKShaderTileMode.Clamp ); canvas.DrawPath(backFilePath, fillPaint); } canvas.Restore(); canvas.Save(); using (SKPath frontFilePath = SKPath.ParseSvgPathData("M170.491,158.681H25.567L22.753,49.764H173.3Z")) { frontFilePath.Transform(SKMatrix.MakeScale(density, density)); frontFilePath.GetTightBounds(out var frontFilePathTightBounds); var translateXFrontFilePath = info.Width * 0.5f - frontFilePathTightBounds.MidX; var translateYFrontFilePath = info.Height - frontFilePathTightBounds.Bottom - 20f; canvas.Translate(translateXFrontFilePath, translateYFrontFilePath); fillPaint.Shader = SKShader.CreateLinearGradient( new SKPoint(info.Width * 0.5f, 0), new SKPoint(info.Width * 0.5f, info.Height), new SKColor[] { SKColor.Parse("#cccccc"), SKColor.Parse("#cecece"), SKColor.Parse("#d6d6d6"), SKColor.Parse("#e3e3e3"), SKColor.Parse("#f6f6f6"), SKColor.Parse("#ffffff") }, new float[] { 0, 0.427f, 0.64f, 0.806f, 0.947f, 1 }, SKShaderTileMode.Clamp ); canvas.DrawPath(frontFilePath, fillPaint); } canvas.Restore(); canvas.Save(); using (SKPath frontPath = SKPath.ParseSvgPathData("M165.433,164.917a4.106,4.106,0,0,1-4.089,4.011H7.735a4.578,4.578,0,0,1-4.325-4.292L0,68.019A3.948,3.948,0,0,1,3.95,64H164.895a3.948,3.948,0,0,1,3.95,4.02Z")) { frontPath.Transform(SKMatrix.MakeScale(density, density)); frontPath.GetTightBounds(out var frontPathTightBounds); fillPaint.Shader = SKShader.CreateLinearGradient( new SKPoint(info.Width * 0.5f, 0), new SKPoint(info.Width * 0.5f, info.Height), new SKColor[] { SKColor.Parse(Color3), SKColor.Parse(Color4), SKColor.Parse(Color5), SKColor.Parse(Color6) }, new float[] { 0, 0.595f, 0.957f, 1 }, SKShaderTileMode.Clamp ); SKMatrix matrix = SKMatrix.MakeTranslation(-frontPathTightBounds.Right, -frontPathTightBounds.Bottom); SKMatrix44 matrix44 = SKMatrix44.CreateIdentity(); matrix44.PostConcat(SKMatrix44.CreateRotationDegrees(1, 0, 0, -FrontPathDegree)); matrix44.PostConcat(SKMatrix44.CreateRotationDegrees(0, 1, 0, -0.5f * FrontPathDegree)); matrix44.PostConcat(SKMatrix44.CreateRotationDegrees(0, 0, 1, 0)); SKMatrix.PostConcat(ref matrix, matrix44.Matrix); SKMatrix.PostConcat(ref matrix, SKMatrix.MakeTranslation(frontPathTightBounds.Right, frontPathTightBounds.Bottom)); canvas.SetMatrix(matrix); var translateXFrontPath = info.Width * 0.5f - frontPathTightBounds.MidX; var translateYFrontPath = info.Height - frontPathTightBounds.Bottom - 20f; canvas.Translate(translateXFrontPath, translateYFrontPath); canvas.DrawPath(frontPath, fillPaint); } canvas.Restore(); }
public void DrawUse(SvgUse svgUse, bool ignoreDisplay) { if (!CanDraw(svgUse, ignoreDisplay)) { return; } var svgVisualElement = SkiaUtil.GetReference <SvgVisualElement>(svgUse, svgUse.ReferencedElement); if (svgVisualElement == null || SkiaUtil.HasRecursiveReference(svgUse)) { return; } float x = svgUse.X.ToDeviceValue(null, UnitRenderingType.Horizontal, svgUse); float y = svgUse.Y.ToDeviceValue(null, UnitRenderingType.Vertical, svgUse); var skMatrixTranslateXY = SKMatrix.MakeTranslation(x, y); var skMatrix = SkiaUtil.GetSKMatrix(svgUse.Transforms); SKMatrix.PreConcat(ref skMatrix, ref skMatrixTranslateXY); var ew = svgUse.Width.ToDeviceValue(null, UnitRenderingType.Horizontal, svgUse); var eh = svgUse.Height.ToDeviceValue(null, UnitRenderingType.Vertical, svgUse); if (ew > 0 && eh > 0) { var _attributes = svgVisualElement.GetType().GetField("_attributes", BindingFlags.NonPublic | BindingFlags.Instance); if (_attributes != null) { var attributes = _attributes.GetValue(svgVisualElement) as SvgAttributeCollection; if (attributes != null) { var viewBox = attributes.GetAttribute <SvgViewBox>("viewBox"); if (viewBox != SvgViewBox.Empty && Math.Abs(ew - viewBox.Width) > float.Epsilon && Math.Abs(eh - viewBox.Height) > float.Epsilon) { var sw = ew / viewBox.Width; var sh = eh / viewBox.Height; var skMatrixTranslateSWSH = SKMatrix.MakeTranslation(sw, sh); SKMatrix.PreConcat(ref skMatrix, ref skMatrixTranslateSWSH); } } } } var originalParent = svgUse.Parent; var useParent = svgUse.GetType().GetField("_parent", BindingFlags.NonPublic | BindingFlags.Instance); if (useParent != null) { useParent.SetValue(svgVisualElement, svgUse); } svgVisualElement.InvalidateChildPaths(); _skCanvas.Save(); SetTransform(skMatrix); SetClipPath(svgUse, _disposable); var skPaintOpacity = SetOpacity(svgUse, _disposable); var skPaintFilter = SetFilter(svgUse, _disposable); if (svgVisualElement is SvgSymbol svgSymbol) { DrawSymbol(svgSymbol, ignoreDisplay); } else { Draw(svgVisualElement, ignoreDisplay); } if (skPaintFilter != null) { _skCanvas.Restore(); } if (skPaintOpacity != null) { _skCanvas.Restore(); } _skCanvas.Restore(); if (useParent != null) { useParent.SetValue(svgVisualElement, originalParent); } }
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); SKMatrix.PreConcat(ref Transform, ref skMatrixTranslateXY); } Fill = null; Stroke = null; // TODO: Transform _skBounds using _skMatrix. SKMatrix.MapRect(ref Transform, out TransformedBounds, ref TransformedBounds); try { if (referencedElementParent != null) { referencedElementParent.SetValue(svgReferencedElement, originalReferencedElementParent); } } catch (Exception ex) { Debug.WriteLine(ex.Message); Debug.WriteLine(ex.StackTrace); } }
private SKMatrix ReadTransform(string raw) { var t = SKMatrix.MakeIdentity(); if (string.IsNullOrWhiteSpace(raw)) { return(t); } var calls = raw.Trim().Split(new[] { ')' }, StringSplitOptions.RemoveEmptyEntries); foreach (var c in calls) { var args = c.Split(new[] { '(', ',', ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); var nt = SKMatrix.MakeIdentity(); switch (args[0]) { case "matrix": if (args.Length == 7) { nt.Values = new float[] { ReadNumber(args[1]), ReadNumber(args[3]), ReadNumber(args[5]), ReadNumber(args[2]), ReadNumber(args[4]), ReadNumber(args[6]), 0, 0, 1 }; } else { LogOrThrow($"Matrices are expected to have 6 elements, this one has {args.Length - 1}"); } break; case "translate": if (args.Length >= 3) { nt = SKMatrix.MakeTranslation(ReadNumber(args[1]), ReadNumber(args[2])); } else if (args.Length >= 2) { nt = SKMatrix.MakeTranslation(ReadNumber(args[1]), 0); } break; case "scale": if (args.Length >= 3) { nt = SKMatrix.MakeScale(ReadNumber(args[1]), ReadNumber(args[2])); } else if (args.Length >= 2) { var sx = ReadNumber(args[1]); nt = SKMatrix.MakeScale(sx, sx); } break; case "rotate": var a = ReadNumber(args[1]); if (args.Length >= 4) { var x = ReadNumber(args[2]); var y = ReadNumber(args[3]); var t1 = SKMatrix.MakeTranslation(x, y); var t2 = SKMatrix.MakeRotationDegrees(a); var t3 = SKMatrix.MakeTranslation(-x, -y); SKMatrix.Concat(ref nt, ref t1, ref t2); SKMatrix.Concat(ref nt, ref nt, ref t3); } else { nt = SKMatrix.MakeRotationDegrees(a); } break; default: LogOrThrow($"Can't transform {args[0]}"); break; } SKMatrix.Concat(ref t, ref t, ref nt); } return(t); }
private void ReadElement(XElement e, SKCanvas canvas, SKPaint stroke, SKPaint fill) { // transform matrix var transform = ReadTransform(e.Attribute("transform")?.Value ?? string.Empty); canvas.Save(); canvas.Concat(ref transform); // SVG element var elementName = e.Name.LocalName; var isGroup = elementName == "g"; // read style var style = ReadPaints(e, ref stroke, ref fill, isGroup); // parse elements switch (elementName) { case "text": if (stroke != null || fill != null) { ReadText(e, canvas, stroke?.Clone(), fill?.Clone()); } break; case "rect": if (stroke != null || fill != null) { var x = ReadNumber(e.Attribute("x")); var y = ReadNumber(e.Attribute("y")); var width = ReadNumber(e.Attribute("width")); var height = ReadNumber(e.Attribute("height")); var rx = ReadNumber(e.Attribute("rx")); var ry = ReadNumber(e.Attribute("ry")); var rect = SKRect.Create(x, y, width, height); if (rx > 0 || ry > 0) { if (fill != null) { canvas.DrawRoundRect(rect, rx, ry, fill); } if (stroke != null) { canvas.DrawRoundRect(rect, rx, ry, stroke); } } else { if (fill != null) { canvas.DrawRect(rect, fill); } if (stroke != null) { canvas.DrawRect(rect, stroke); } } } break; case "ellipse": if (stroke != null || fill != null) { var cx = ReadNumber(e.Attribute("cx")); var cy = ReadNumber(e.Attribute("cy")); var rx = ReadNumber(e.Attribute("rx")); var ry = ReadNumber(e.Attribute("ry")); if (fill != null) { canvas.DrawOval(cx, cy, rx, ry, fill); } if (stroke != null) { canvas.DrawOval(cx, cy, rx, ry, stroke); } } break; case "circle": if (stroke != null || fill != null) { var cx = ReadNumber(e.Attribute("cx")); var cy = ReadNumber(e.Attribute("cy")); var rr = ReadNumber(e.Attribute("r")); if (fill != null) { canvas.DrawCircle(cx, cy, rr, fill); } if (stroke != null) { canvas.DrawCircle(cx, cy, rr, stroke); } } break; case "path": if (stroke != null || fill != null) { var d = e.Attribute("d")?.Value; if (!string.IsNullOrWhiteSpace(d)) { var path = SKPath.ParseSvgPathData(d); if (fill != null) { canvas.DrawPath(path, fill); } if (stroke != null) { canvas.DrawPath(path, stroke); } } } break; case "polygon": case "polyline": if (stroke != null || fill != null) { var close = elementName == "polygon"; var p = e.Attribute("points")?.Value; if (!string.IsNullOrWhiteSpace(p)) { var path = ReadPolyPath(p, close); if (fill != null) { canvas.DrawPath(path, fill); } if (stroke != null) { canvas.DrawPath(path, stroke); } } } break; case "g": if (e.HasElements) { // get current group opacity float groupOpacity = ReadOpacity(style); if (groupOpacity != 1.0f) { var opacity = (byte)(255 * groupOpacity); var opacityPaint = new SKPaint { Color = SKColors.Black.WithAlpha(opacity) }; // apply the opacity canvas.SaveLayer(opacityPaint); } foreach (var gElement in e.Elements()) { ReadElement(gElement, canvas, stroke?.Clone(), fill?.Clone()); } // restore state if (groupOpacity != 1.0f) { canvas.Restore(); } } break; case "use": if (e.HasAttributes) { var href = ReadHref(e); if (href != null) { // TODO: copy/process other attributes var x = ReadNumber(e.Attribute("x")); var y = ReadNumber(e.Attribute("y")); var useTransform = SKMatrix.MakeTranslation(x, y); canvas.Save(); canvas.Concat(ref useTransform); ReadElement(href, canvas, stroke?.Clone(), fill?.Clone()); canvas.Restore(); } } break; case "line": if (stroke != null) { var x1 = ReadNumber(e.Attribute("x1")); var x2 = ReadNumber(e.Attribute("x2")); var y1 = ReadNumber(e.Attribute("y1")); var y2 = ReadNumber(e.Attribute("y2")); canvas.DrawLine(x1, y1, x2, y2, stroke); } break; case "switch": if (e.HasElements) { foreach (var ee in e.Elements()) { var requiredFeatures = ee.Attribute("requiredFeatures"); var requiredExtensions = ee.Attribute("requiredExtensions"); var systemLanguage = ee.Attribute("systemLanguage"); // TODO: evaluate requiredFeatures, requiredExtensions and systemLanguage var isVisible = requiredFeatures == null && requiredExtensions == null && systemLanguage == null; if (isVisible) { ReadElement(ee, canvas, stroke?.Clone(), fill?.Clone()); } } } break; case "defs": case "title": case "desc": case "description": // already read earlier break; default: LogOrThrow($"SVG element '{elementName}' is not supported"); break; } // restore matrix canvas.Restore(); }
public static SKMatrix ToSKMatrix(this SvgTransformCollection svgTransformCollection) { var skMatrixTotal = SKMatrix.MakeIdentity(); if (svgTransformCollection == null) { return(skMatrixTotal); } foreach (var svgTransform in svgTransformCollection) { switch (svgTransform) { case SvgMatrix svgMatrix: { var skMatrix = svgMatrix.ToSKMatrix(); skMatrixTotal = skMatrixTotal.PreConcat(skMatrix); } break; case SvgRotate svgRotate: { var skMatrixRotate = SKMatrix.MakeRotationDegrees(svgRotate.Angle, svgRotate.CenterX, svgRotate.CenterY); skMatrixTotal = skMatrixTotal.PreConcat(skMatrixRotate); } break; case SvgScale svgScale: { var skMatrixScale = SKMatrix.MakeScale(svgScale.X, svgScale.Y); skMatrixTotal = skMatrixTotal.PreConcat(skMatrixScale); } break; case SvgShear svgShear: { // Not in the svg specification. } break; case SvgSkew svgSkew: { float sx = (float)Math.Tan(Math.PI * svgSkew.AngleX / 180); float sy = (float)Math.Tan(Math.PI * svgSkew.AngleY / 180); var skMatrixSkew = SKMatrix.MakeSkew(sx, sy); skMatrixTotal = skMatrixTotal.PreConcat(skMatrixSkew); } break; case SvgTranslate svgTranslate: { var skMatrixTranslate = SKMatrix.MakeTranslation(svgTranslate.X, svgTranslate.Y); skMatrixTotal = skMatrixTotal.PreConcat(skMatrixTranslate); } break; default: break; } } return(skMatrixTotal); }
public MaskDrawable(SvgMask svgMask, SKRect skOwnerBounds, Drawable?root, Drawable?parent, Attributes ignoreAttributes = Attributes.None) : base(svgMask, root, parent) { IgnoreAttributes = ignoreAttributes; IsDrawable = true; if (!IsDrawable) { return; } var maskUnits = svgMask.MaskUnits; var maskContentUnits = svgMask.MaskContentUnits; var xUnit = svgMask.X; var yUnit = svgMask.Y; var widthUnit = svgMask.Width; var heightUnit = svgMask.Height; float x = xUnit.ToDeviceValue(UnitRenderingType.Horizontal, svgMask, skOwnerBounds); float y = yUnit.ToDeviceValue(UnitRenderingType.Vertical, svgMask, skOwnerBounds); float width = widthUnit.ToDeviceValue(UnitRenderingType.Horizontal, svgMask, skOwnerBounds); float height = heightUnit.ToDeviceValue(UnitRenderingType.Vertical, svgMask, skOwnerBounds); if (width <= 0 || height <= 0) { IsDrawable = false; return; } if (maskUnits == SvgCoordinateUnits.ObjectBoundingBox) { if (xUnit.Type != SvgUnitType.Percentage) { x *= skOwnerBounds.Width; } if (yUnit.Type != SvgUnitType.Percentage) { y *= skOwnerBounds.Height; } if (widthUnit.Type != SvgUnitType.Percentage) { width *= skOwnerBounds.Width; } if (heightUnit.Type != SvgUnitType.Percentage) { height *= skOwnerBounds.Height; } x += skOwnerBounds.Left; y += skOwnerBounds.Top; } SKRect skRectTransformed = SKRect.Create(x, y, width, height); var skMatrix = SKMatrix.MakeIdentity(); if (maskContentUnits == SvgCoordinateUnits.ObjectBoundingBox) { var skBoundsTranslateTransform = SKMatrix.MakeTranslation(skOwnerBounds.Left, skOwnerBounds.Top); SKMatrix.PreConcat(ref skMatrix, ref skBoundsTranslateTransform); var skBoundsScaleTransform = SKMatrix.MakeScale(skOwnerBounds.Width, skOwnerBounds.Height); SKMatrix.PreConcat(ref skMatrix, ref skBoundsScaleTransform); } CreateChildren(svgMask, skOwnerBounds, root, this, ignoreAttributes); Overflow = skRectTransformed; IsAntialias = SvgPaintingExtensions.IsAntialias(svgMask); TransformedBounds = skRectTransformed; Transform = skMatrix; Fill = null; Stroke = null; // TODO: Transform _skBounds using _skMatrix. SKMatrix.MapRect(ref Transform, out TransformedBounds, ref TransformedBounds); }