Skip to content

Commit 2fadcbc

Browse files
Update refs and add tests
1 parent 16c34fa commit 2fadcbc

24 files changed

Lines changed: 112 additions & 39 deletions

File tree

src/ImageSharp.Drawing/ImageSharp.Drawing.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
<None Include="..\..\shared-infrastructure\branding\icons\imagesharp.drawing\sixlabors.imagesharp.drawing.128.png" Pack="true" PackagePath="" />
1818
</ItemGroup>
1919
<ItemGroup>
20-
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta16" />
21-
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.1-alpha.0.1" />
20+
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta16.13" />
21+
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.1-alpha.0.2" />
2222
</ItemGroup>
2323
<Import Project="..\..\shared-infrastructure\src\SharedInfrastructure\SharedInfrastructure.projitems" Label="Shared" />
2424
</Project>

src/ImageSharp.Drawing/Shapes/Path.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,11 @@ public static bool TryParseSvgPath(ReadOnlySpan<char> svgPath, out IPath value)
274274
svgPath = FindScaler(svgPath, out float largeArc);
275275
svgPath = TrimSeparator(svgPath);
276276
svgPath = FindScaler(svgPath, out float sweep);
277-
278277
svgPath = FindPoint(svgPath, out PointF point, relative, c);
279-
if (svgPath.Length > 0)
278+
279+
// TODO: Skia compares the input SVG with the chars not the length.
280+
// Maybe we can do something with SpanAction<T>?
281+
// if (svgPath.Length > 0)
280282
{
281283
builder.ArcTo(radiiX, radiiY, angle, largeArc == 1, sweep == 1, point);
282284
c = point;

src/ImageSharp.Drawing/Shapes/PathBuilder.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,8 @@ public void Clear()
451451
this.currentFigure = new Figure();
452452
this.figures.Clear();
453453
this.figures.Add(this.currentFigure);
454+
455+
// TODO: Should we reset currentPoint here instead?
454456
}
455457

456458
private class Figure

src/ImageSharp.Drawing/Shapes/Text/BaseGlyphBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ bool IGlyphRenderer.BeginGlyph(FontRectangle bounds, GlyphRendererParameters par
5959
/// <param name="point">The point.</param>
6060
void IGlyphRenderer.CubicBezierTo(Vector2 secondControlPoint, Vector2 thirdControlPoint, Vector2 point)
6161
{
62-
this.Builder.AddBezier(this.currentPoint, secondControlPoint, thirdControlPoint, point);
62+
this.Builder.AddCubicBezier(this.currentPoint, secondControlPoint, thirdControlPoint, point);
6363
this.currentPoint = point;
6464
}
6565

@@ -100,7 +100,7 @@ void IGlyphRenderer.MoveTo(Vector2 point)
100100
/// <param name="point">The point.</param>
101101
void IGlyphRenderer.QuadraticBezierTo(Vector2 secondControlPoint, Vector2 point)
102102
{
103-
this.Builder.AddBezier(this.currentPoint, secondControlPoint, point);
103+
this.Builder.AddQuadraticBezier(this.currentPoint, secondControlPoint, point);
104104
this.currentPoint = point;
105105
}
106106

src/ImageSharp.Drawing/Shapes/Text/PathGlyphBuilder.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ internal sealed class PathGlyphBuilder : GlyphBuilder
1616
private const float Pi = MathF.PI;
1717
private readonly IPathInternals path;
1818
private Vector2 textOffset;
19-
private TextOptions textOptions;
19+
private readonly TextOptions textOptions;
2020

2121
/// <summary>
2222
/// Initializes a new instance of the <see cref="PathGlyphBuilder"/> class.
@@ -72,9 +72,9 @@ private void TransformGlyph(FontRectangle bounds)
7272
SegmentInfo pathPoint = this.path.PointAlongPath(bounds.Left + halfWidth);
7373

7474
// Now offset our target point since we're aligning the bottom-left location of our glyph against the path.
75-
// TODO: This is good and accurate when we are vertically aligned to the path however the distance between
75+
// This is good and accurate when we are vertically aligned to the path however the distance between
7676
// characters in multiline text scales with the angle and vertical offset.
77-
// It would be good to be able to fix this.
77+
// This is expected and consistant with other libraries. Multiple line text should be rendered using multiple paths to avoid this behavior.
7878
Vector2 targetPoint = (Vector2)pathPoint.Point + new Vector2(-halfWidth, bounds.Top) - bounds.Location - this.textOffset;
7979

8080
// Due to how matrix combining works you have to combine this in the reverse order of operation.

tests/ImageSharp.Drawing.Tests/Drawing/Text/DrawTextOnImageTests.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,60 @@ public void CanRotateOutlineFont_Issue175<TPixel>(
494494
appendSourceFileOrDescription: true);
495495
}
496496

497+
[Theory]
498+
[WithBlankImage(100, 100, PixelTypes.Rgba32, "M10,90 Q90,90 90,45 Q90,10 50,10 Q10,10 10,40 Q10,70 45,70 Q70,70 75,50", "spiral")]
499+
[WithBlankImage(350, 350, PixelTypes.Rgba32, "M275 175 A100 100 0 1 1 275 174", "circle")]
500+
[WithBlankImage(120, 120, PixelTypes.Rgba32, "M50,10 L 90 90 L 10 90 L50 10", "triangle")]
501+
public void CanDrawTextAlongPathHorizontal<TPixel>(TestImageProvider<TPixel> provider, string svgPath, string exampleImageKey)
502+
where TPixel : unmanaged, IPixel<TPixel>
503+
{
504+
bool parsed = Path.TryParseSvgPath(svgPath, out IPath path);
505+
Assert.True(parsed);
506+
507+
Font font = CreateFont(TestFonts.OpenSans, 13);
508+
TextOptions textOptions = new(font)
509+
{
510+
WrappingLength = path.ComputeLength(),
511+
VerticalAlignment = VerticalAlignment.Bottom,
512+
HorizontalAlignment = HorizontalAlignment.Left,
513+
};
514+
515+
const string text = "Quick brown fox jumps over the lazy dog.";
516+
IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, path, textOptions);
517+
518+
provider.RunValidatingProcessorTest(
519+
c => c.Fill(Color.White).Draw(Color.Red, 1, path).Fill(Color.Black, glyphs),
520+
new { type = exampleImageKey },
521+
comparer: ImageComparer.TolerantPercentage(0.002f));
522+
}
523+
524+
[Theory]
525+
[WithBlankImage(350, 350, PixelTypes.Rgba32, "M225 175 A50 50 0 1 1 225 174", "circle")]
526+
[WithBlankImage(250, 250, PixelTypes.Rgba32, "M100,60 L 140 140 L 60 140 L100 60", "triangle")]
527+
public void CanDrawTextAlongPathVertical<TPixel>(TestImageProvider<TPixel> provider, string svgPath, string exampleImageKey)
528+
where TPixel : unmanaged, IPixel<TPixel>
529+
{
530+
bool parsed = Path.TryParseSvgPath(svgPath, out IPath path);
531+
Assert.True(parsed);
532+
533+
Font font = CreateFont(TestFonts.OpenSans, 13);
534+
TextOptions textOptions = new(font)
535+
{
536+
WrappingLength = path.ComputeLength() / 4,
537+
VerticalAlignment = VerticalAlignment.Bottom,
538+
HorizontalAlignment = HorizontalAlignment.Left,
539+
LayoutMode = LayoutMode.VerticalLeftRight
540+
};
541+
542+
const string text = "Quick brown fox jumps over the lazy dog.";
543+
IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, path, textOptions);
544+
545+
provider.RunValidatingProcessorTest(
546+
c => c.Fill(Color.White).Draw(Color.Red, 1, path).Fill(Color.Black, glyphs),
547+
new { type = exampleImageKey },
548+
comparer: ImageComparer.TolerantPercentage(0.002f));
549+
}
550+
497551
private static string Repeat(string str, int times) => string.Concat(Enumerable.Repeat(str, times));
498552

499553
private static string ToTestOutputDisplayText(string text)

tests/ImageSharp.Drawing.Tests/Shapes/SvgPath.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ public class SvgPath
1313
[Theory]
1414
[WithBlankImage(110, 70, PixelTypes.Rgba32, "M20,30 L40,5 L60,30 L80, 55 L100, 30", "zag")]
1515
[WithBlankImage(110, 50, PixelTypes.Rgba32, "M20,30 Q40,5 60,30 T100,30", "wave")]
16-
[WithBlankImage(500, 400, PixelTypes.Rgba32, @"M10,350 l 50,-25 a25,25 -30 0,1 50,-25 l 50,-25 a25,50 -30 0,1 50,-25 l 50,-25 a25,75 -30 0,1 50,-25 l 50,-25 a25,100 -30 0,1 50,-25 l 50,-25", "bumpy")]
17-
[WithBlankImage(500, 400, PixelTypes.Rgba32, @"M300,200 h-150 a150,150 0 1,0 150,-150 z", "pie_small")]
18-
[WithBlankImage(500, 400, PixelTypes.Rgba32, @"M275,175 v-150 a150,150 0 0,0 -150,150 z", "pie_big")]
19-
[WithBlankImage(100, 100, PixelTypes.Rgba32, @"M50,50 L50,20 L80,50 z M40,60 L40,90 L10,60 z", "arrows")]
20-
[WithBlankImage(500, 400, PixelTypes.Rgba32, @"M 10 315 L 110 215 A 30 50 0 0 1 162.55 162.45 L 172.55 152.45 A 30 50 -45 0 1 215.1 109.9 L 315 10", "chopped_oval")]
16+
[WithBlankImage(500, 400, PixelTypes.Rgba32, "M10,350 l 50,-25 a25,25 -30 0,1 50,-25 l 50,-25 a25,50 -30 0,1 50,-25 l 50,-25 a25,75 -30 0,1 50,-25 l 50,-25 a25,100 -30 0,1 50,-25 l 50,-25", "bumpy")]
17+
[WithBlankImage(500, 400, PixelTypes.Rgba32, "M300,200 h-150 a150,150 0 1,0 150,-150 z", "pie_small")]
18+
[WithBlankImage(500, 400, PixelTypes.Rgba32, "M275,175 v-150 a150,150 0 0,0 -150,150 z", "pie_big")]
19+
[WithBlankImage(100, 100, PixelTypes.Rgba32, "M50,50 L50,20 L80,50 z M40,60 L40,90 L10,60 z", "arrows")]
20+
[WithBlankImage(500, 400, PixelTypes.Rgba32, "M 10 315 L 110 215 A 30 50 0 0 1 162.55 162.45 L 172.55 152.45 A 30 50 -45 0 1 215.1 109.9 L 315 10", "chopped_oval")]
2121
public void RenderSvgPath<TPixel>(TestImageProvider<TPixel> provider, string svgPath, string exampleImageKey)
2222
where TPixel : unmanaged, IPixel<TPixel>
2323
{
24-
var parsed = Path.TryParseSvgPath(svgPath, out var path);
24+
bool parsed = Path.TryParseSvgPath(svgPath, out IPath path);
2525
Assert.True(parsed);
2626

2727
provider.RunValidatingProcessorTest(
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)