diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
index a9afe37f89..9d24c1777e 100644
--- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
+++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj
@@ -36,7 +36,7 @@
-
+
All
diff --git a/src/ImageSharp.Drawing/Paths/DrawPathCollection.cs b/src/ImageSharp.Drawing/Paths/DrawPathCollection.cs
index 5cb499415f..877737653d 100644
--- a/src/ImageSharp.Drawing/Paths/DrawPathCollection.cs
+++ b/src/ImageSharp.Drawing/Paths/DrawPathCollection.cs
@@ -30,7 +30,7 @@ namespace ImageSharp
{
foreach (IPath path in paths)
{
- source.Draw(pen, new ShapePath(path), options);
+ source.Draw(pen, path, options);
}
return source;
diff --git a/src/ImageSharp.Drawing/Text/DrawText.Path.cs b/src/ImageSharp.Drawing/Text/DrawText.Path.cs
new file mode 100644
index 0000000000..2bc23b64bc
--- /dev/null
+++ b/src/ImageSharp.Drawing/Text/DrawText.Path.cs
@@ -0,0 +1,200 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System.Numerics;
+
+ using Drawing;
+ using Drawing.Brushes;
+ using Drawing.Pens;
+ using ImageSharp.PixelFormats;
+ using SixLabors.Fonts;
+ using SixLabors.Shapes;
+
+ ///
+ /// Extension methods for the type.
+ ///
+ public static partial class ImageExtensions
+ {
+ ///
+ /// Draws the text onto the the image filled via the brush.
+ ///
+ /// The type of the color.
+ /// The image this method extends.
+ /// The text.
+ /// The font.
+ /// The color.
+ /// The path.
+ ///
+ /// The .
+ ///
+ public static Image DrawText(this Image source, string text, Font font, TPixel color, IPath path)
+ where TPixel : struct, IPixel
+ {
+ return source.DrawText(text, font, color, path, TextGraphicsOptions.Default);
+ }
+
+ ///
+ /// Draws the text onto the the image filled via the brush.
+ ///
+ /// The type of the color.
+ /// The image this method extends.
+ /// The text.
+ /// The font.
+ /// The color.
+ /// The path.
+ /// The options.
+ ///
+ /// The .
+ ///
+ public static Image DrawText(this Image source, string text, Font font, TPixel color, IPath path, TextGraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ return source.DrawText(text, font, Brushes.Solid(color), null, path, options);
+ }
+
+ ///
+ /// Draws the text onto the the image filled via the brush.
+ ///
+ /// The type of the color.
+ /// The image this method extends.
+ /// The text.
+ /// The font.
+ /// The brush.
+ /// The location.
+ ///
+ /// The .
+ ///
+ public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPath path)
+ where TPixel : struct, IPixel
+ {
+ return source.DrawText(text, font, brush, path, TextGraphicsOptions.Default);
+ }
+
+ ///
+ /// Draws the text onto the the image filled via the brush.
+ ///
+ /// The type of the color.
+ /// The image this method extends.
+ /// The text.
+ /// The font.
+ /// The brush.
+ /// The path.
+ /// The options.
+ ///
+ /// The .
+ ///
+ public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPath path, TextGraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ return source.DrawText(text, font, brush, null, path, options);
+ }
+
+ ///
+ /// Draws the text onto the the image outlined via the pen.
+ ///
+ /// The type of the color.
+ /// The image this method extends.
+ /// The text.
+ /// The font.
+ /// The pen.
+ /// The path.
+ ///
+ /// The .
+ ///
+ public static Image DrawText(this Image source, string text, Font font, IPen pen, IPath path)
+ where TPixel : struct, IPixel
+ {
+ return source.DrawText(text, font, pen, path, TextGraphicsOptions.Default);
+ }
+
+ ///
+ /// Draws the text onto the the image outlined via the pen.
+ ///
+ /// The type of the color.
+ /// The image this method extends.
+ /// The text.
+ /// The font.
+ /// The pen.
+ /// The path.
+ /// The options.
+ ///
+ /// The .
+ ///
+ public static Image DrawText(this Image source, string text, Font font, IPen pen, IPath path, TextGraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ return source.DrawText(text, font, null, pen, path, options);
+ }
+
+ ///
+ /// Draws the text onto the the image filled via the brush then outlined via the pen.
+ ///
+ /// The type of the color.
+ /// The image this method extends.
+ /// The text.
+ /// The font.
+ /// The brush.
+ /// The pen.
+ /// The path.
+ ///
+ /// The .
+ ///
+ public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, IPath path)
+ where TPixel : struct, IPixel
+ {
+ return source.DrawText(text, font, brush, pen, path, TextGraphicsOptions.Default);
+ }
+
+ ///
+ /// Draws the text onto the the image filled via the brush then outlined via the pen.
+ ///
+ /// The type of the color.
+ /// The image this method extends.
+ /// The text.
+ /// The font.
+ /// The brush.
+ /// The pen.
+ /// The path.
+ /// The options.
+ ///
+ /// The .
+ ///
+ public static Image DrawText(this Image source, string text, Font font, IBrush brush, IPen pen, IPath path, TextGraphicsOptions options)
+ where TPixel : struct, IPixel
+ {
+ Vector2 dpi = DefaultTextDpi;
+ if (options.UseImageResolution)
+ {
+ dpi = new Vector2((float)source.MetaData.HorizontalResolution, (float)source.MetaData.VerticalResolution);
+ }
+
+ var style = new FontSpan(font, dpi)
+ {
+ ApplyKerning = options.ApplyKerning,
+ TabWidth = options.TabWidth,
+ WrappingWidth = options.WrapTextWidth,
+ HorizontalAlignment = options.HorizontalAlignment,
+ VerticalAlignment = options.VerticalAlignment
+ };
+
+ IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, path, style);
+
+ var pathOptions = (GraphicsOptions)options;
+ if (brush != null)
+ {
+ source.Fill(brush, glyphs, pathOptions);
+ }
+
+ if (pen != null)
+ {
+ source.Draw(pen, glyphs, pathOptions);
+ }
+
+ return source;
+ }
+ }
+}
diff --git a/src/ImageSharp.Drawing/Text/DrawText.cs b/src/ImageSharp.Drawing/Text/DrawText.cs
index 6bb87dcf5f..3b0d3db411 100644
--- a/src/ImageSharp.Drawing/Text/DrawText.cs
+++ b/src/ImageSharp.Drawing/Text/DrawText.cs
@@ -183,7 +183,7 @@ namespace ImageSharp
VerticalAlignment = options.VerticalAlignment
};
- IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, style).Translate(location); // todo move to better API
+ IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, location, style);
var pathOptions = (GraphicsOptions)options;
if (brush != null)
diff --git a/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs b/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs
deleted file mode 100644
index 5148236d8a..0000000000
--- a/tests/ImageSharp.Tests/Drawing/Paths/DrawPathCollection.cs
+++ /dev/null
@@ -1,179 +0,0 @@
-
-namespace ImageSharp.Tests.Drawing.Paths
-{
- using System;
-
- using ImageSharp.Drawing.Brushes;
-
- using Xunit;
- using ImageSharp.Drawing;
- using System.Numerics;
- using SixLabors.Shapes;
- using ImageSharp.Drawing.Processors;
- using ImageSharp.Drawing.Pens;
- using ImageSharp.PixelFormats;
-
- public class DrawPathCollection : IDisposable
- {
- float thickness = 7.2f;
- GraphicsOptions noneDefault = new GraphicsOptions();
- Rgba32 color = Rgba32.HotPink;
- SolidBrush brush = Brushes.Solid(Rgba32.HotPink);
- Pen pen = new Pen(Rgba32.Gray, 99.9f);
- IPath path1 = new SixLabors.Shapes.Path(new LinearLineSegment(new Vector2[] {
- new Vector2(10,10),
- new Vector2(20,10),
- new Vector2(20,10),
- new Vector2(30,10),
- }));
-
- IPath path2 = new SixLabors.Shapes.Path(new LinearLineSegment(new Vector2[] {
- new Vector2(10,10),
- new Vector2(20,10),
- new Vector2(20,10),
- new Vector2(30,10),
- }));
-
- IPathCollection pathCollection;
- private ProcessorWatchingImage img;
-
- public DrawPathCollection()
- {
- this.pathCollection = new PathCollection(this.path1, this.path2);
- this.img = new Paths.ProcessorWatchingImage(10, 10);
- }
-
- public void Dispose()
- {
- img.Dispose();
- }
-
- [Fact]
- public void CorrectlySetsBrushThicknessAndPath()
- {
- img.Draw(brush, thickness, pathCollection);
-
- Assert.NotEmpty(img.ProcessorApplications);
-
- for (var i = 0; i < 2; i++)
- {
- DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor);
-
- Assert.Equal(GraphicsOptions.Default, processor.Options);
-
- ShapePath shapepath = Assert.IsType(processor.Path);
- Assert.Contains(shapepath.Path, this.pathCollection);
-
- Pen pen = Assert.IsType>(processor.Pen);
- Assert.Equal(brush, pen.Brush);
- Assert.Equal(thickness, pen.Width);
- }
- }
-
- [Fact]
- public void CorrectlySetsBrushThicknessPathAndOptions()
- {
- img.Draw(brush, thickness, pathCollection, noneDefault);
-
- Assert.NotEmpty(img.ProcessorApplications);
-
- for (var i = 0; i < 2; i++)
- {
- DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor);
-
- Assert.Equal(noneDefault, processor.Options);
-
- ShapePath shapepath = Assert.IsType(processor.Path);
- Assert.Contains(shapepath.Path, pathCollection);
-
- Pen pen = Assert.IsType>(processor.Pen);
- Assert.Equal(brush, pen.Brush);
- Assert.Equal(thickness, pen.Width);
- }
- }
-
- [Fact]
- public void CorrectlySetsColorThicknessAndPath()
- {
- img.Draw(color, thickness, pathCollection);
-
- Assert.NotEmpty(img.ProcessorApplications);
- for (var i = 0; i < 2; i++)
- {
- DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor);
-
- Assert.Equal(GraphicsOptions.Default, processor.Options);
-
- ShapePath shapepath = Assert.IsType(processor.Path);
- Assert.Contains(shapepath.Path, pathCollection);
-
- Pen pen = Assert.IsType>(processor.Pen);
- Assert.Equal(thickness, pen.Width);
-
- SolidBrush brush = Assert.IsType>(pen.Brush);
- Assert.Equal(color, brush.Color);
- }
- }
-
- [Fact]
- public void CorrectlySetsColorThicknessPathAndOptions()
- {
- img.Draw(color, thickness, pathCollection, noneDefault);
-
- Assert.Equal(2, img.ProcessorApplications.Count);
- for (var i = 0; i < 2; i++)
- {
- DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor);
-
- Assert.Equal(noneDefault, processor.Options);
-
- ShapePath shapepath = Assert.IsType(processor.Path);
- Assert.Contains(shapepath.Path, pathCollection);
-
- Pen pen = Assert.IsType>(processor.Pen);
- Assert.Equal(thickness, pen.Width);
-
- SolidBrush brush = Assert.IsType>(pen.Brush);
- Assert.Equal(color, brush.Color);
- }
- }
-
- [Fact]
- public void CorrectlySetsPenAndPath()
- {
- img.Draw(pen, pathCollection);
-
- Assert.Equal(2, img.ProcessorApplications.Count);
- for (var i = 0; i < 2; i++)
- {
- DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor);
-
- Assert.Equal(GraphicsOptions.Default, processor.Options);
-
- ShapePath shapepath = Assert.IsType(processor.Path);
- Assert.Contains(shapepath.Path, pathCollection);
-
- Assert.Equal(pen, processor.Pen);
- }
- }
-
- [Fact]
- public void CorrectlySetsPenPathAndOptions()
- {
- img.Draw(pen, pathCollection, noneDefault);
-
- Assert.Equal(2, img.ProcessorApplications.Count);
- for (var i = 0; i < 2; i++)
- {
- DrawPathProcessor processor = Assert.IsType>(img.ProcessorApplications[i].processor);
-
- Assert.Equal(noneDefault, processor.Options);
-
- ShapePath shapepath = Assert.IsType(processor.Path);
- Assert.Contains(shapepath.Path, pathCollection);
-
- Assert.Equal(pen, processor.Pen);
- }
- }
- }
-}
diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs
new file mode 100644
index 0000000000..60fe44acdf
--- /dev/null
+++ b/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs
@@ -0,0 +1,251 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp.Tests.Drawing.Text
+{
+ using System;
+ using System.Numerics;
+
+ using ImageSharp.Drawing;
+ using ImageSharp.Drawing.Brushes;
+ using ImageSharp.Drawing.Pens;
+ using ImageSharp.Drawing.Processors;
+ using ImageSharp.PixelFormats;
+ using ImageSharp.Tests.Drawing.Paths;
+
+ using SixLabors.Fonts;
+ using SixLabors.Shapes;
+
+ using Xunit;
+
+ public class DrawText_Path : IDisposable
+ {
+ Rgba32 color = Rgba32.HotPink;
+
+ SolidBrush brush = Brushes.Solid(Rgba32.HotPink);
+
+ IPath path = new SixLabors.Shapes.Path(
+ new LinearLineSegment(
+ new Vector2[] { new Vector2(10, 10), new Vector2(20, 10), new Vector2(20, 10), new Vector2(30, 10), }));
+
+ private ProcessorWatchingImage img;
+
+ private readonly FontCollection FontCollection;
+
+ private readonly Font Font;
+
+ public DrawText_Path()
+ {
+ this.FontCollection = new FontCollection();
+ this.Font = this.FontCollection.Install(TestFontUtilities.GetPath("SixLaborsSampleAB.woff"));
+ this.img = new ProcessorWatchingImage(10, 10);
+ }
+
+ public void Dispose()
+ {
+ this.img.Dispose();
+ }
+
+ [Fact]
+ public void FillsForEachACharachterWhenBrushSetAndNotPen()
+ {
+ this.img.DrawText(
+ "123",
+ this.Font,
+ Brushes.Solid(Rgba32.Red),
+ null,
+ path,
+ new TextGraphicsOptions(true));
+
+ Assert.NotEmpty(this.img.ProcessorApplications);
+ Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied
+ Assert.IsType>(this.img.ProcessorApplications[0].processor);
+ }
+
+ [Fact]
+ public void FillsForEachACharachterWhenBrushSetAndNotPenDefaultOptions()
+ {
+ this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, path);
+
+ Assert.NotEmpty(this.img.ProcessorApplications);
+ Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied
+ Assert.IsType>(this.img.ProcessorApplications[0].processor);
+ }
+
+ [Fact]
+ public void FillsForEachACharachterWhenBrushSet()
+ {
+ this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), path, new TextGraphicsOptions(true));
+
+ Assert.NotEmpty(this.img.ProcessorApplications);
+ Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied
+ Assert.IsType>(this.img.ProcessorApplications[0].processor);
+ }
+
+ [Fact]
+ public void FillsForEachACharachterWhenBrushSetDefaultOptions()
+ {
+ this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), path);
+
+ Assert.NotEmpty(this.img.ProcessorApplications);
+ Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied
+ Assert.IsType>(this.img.ProcessorApplications[0].processor);
+ }
+
+ [Fact]
+ public void FillsForEachACharachterWhenColorSet()
+ {
+ this.img.DrawText("123", this.Font, Rgba32.Red, path, new TextGraphicsOptions(true));
+
+ Assert.NotEmpty(this.img.ProcessorApplications);
+ Assert.Equal(3, this.img.ProcessorApplications.Count);
+ FillRegionProcessor processor =
+ Assert.IsType>(this.img.ProcessorApplications[0].processor);
+
+ SolidBrush brush = Assert.IsType>(processor.Brush);
+ Assert.Equal(Rgba32.Red, brush.Color);
+ }
+
+ [Fact]
+ public void FillsForEachACharachterWhenColorSetDefaultOptions()
+ {
+ this.img.DrawText("123", this.Font, Rgba32.Red, path);
+
+ Assert.NotEmpty(this.img.ProcessorApplications);
+ Assert.Equal(3, this.img.ProcessorApplications.Count);
+ Assert.IsType>(this.img.ProcessorApplications[0].processor);
+ FillRegionProcessor processor =
+ Assert.IsType>(this.img.ProcessorApplications[0].processor);
+
+ SolidBrush brush = Assert.IsType>(processor.Brush);
+ Assert.Equal(Rgba32.Red, brush.Color);
+ }
+
+ [Fact]
+ public void DrawForEachACharachterWhenPenSetAndNotBrush()
+ {
+ this.img.DrawText(
+ "123",
+ this.Font,
+ null,
+ Pens.Dash(Rgba32.Red, 1),
+ path,
+ new TextGraphicsOptions(true));
+
+ Assert.NotEmpty(this.img.ProcessorApplications);
+ Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied
+ Assert.IsType>(this.img.ProcessorApplications[0].processor);
+ }
+
+ [Fact]
+ public void DrawForEachACharachterWhenPenSetAndNotBrushDefaultOptions()
+ {
+ this.img.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), path);
+
+ Assert.NotEmpty(this.img.ProcessorApplications);
+ Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied
+ Assert.IsType>(this.img.ProcessorApplications[0].processor);
+ }
+
+ [Fact]
+ public void DrawForEachACharachterWhenPenSet()
+ {
+ this.img.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), path, new TextGraphicsOptions(true));
+
+ Assert.NotEmpty(this.img.ProcessorApplications);
+ Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied
+ Assert.IsType>(this.img.ProcessorApplications[0].processor);
+ }
+
+ [Fact]
+ public void DrawForEachACharachterWhenPenSetDefaultOptions()
+ {
+ this.img.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), path);
+
+ Assert.NotEmpty(this.img.ProcessorApplications);
+ Assert.Equal(3, this.img.ProcessorApplications.Count); // 3 fills where applied
+ Assert.IsType>(this.img.ProcessorApplications[0].processor);
+ }
+
+ [Fact]
+ public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSet()
+ {
+ this.img.DrawText(
+ "123",
+ this.Font,
+ Brushes.Solid(Rgba32.Red),
+ Pens.Dash(Rgba32.Red, 1),
+ path,
+ new TextGraphicsOptions(true));
+
+ Assert.NotEmpty(this.img.ProcessorApplications);
+ Assert.Equal(6, this.img.ProcessorApplications.Count);
+ }
+
+ [Fact]
+ public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSetDefaultOptions()
+ {
+ this.img.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), path);
+
+ Assert.NotEmpty(this.img.ProcessorApplications);
+ Assert.Equal(6, this.img.ProcessorApplications.Count);
+ }
+
+ [Fact]
+ public void BrushAppliesBeforPen()
+ {
+ this.img.DrawText(
+ "1",
+ this.Font,
+ Brushes.Solid(Rgba32.Red),
+ Pens.Dash(Rgba32.Red, 1),
+ path,
+ new TextGraphicsOptions(true));
+
+ Assert.NotEmpty(this.img.ProcessorApplications);
+ Assert.Equal(2, this.img.ProcessorApplications.Count);
+ Assert.IsType>(this.img.ProcessorApplications[0].processor);
+ Assert.IsType>(this.img.ProcessorApplications[1].processor);
+ }
+
+ [Fact]
+ public void BrushAppliesBeforPenDefaultOptions()
+ {
+ this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), path);
+
+ Assert.NotEmpty(this.img.ProcessorApplications);
+ Assert.Equal(2, this.img.ProcessorApplications.Count);
+ Assert.IsType>(this.img.ProcessorApplications[0].processor);
+ Assert.IsType>(this.img.ProcessorApplications[1].processor);
+ }
+
+ [Fact]
+ public void GlyphHeightChangesBasedOnuseImageResolutionFlag()
+ {
+ this.img.MetaData.VerticalResolution = 1;
+ this.img.MetaData.HorizontalResolution = 1;
+ this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), path, new TextGraphicsOptions(true) {
+ UseImageResolution = false
+ });
+
+ this.img.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), path, new TextGraphicsOptions(true)
+ {
+ UseImageResolution = true
+ });
+
+ Assert.NotEmpty(this.img.ProcessorApplications);
+ Assert.Equal(2, this.img.ProcessorApplications.Count);
+ FillRegionProcessor ownResolution = Assert.IsType>(this.img.ProcessorApplications[0].processor);
+ FillRegionProcessor imgResolution = Assert.IsType>(this.img.ProcessorApplications[1].processor);
+
+ ShapeRegion ownRegion = Assert.IsType(ownResolution.Region);
+ ShapeRegion imgRegion = Assert.IsType(imgResolution.Region);
+
+ // magic numbers based on the font used at well known resolutions
+ Assert.Equal(7.44, ownRegion.Shape.Bounds.Height, 2);
+ Assert.Equal(0.1, imgRegion.Shape.Bounds.Height, 2);
+ }
+ }
+}