Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 38 additions & 5 deletions src/ShimSkiaSharp/SKPathBoundsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,11 @@ public static void AddCubicBounds(SKPoint p0, SKPoint p1, SKPoint p2, SKPoint p3

public static void AddArcBounds(SKPoint p0, SKPoint p1, float rx, float ry, float angle, SKPathArcSize largeArc, SKPathDirection sweep, ref SKRect bounds)
{
ComputePointBounds(p0.X, p0.Y, ref bounds);
ComputePointBounds(p1.X, p1.Y, ref bounds);

if (rx <= 0f || ry <= 0f)
{
ComputePointBounds(p0.X, p0.Y, ref bounds);
ComputePointBounds(p1.X, p1.Y, ref bounds);
return;
}

Expand Down Expand Up @@ -192,10 +193,42 @@ public static void AddArcBounds(SKPoint p0, SKPoint p1, float rx, float ry, floa
else if (sweepFlag && deltaAngle < 0)
deltaAngle += 2f * (float)Math.PI;

const int segments = 20;
for (var i = 0; i <= segments; i++)
static float NormalizeAngle(float a)
{
var twoPi = 2f * (float)Math.PI;
a %= twoPi;
if (a < 0f)
a += twoPi;
return a;
}

static bool IsAngleOnArc(float angle, float start, float sweep)
{
var theta = startAngle + deltaAngle * i / segments;
var normStart = NormalizeAngle(start);
var normEnd = NormalizeAngle(start + sweep);
var normAngle = NormalizeAngle(angle);

if (sweep >= 0f)
{
if (normStart <= normEnd)
return normAngle >= normStart && normAngle <= normEnd;
return normAngle >= normStart || normAngle <= normEnd;
}
else
{
if (normEnd <= normStart)
return normAngle <= normStart && normAngle >= normEnd;
return normAngle <= normStart || normAngle >= normEnd;
}
}

var candidates = new float[] { 0f, (float)Math.PI / 2f, (float)Math.PI, 3f * (float)Math.PI / 2f };

foreach (var theta in candidates)
{
if (!IsAngleOnArc(theta, startAngle, deltaAngle))
continue;

var cosTheta = (float)Math.Cos(theta);
var sinTheta = (float)Math.Sin(theta);
var x = cosPhi * rx * cosTheta - sinPhi * ry * sinTheta + cx;
Expand Down
7 changes: 7 additions & 0 deletions src/Svg.Controls.Avalonia/AvaloniaSvgAssetLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,20 @@

namespace Avalonia.Svg;

/// <summary>
/// Asset loader implementation using Avalonia types.
/// </summary>
public class AvaloniaSvgAssetLoader : SM.ISvgAssetLoader
{
/// <inheritdoc />
public SKImage LoadImage(Stream stream)
{
var data = SKImage.FromStream(stream);
using var image = new AMI.Bitmap(stream);
return new SKImage {Data = data, Width = (float)image.Size.Width, Height = (float)image.Size.Height};
}

/// <inheritdoc />
public List<SM.TypefaceSpan> FindTypefaces(string? text, ShimSkiaSharp.SKPaint paintPreferredTypeface)
{
var ret = new List<SM.TypefaceSpan>();
Expand Down Expand Up @@ -123,6 +128,7 @@ runningTypeface is not { } typeface
return ret;
}

/// <inheritdoc />
public SKFontMetrics GetFontMetrics(SKPaint paint)
{
var typeface = paint.Typeface.ToTypeface() ?? Typeface.Default;
Expand All @@ -138,6 +144,7 @@ public SKFontMetrics GetFontMetrics(SKPaint paint)
};
}

/// <inheritdoc />
public float MeasureText(string? text, SKPaint paint, ref SKRect bounds)
{
if (string.IsNullOrEmpty(text))
Expand Down
34 changes: 28 additions & 6 deletions src/Svg.Controls.Skia.Avalonia/SvgSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,16 @@ public sealed class SvgSource : IDisposable

public string? Css { get; init; }

public SKSvg? Svg => _skSvg;
public SKSvg? Svg
{
get
{
lock (Sync)
{
return _skSvg;
}
}
}

public SvgParameters? Parameters => _originalParameters;

Expand All @@ -50,7 +59,8 @@ public SKPicture? Picture
{
if (_picture is null && Path is not null)
{
_picture = LoadImpl(this, Path, _baseUri, new SvgParameters(Entities, Css));
var entitiesCopy = Entities is null ? null : new Dictionary<string, string>(Entities);
_picture = LoadImpl(this, Path, _baseUri, new SvgParameters(entitiesCopy, Css));
}

return _picture;
Expand Down Expand Up @@ -114,7 +124,10 @@ static SvgSource()

var skSvg = new SKSvg();
skSvg.Load(path, parameters);
source._skSvg = skSvg;
lock (source.Sync)
{
source._skSvg = skSvg;
}
return skSvg.Picture;
}

Expand All @@ -133,7 +146,10 @@ static SvgSource()

var skSvg = new SKSvg();
skSvg.Load(source._originalStream, parameters);
source._skSvg = skSvg;
lock (source.Sync)
{
source._skSvg = skSvg;
}
return skSvg.Picture;
}

Expand Down Expand Up @@ -231,7 +247,10 @@ static SvgSource()
var source = new SvgSource(default(Uri));
source._picture = FromSvg(svg);
// loading from SVG string does not store SKSvg instance
source._skSvg = null;
lock (source.Sync)
{
source._skSvg = null;
}
return source;
}

Expand All @@ -257,7 +276,10 @@ static SvgSource()
{
var source = new SvgSource(default(Uri));
source._picture = FromSvgDocument(document);
source._skSvg = null;
lock (source.Sync)
{
source._skSvg = null;
}
return source;
}

Expand Down
58 changes: 58 additions & 0 deletions src/Svg.Skia/SKSvg.HitTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ namespace Svg.Skia;

public partial class SKSvg
{
/// <summary>
/// Returns drawables that hit-test against a point in picture coordinates.
/// </summary>
/// <param name="point">Point in picture coordinate space.</param>
/// <returns>Enumerable of drawables containing the point.</returns>
public IEnumerable<DrawableBase> HitTestDrawables(SKPoint point)
{
if (Drawable is DrawableBase drawable)
Expand All @@ -20,6 +25,11 @@ public IEnumerable<DrawableBase> HitTestDrawables(SKPoint point)
}
}

/// <summary>
/// Returns drawables that intersect with a rectangle in picture coordinates.
/// </summary>
/// <param name="rect">Rectangle in picture coordinate space.</param>
/// <returns>Enumerable of drawables intersecting the rectangle.</returns>
public IEnumerable<DrawableBase> HitTestDrawables(SKRect rect)
{
if (Drawable is DrawableBase drawable)
Expand All @@ -31,6 +41,11 @@ public IEnumerable<DrawableBase> HitTestDrawables(SKRect rect)
}
}

/// <summary>
/// Returns SVG elements that hit-test against a point in picture coordinates.
/// </summary>
/// <param name="point">Point in picture coordinate space.</param>
/// <returns>Enumerable of elements containing the point.</returns>
public IEnumerable<SvgElement> HitTestElements(SKPoint point)
{
if (Drawable is DrawableBase drawable)
Expand All @@ -42,6 +57,11 @@ public IEnumerable<SvgElement> HitTestElements(SKPoint point)
}
}

/// <summary>
/// Returns SVG elements that intersect with a rectangle in picture coordinates.
/// </summary>
/// <param name="rect">Rectangle in picture coordinate space.</param>
/// <returns>Enumerable of elements intersecting the rectangle.</returns>
public IEnumerable<SvgElement> HitTestElements(SKRect rect)
{
if (Drawable is DrawableBase drawable)
Expand All @@ -53,6 +73,13 @@ public IEnumerable<SvgElement> HitTestElements(SKRect rect)
}
}

/// <summary>
/// Converts a point from canvas coordinates to picture coordinates.
/// </summary>
/// <param name="point">The point in canvas coordinate space.</param>
/// <param name="canvasMatrix">Current canvas transform.</param>
/// <param name="picturePoint">Resulting point in picture coordinates.</param>
/// <returns><c>true</c> if conversion succeeded.</returns>
public bool TryGetPicturePoint(SKPoint point, SKMatrix canvasMatrix, out SKPoint picturePoint)
{
if (!canvasMatrix.TryInvert(out var inverse))
Expand All @@ -65,6 +92,13 @@ public bool TryGetPicturePoint(SKPoint point, SKMatrix canvasMatrix, out SKPoint
return true;
}

/// <summary>
/// Converts a rectangle from canvas coordinates to picture coordinates.
/// </summary>
/// <param name="rect">The rectangle in canvas coordinate space.</param>
/// <param name="canvasMatrix">Current canvas transform.</param>
/// <param name="pictureRect">Resulting rectangle in picture coordinates.</param>
/// <returns><c>true</c> if conversion succeeded.</returns>
public bool TryGetPictureRect(SKRect rect, SKMatrix canvasMatrix, out SKRect pictureRect)
{
if (!canvasMatrix.TryInvert(out var inverse))
Expand All @@ -78,6 +112,12 @@ public bool TryGetPictureRect(SKRect rect, SKMatrix canvasMatrix, out SKRect pic
return true;
}

/// <summary>
/// Returns drawables that hit-test against a point in canvas coordinates.
/// </summary>
/// <param name="point">Point in canvas coordinate space.</param>
/// <param name="canvasMatrix">Current canvas transform.</param>
/// <returns>Enumerable of drawables containing the point.</returns>
public IEnumerable<DrawableBase> HitTestDrawables(SKPoint point, SKMatrix canvasMatrix)
{
if (TryGetPicturePoint(point, canvasMatrix, out var pp))
Expand All @@ -89,6 +129,12 @@ public IEnumerable<DrawableBase> HitTestDrawables(SKPoint point, SKMatrix canvas
}
}

/// <summary>
/// Returns drawables that intersect with a rectangle in canvas coordinates.
/// </summary>
/// <param name="rect">Rectangle in canvas coordinate space.</param>
/// <param name="canvasMatrix">Current canvas transform.</param>
/// <returns>Enumerable of drawables intersecting the rectangle.</returns>
public IEnumerable<DrawableBase> HitTestDrawables(SKRect rect, SKMatrix canvasMatrix)
{
if (TryGetPictureRect(rect, canvasMatrix, out var pr))
Expand All @@ -100,6 +146,12 @@ public IEnumerable<DrawableBase> HitTestDrawables(SKRect rect, SKMatrix canvasMa
}
}

/// <summary>
/// Returns SVG elements that hit-test against a point in canvas coordinates.
/// </summary>
/// <param name="point">Point in canvas coordinate space.</param>
/// <param name="canvasMatrix">Current canvas transform.</param>
/// <returns>Enumerable of elements containing the point.</returns>
public IEnumerable<SvgElement> HitTestElements(SKPoint point, SKMatrix canvasMatrix)
{
if (TryGetPicturePoint(point, canvasMatrix, out var pp))
Expand All @@ -111,6 +163,12 @@ public IEnumerable<SvgElement> HitTestElements(SKPoint point, SKMatrix canvasMat
}
}

/// <summary>
/// Returns SVG elements that intersect with a rectangle in canvas coordinates.
/// </summary>
/// <param name="rect">Rectangle in canvas coordinate space.</param>
/// <param name="canvasMatrix">Current canvas transform.</param>
/// <returns>Enumerable of elements intersecting the rectangle.</returns>
public IEnumerable<SvgElement> HitTestElements(SKRect rect, SKMatrix canvasMatrix)
{
if (TryGetPictureRect(rect, canvasMatrix, out var pr))
Expand Down
2 changes: 1 addition & 1 deletion src/Svg.Skia/SKSvg.Model.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,6 @@ private void Reset()
public void Dispose()
{
Reset();
_originalStream?.Dispose();
_originalStream?.Dispose();
}
}
11 changes: 11 additions & 0 deletions src/Svg.Skia/SkiaSvgAssetLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,31 @@

namespace Svg.Skia;

/// <summary>
/// Asset loader implementation using SkiaSharp types.
/// </summary>
public class SkiaSvgAssetLoader : Model.ISvgAssetLoader
{
private readonly SkiaModel _skiaModel;

/// <summary>
/// Initializes a new instance of <see cref="SkiaSvgAssetLoader"/>.
/// </summary>
/// <param name="skiaModel">Model used to convert font data.</param>
public SkiaSvgAssetLoader(SkiaModel skiaModel)
{
_skiaModel = skiaModel;
}

/// <inheritdoc />
public ShimSkiaSharp.SKImage LoadImage(System.IO.Stream stream)
{
var data = ShimSkiaSharp.SKImage.FromStream(stream);
using var image = SkiaSharp.SKImage.FromEncodedData(data);
return new ShimSkiaSharp.SKImage {Data = data, Width = image.Width, Height = image.Height};
}

/// <inheritdoc />
public List<Model.TypefaceSpan> FindTypefaces(string? text, ShimSkiaSharp.SKPaint paintPreferredTypeface)
{
var ret = new List<Model.TypefaceSpan>();
Expand Down Expand Up @@ -107,6 +116,7 @@ runningPaint.Typeface is null
return ret;
}

/// <inheritdoc />
public ShimSkiaSharp.SKFontMetrics GetFontMetrics(ShimSkiaSharp.SKPaint paint)
{
using var skPaint = _skiaModel.ToSKPaint(paint);
Expand All @@ -126,6 +136,7 @@ public ShimSkiaSharp.SKFontMetrics GetFontMetrics(ShimSkiaSharp.SKPaint paint)
};
}

/// <inheritdoc />
public float MeasureText(string? text, ShimSkiaSharp.SKPaint paint, ref ShimSkiaSharp.SKRect bounds)
{
using var skPaint = _skiaModel.ToSKPaint(paint);
Expand Down
Loading