private static SKMatrix CreateRotationMatrix(IReadOnlyViewport viewport, MRect rect, SKMatrix priorMatrix) { // The front-end sets up the canvas with a matrix based on screen scaling (e.g. retina). // We need to retain that effect by combining our matrix with the incoming matrix. // We'll create four matrices in addition to the incoming matrix. They perform the // zoom scale, focal point offset, user rotation and finally, centering in the screen. var userRotation = SKMatrix.CreateRotationDegrees((float)viewport.Rotation); var focalPointOffset = SKMatrix.CreateTranslation( (float)(rect.Left - viewport.Center.X), (float)(viewport.Center.Y - rect.Top)); var zoomScale = SKMatrix.CreateScale((float)(1.0 / viewport.Resolution), (float)(1.0 / viewport.Resolution)); var centerInScreen = SKMatrix.CreateTranslation((float)(viewport.Width / 2.0), (float)(viewport.Height / 2.0)); // We'll concatenate them like so: incomingMatrix * centerInScreen * userRotation * zoomScale * focalPointOffset var matrix = SKMatrix.Concat(zoomScale, focalPointOffset); matrix = SKMatrix.Concat(userRotation, matrix); matrix = SKMatrix.Concat(centerInScreen, matrix); matrix = SKMatrix.Concat(priorMatrix, matrix); return(matrix); }
public void TransformConvertsToMatrix() { var matrix44 = SKMatrix44.CreateRotationDegrees(0, 0, 1, 45); var matrix = SKMatrix.CreateRotationDegrees(45); Assert.Equal(matrix.Values, matrix44.Matrix.Values); }
/// <inheritdoc /> public override void Render(SKCanvas canvas, SKRect bounds, SKPaint paint) { _lastBounds = bounds; // For brevity's sake ColorGradient gradient = Properties.Colors.BaseValue; SKMatrix matrix = SKMatrix.Concat( SKMatrix.CreateRotationDegrees(Properties.Rotation, bounds.MidX, bounds.MidY), SKMatrix.CreateTranslation(_scrollX, _scrollY) ); // LinearGradientRepeatMode.Mirror is currently the only setting that requires a different tile mode SKShaderTileMode tileMode = Properties.RepeatMode.CurrentValue == LinearGradientRepeatMode.Mirror ? SKShaderTileMode.Mirror : SKShaderTileMode.Repeat; // Render gradient paint.Shader = SKShader.CreateLinearGradient( new SKPoint(bounds.Left, bounds.Top), new SKPoint( (Properties.Orientation == LinearGradientOrientationMode.Horizontal ? bounds.Right : bounds.Left) * Properties.WaveSize / 100, (Properties.Orientation == LinearGradientOrientationMode.Horizontal ? bounds.Top : bounds.Bottom) * Properties.WaveSize / 100 ), gradient.GetColorsArray(0, Properties.RepeatMode.CurrentValue == LinearGradientRepeatMode.RepeatSeamless), gradient.GetPositionsArray(0, Properties.RepeatMode.CurrentValue == LinearGradientRepeatMode.RepeatSeamless), tileMode, matrix ); canvas.DrawRect(bounds, paint); paint.Shader?.Dispose(); paint.Shader = null; }
public void ImplicitFromMatrix() { var matrix = SKMatrix.CreateRotationDegrees(45); var matrix44 = (SKMatrix44)matrix; Assert.Equal(matrix.Values, matrix44.Matrix.Values); }
internal override void Render(SKSurface surface) { foreach (var shape in Shapes) { surface.Canvas.Save(); var visualMatrix = surface.Canvas.TotalMatrix; visualMatrix = visualMatrix.PreConcat(SKMatrix.CreateTranslation(shape.Offset.X, shape.Offset.Y)); if (shape.Scale != new Vector2(1, 1)) { visualMatrix = visualMatrix.PreConcat(SKMatrix.CreateScale(shape.Scale.X, shape.Scale.Y)); } if (shape.RotationAngleInDegrees != 0) { visualMatrix = visualMatrix.PreConcat(SKMatrix.CreateRotationDegrees(shape.RotationAngleInDegrees, shape.CenterPoint.X, shape.CenterPoint.Y)); } if (shape.TransformMatrix != Matrix3x2.Identity) { visualMatrix = visualMatrix.PreConcat(shape.TransformMatrix.ToSKMatrix44().Matrix); } surface.Canvas.SetMatrix(visualMatrix); shape.Render(surface); surface.Canvas.Restore(); } }
public void RotationToMatrixIsCorrect() { var m = SKMatrix.CreateRotationDegrees(45); var rsm = SKRotationScaleMatrix.CreateRotationDegrees(45, 0, 0).ToMatrix(); Assert.Equal(m.Values, rsm.Values); }
public void ImplicitFromRotationScale() { var rs = SKRotationScaleMatrix.CreateRotationDegrees(45, 0, 0); var matrix = SKMatrix.CreateRotationDegrees(45); var matrix44 = (SKMatrix44)rs.ToMatrix(); Assert.Equal(matrix.Values, matrix44.Matrix.Values); }
public SkiaHatchBrush(ArgbColor color, float angle, float distance = 5, float widh = 2f) { _skPaint = new SKPaint() { PathEffect = SKPathEffect.Create2DLine(widh, Multiply(SKMatrix.CreateRotationDegrees(angle), SKMatrix.CreateScale(1.007f, distance))), Color = color.ToSKColor(), IsAntialias = true, }; }
public void Rotate90(bool left) { float degree = left ? -90 : 90; _rotatedDegrees += degree; Matrix = Matrix.PostConcat(SKMatrix.CreateRotationDegrees(degree)); CanvasView?.InvalidateSurface(); }
public void CanRotatePoints() { var point = new SKPoint(40, -10); var matrix = SKMatrix.CreateRotationDegrees(90); var newPoint = matrix.MapPoint(point); Assert.Equal(10, newPoint.X, PRECISION); Assert.Equal(40, newPoint.Y, PRECISION); }
private void AddRectangle2() { var rectangle = new SkiaSharp.Elements.Rectangle(SKRect.Create(120, 150, 100, 100)) { FillColor = new SKColor(SKColors.SkyBlue.Red, SKColors.SkyBlue.Green, SKColors.SkyBlue.Blue, 200), Transformation = SKMatrix.CreateRotationDegrees(45) }; canvas.Elements.Add(rectangle); }
public static void AddOpenArrow(this SKPath path, SKPoint point, SKPoint normal) { var matrix1 = SKMatrix.CreateRotationDegrees(35); var rotated1 = matrix1.MapVector(normal.X, normal.Y); var matrix2 = SKMatrix.CreateRotationDegrees(-35); var rotated2 = matrix2.MapVector(normal.X, normal.Y); path.MoveTo(point + new SKPoint(rotated1.X * 8, rotated1.Y * 8)); path.LineTo(point); path.LineTo(point + new SKPoint(rotated2.X * 8, rotated2.Y * 8)); }
private static void RenderVisual(SKSurface surface, SKImageInfo info, Visual visual) { if (visual.Opacity != 0) { surface.Canvas.Save(); var visualMatrix = surface.Canvas.TotalMatrix; visualMatrix = visualMatrix.PreConcat(SKMatrix.CreateTranslation(visual.Offset.X, visual.Offset.Y)); if (visual.RotationAngleInDegrees != 0) { visualMatrix = visualMatrix.PreConcat(SKMatrix.CreateRotationDegrees(visual.RotationAngleInDegrees, visual.CenterPoint.X, visual.CenterPoint.Y)); } if (visual.TransformMatrix != Matrix4x4.Identity) { visualMatrix = visualMatrix.PreConcat(visual.TransformMatrix.ToSKMatrix44().Matrix); } surface.Canvas.SetMatrix(visualMatrix); if (visual.Clip is InsetClip insetClip) { surface.Canvas.ClipRect(new SKRect { Top = insetClip.TopInset, Bottom = insetClip.BottomInset, Left = insetClip.LeftInset, Right = insetClip.RightInset }); } visual.Render(surface, info); switch (visual) { case SpriteVisual spriteVisual: foreach (var inner in spriteVisual.Children) { RenderVisual(surface, info, inner); } break; case ContainerVisual containerVisual: foreach (var inner in containerVisual.Children) { RenderVisual(surface, info, inner); } break; } surface.Canvas.Restore(); } }
public void WillFailToTransformWithInvalidTransformation() { var rect = SKRect.Create(10, 10, 100, 100); var offset = rect; offset.Offset(2, 2); var rrect = new SKRoundRect(rect, 5, 5); var transformed = rrect.Transform(SKMatrix.CreateRotationDegrees(30)); Assert.Null(transformed); }
/********************************************************************** *********************************************************************/ // draws an arrow from receiver to sender for the given round static void DrawAckArrow(SKCanvas canvas, int round, SKPaint paint, String part, String dupAck, String cwnd, String tresh) { SKPoint arrowBegin, arrowEnd; arrowBegin = new SKPoint(); arrowEnd = new SKPoint(); switch (part) { case "start": // arrow start arrowBegin = new SKPoint(xEnd * xe, (yStart + yWidthStep * round) * ye); arrowEnd = new SKPoint((xEnd - arrowLength) * xe, (yStart + yWidthStep * round) * ye); break; case "arrive": // arrow arrive arrowBegin = new SKPoint((xStart + arrowLength) * xe, (yStart + yWidthStep * round) * ye); arrowEnd = new SKPoint(xStart * xe, (yStart + yWidthStep * round) * ye); DrawTextDupAck(canvas, round, dupAck, sk_blackTextSmall); DrawTextCwnd(canvas, round, cwnd, sk_blackTextSmall); DrawTextTresh(canvas, round, tresh, sk_blackTextSmall); break; } canvas.DrawLine(arrowBegin, arrowEnd, paint); // arrow head //calculate stuff double a = Math.Abs(arrowBegin.Y - arrowEnd.Y); double b = Math.Abs(arrowBegin.X - arrowEnd.X); //arrow head: calculate rotation degree double cos = a / b; double arcCos = Math.Acos(cos); double degree = arcCos * (180.0 / Math.PI); float rotationDegree = (float)degree - 45f; //arrow head: draw SKPath head = new SKPath(); head.MoveTo(arrowEnd); head.RLineTo(xPercent(0.02f), -xPercent(0.01f)); head.RLineTo(xPercent(0.0f), xPercent(0.02f)); head.Close(); var rotate = SKMatrix.CreateRotationDegrees(rotationDegree, arrowEnd.X, arrowEnd.Y); //head.Transform(rotate); canvas.DrawPath(head, paint); }
private void RenderVisual(SKSurface surface, SKImageInfo info, Visual visual) { if (visual.Opacity != 0 && visual.IsVisible) { surface.Canvas.Save(); var visualMatrix = surface.Canvas.TotalMatrix; visualMatrix = visualMatrix.PreConcat(SKMatrix.CreateTranslation(visual.Offset.X, visual.Offset.Y)); visualMatrix = visualMatrix.PreConcat(SKMatrix.CreateTranslation(visual.AnchorPoint.X, visual.AnchorPoint.Y)); if (visual.RotationAngleInDegrees != 0) { visualMatrix = visualMatrix.PreConcat(SKMatrix.CreateRotationDegrees(visual.RotationAngleInDegrees, visual.CenterPoint.X, visual.CenterPoint.Y)); } if (visual.TransformMatrix != Matrix4x4.Identity) { visualMatrix = visualMatrix.PreConcat(visual.TransformMatrix.ToSKMatrix44().Matrix); } surface.Canvas.SetMatrix(visualMatrix); ApplyClip(surface, visual); using var opacityDisposable = PushOpacity(visual.Opacity); visual.Render(surface, info); switch (visual) { case SpriteVisual spriteVisual: foreach (var inner in spriteVisual.Children) { RenderVisual(surface, info, inner); } break; case ContainerVisual containerVisual: foreach (var inner in containerVisual.Children) { RenderVisual(surface, info, inner); } break; } surface.Canvas.Restore(); } }
private void Play() { new Animation((value) => { canvas.SuspendLayout(); _rectangle.Transformation = SKMatrix.CreateRotationDegrees(360 * (float)value); _rectangle.Location = new SKPoint(_startLocation.X + (100 * (float)value), _startLocation.Y + (100 * (float)value)); canvas.ResumeLayout(true); }) .Commit(this, "Anim", length: 2000, easing: Easing.SpringOut); }
public static SKMatrix ToSKMatrix(this IViewport viewport) { var mapCenterX = (float)viewport.Width * 0.5f; var mapCenterY = (float)viewport.Height * 0.5f; var invertedResolution = 1f / (float)viewport.Resolution; var matrix = SKMatrix.CreateScale(invertedResolution, invertedResolution, mapCenterX, mapCenterY); matrix = SKMatrix.Concat(matrix, SKMatrix.CreateScale(1, -1, 0, -mapCenterY)); // As a consequence images will be up side down :( if (viewport.IsRotated) { matrix = SKMatrix.Concat(matrix, SKMatrix.CreateRotationDegrees((float)-viewport.Rotation)); } matrix = SKMatrix.Concat(matrix, SKMatrix.CreateTranslation((float)-viewport.Center.X, (float)-viewport.Center.Y)); return(matrix); }
internal void RenderVisual(SKSurface surface, Visual visual) { if (visual.Opacity != 0 && visual.IsVisible) { if (visual.ShadowState is { } shadow) { surface.Canvas.SaveLayer(shadow.Paint); } else { surface.Canvas.Save(); } var visualMatrix = surface.Canvas.TotalMatrix; visualMatrix = visualMatrix.PreConcat(SKMatrix.CreateTranslation(visual.Offset.X, visual.Offset.Y)); visualMatrix = visualMatrix.PreConcat(SKMatrix.CreateTranslation(visual.AnchorPoint.X, visual.AnchorPoint.Y)); if (visual.RotationAngleInDegrees != 0) { visualMatrix = visualMatrix.PreConcat(SKMatrix.CreateRotationDegrees(visual.RotationAngleInDegrees, visual.CenterPoint.X, visual.CenterPoint.Y)); } if (visual.TransformMatrix != Matrix4x4.Identity) { visualMatrix = visualMatrix.PreConcat(visual.TransformMatrix.ToSKMatrix44().Matrix); } surface.Canvas.SetMatrix(visualMatrix); ApplyClip(surface, visual); using var opacityDisposable = PushOpacity(visual.Opacity); visual.Render(surface); if (visual is ContainerVisual containerVisual) { var children = containerVisual.GetChildrenInRenderOrder(); for (var i = 0; i < children.Count; i++) { RenderVisual(surface, children[i]); } } surface.Canvas.Restore(); }
/// <inheritdoc /> public override void Render(SKCanvas canvas, SKRect bounds, SKPaint paint) { // TODO: Investigate performance paint.Shader = SKShader.CreateSweepGradient( new SKPoint(bounds.MidX, bounds.MidY), Properties.Colors.BaseValue.GetColorsArray(Properties.ColorsMultiplier), Properties.Colors.BaseValue.GetPositionsArray(Properties.ColorsMultiplier), SKShaderTileMode.Clamp, Properties.StartAngle, Properties.EndAngle, SKMatrix.CreateRotationDegrees(_rotation, bounds.MidX, bounds.MidY) ); canvas.DrawRect(bounds, paint); paint.Shader?.Dispose(); paint.Shader = null; }
void DrawRotatedWithMatrices(SKCanvas canvas, SKPath path, SKPaint fill, SKPaint outline, ChartData item, float degrees, int cx, int cy) { var identity = SKMatrix.CreateIdentity(); var translate = SKMatrix.CreateTranslation(-cx, -cy); var rotate = SKMatrix.CreateRotationDegrees(degrees); //angleBox.Text = degrees.ToString(); var translate2 = SKMatrix.CreateTranslation(cx, cy); SKMatrix.PostConcat(ref identity, translate); SKMatrix.PostConcat(ref identity, rotate); SKMatrix.PostConcat(ref identity, translate2); path.Transform(identity); canvas.DrawPath(path, fill); canvas.DrawPath(path, outline); }
/// <inheritdoc /> public override void Render(SKCanvas canvas, SKRect bounds, SKPaint paint) { SKMatrix matrix = SKMatrix.Concat( SKMatrix.CreateTranslation(_scrollX, _scrollY), SKMatrix.CreateRotationDegrees(Properties.Rotation, bounds.MidX, bounds.MidY) ); paint.Shader = SKShader.CreateLinearGradient( new SKPoint(bounds.Left, bounds.Top), new SKPoint(bounds.Right, bounds.Top), Properties.Colors.BaseValue.GetColorsArray(Properties.ColorsMultiplier), Properties.Colors.BaseValue.GetPositionsArray(Properties.ColorsMultiplier), SKShaderTileMode.Repeat, matrix ); canvas.DrawRect(bounds, paint); paint.Shader?.Dispose(); paint.Shader = null; }
private void Play() { new Animation((value) => { canvas.SuspendLayout(); for (var y = 0; y < 10; y++) { var index = y * 10; for (var x = 0; x < 10; x++) { var ele = canvas.Elements[index + x]; var startX = ((x + 1) * 40); var diffX = startX - 400; if (diffX < 0) { diffX *= -1; } diffX = diffX - startX; startX += 60; var startY = ((y + 1) * 40); var diffY = startY - 400; if (diffY < 0) { diffY *= -1; } diffY = diffY - startY; startY += 60; ele.Location = new SKPoint(startX + (diffX * (float)value), startY + (diffY * (float)value)); ele.Transformation = SKMatrix.CreateRotationDegrees(360 * (float)value); } } canvas.ResumeLayout(true); }) .Commit(this, "Anim", length: 3000, easing: Easing.SpringOut, repeat: () => true); }
public void Draw(SKCanvas canvas, IReadOnlyViewport viewport, IWidget widget, float layerOpacity) { var button = (ButtonWidget)widget; if (button.Picture == null && string.IsNullOrEmpty(button.SvgImage)) { return; } button.Picture ??= button.SvgImage?.LoadSvg(); var picture = button.Picture as SKPicture; if (picture == null) { return; } if (button.Envelope == null) { return; } // Get the scale for picture in each direction var scaleX = (float)(button.Envelope.Width / picture.CullRect.Width); var scaleY = (float)(button.Envelope.Height / picture.CullRect.Height); // Rotate picture var matrix = SKMatrix.CreateRotationDegrees(button.Rotation, picture.CullRect.Width / 2f, picture.CullRect.Height / 2f); // Create a scale matrix matrix = matrix.PostConcat(SKMatrix.CreateScale(scaleX, scaleY)); // Translate picture to right place matrix = matrix.PostConcat(SKMatrix.CreateTranslation((float)button.Envelope.MinX, (float)button.Envelope.MinY)); using var skPaint = new SKPaint { IsAntialias = true }; canvas.DrawPicture(picture, ref matrix, skPaint); }
public static SKMatrix GetMatrix(SvgImage svg, float left, float top, float widthR, float heightR, float rotate = 0) { float canvasMin = Math.Min(widthR, heightR); // get the size of the picture float svgMax = Math.Max(svg.ViewBox.Width, svg.ViewBox.Height); // get the scale to fill the screen float scale = canvasMin / svgMax; var width = svg.ViewBox.Width * scale; var height = svg.ViewBox.Height * scale; var matrix = SKMatrix.Identity; if (rotate > 0) { matrix = matrix.PreConcat(SKMatrix.CreateRotationDegrees(rotate, (left + widthR / 2), (top + heightR / 2))); } matrix = matrix.PreConcat(SKMatrix.CreateTranslation(left + (widthR - width) / 2F, top + (heightR - height) / 2F)); matrix = matrix.PreConcat(SKMatrix.CreateScale(scale, scale)); return(matrix); }
/// <inheritdoc /> public SKPath GetLayerPath(Layer layer, bool includeTranslation, bool includeScale, bool includeRotation, SKPoint?anchorOverride = null) { SKRect layerBounds = GetLayerBounds(layer).ToSKRect(); // Apply transformation like done by the core during layer rendering (same differences apply as in GetLayerTransformGroup) SKPoint anchorPosition = GetLayerAnchorPosition(layer).ToSKPoint(); if (anchorOverride != null) { anchorPosition = anchorOverride.Value; } SKPoint anchorProperty = layer.Transform.AnchorPoint.CurrentValue; // Translation originates from the unscaled center of the shape and is tied to the anchor float x = anchorPosition.X - layerBounds.MidX - anchorProperty.X * layerBounds.Width; float y = anchorPosition.Y - layerBounds.MidY - anchorProperty.Y * layerBounds.Height; SKPath path = new(); path.AddRect(layerBounds); if (includeTranslation) { path.Transform(SKMatrix.CreateTranslation(x, y)); } if (includeScale) { path.Transform(SKMatrix.CreateScale(layer.Transform.Scale.CurrentValue.Width / 100f, layer.Transform.Scale.CurrentValue.Height / 100f, anchorPosition.X, anchorPosition.Y)); } if (includeRotation) { path.Transform(SKMatrix.CreateRotationDegrees(layer.Transform.Rotation.CurrentValue, anchorPosition.X, anchorPosition.Y)); } return(path); }
/// <summary> /// Configure paint wrapper for using gradient brush. /// </summary> /// <param name="paintWrapper">Paint wrapper.</param> /// <param name="targetSize">Target size.</param> /// <param name="gradientBrush">Gradient brush.</param> private void ConfigureGradientBrush(ref PaintWrapper paintWrapper, Size targetSize, IGradientBrush gradientBrush) { var tileMode = gradientBrush.SpreadMethod.ToSKShaderTileMode(); var stopColors = gradientBrush.GradientStops.Select(s => s.Color.ToSKColor()).ToArray(); var stopOffsets = gradientBrush.GradientStops.Select(s => (float)s.Offset).ToArray(); switch (gradientBrush) { case ILinearGradientBrush linearGradient: { 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)) { paintWrapper.Paint.Shader = shader; } break; } case IRadialGradientBrush radialGradient: { var center = radialGradient.Center.ToPixels(targetSize).ToSKPoint(); var radius = (float)(radialGradient.Radius * targetSize.Width); var origin = radialGradient.GradientOrigin.ToPixels(targetSize).ToSKPoint(); if (origin.Equals(center)) { // when the origin is the same as the center the Skia RadialGradient acts the same as D2D using (var shader = SKShader.CreateRadialGradient(center, radius, stopColors, stopOffsets, tileMode)) { paintWrapper.Paint.Shader = shader; } } else { // when the origin is different to the center use a two point ConicalGradient to match the behaviour of D2D // reverse the order of the stops to match D2D var reversedColors = new SKColor[stopColors.Length]; Array.Copy(stopColors, reversedColors, stopColors.Length); Array.Reverse(reversedColors); // and then reverse the reference point of the stops var reversedStops = new float[stopOffsets.Length]; for (var i = 0; i < stopOffsets.Length; i++) { reversedStops[i] = stopOffsets[i]; if (reversedStops[i] > 0 && reversedStops[i] < 1) { reversedStops[i] = Math.Abs(1 - stopOffsets[i]); } } // compose with a background colour of the final stop to match D2D's behaviour of filling with the final color using (var shader = SKShader.CreateCompose( SKShader.CreateColor(reversedColors[0]), SKShader.CreateTwoPointConicalGradient(center, radius, origin, 0, reversedColors, reversedStops, tileMode) )) { paintWrapper.Paint.Shader = shader; } } break; } case IConicGradientBrush conicGradient: { var center = conicGradient.Center.ToPixels(targetSize).ToSKPoint(); // Skia's default is that angle 0 is from the right hand side of the center point // but we are matching CSS where the vertical point above the center is 0. var angle = (float)(conicGradient.Angle - 90); var rotation = SKMatrix.CreateRotationDegrees(angle, center.X, center.Y); using (var shader = SKShader.CreateSweepGradient(center, stopColors, stopOffsets, rotation)) { paintWrapper.Paint.Shader = shader; } break; } } }
public bool Draw(SKCanvas canvas, IReadOnlyViewport viewport, ILayer layer, IFeature feature, IStyle istyle, ISymbolCache symbolCache) { var style = ((TiledBitmapStyle)istyle); if (style.image == null) { return(false); } var position = feature.Geometry.BoundingBox.Centroid; var dest = viewport.WorldToScreen(position); var zoom = 1 / (float)viewport.Resolution; canvas.Translate((float)dest.X, (float)dest.Y); canvas.Scale(zoom, zoom); canvas.RotateDegrees((float)viewport.Rotation, 0.0f, 0.0f); if (style.rotation != 0) { canvas.RotateDegrees(style.rotation, 0.0f, 0.0f); } //#TODO store paint with shader in the style using (SKPaint paint = new SKPaint()) { if (style.rotation == 0) //Weird artifacting on 0 rotation, no idea why. Seems Skia bug. { style.rotation = 180; } SKMatrix shaderTransform = SKMatrix.CreateScale((float)viewport.Resolution, (float)viewport.Resolution); if (style.rotation != 0) { shaderTransform = SKMatrix.Concat(shaderTransform, SKMatrix.CreateRotationDegrees(-style.rotation)); } paint.Shader = SKShader.CreateImage(style.image, SKShaderTileMode.Repeat, SKShaderTileMode.Repeat, shaderTransform); paint.ColorFilter = style.colorFilter; //style.image.Encode().SaveTo(File.Create($"P:/{style.image.UniqueId}.png")); if (style.ellipse) { canvas.DrawOval(0, 0, style.rect.Right, style.rect.Bottom, paint); } else { canvas.DrawRect(style.rect, paint); } if (style.border) { var borderPaint = new SKPaint { Style = SKPaintStyle.Stroke, Color = style.color }; if (style.ellipse) { canvas.DrawOval(0, 0, style.rect.Right, style.rect.Bottom, borderPaint); } else { canvas.DrawRect(style.rect, borderPaint); } } } return(true); }
public static SKMatrix CreateRotationDegrees(float angle, SKPoint p) { return(SKMatrix.CreateRotationDegrees(angle, p.X, p.Y)); }
/// <summary> /// Get arrow path for the specified annotation position /// </summary> /// <param name="graphics"></param> /// <param name="position"></param> /// <returns></returns> private SKPath GetArrowPath( ChartGraphics graphics, SKRect position) { // Get absolute position SKRect positionAbs = graphics.GetAbsoluteRectangle(position); SKPoint firstPoint = positionAbs.Location; SKPoint secondPoint = new(positionAbs.Right, positionAbs.Bottom); // Calculate arrow length float deltaX = secondPoint.X - firstPoint.X; float deltaY = secondPoint.Y - firstPoint.Y; float arrowLength = (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY); // Create unrotated graphics path for the arrow started at the annotation location // and going to the right for the length of the rotated arrow. SKPath path = new(); float pointerRatio = 2.1f; SKPoint[] points; if (ArrowStyle == ArrowStyle.Simple) { points = new SKPoint[] { firstPoint, new SKPoint(firstPoint.X + ArrowSize * pointerRatio, firstPoint.Y - ArrowSize * pointerRatio), new SKPoint(firstPoint.X + ArrowSize * pointerRatio, firstPoint.Y - ArrowSize), new SKPoint(firstPoint.X + arrowLength, firstPoint.Y - ArrowSize), new SKPoint(firstPoint.X + arrowLength, firstPoint.Y + ArrowSize), new SKPoint(firstPoint.X + ArrowSize * pointerRatio, firstPoint.Y + ArrowSize), new SKPoint(firstPoint.X + ArrowSize * pointerRatio, firstPoint.Y + ArrowSize * pointerRatio) }; } else if (ArrowStyle == ArrowStyle.DoubleArrow) { points = new SKPoint[] { firstPoint, new SKPoint(firstPoint.X + ArrowSize * pointerRatio, firstPoint.Y - ArrowSize * pointerRatio), new SKPoint(firstPoint.X + ArrowSize * pointerRatio, firstPoint.Y - ArrowSize), new SKPoint(firstPoint.X + arrowLength - ArrowSize * pointerRatio, firstPoint.Y - ArrowSize), new SKPoint(firstPoint.X + arrowLength - ArrowSize * pointerRatio, firstPoint.Y - ArrowSize * pointerRatio), new SKPoint(firstPoint.X + arrowLength, firstPoint.Y), new SKPoint(firstPoint.X + arrowLength - ArrowSize * pointerRatio, firstPoint.Y + ArrowSize * pointerRatio), new SKPoint(firstPoint.X + arrowLength - ArrowSize * pointerRatio, firstPoint.Y + ArrowSize), new SKPoint(firstPoint.X + ArrowSize * pointerRatio, firstPoint.Y + ArrowSize), new SKPoint(firstPoint.X + ArrowSize * pointerRatio, firstPoint.Y + ArrowSize * pointerRatio) }; } else if (ArrowStyle == ArrowStyle.Tailed) { float tailRatio = 2.1f; points = new SKPoint[] { firstPoint, new SKPoint(firstPoint.X + ArrowSize * pointerRatio, firstPoint.Y - ArrowSize * pointerRatio), new SKPoint(firstPoint.X + ArrowSize * pointerRatio, firstPoint.Y - ArrowSize), new SKPoint(firstPoint.X + arrowLength, firstPoint.Y - ArrowSize * tailRatio), new SKPoint(firstPoint.X + arrowLength - ArrowSize * tailRatio, firstPoint.Y), new SKPoint(firstPoint.X + arrowLength, firstPoint.Y + ArrowSize * tailRatio), new SKPoint(firstPoint.X + ArrowSize * pointerRatio, firstPoint.Y + ArrowSize), new SKPoint(firstPoint.X + ArrowSize * pointerRatio, firstPoint.Y + ArrowSize * pointerRatio) }; } else { throw (new InvalidOperationException(SR.ExceptionAnnotationArrowStyleUnknown)); } path.AddLines(points); path.Close(); // Calculate arrow angle float angle = (float)(Math.Atan(deltaY / deltaX) * 180f / Math.PI); if (deltaX < 0) { angle += 180f; } // Rotate arrow path around the first point SKMatrix matrix = SKMatrix.CreateRotationDegrees(angle, firstPoint.X, firstPoint.Y); path.Transform(matrix); return(path); }