From 75209ca205d37b2d7c0b58aaa905cc6c0bd1711b Mon Sep 17 00:00:00 2001 From: Xavier Cho Date: Thu, 12 Sep 2019 12:55:05 +0900 Subject: [PATCH 1/6] Remove a redundant check for the paint boundary --- src/ImageSharp.Drawing/Processing/PathGradientBrush.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs index fc84e4a1f..553ed181c 100644 --- a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs @@ -231,11 +231,6 @@ namespace SixLabors.ImageSharp.Processing return new Color(this.centerColor).ToPixel(); } - if (!this.path.Contains(point)) - { - return Color.Transparent.ToPixel(); - } - Vector2 direction = Vector2.Normalize(point - this.center); PointF end = point + (PointF)(direction * this.maxDistance); From b9d6a14af6a85a257f15976e4b0f6c8271f23d78 Mon Sep 17 00:00:00 2001 From: Xavier Cho Date: Thu, 12 Sep 2019 12:55:28 +0900 Subject: [PATCH 2/6] Ignore invalid gradient positions rather than throwing an error There can be such cases where it fails to find intersection points for a given position, especially when the polygon is small. Such an input would result in an error previously, but now it renders correctly. --- src/ImageSharp.Drawing/Processing/PathGradientBrush.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs index 553ed181c..636551b64 100644 --- a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs @@ -237,6 +237,11 @@ namespace SixLabors.ImageSharp.Processing (Edge edge, Intersection? info) = this.FindIntersection(point, end); + if (!info.HasValue) + { + return Color.Transparent.ToPixel(); + } + PointF intersection = info.Value.Point; Vector4 edgeColor = edge.ColorAt(intersection); From ad56a756ceba888b37979031f7f72285b7bbca09 Mon Sep 17 00:00:00 2001 From: Turnerj Date: Thu, 12 Sep 2019 16:27:56 +0930 Subject: [PATCH 3/6] Fix horizontally out-of-bounds error when drawing text --- .../Text/DrawTextProcessor{TPixel}.cs | 17 +++++++++++------ .../Drawing/Text/DrawTextOnImageTests.cs | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs index 3809200d5..ea042635d 100644 --- a/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs +++ b/src/ImageSharp.Drawing/Processing/Processors/Text/DrawTextProcessor{TPixel}.cs @@ -90,26 +90,31 @@ namespace SixLabors.ImageSharp.Processing.Processors.Text Buffer2D buffer = operation.Map; int startY = operation.Location.Y; int startX = operation.Location.X; - int offSetSpan = 0; + int offsetSpan = 0; if (startX < 0) { - offSetSpan = -startX; + offsetSpan = -startX; startX = 0; } - int fistRow = 0; + if (startX >= source.Width) + { + continue; + } + + int firstRow = 0; if (startY < 0) { - fistRow = -startY; + firstRow = -startY; } int maxHeight = source.Height - startY; int end = Math.Min(operation.Map.Height, maxHeight); - for (int row = fistRow; row < end; row++) + for (int row = firstRow; row < end; row++) { int y = startY + row; - Span span = buffer.GetRowSpan(row).Slice(offSetSpan); + Span span = buffer.GetRowSpan(row).Slice(offsetSpan); app.Apply(span, startX, y); } } diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs index 7bebdabd3..dc1ccc813 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs @@ -65,6 +65,24 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text } } + [Theory] + [WithSolidFilledImages(1500, 500, "White", PixelTypes.Rgba32)] + public void DoesntThrowExceptionWhenOverlappingRightEdge_Issue688_2(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image img = provider.GetImage()) + { + Font font = SystemFonts.CreateFont("Arial", 39, FontStyle.Regular); + string text = new string('a', 10000); // exception + // string text = "Hello"; // no exception + Rgba32 color = Rgba32.Black; + var point = new PointF(100, 100); + + img.Mutate(ctx => ctx.DrawText(text, font, color, point)); + } + } + + [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)] From 6a0ce745e8fad13139ebbee05a376316eaaf26b1 Mon Sep 17 00:00:00 2001 From: Turnerj Date: Thu, 12 Sep 2019 16:41:11 +0930 Subject: [PATCH 4/6] Updated font used for test --- tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs index dc1ccc813..90740a4bb 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs @@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text { using (Image img = provider.GetImage()) { - Font font = SystemFonts.CreateFont("Arial", 39, FontStyle.Regular); + Font font = SystemFonts.CreateFont("OpenSans-Regular.ttf", 39, FontStyle.Regular); string text = new string('a', 10000); // exception // string text = "Hello"; // no exception Rgba32 color = Rgba32.Black; From fa1d9efaa2e43f31e2c6f0ea9a53eb95598b21f9 Mon Sep 17 00:00:00 2001 From: Turnerj Date: Thu, 12 Sep 2019 16:56:30 +0930 Subject: [PATCH 5/6] Use local CreateFont method --- tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs index 90740a4bb..a767a686e 100644 --- a/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs +++ b/tests/ImageSharp.Tests/Drawing/Text/DrawTextOnImageTests.cs @@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing.Text { using (Image img = provider.GetImage()) { - Font font = SystemFonts.CreateFont("OpenSans-Regular.ttf", 39, FontStyle.Regular); + Font font = CreateFont("OpenSans-Regular.ttf", 39); string text = new string('a', 10000); // exception // string text = "Hello"; // no exception Rgba32 color = Rgba32.Black; From 7e06b63420dea2ca51ecaad108d182ff2bc7919e Mon Sep 17 00:00:00 2001 From: Xavier Cho Date: Thu, 12 Sep 2019 19:37:07 +0900 Subject: [PATCH 6/6] Accept points instead of line segments as constructor argument Note that this is a breaking change. --- .../Processing/PathGradientBrush.cs | 43 +++++----- .../Drawing/FillPathGradientBrushTests.cs | 86 ++++--------------- 2 files changed, 39 insertions(+), 90 deletions(-) diff --git a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs index 636551b64..7315dc5a3 100644 --- a/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs +++ b/src/ImageSharp.Drawing/Processing/PathGradientBrush.cs @@ -18,8 +18,6 @@ namespace SixLabors.ImageSharp.Processing /// public sealed class PathGradientBrush : IBrush { - private readonly Polygon path; - private readonly IList edges; private readonly Color centerColor; @@ -27,20 +25,20 @@ namespace SixLabors.ImageSharp.Processing /// /// Initializes a new instance of the class. /// - /// Line segments of a polygon that represents the gradient area. + /// Points that constitute a polygon that represents the gradient area. /// Array of colors that correspond to each point in the polygon. /// Color at the center of the gradient area to which the other colors converge. - public PathGradientBrush(ILineSegment[] lines, Color[] colors, Color centerColor) + public PathGradientBrush(PointF[] points, Color[] colors, Color centerColor) { - if (lines == null) + if (points == null) { - throw new ArgumentNullException(nameof(lines)); + throw new ArgumentNullException(nameof(points)); } - if (lines.Length < 3) + if (points.Length < 3) { throw new ArgumentOutOfRangeException( - nameof(lines), + nameof(points), "There must be at least 3 lines to construct a path gradient brush."); } @@ -56,22 +54,30 @@ namespace SixLabors.ImageSharp.Processing "One or more color is needed to construct a path gradient brush."); } - this.path = new Polygon(lines); + int size = points.Length; + + var lines = new ILineSegment[size]; + + for (int i = 0; i < size; i++) + { + lines[i] = new LinearLineSegment(points[i % size], points[(i + 1) % size]); + } + this.centerColor = centerColor; Color ColorAt(int index) => colors[index % colors.Length]; - this.edges = this.path.LineSegments.Select(s => new Path(s)) + this.edges = lines.Select(s => new Path(s)) .Select((path, i) => new Edge(path, ColorAt(i), ColorAt(i + 1))).ToList(); } /// /// Initializes a new instance of the class. /// - /// Line segments of a polygon that represents the gradient area. + /// Points that constitute a polygon that represents the gradient area. /// Array of colors that correspond to each point in the polygon. - public PathGradientBrush(ILineSegment[] lines, Color[] colors) - : this(lines, colors, CalculateCenterColor(colors)) + public PathGradientBrush(PointF[] points, Color[] colors) + : this(points, colors, CalculateCenterColor(colors)) { } @@ -82,7 +88,7 @@ namespace SixLabors.ImageSharp.Processing GraphicsOptions options) where TPixel : struct, IPixel { - return new PathGradientBrushApplicator(source, this.path, this.edges, this.centerColor, options); + return new PathGradientBrushApplicator(source, this.edges, this.centerColor, options); } private static Color CalculateCenterColor(Color[] colors) @@ -182,8 +188,6 @@ namespace SixLabors.ImageSharp.Processing private class PathGradientBrushApplicator : BrushApplicator where TPixel : struct, IPixel { - private readonly Path path; - private readonly PointF center; private readonly Vector4 centerColor; @@ -196,24 +200,21 @@ namespace SixLabors.ImageSharp.Processing /// Initializes a new instance of the class. /// /// The source image. - /// A polygon that represents the gradient area. /// Edges of the polygon. /// Color at the center of the gradient area to which the other colors converge. /// The options. public PathGradientBrushApplicator( ImageFrame source, - Path path, IList edges, Color centerColor, GraphicsOptions options) : base(source, options) { - this.path = path; this.edges = edges; - PointF[] points = path.LineSegments.Select(s => s.EndPoint).ToArray(); + PointF[] points = edges.Select(s => s.Start).ToArray(); - this.center = points.Aggregate((p1, p2) => p1 + p2) / points.Length; + this.center = points.Aggregate((p1, p2) => p1 + p2) / edges.Count; this.centerColor = centerColor.ToVector4(); this.maxDistance = points.Select(p => (Vector2)(p - this.center)).Select(d => d.Length()).Max(); diff --git a/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs b/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs index d76893108..1ab747baf 100644 --- a/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillPathGradientBrushTests.cs @@ -7,7 +7,6 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.Primitives; -using SixLabors.Shapes; using Xunit; @@ -27,17 +26,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing TolerantComparer, image => { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), - new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), - new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), - new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) - }; - + PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; Color[] colors = { Color.Black, Color.Red, Color.Yellow, Color.Green }; - var brush = new PathGradientBrush(path, colors); + var brush = new PathGradientBrush(points, colors); image.Mutate(x => x.Fill(brush)); image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); @@ -53,16 +45,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing TolerantComparer, image => { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(5, 0), new PointF(10, 10)), - new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), - new LinearLineSegment(new PointF(0, 10), new PointF(5, 0)) - }; - + PointF[] points = { new PointF(5, 0), new PointF(10, 10), new PointF(0, 10) }; Color[] colors = { Color.Red, Color.Green, Color.Blue }; - var brush = new PathGradientBrush(path, colors); + var brush = new PathGradientBrush(points, colors); image.Mutate(x => x.Fill(brush)); image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); @@ -76,17 +62,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing { using (Image image = provider.GetImage()) { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), - new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), - new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), - new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) - }; - + PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; Color[] colors = { Color.Red }; - var brush = new PathGradientBrush(path, colors); + var brush = new PathGradientBrush(points, colors); image.Mutate(x => x.Fill(brush)); @@ -103,17 +82,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing TolerantComparer, image => { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), - new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), - new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), - new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) - }; - + PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; Color[] colors = { Color.Red, Color.Yellow }; - var brush = new PathGradientBrush(path, colors); + var brush = new PathGradientBrush(points, colors); image.Mutate(x => x.Fill(brush)); image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); @@ -129,17 +101,10 @@ namespace SixLabors.ImageSharp.Tests.Drawing TolerantComparer, image => { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), - new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), - new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), - new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) - }; - + PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; Color[] colors = { Color.Black, Color.Red, Color.Yellow, Color.Green }; - var brush = new PathGradientBrush(path, colors, Color.White); + var brush = new PathGradientBrush(points, colors, Color.White); image.Mutate(x => x.Fill(brush)); image.DebugSave(provider, appendPixelTypeToFileName: false, appendSourceFileOrDescription: false); @@ -157,17 +122,12 @@ namespace SixLabors.ImageSharp.Tests.Drawing } [Fact] - public void ShouldThrowArgumentOutOfRangeExceptionWhenLessThan3LinesAreGiven() + public void ShouldThrowArgumentOutOfRangeExceptionWhenLessThan3PointsAreGiven() { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), - new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)) - }; - + PointF[] points = { new PointF(0, 0), new PointF(10, 0) }; Color[] colors = { Color.Black, Color.Red, Color.Yellow, Color.Green }; - PathGradientBrush Create() => new PathGradientBrush(path, colors, Color.White); + PathGradientBrush Create() => new PathGradientBrush(points, colors, Color.White); Assert.Throws(Create); } @@ -175,15 +135,9 @@ namespace SixLabors.ImageSharp.Tests.Drawing [Fact] public void ShouldThrowArgumentNullExceptionWhenColorsAreNull() { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), - new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), - new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), - new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) - }; + PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; - PathGradientBrush Create() => new PathGradientBrush(path, null, Color.White); + PathGradientBrush Create() => new PathGradientBrush(points, null, Color.White); Assert.Throws(Create); } @@ -191,17 +145,11 @@ namespace SixLabors.ImageSharp.Tests.Drawing [Fact] public void ShouldThrowArgumentOutOfRangeExceptionWhenEmptyColorArrayIsGiven() { - ILineSegment[] path = - { - new LinearLineSegment(new PointF(0, 0), new PointF(10, 0)), - new LinearLineSegment(new PointF(10, 0), new PointF(10, 10)), - new LinearLineSegment(new PointF(10, 10), new PointF(0, 10)), - new LinearLineSegment(new PointF(0, 10), new PointF(0, 0)) - }; + PointF[] points = { new PointF(0, 0), new PointF(10, 0), new PointF(10, 10), new PointF(0, 10) }; var colors = new Color[0]; - PathGradientBrush Create() => new PathGradientBrush(path, colors, Color.White); + PathGradientBrush Create() => new PathGradientBrush(points, colors, Color.White); Assert.Throws(Create); }