diff --git a/src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs b/src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs
index 95ac3fe29..6eb6a15d0 100644
--- a/src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs
+++ b/src/ImageSharp.Drawing/Processing/Drawing/Processors/FillRegionProcessor.cs
@@ -37,22 +37,29 @@ namespace SixLabors.ImageSharp.Processing.Drawing.Processors
}
///
- /// Gets the brush.
+ /// Initializes a new instance of the class.
+ ///
+ public FillRegionProcessor()
+ {
+ }
+
+ ///
+ /// Gets or sets the brush.
///
- public IBrush Brush { get; }
+ public IBrush Brush { get; set; }
///
- /// Gets the region that this processor applies to.
+ /// Gets or sets the region that this processor applies to.
///
- public Region Region { get; }
+ public Region Region { get; set; }
///
- /// Gets the options.
+ /// Gets or sets the options.
///
///
/// The options.
///
- public GraphicsOptions Options { get; }
+ public GraphicsOptions Options { get; set; }
///
protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
diff --git a/src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.Path.cs b/src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.Path.cs
index 9de73afcc..e0c133d50 100644
--- a/src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.Path.cs
+++ b/src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.Path.cs
@@ -6,6 +6,7 @@ using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
+using SixLabors.ImageSharp.Processing.Text.Processors;
using SixLabors.Shapes;
namespace SixLabors.ImageSharp.Processing.Text
@@ -147,33 +148,6 @@ namespace SixLabors.ImageSharp.Processing.Text
///
public static IImageProcessingContext DrawText(this IImageProcessingContext source, TextGraphicsOptions options, string text, Font font, IBrush brush, IPen pen, IPath path)
where TPixel : struct, IPixel
- {
- float dpiX = DefaultTextDpi;
- float dpiY = DefaultTextDpi;
-
- var style = new RendererOptions(font, dpiX, dpiY)
- {
- 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(pathOptions, brush, glyphs);
- }
-
- if (pen != null)
- {
- source.Draw(pathOptions, pen, glyphs);
- }
-
- return source;
- }
+ => source.ApplyProcessor(new DrawTextOnPathProcessor(options, text, font, brush, pen, path));
}
}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.cs b/src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.cs
index 8fede9693..ed7a7bbfa 100644
--- a/src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.cs
+++ b/src/ImageSharp.Drawing/Processing/Text/DrawTextExtensions.cs
@@ -6,6 +6,7 @@ using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Drawing;
using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
+using SixLabors.ImageSharp.Processing.Text.Processors;
using SixLabors.Primitives;
using SixLabors.Shapes;
@@ -16,8 +17,6 @@ namespace SixLabors.ImageSharp.Processing.Text
///
public static partial class DrawTextExtensions
{
- private static readonly int DefaultTextDpi = 72;
-
///
/// Draws the text onto the the image filled via the brush.
///
@@ -150,33 +149,6 @@ namespace SixLabors.ImageSharp.Processing.Text
///
public static IImageProcessingContext DrawText(this IImageProcessingContext source, TextGraphicsOptions options, string text, Font font, IBrush brush, IPen pen, PointF location)
where TPixel : struct, IPixel
- {
- float dpiX = DefaultTextDpi;
- float dpiY = DefaultTextDpi;
-
- var style = new RendererOptions(font, dpiX, dpiY, location)
- {
- ApplyKerning = options.ApplyKerning,
- TabWidth = options.TabWidth,
- WrappingWidth = options.WrapTextWidth,
- HorizontalAlignment = options.HorizontalAlignment,
- VerticalAlignment = options.VerticalAlignment
- };
-
- IPathCollection glyphs = TextBuilder.GenerateGlyphs(text, style);
-
- var pathOptions = (GraphicsOptions)options;
- if (brush != null)
- {
- source.Fill(pathOptions, brush, glyphs);
- }
-
- if (pen != null)
- {
- source.Draw(pathOptions, pen, glyphs);
- }
-
- return source;
- }
+ => source.ApplyProcessor(new DrawTextProcessor(options, text, font, brush, pen, location));
}
}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/Processing/Text/Processors/DrawTextOnPathProcessor.cs b/src/ImageSharp.Drawing/Processing/Text/Processors/DrawTextOnPathProcessor.cs
new file mode 100644
index 000000000..c8a51865c
--- /dev/null
+++ b/src/ImageSharp.Drawing/Processing/Text/Processors/DrawTextOnPathProcessor.cs
@@ -0,0 +1,140 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Threading.Tasks;
+using SixLabors.Fonts;
+using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Primitives;
+using SixLabors.ImageSharp.Processing.Drawing.Brushes;
+using SixLabors.ImageSharp.Processing.Drawing.Pens;
+using SixLabors.ImageSharp.Processing.Drawing.Processors;
+using SixLabors.ImageSharp.Processing.Processors;
+using SixLabors.Primitives;
+using SixLabors.Shapes;
+
+namespace SixLabors.ImageSharp.Processing.Text.Processors
+{
+ ///
+ /// Using the brush as a source of pixels colors blends the brush color with source.
+ ///
+ /// The pixel format.
+ internal class DrawTextOnPathProcessor : ImageProcessor
+ where TPixel : struct, IPixel
+ {
+ private FillRegionProcessor fillRegionProcessor = null;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options
+ /// The text we want to render
+ /// The font we want to render with
+ /// The brush to source pixel colors from.
+ /// The pen to outline text with.
+ /// The path on which to draw the text along.
+ public DrawTextOnPathProcessor(TextGraphicsOptions options, string text, Font font, IBrush brush, IPen pen, IPath path)
+ {
+ this.Brush = brush;
+ this.Options = options;
+ this.Text = text;
+ this.Pen = pen;
+ this.Font = font;
+ this.Path = path;
+ }
+
+ ///
+ /// Gets or sets the brush.
+ ///
+ public IBrush Brush { get; set; }
+
+ ///
+ /// Gets or sets the options
+ ///
+ private TextGraphicsOptions Options { get; set; }
+
+ ///
+ /// Gets or sets the text
+ ///
+ private string Text { get; set; }
+
+ ///
+ /// Gets or sets the pen used for outlining the text, if Null then we will not outline
+ ///
+ public IPen Pen { get; set; }
+
+ ///
+ /// Gets or sets the font used to render the text.
+ ///
+ public Font Font { get; set; }
+
+ ///
+ /// Gets or sets the path to draw the text along.
+ ///
+ public IPath Path { get; set; }
+
+ protected override void BeforeImageApply(Image source, Rectangle sourceRectangle)
+ {
+ base.BeforeImageApply(source, sourceRectangle);
+
+ // do everythign at the image level as we are deligating the processing down to other processors
+ var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY)
+ {
+ ApplyKerning = this.Options.ApplyKerning,
+ TabWidth = this.Options.TabWidth,
+ WrappingWidth = this.Options.WrapTextWidth,
+ HorizontalAlignment = this.Options.HorizontalAlignment,
+ VerticalAlignment = this.Options.VerticalAlignment
+ };
+
+ IPathCollection glyphs = TextBuilder.GenerateGlyphs(this.Text, this.Path, style);
+
+ var pathOptions = (GraphicsOptions)this.Options;
+ if (this.Brush != null)
+ {
+ // we will reuse the processor for all fill operations to reduce allocations
+ if (this.fillRegionProcessor == null)
+ {
+ this.fillRegionProcessor = new FillRegionProcessor()
+ {
+ Brush = this.Brush,
+ Options = pathOptions
+ };
+ }
+
+ foreach (IPath p in glyphs)
+ {
+ this.fillRegionProcessor.Region = new ShapeRegion(p);
+ this.fillRegionProcessor.Apply(source, sourceRectangle);
+ }
+ }
+
+ if (this.Pen != null)
+ {
+ // we will reuse the processor for all fill operations to reduce allocations
+ if (this.fillRegionProcessor == null)
+ {
+ this.fillRegionProcessor = new FillRegionProcessor()
+ {
+ Brush = this.Brush,
+ Options = pathOptions
+ };
+ }
+
+ foreach (IPath p in glyphs)
+ {
+ this.fillRegionProcessor.Region = new ShapePath(p, this.Pen);
+ this.fillRegionProcessor.Apply(source, sourceRectangle);
+ }
+ }
+ }
+
+ ///
+ protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
+ {
+ // this is a no-op as we have processes all as an image, we should be able to pass out of before email apply a skip frames outcome
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/Processing/Text/Processors/DrawTextProcessor.cs b/src/ImageSharp.Drawing/Processing/Text/Processors/DrawTextProcessor.cs
new file mode 100644
index 000000000..b029ff516
--- /dev/null
+++ b/src/ImageSharp.Drawing/Processing/Text/Processors/DrawTextProcessor.cs
@@ -0,0 +1,458 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using SixLabors.Fonts;
+using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.Memory;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Primitives;
+using SixLabors.ImageSharp.Processing.Drawing.Brushes;
+using SixLabors.ImageSharp.Processing.Drawing.Pens;
+using SixLabors.ImageSharp.Processing.Drawing.Processors;
+using SixLabors.ImageSharp.Processing.Processors;
+using SixLabors.Primitives;
+using SixLabors.Shapes;
+
+namespace SixLabors.ImageSharp.Processing.Text.Processors
+{
+ ///
+ /// Using the brush as a source of pixels colors blends the brush color with source.
+ ///
+ /// The pixel format.
+ internal class DrawTextProcessor : ImageProcessor
+ where TPixel : struct, IPixel
+ {
+ private FillRegionProcessor fillRegionProcessor = null;
+
+ private CachingGlyphRenderer textRenderer;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options
+ /// The text we want to render
+ /// The font we want to render with
+ /// The brush to source pixel colors from.
+ /// The pen to outline text with.
+ /// The location on the image to start drawign the text from.
+ public DrawTextProcessor(TextGraphicsOptions options, string text, Font font, IBrush brush, IPen pen, PointF location)
+ {
+ this.Brush = brush;
+ this.Options = options;
+ this.Text = text;
+ this.Pen = pen;
+ this.Font = font;
+ this.Location = location;
+ }
+
+ ///
+ /// Gets or sets the brush.
+ ///
+ public IBrush Brush { get; set; }
+
+ ///
+ /// Gets or sets the options
+ ///
+ public TextGraphicsOptions Options { get; set; }
+
+ ///
+ /// Gets or sets the text
+ ///
+ public string Text { get; set; }
+
+ ///
+ /// Gets or sets the pen used for outlining the text, if Null then we will not outline
+ ///
+ public IPen Pen { get; set; }
+
+ ///
+ /// Gets or sets the font used to render the text.
+ ///
+ public Font Font { get; set; }
+
+ ///
+ /// Gets or sets the location to draw the text at.
+ ///
+ public PointF Location { get; set; }
+
+ protected override void BeforeImageApply(Image source, Rectangle sourceRectangle)
+ {
+ base.BeforeImageApply(source, sourceRectangle);
+
+ // user slow path if pen is set and fast render for brush only rendering
+
+ // do everythign at the image level as we are deligating the processing down to other processors
+ var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location)
+ {
+ ApplyKerning = this.Options.ApplyKerning,
+ TabWidth = this.Options.TabWidth,
+ WrappingWidth = this.Options.WrapTextWidth,
+ HorizontalAlignment = this.Options.HorizontalAlignment,
+ VerticalAlignment = this.Options.VerticalAlignment
+ };
+
+ if (this.Pen != null)
+ {
+ IPathCollection glyphs = TextBuilder.GenerateGlyphs(this.Text, style);
+
+ var pathOptions = (GraphicsOptions)this.Options;
+ if (this.Brush != null)
+ {
+ // we will reuse the processor for all fill operations to reduce allocations
+ if (this.fillRegionProcessor == null)
+ {
+ this.fillRegionProcessor = new FillRegionProcessor()
+ {
+ Brush = this.Brush,
+ Options = pathOptions
+ };
+ }
+
+ foreach (IPath p in glyphs)
+ {
+ this.fillRegionProcessor.Region = new ShapeRegion(p);
+ this.fillRegionProcessor.Apply(source, sourceRectangle);
+ }
+ }
+
+ // we will reuse the processor for all fill operations to reduce allocations
+ if (this.fillRegionProcessor == null)
+ {
+ this.fillRegionProcessor = new FillRegionProcessor()
+ {
+ Brush = this.Brush,
+ Options = pathOptions
+ };
+ }
+
+ foreach (IPath p in glyphs)
+ {
+ this.fillRegionProcessor.Region = new ShapePath(p, this.Pen);
+ this.fillRegionProcessor.Apply(source, sourceRectangle);
+ }
+ }
+ else
+ {
+ this.textRenderer = new CachingGlyphRenderer(source.GetMemoryManager());
+ this.textRenderer.Options = (GraphicsOptions)this.Options;
+ TextRenderer.RenderTextTo(this.textRenderer, this.Text, style);
+ }
+ }
+
+ protected override void AfterImageApply(Image source, Rectangle sourceRectangle)
+ {
+ base.AfterImageApply(source, sourceRectangle);
+ this.textRenderer?.Dispose();
+ this.textRenderer = null;
+ }
+
+ ///
+ protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
+ {
+ // this is a no-op as we have processes all as an image, we should be able to pass out of before email apply a skip frames outcome
+ if (this.Pen == null && this.Brush != null && this.textRenderer != null && this.textRenderer.Operations.Count > 0)
+ {
+ // we have rendered at the image level now we can draw
+ List operations = this.textRenderer.Operations;
+
+ using (BrushApplicator app = this.Brush.CreateApplicator(source, sourceRectangle, this.textRenderer.Options))
+ {
+ foreach (DrawingOperation operation in operations)
+ {
+ IBuffer2D buffer = operation.Map;
+ int startY = operation.Location.Y;
+ int startX = operation.Location.X;
+ int end = operation.Map.Height;
+ for (int row = 0; row < end; row++)
+ {
+ int y = startY + row;
+ app.Apply(buffer.GetRowSpan(row), startX, y);
+ }
+ }
+ }
+ }
+ }
+
+ private struct DrawingOperation
+ {
+ public IBuffer2D Map { get; set; }
+
+ public Point Location { get; set; }
+ }
+
+ private class CachingGlyphRenderer : IGlyphRenderer, IDisposable
+ {
+ private PathBuilder builder;
+
+ private Point currentRenderPosition = default(Point);
+ private int currentRenderingGlyph = 0;
+
+ private PointF currentPoint = default(PointF);
+ private Dictionary> glyphMap = new Dictionary>();
+
+ public CachingGlyphRenderer(MemoryManager memoryManager)
+ {
+ this.MemoryManager = memoryManager;
+ this.builder = new PathBuilder();
+ }
+
+ public List Operations { get; } = new List();
+
+ public MemoryManager MemoryManager { get; internal set; }
+
+ public GraphicsOptions Options { get; internal set; }
+
+ public void BeginFigure()
+ {
+ this.builder.StartFigure();
+ }
+
+ public bool BeginGlyph(RectangleF bounds, int cacheKey)
+ {
+ this.currentRenderPosition = Point.Truncate(bounds.Location);
+ this.currentRenderingGlyph = cacheKey;
+
+ if (this.glyphMap.ContainsKey(this.currentRenderingGlyph))
+ {
+ // we have already drawn the glyph vectors skip trying again
+ return false;
+ }
+
+ // we check to see if we have a render cache and if we do then we render else
+ this.builder.Clear();
+
+ // ensure all glyphs render around [zero, zero] so offset negative root positions so when we draw the glyph we can offet it back
+ this.builder.SetOrigin(new PointF(-(int)bounds.X, -(int)bounds.Y));
+
+ return true;
+ }
+
+ public void BeginText(RectangleF bounds)
+ {
+ // not concerned about this one
+ this.Operations.Clear();
+ }
+
+ public void CubicBezierTo(PointF secondControlPoint, PointF thirdControlPoint, PointF point)
+ {
+ this.builder.AddBezier(this.currentPoint, secondControlPoint, thirdControlPoint, point);
+ this.currentPoint = point;
+ }
+
+ public void Dispose()
+ {
+ foreach (KeyValuePair> m in this.glyphMap)
+ {
+ m.Value.Dispose();
+ }
+ }
+
+ public void EndFigure()
+ {
+ this.builder.CloseFigure();
+ }
+
+ public void EndGlyph()
+ {
+ if (!this.glyphMap.ContainsKey(this.currentRenderingGlyph))
+ {
+ this.RenderToCache();
+ }
+
+ this.Operations.Add(new DrawingOperation
+ {
+ Location = this.currentRenderPosition,
+ Map = this.glyphMap[this.currentRenderingGlyph]
+ });
+ }
+
+ private void RenderToCache()
+ {
+ IPath path = this.builder.Build();
+
+ var size = Rectangle.Ceiling(path.Bounds);
+ float subpixelCount = 4;
+ float offset = 0.5f;
+ if (this.Options.Antialias)
+ {
+ offset = 0f; // we are antialising skip offsetting as real antalising should take care of offset.
+ subpixelCount = this.Options.AntialiasSubpixelDepth;
+ if (subpixelCount < 4)
+ {
+ subpixelCount = 4;
+ }
+ }
+
+ // take the path inside the path builder, scan thing and generate a Buffer2d representing the glyph and cache it.
+ Buffer2D fullBuffer = this.MemoryManager.Allocate2D(size.Width + 1, size.Height + 1, true);
+ this.glyphMap.Add(this.currentRenderingGlyph, fullBuffer);
+ using (IBuffer bufferBacking = this.MemoryManager.Allocate(path.MaxIntersections))
+ using (IBuffer rowIntersectionBuffer = this.MemoryManager.Allocate(size.Width))
+ {
+ float subpixelFraction = 1f / subpixelCount;
+ float subpixelFractionPoint = subpixelFraction / subpixelCount;
+
+ for (int y = 0; y <= size.Height; y++)
+ {
+ Span scanline = fullBuffer.GetRowSpan(y);
+ bool scanlineDirty = false;
+ float yPlusOne = y + 1;
+
+ for (float subPixel = (float)y; subPixel < yPlusOne; subPixel += subpixelFraction)
+ {
+ var start = new PointF(path.Bounds.Left - 1, subPixel);
+ var end = new PointF(path.Bounds.Right + 1, subPixel);
+ Span intersectionSpan = rowIntersectionBuffer.Span;
+ Span buffer = bufferBacking.Span;
+ int pointsFound = path.FindIntersections(start, end, intersectionSpan);
+
+ if (pointsFound == 0)
+ {
+ // nothing on this line skip
+ continue;
+ }
+
+ for (int i = 0; i < pointsFound && i < intersectionSpan.Length; i++)
+ {
+ buffer[i] = intersectionSpan[i].X;
+ }
+
+ QuickSort(buffer.Slice(0, pointsFound));
+
+ for (int point = 0; point < pointsFound; point += 2)
+ {
+ // points will be paired up
+ float scanStart = buffer[point];
+ float scanEnd = buffer[point + 1];
+ int startX = (int)MathF.Floor(scanStart + offset);
+ int endX = (int)MathF.Floor(scanEnd + offset);
+
+ if (startX >= 0 && startX < scanline.Length)
+ {
+ for (float x = scanStart; x < startX + 1; x += subpixelFraction)
+ {
+ scanline[startX] += subpixelFractionPoint;
+ scanlineDirty = true;
+ }
+ }
+
+ if (endX >= 0 && endX < scanline.Length)
+ {
+ for (float x = endX; x < scanEnd; x += subpixelFraction)
+ {
+ scanline[endX] += subpixelFractionPoint;
+ scanlineDirty = true;
+ }
+ }
+
+ int nextX = startX + 1;
+ endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
+ nextX = Math.Max(nextX, 0);
+ for (int x = nextX; x < endX; x++)
+ {
+ scanline[x] += subpixelFraction;
+ scanlineDirty = true;
+ }
+ }
+ }
+
+ if (scanlineDirty)
+ {
+ if (!this.Options.Antialias)
+ {
+ for (int x = 0; x < size.Width; x++)
+ {
+ if (scanline[x] >= 0.5)
+ {
+ scanline[x] = 1;
+ }
+ else
+ {
+ scanline[x] = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static void Swap(Span data, int left, int right)
+ {
+ float tmp = data[left];
+ data[left] = data[right];
+ data[right] = tmp;
+ }
+
+ private static void QuickSort(Span data)
+ {
+ QuickSort(data, 0, data.Length - 1);
+ }
+
+ private static void QuickSort(Span data, int lo, int hi)
+ {
+ if (lo < hi)
+ {
+ int p = Partition(data, lo, hi);
+ QuickSort(data, lo, p);
+ QuickSort(data, p + 1, hi);
+ }
+ }
+
+ private static int Partition(Span data, int lo, int hi)
+ {
+ float pivot = data[lo];
+ int i = lo - 1;
+ int j = hi + 1;
+ while (true)
+ {
+ do
+ {
+ i = i + 1;
+ }
+ while (data[i] < pivot && i < hi);
+
+ do
+ {
+ j = j - 1;
+ }
+ while (data[j] > pivot && j > lo);
+
+ if (i >= j)
+ {
+ return j;
+ }
+
+ Swap(data, i, j);
+ }
+ }
+
+ public void EndText()
+ {
+ }
+
+ public void LineTo(PointF point)
+ {
+ this.builder.AddLine(this.currentPoint, point);
+ this.currentPoint = point;
+ }
+
+ public void MoveTo(PointF point)
+ {
+ this.builder.StartFigure();
+ this.currentPoint = point;
+ }
+
+ public void QuadraticBezierTo(PointF secondControlPoint, PointF point)
+ {
+ this.builder.AddBezier(this.currentPoint, secondControlPoint, point);
+ this.currentPoint = point;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp.Drawing/Processing/Text/TextGraphicsOptions.cs b/src/ImageSharp.Drawing/Processing/Text/TextGraphicsOptions.cs
index bba473ddb..aaa6dea56 100644
--- a/src/ImageSharp.Drawing/Processing/Text/TextGraphicsOptions.cs
+++ b/src/ImageSharp.Drawing/Processing/Text/TextGraphicsOptions.cs
@@ -11,6 +11,8 @@ namespace SixLabors.ImageSharp.Processing.Text
///
public struct TextGraphicsOptions
{
+ private const int DefaultTextDpi = 72;
+
///
/// Represents the default .
///
@@ -26,11 +28,16 @@ namespace SixLabors.ImageSharp.Processing.Text
private float? tabWidth;
+ private float? dpiX;
+
+ private float? dpiY;
+
private PixelBlenderMode blenderMode;
private float wrapTextWidth;
private HorizontalAlignment? horizontalAlignment;
+
private VerticalAlignment? verticalAlignment;
///
@@ -49,6 +56,8 @@ namespace SixLabors.ImageSharp.Processing.Text
this.blenderMode = PixelBlenderMode.Normal;
this.blendPercentage = 1;
this.antialias = enableAntialiasing;
+ this.dpiX = DefaultTextDpi;
+ this.dpiY = DefaultTextDpi;
}
///
@@ -90,6 +99,16 @@ namespace SixLabors.ImageSharp.Processing.Text
///
public float WrapTextWidth { get => this.wrapTextWidth; set => this.wrapTextWidth = value; }
+ ///
+ /// Gets or sets a value indicating the DPI to render text along the X axis.
+ ///
+ public float DpiX { get => this.dpiX ?? DefaultTextDpi; set => this.dpiX = value; }
+
+ ///
+ /// Gets or sets a value indicating the DPI to render text along the Y axis.
+ ///
+ public float DpiY { get => this.dpiY ?? DefaultTextDpi; set => this.dpiY = value; }
+
///
/// Gets or sets a value indicating how to align the text relative to the rendering space.
/// If is greater than zero it will align relative to the space
diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs
new file mode 100644
index 000000000..96912a6df
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Drawing/DrawText.cs
@@ -0,0 +1,101 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using BenchmarkDotNet.Attributes;
+using System.IO;
+using System.Numerics;
+
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing;
+using SixLabors.ImageSharp.Processing.Text;
+using SixLabors.ImageSharp.Processing.Overlays;
+using SixLabors.ImageSharp.Processing.Drawing;
+using System.Linq;
+
+namespace SixLabors.ImageSharp.Benchmarks
+{
+
+ [MemoryDiagnoser]
+ public class DrawText : BenchmarkBase
+ {
+
+ [Params(10, 100)]
+ public int TextIterations{ get; set; }
+ public string TextPhrase { get; set; } = "Hello World";
+ public string TextToRender => string.Join(" ", Enumerable.Repeat(TextPhrase, TextIterations));
+
+
+ [Benchmark(Baseline = true, Description = "System.Drawing Draw Text")]
+ public void DrawTextSystemDrawing()
+ {
+ using (Bitmap destination = new Bitmap(800, 800))
+ {
+
+ using (Graphics graphics = Graphics.FromImage(destination))
+ {
+ graphics.InterpolationMode = InterpolationMode.Default;
+ graphics.SmoothingMode = SmoothingMode.AntiAlias;
+ Pen pen = new Pen(System.Drawing.Color.HotPink, 10);
+ var font = new Font("Arial", 12, GraphicsUnit.Point);
+ graphics.DrawString(TextToRender, font, Brushes.HotPink, new RectangleF(10, 10, 780, 780));
+ }
+ }
+ }
+
+
+ [Benchmark(Description = "ImageSharp Draw Text - Cached Glyphs")]
+ public void DrawTextCore()
+ {
+ using (Image image = new Image(800, 800))
+ {
+ var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12);
+ image.Mutate(x => x.ApplyProcessor(new SixLabors.ImageSharp.Processing.Text.Processors.DrawTextProcessor(new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, SixLabors.ImageSharp.Processing.Drawing.Brushes.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10))));
+ }
+ }
+
+ [Benchmark(Description = "ImageSharp Draw Text - Nieve")]
+ public void DrawTextCoreOld()
+ {
+ using (Image image = new Image(800, 800))
+ {
+ var font = SixLabors.Fonts.SystemFonts.CreateFont("Arial", 12);
+ image.Mutate(x => DrawTextOldVersion(x, new TextGraphicsOptions(true) { WrapTextWidth = 780 }, TextToRender, font, SixLabors.ImageSharp.Processing.Drawing.Brushes.Brushes.Solid(Rgba32.HotPink), null, new SixLabors.Primitives.PointF(10, 10)));
+ }
+
+ IImageProcessingContext DrawTextOldVersion(IImageProcessingContext source, TextGraphicsOptions options, string text, SixLabors.Fonts.Font font, SixLabors.ImageSharp.Processing.Drawing.Brushes.IBrush brush, SixLabors.ImageSharp.Processing.Drawing.Pens.IPen pen, SixLabors.Primitives.PointF location)
+ where TPixel : struct, IPixel
+ {
+ float dpiX = 72;
+ float dpiY = 72;
+
+ var style = new SixLabors.Fonts.RendererOptions(font, dpiX, dpiY, location)
+ {
+ ApplyKerning = options.ApplyKerning,
+ TabWidth = options.TabWidth,
+ WrappingWidth = options.WrapTextWidth,
+ HorizontalAlignment = options.HorizontalAlignment,
+ VerticalAlignment = options.VerticalAlignment
+ };
+
+ Shapes.IPathCollection glyphs = Shapes.TextBuilder.GenerateGlyphs(text, style);
+
+ var pathOptions = (GraphicsOptions)options;
+ if (brush != null)
+ {
+ source.Fill(pathOptions, brush, glyphs);
+ }
+
+ if (pen != null)
+ {
+ source.Draw(pathOptions, pen, glyphs);
+ }
+
+ return source;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Drawing/OldProcessors/DrawTextProcessorV1.cs b/tests/ImageSharp.Benchmarks/Drawing/OldProcessors/DrawTextProcessorV1.cs
new file mode 100644
index 000000000..3faaec2c2
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Drawing/OldProcessors/DrawTextProcessorV1.cs
@@ -0,0 +1,138 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using SixLabors.Fonts;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Primitives;
+using SixLabors.ImageSharp.Processing.Drawing.Brushes;
+using SixLabors.ImageSharp.Processing.Drawing.Pens;
+using SixLabors.ImageSharp.Processing.Drawing.Processors;
+using SixLabors.ImageSharp.Processing.Processors;
+using SixLabors.ImageSharp.Processing.Text;
+using SixLabors.Primitives;
+using SixLabors.Shapes;
+
+namespace SixLabors.ImageSharp.Benchmarks.Drawing.OldProcessors
+{
+
+ ///
+ /// Using the brush as a source of pixels colors blends the brush color with source.
+ ///
+ /// The pixel format.
+ internal class DrawTextProcessor : ImageProcessor
+ where TPixel : struct, IPixel
+ {
+ private FillRegionProcessor fillRegionProcessor = null;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The options
+ /// The text we want to render
+ /// The font we want to render with
+ /// The brush to source pixel colors from.
+ /// The pen to outline text with.
+ /// The location on the image to start drawign the text from.
+ public DrawTextProcessor(TextGraphicsOptions options, string text, Font font, IBrush brush, IPen pen, PointF location)
+ {
+ this.Brush = brush;
+ this.Options = options;
+ this.Text = text;
+ this.Pen = pen;
+ this.Font = font;
+ this.Location = location;
+ }
+
+ ///
+ /// Gets or sets the brush.
+ ///
+ public IBrush Brush { get; set; }
+
+ ///
+ /// Gets or sets the options
+ ///
+ public TextGraphicsOptions Options { get; set; }
+
+ ///
+ /// Gets or sets the text
+ ///
+ public string Text { get; set; }
+
+ ///
+ /// Gets or sets the pen used for outlining the text, if Null then we will not outline
+ ///
+ public IPen Pen { get; set; }
+
+ ///
+ /// Gets or sets the font used to render the text.
+ ///
+ public Font Font { get; set; }
+
+ ///
+ /// Gets or sets the location to draw the text at.
+ ///
+ public PointF Location { get; set; }
+
+ protected override void BeforeImageApply(Image source, Rectangle sourceRectangle)
+ {
+ base.BeforeImageApply(source, sourceRectangle);
+
+ // do everythign at the image level as we are deligating the processing down to other processors
+ var style = new RendererOptions(this.Font, this.Options.DpiX, this.Options.DpiY, this.Location)
+ {
+ ApplyKerning = this.Options.ApplyKerning,
+ TabWidth = this.Options.TabWidth,
+ WrappingWidth = this.Options.WrapTextWidth,
+ HorizontalAlignment = this.Options.HorizontalAlignment,
+ VerticalAlignment = this.Options.VerticalAlignment
+ };
+
+ IPathCollection glyphs = TextBuilder.GenerateGlyphs(this.Text, style);
+
+ var pathOptions = (GraphicsOptions)this.Options;
+ if (this.Brush != null)
+ {
+ // we will reuse the processor for all fill operations to reduce allocations
+ if (this.fillRegionProcessor == null)
+ {
+ this.fillRegionProcessor = new FillRegionProcessor()
+ {
+ Brush = this.Brush,
+ Options = pathOptions
+ };
+ }
+
+ foreach (IPath p in glyphs)
+ {
+ this.fillRegionProcessor.Region = new ShapeRegion(p);
+ this.fillRegionProcessor.Apply(source, sourceRectangle);
+ }
+ }
+
+ if (this.Pen != null)
+ {
+ // we will reuse the processor for all fill operations to reduce allocations
+ if (this.fillRegionProcessor == null)
+ {
+ this.fillRegionProcessor = new FillRegionProcessor()
+ {
+ Brush = this.Brush,
+ Options = pathOptions
+ };
+ }
+
+ foreach (IPath p in glyphs)
+ {
+ this.fillRegionProcessor.Region = new ShapePath(p, this.Pen);
+ this.fillRegionProcessor.Apply(source, sourceRectangle);
+ }
+ }
+ }
+
+ ///
+ protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
+ {
+ // this is a no-op as we have processes all as an image, we should be able to pass out of before email apply a skip frames outcome
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs
index 4649bee6b..d352489b8 100644
--- a/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs
+++ b/tests/ImageSharp.Tests/Drawing/Text/DrawText.Path.cs
@@ -8,6 +8,7 @@ using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.ImageSharp.Processing.Drawing.Processors;
using SixLabors.ImageSharp.Processing.Text;
+using SixLabors.ImageSharp.Processing.Text.Processors;
using SixLabors.Shapes;
using Xunit;
@@ -44,9 +45,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
null,
this.path);
- this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ this.Verify>(0);
}
[Fact]
@@ -54,9 +53,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, this.path);
- this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ this.Verify>(0);
}
[Fact]
@@ -64,9 +61,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Brushes.Solid(Rgba32.Red), this.path);
- this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ this.Verify>(0);
}
[Fact]
@@ -74,9 +69,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), this.path);
- this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ this.Verify>(0);
}
[Fact]
@@ -84,9 +77,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Rgba32.Red, this.path);
- var processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ var processor = this.Verify>(0);
SolidBrush brush = Assert.IsType>(processor.Brush);
Assert.Equal(Rgba32.Red, brush.Color);
@@ -97,9 +88,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, Rgba32.Red, this.path);
- FillRegionProcessor processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ DrawTextOnPathProcessor processor = this.Verify>(0);
SolidBrush brush = Assert.IsType>(processor.Brush);
Assert.Equal(Rgba32.Red, brush.Color);
@@ -116,9 +105,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
Pens.Dash(Rgba32.Red, 1),
this.path);
- var processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ var processor = this.Verify>(0);
}
[Fact]
@@ -126,9 +113,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), this.path);
- var processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ var processor = this.Verify>(0);
}
[Fact]
@@ -136,9 +121,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Pens.Dash(Rgba32.Red, 1), this.path);
- var processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ var processor = this.Verify>(0);
}
[Fact]
@@ -146,9 +129,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), this.path);
- FillRegionProcessor processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ DrawTextOnPathProcessor processor = this.Verify>(0);
}
[Fact]
@@ -162,12 +143,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
Pens.Dash(Rgba32.Red, 1),
this.path);
- var processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
- this.Verify>(3);
- this.Verify>(4);
- this.Verify>(5);
+ var processor = this.Verify>(0);
}
[Fact]
@@ -175,12 +151,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), this.path);
- var processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
- this.Verify>(3);
- this.Verify>(4);
- this.Verify>(5);
+ var processor = this.Verify>(0);
}
[Fact]
@@ -194,8 +165,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
Pens.Dash(Rgba32.Red, 1),
this.path);
- var processor = this.Verify>(0);
- this.Verify>(1);
+ var processor = this.Verify>(0);
}
[Fact]
@@ -203,8 +173,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), this.path);
- var processor = this.Verify>(0);
- this.Verify>(1);
+ var processor = this.Verify>(0);
}
}
}
diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs
index 88b650a3e..2a03eb415 100644
--- a/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs
+++ b/tests/ImageSharp.Tests/Drawing/Text/DrawText.cs
@@ -8,6 +8,8 @@ using SixLabors.ImageSharp.Processing.Drawing.Brushes;
using SixLabors.ImageSharp.Processing.Drawing.Pens;
using SixLabors.ImageSharp.Processing.Drawing.Processors;
using SixLabors.ImageSharp.Processing.Text;
+using SixLabors.ImageSharp.Processing.Text.Processors;
+using SixLabors.Primitives;
using SixLabors.Shapes;
using Xunit;
@@ -44,9 +46,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
null,
Vector2.Zero);
- this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ this.Verify>(0);
}
[Fact]
@@ -54,9 +54,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), null, Vector2.Zero);
- this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ this.Verify>(0);
}
[Fact]
@@ -64,9 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero);
- this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ this.Verify>(0);
}
[Fact]
@@ -74,9 +70,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Vector2.Zero);
- this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ this.Verify>(0);
}
[Fact]
@@ -84,9 +78,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Rgba32.Red, Vector2.Zero);
- var processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ var processor = this.Verify>(0);
SolidBrush brush = Assert.IsType>(processor.Brush);
Assert.Equal(Rgba32.Red, brush.Color);
@@ -97,9 +89,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, Rgba32.Red, Vector2.Zero);
- var processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ var processor = this.Verify>(0);
SolidBrush brush = Assert.IsType>(processor.Brush);
Assert.Equal(Rgba32.Red, brush.Color);
@@ -116,9 +106,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
Pens.Dash(Rgba32.Red, 1),
Vector2.Zero);
- var processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ var processor = this.Verify>(0);
}
[Fact]
@@ -126,9 +114,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, null, Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
- var processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ var processor = this.Verify>(0);
}
[Fact]
@@ -136,9 +122,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText(new TextGraphicsOptions(true), "123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
- var processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ var processor = this.Verify>(0);
}
[Fact]
@@ -146,9 +130,14 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
{
this.operations.DrawText("123", this.Font, Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
- var processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
+ var processor = this.Verify>(0);
+
+ Assert.Equal("123", processor.Text);
+ Assert.Equal(this.Font, processor.Font);
+ var penBrush = Assert.IsType>(processor.Pen.StrokeFill);
+ Assert.Equal(Rgba32.Red, penBrush.Color);
+ Assert.Equal(1, processor.Pen.StrokeWidth);
+ Assert.Equal(PointF.Empty, processor.Location);
}
[Fact]
@@ -162,50 +151,16 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
Pens.Dash(Rgba32.Red, 1),
Vector2.Zero);
- var processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
-
- this.Verify>(3);
- this.Verify>(4);
- this.Verify>(5);
- }
-
- [Fact]
- public void DrawForEachACharachterWhenPenSetAndFillFroEachWhenBrushSetDefaultOptions()
- {
- this.operations.DrawText("123", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
-
- var processor = this.Verify>(0);
- this.Verify>(1);
- this.Verify>(2);
- this.Verify>(3);
- this.Verify>(4);
- this.Verify>(5);
- }
-
- [Fact]
- public void BrushAppliesBeforePen()
- {
- this.operations.DrawText(
- new TextGraphicsOptions(true),
- "1",
- this.Font,
- Brushes.Solid(Rgba32.Red),
- Pens.Dash(Rgba32.Red, 1),
- Vector2.Zero);
-
- var processor = this.Verify>(0);
- this.Verify>(1);
- }
+ var processor = this.Verify>(0);
- [Fact]
- public void BrushAppliesBeforPenDefaultOptions()
- {
- this.operations.DrawText("1", this.Font, Brushes.Solid(Rgba32.Red), Pens.Dash(Rgba32.Red, 1), Vector2.Zero);
-
- var processor = this.Verify>(0);
- this.Verify>(1);
+ Assert.Equal("123", processor.Text);
+ Assert.Equal(this.Font, processor.Font);
+ var brush = Assert.IsType>(processor.Brush);
+ Assert.Equal(Rgba32.Red, brush.Color);
+ Assert.Equal(PointF.Empty, processor.Location);
+ var penBrush = Assert.IsType>(processor.Pen.StrokeFill);
+ Assert.Equal(Rgba32.Red, penBrush.Color);
+ Assert.Equal(1, processor.Pen.StrokeWidth);
}
}
}
diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs
index a9c7a6ebb..f90d5996e 100644
--- a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs
+++ b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
using System;
using System.Linq;
using System.Text;
-
+ using SixLabors.ImageSharp.Processing.Drawing.Brushes.GradientBrushes;
using SixLabors.Primitives;
[GroupOutput("Drawing/Text")]
@@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
private const string TestText2 =
"THISISTESTWORDS ";
-
+
[Theory]
[WithSolidFilledImages(200, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "SixLaborsSampleAB.woff", AB)]
[WithSolidFilledImages(900, 100, "White", PixelTypes.Rgba32, 50, 0, 0, "OpenSans-Regular.ttf", TestText)]
@@ -51,9 +51,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
provider.VerifyOperation(
img =>
- {
- img.Mutate(c => c.DrawText(text, new Font(font, fontSize), color, new PointF(x, y)));
- },
+ {
+ img.Mutate(c => c.DrawText(text, new Font(font, fontSize), color, new PointF(x, y)));
+ },
$"{fontName}-{fontSize}-{fnDisplayText}-({x},{y})",
appendPixelTypeToFileName: false,
appendSourceFileOrDescription: true);
@@ -84,12 +84,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text
}
var textOptions = new TextGraphicsOptions
- {
- Antialias = true,
- ApplyKerning = true,
- VerticalAlignment = VerticalAlignment.Top,
- HorizontalAlignment = HorizontalAlignment.Left,
- };
+ {
+ Antialias = true,
+ ApplyKerning = true,
+ VerticalAlignment = VerticalAlignment.Top,
+ HorizontalAlignment = HorizontalAlignment.Left,
+ };
TPixel color = NamedColors.Black;
provider.VerifyOperation(
diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj
index 139df3972..fa165de5f 100644
--- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj
+++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj
@@ -45,6 +45,12 @@
+
+ PreserveNewest
+
+
+ PreserveNewest
+
PreserveNewest