From 3bdb893cc1db6b6548b4540e70b6bf907da5365f Mon Sep 17 00:00:00 2001 From: Anthony Truskinger Date: Tue, 1 Dec 2020 02:11:15 +1000 Subject: [PATCH] Updated dependencies, removed patches, add new ones - Updated to .NET 5 - Updated ImageSharp.Drawing. Some bugs have been fixed, others introduced. - Fixed score drawing bugs and faulty event asset --- AudioAnalysis.sln | 1 + global.json | 2 +- src/AED/AED.fsproj | 10 +- .../AcousticWorkbench.csproj | 2 +- .../Acoustics.Shared.FSharp.fsproj | 6 + src/Acoustics.Shared/Acoustics.Shared.csproj | 20 +-- src/Acoustics.Shared/ImageSharp/Drawing.cs | 143 ++++++++++++------ src/Acoustics.Tools/Acoustics.Tools.csproj | 2 +- src/AnalysisBase/AnalysisBase.csproj | 2 +- src/AnalysisPrograms/AnalysisPrograms.csproj | 8 +- src/AudioAnalysisTools/AcousticEvent.cs | 6 +- .../AudioAnalysisTools.csproj | 4 +- .../Events/Drawing/EventDrawer.cs | 6 +- src/TowseyLibrary/ImageTools.cs | 36 ++--- src/TowseyLibrary/TowseyLibrary.csproj | 6 +- tests/AED.Test/AED.Test.fsproj | 6 + tests/Acoustics.Test/Acoustics.Test.csproj | 10 +- .../AudioAnalysisTools/AcousticEventTests.cs | 1 - .../Shared/Drawing/DrawLineTest.cs | 38 +++-- .../Shared/Drawing/DrawingTests.cs | 32 ++-- .../Shared/Drawing/RectangleCornerBugTest.cs | 19 +-- .../TestHelpers/GeneratedImageTest.cs | 11 ++ tests/Acoustics.Test/TestHelpers/TestImage.cs | 2 + ...ticEventTests_SuperimposeEventsOnImage.png | 4 +- tests/Fixtures/roboto_font_test.png | 4 +- 25 files changed, 239 insertions(+), 142 deletions(-) diff --git a/AudioAnalysis.sln b/AudioAnalysis.sln index ae43fd75d..6f51c6015 100644 --- a/AudioAnalysis.sln +++ b/AudioAnalysis.sln @@ -14,6 +14,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Directory.Build.props = Directory.Build.props build\download_ap.ps1 = build\download_ap.ps1 src\git_version.ps1 = src\git_version.ps1 + global.json = global.json README.md = README.md style.ruleset = style.ruleset stylecop.json = stylecop.json diff --git a/global.json b/global.json index 49f9bd412..e8b1b46e6 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "3.1.201", + "version": "5.0.100", "rollForward": "feature" } } \ No newline at end of file diff --git a/src/AED/AED.fsproj b/src/AED/AED.fsproj index af4492b2f..de0c86141 100644 --- a/src/AED/AED.fsproj +++ b/src/AED/AED.fsproj @@ -10,8 +10,8 @@ - - + + @@ -26,4 +26,10 @@ + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + \ No newline at end of file diff --git a/src/AcousticWorkbench/AcousticWorkbench.csproj b/src/AcousticWorkbench/AcousticWorkbench.csproj index eaa4f5b61..3d32fc30f 100644 --- a/src/AcousticWorkbench/AcousticWorkbench.csproj +++ b/src/AcousticWorkbench/AcousticWorkbench.csproj @@ -12,7 +12,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Acoustics.Shared.FSharp/Acoustics.Shared.FSharp.fsproj b/src/Acoustics.Shared.FSharp/Acoustics.Shared.FSharp.fsproj index c7404b21a..1b09e8726 100644 --- a/src/Acoustics.Shared.FSharp/Acoustics.Shared.FSharp.fsproj +++ b/src/Acoustics.Shared.FSharp/Acoustics.Shared.FSharp.fsproj @@ -41,4 +41,10 @@ PreserveNewest + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + \ No newline at end of file diff --git a/src/Acoustics.Shared/Acoustics.Shared.csproj b/src/Acoustics.Shared/Acoustics.Shared.csproj index 561805fa8..33b96196d 100644 --- a/src/Acoustics.Shared/Acoustics.Shared.csproj +++ b/src/Acoustics.Shared/Acoustics.Shared.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 Acoustics.Shared @@ -15,7 +15,7 @@ - + @@ -24,23 +24,23 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + - - + + - - + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + \ No newline at end of file diff --git a/src/Acoustics.Shared/ImageSharp/Drawing.cs b/src/Acoustics.Shared/ImageSharp/Drawing.cs index 89881f7e2..b479b44b7 100644 --- a/src/Acoustics.Shared/ImageSharp/Drawing.cs +++ b/src/Acoustics.Shared/ImageSharp/Drawing.cs @@ -9,6 +9,7 @@ namespace Acoustics.Shared.ImageSharp using System.Linq; using SixLabors.Fonts; using SixLabors.ImageSharp; + using SixLabors.ImageSharp.Drawing; using SixLabors.ImageSharp.Drawing.Processing; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -47,12 +48,15 @@ public static class Drawing GraphicsOptions = new GraphicsOptions() { Antialias = false, - BlendPercentage = 1, - ColorBlendingMode = PixelColorBlendingMode.Normal, - AntialiasSubpixelDepth = 0, + //BlendPercentage = 1, + //ColorBlendingMode = PixelColorBlendingMode.Normal, + //AntialiasSubpixelDepth = 0, + + }, + ShapeOptions = new ShapeOptions() + { + //IntersectionRule = SixLabors.ImageSharp.Drawing.IntersectionRule.Nonzero, }, - - //IntersectionRule = IntersectionRule.OddEven, }; /// @@ -182,6 +186,32 @@ public static GlyphMetric[] MeasureCharacters(string text, Font font, PointF loc /// public class NoAA { + /// + /// Defines behaviour for where to draw stroke relative to the true real line + /// defined by a path. + /// + //public enum Basis + //{ + // /// + // /// Draws the line pixels either left of or above the target line. + // /// Essentially on the side of the line that is closer to canvas origin. + // /// + // LeftTop = -1, + + // /// + // /// Draws the line using the default ImageSharp method, which splits stroke + // /// evenly over either side of the middle line. + // /// + // Straddle = 0, + + // /// + // /// Draws the line pixels either right of or below the target line. + // /// Essentially on the side of the line that is further from the canvas origin. + // /// + // RightBottom = 1, + //} + + // // AT 2020-11: Update bug seems to be fixed. public static readonly PointF Bug28Offset = new PointF(0.0f, 0.5f); private readonly IImageProcessingContext context; @@ -193,31 +223,45 @@ public NoAA(IImageProcessingContext context) public void DrawLine(IPen pen, int x1, int y1, int x2, int y2) { - this.DrawLines(pen, new Point(x1, y1), new Point(x2, y2)); + this.DrawLines(pen, new PointF(x1, y1), new PointF(x2, y2)); } + //public void DrawLine(IPen pen, int x1, int y1, int x2, int y2, Basis basis) + //{ + // if (basis is not Basis.Straddle) + // { + // basis = basis switch { + + // } + // } + //} + public void DrawLines(IPen pen, params PointF[] points) { - // i've no idea why, but repeating the first point and last point - // and adding random offsets in reduces visual errors in line drawing! - var slope = points[0].Y.CompareTo(points[^1].Y) switch - { - -1 => 0.0f, - 0 => 0, - 1 => 0.5f, - _ => throw new NotImplementedException(), - }; - var offset = new PointF(slope, Bug28Offset.Y); + // https://github.com/SixLabors/ImageSharp.Drawing/issues/108 + var strokeBugOffset = pen.StrokeWidth / 2f; + int strokeBugEncountered = 0; + var offset = new PointF(0, PenOffset(pen.StrokeWidth)); var modifiedPoints = points .Select(p => p + offset) - .Prepend(points[0] + Bug28Offset) - .Append(points[^1] + Bug28Offset) + .Select(p => + { + if ((p.Y - strokeBugOffset) < 0) + { + strokeBugEncountered++; + return new PointF(p.X, 0f + MathF.Round(strokeBugOffset / 2f)); + } + + return p; + }) .ToArray(); - this.context.DrawLines( - NoAntiAlias, - pen, - modifiedPoints); + if (strokeBugEncountered > points.Length - 1) + { + pen = new Pen(pen.StrokeFill, MathF.Max(1, MathF.Round(strokeBugOffset)), pen.StrokePattern.ToArray()); + } + + this.context.DrawLines(NoAntiAlias, pen, modifiedPoints); } public void DrawLines(Color color, float thickness, params PointF[] points) @@ -228,8 +272,8 @@ public void DrawLines(Color color, float thickness, params PointF[] points) public void DrawRectangle(Pen pen, int x1, int y1, int x2, int y2) { var r = RectangleF.FromLTRB(x1, y1, x2, y2); - r.Offset(Bug28Offset); - this.context.Draw(Drawing.NoAntiAlias, pen, r); + //r.Offset(Bug28Offset); + this.context.Draw(NoAntiAlias, pen, r); } /// @@ -239,14 +283,14 @@ public void DrawRectangle(Pen pen, int x1, int y1, int x2, int y2) public void DrawRectangle(Color color, int x1, int y1, int x2, int y2, float thickness = 1f) { var r = RectangleF.FromLTRB(x1, y1, x2, y2); - r.Offset(Bug28Offset); - this.context.Draw(Drawing.NoAntiAlias, color, thickness, r); + //r.Offset(Bug28Offset); + this.context.Draw(NoAntiAlias, color, thickness, r); } public void DrawRectangle(Pen border, RectangleF rectangle) { - rectangle.Offset(Bug28Offset); - this.context.Draw(Drawing.NoAntiAlias, border, rectangle); + //rectangle.Offset(Bug28Offset); + this.context.Draw(NoAntiAlias, border, rectangle); } /// @@ -258,44 +302,43 @@ public void DrawRectangle(Pen border, RectangleF rectangle) /// ImageSharp's Draw Rectangle is unpredictable and buggy, especially for /// non-antialiased operations. See Acoustics.Test.Shared.Drawing.RectangleCornerBugTest. /// This method instead draws four lines as the border. + /// // AT 2020-11: Update bug seems to be fixed. /// public void DrawBorderInset(Pen border, RectangleF rectangle) { // rounder border thickness border = new Pen( border.StrokeFill, - MathF.Round(border.StrokeWidth), + MathF.Floor(border.StrokeWidth), border.StrokePattern.ToArray()); // first round rectangle to nice coordinates var rect = Rectangle.Round(rectangle); - // construct point coordinats, offset by pen width inset into rectangle. - var penOffset = MathF.Floor(border.StrokeWidth / 2f); - - // empircally found to satisfy tests - i have no idea why it works - var widthAdjustment = ((int)border.StrokeWidth % 2) == 0 ? 0.0f : -0.5f; - - float left = rect.Left + penOffset + 0.0f; - float top = rect.Top + penOffset + +Bug28Offset.Y; - float right = rect.Right - penOffset + widthAdjustment; - float bottom = rect.Bottom - penOffset + Bug28Offset.Y + widthAdjustment; - - this.context.DrawPolygon( - NoAntiAlias, - border, - new PointF(left, top), - new PointF(right, top), - new PointF(right, bottom), - new PointF(left, bottom), - new PointF(left, top)); + // construct point coordinates, offset by pen width inset into rectangle. + // the offset wiggle by 0.5 of a pixel defeats the ImageSharp method of straddling a border over + // the imaginary centerline of the segment. Without this a border of 2px will be rendered with round + // corners and 3px wide. + var penOffset = MathF.Floor(border.StrokeWidth / 2.0f) + PenOffset(border.StrokeWidth); + + float left = rect.Left + penOffset; + float top = rect.Top + penOffset; + float right = rect.Right - penOffset - 1; + float bottom = rect.Bottom - penOffset - 1; + + this.context.Draw(NoAntiAlias, border, RectangleF.FromLTRB(left, top, right, bottom)); } public void FillRectangle(IBrush brush, int x1, int y1, int x2, int y2) { var r = RectangleF.FromLTRB(x1, y1, x2, y2); - r.Offset(Bug28Offset); - this.context.Fill(Drawing.NoAntiAlias, brush, r); + //r.Offset(Bug28Offset); + this.context.Fill(NoAntiAlias, brush, r); + } + + private static float PenOffset(float strokeWidth) + { + return (strokeWidth % 2f == 0) ? -0.5f : 0; } } } diff --git a/src/Acoustics.Tools/Acoustics.Tools.csproj b/src/Acoustics.Tools/Acoustics.Tools.csproj index e552ff2ff..f54b61e0e 100644 --- a/src/Acoustics.Tools/Acoustics.Tools.csproj +++ b/src/Acoustics.Tools/Acoustics.Tools.csproj @@ -16,7 +16,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/AnalysisBase/AnalysisBase.csproj b/src/AnalysisBase/AnalysisBase.csproj index 37e8603b6..8addfd299 100644 --- a/src/AnalysisBase/AnalysisBase.csproj +++ b/src/AnalysisBase/AnalysisBase.csproj @@ -17,7 +17,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/AnalysisPrograms/AnalysisPrograms.csproj b/src/AnalysisPrograms/AnalysisPrograms.csproj index 0b00f337a..bf5927669 100644 --- a/src/AnalysisPrograms/AnalysisPrograms.csproj +++ b/src/AnalysisPrograms/AnalysisPrograms.csproj @@ -1,4 +1,4 @@ - + 9.0.30729 Exe @@ -63,9 +63,9 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/AudioAnalysisTools/AcousticEvent.cs b/src/AudioAnalysisTools/AcousticEvent.cs index e07261a14..47196e2e2 100644 --- a/src/AudioAnalysisTools/AcousticEvent.cs +++ b/src/AudioAnalysisTools/AcousticEvent.cs @@ -441,7 +441,7 @@ public void DrawEvent(Image imageToReturn, double framesPerSecond, double imageToReturn.Mutate(g => g.NoAA().DrawRectangle(borderPen, t1, y1, t2, y2)); - //draw on the elements from the hit matrix + // draw on the elements from the hit matrix if (this.HitElements != null) { foreach (var hitElement in this.HitElements) @@ -450,12 +450,12 @@ public void DrawEvent(Image imageToReturn, double framesPerSecond, double } } - //draw the score bar to indicate relative score + // draw the score bar to indicate relative score var eventHeight = y2 - y1 + 1; int scoreHt = (int)Math.Round(eventHeight * this.ScoreNormalised); imageToReturn.Mutate(g => { - g.NoAA().DrawLine(scorePen, t1, y2 - scoreHt, t1, y2 + 1); + g.NoAA().DrawLine(scorePen, t1, y2 - scoreHt + 1, t1, y2); g.DrawTextSafe(this.Name, Drawing.Tahoma6, Color.Black, new PointF(t1, y1 - 4)); }); } diff --git a/src/AudioAnalysisTools/AudioAnalysisTools.csproj b/src/AudioAnalysisTools/AudioAnalysisTools.csproj index 2605ff39a..7283860a1 100644 --- a/src/AudioAnalysisTools/AudioAnalysisTools.csproj +++ b/src/AudioAnalysisTools/AudioAnalysisTools.csproj @@ -29,8 +29,8 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/AudioAnalysisTools/Events/Drawing/EventDrawer.cs b/src/AudioAnalysisTools/Events/Drawing/EventDrawer.cs index 6162f709a..b3e70a685 100644 --- a/src/AudioAnalysisTools/Events/Drawing/EventDrawer.cs +++ b/src/AudioAnalysisTools/Events/Drawing/EventDrawer.cs @@ -36,11 +36,15 @@ public static void DrawScoreIndicator(this SpectralEvent @event, IImageProcessin var rect = options.Converters.GetPixelRectangle(@event); + // we'drawing events with an inset rectangle border, which means bottom and right borders are one pixel closer than normal + // we need to account for this difference here + var insetBorderOffset = options.Border.StrokeWidth; // usually 1px + // truncate score bar to neatest whole pixel after scaling by height var scaledHeight = (int)((float)normalizedScore * rect.Height); var top = new PointF(rect.Left, rect.Bottom - scaledHeight); - var bottom = new PointF(rect.Left, rect.Bottom); + var bottom = new PointF(rect.Left, rect.Bottom - insetBorderOffset); // the order of the supplied points is important! // DO NOT CHANGE diff --git a/src/TowseyLibrary/ImageTools.cs b/src/TowseyLibrary/ImageTools.cs index cdac940bd..f9828d83b 100644 --- a/src/TowseyLibrary/ImageTools.cs +++ b/src/TowseyLibrary/ImageTools.cs @@ -12,6 +12,7 @@ namespace TowseyLibrary using Acoustics.Shared.ImageSharp; using AForge.Imaging.Filters; using MathNet.Numerics.LinearAlgebra; + using MoreLinq; using SixLabors.ImageSharp; using SixLabors.ImageSharp.ColorSpaces; using SixLabors.ImageSharp.ColorSpaces.Conversion; @@ -3774,7 +3775,6 @@ public static Image CombineImagesVertically(int? maximumWidth, Image[] /// /// Stacks the passed images one on top of the other. - /// Assumes that all images have the same width. /// /// A list of images. /// A single image. @@ -3788,33 +3788,25 @@ public static Image CombineImagesInLine(List> list) /// Stacks the passed images one on top of the other. /// Assumes that all images have the same width. /// - /// An array of images. + /// An array of images. /// A single image. - public static Image CombineImagesInLine(params Image[] array) + public static Image CombineImagesInLine(params Image[] images) where T : unmanaged, IPixel { - int height = 0; - int compositeWidth = 0; - foreach (var image in array) - { - if (image == null) - { - continue; - } + return CombineImagesInLine(Color.Black, images); + } - compositeWidth += image.Width; - if (height < image.Height) - { - height = image.Height; - } - } + public static Image CombineImagesInLine(Color fill, params Image[] images) + where T : unmanaged, IPixel + { + var maxHeight = images.Max(i => i.Height); + var totalWidth = images.Sum(i => i.Width); - //Image compositeBmp = new Image(compositeWidth, height); - var compositeBmp = Drawing.NewImage(compositeWidth, height, Color.Black); + var composite = Drawing.NewImage(totalWidth, maxHeight, fill); int xOffset = 0; - compositeBmp.Mutate(x => + composite.Mutate(x => { - foreach (var image in array) + foreach (var image in images) { if (image == null) { @@ -3826,7 +3818,7 @@ public static Image CombineImagesInLine(params Image[] array) } }); - return compositeBmp; + return composite; } public static Tuple DetectLine(double[,] m, int row, int col, int lineLength, double centreThreshold, int resolutionAngle) diff --git a/src/TowseyLibrary/TowseyLibrary.csproj b/src/TowseyLibrary/TowseyLibrary.csproj index a331a6284..36329beb8 100644 --- a/src/TowseyLibrary/TowseyLibrary.csproj +++ b/src/TowseyLibrary/TowseyLibrary.csproj @@ -24,9 +24,9 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/AED.Test/AED.Test.fsproj b/tests/AED.Test/AED.Test.fsproj index 6b3e0c758..a3d1d981a 100644 --- a/tests/AED.Test/AED.Test.fsproj +++ b/tests/AED.Test/AED.Test.fsproj @@ -41,4 +41,10 @@ + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + \ No newline at end of file diff --git a/tests/Acoustics.Test/Acoustics.Test.csproj b/tests/Acoustics.Test/Acoustics.Test.csproj index 2e5aa7cd1..e715340bb 100644 --- a/tests/Acoustics.Test/Acoustics.Test.csproj +++ b/tests/Acoustics.Test/Acoustics.Test.csproj @@ -30,8 +30,8 @@ - - + + @@ -45,4 +45,10 @@ + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + \ No newline at end of file diff --git a/tests/Acoustics.Test/AudioAnalysisTools/AcousticEventTests.cs b/tests/Acoustics.Test/AudioAnalysisTools/AcousticEventTests.cs index 45efe10f3..1c4a3ad96 100644 --- a/tests/Acoustics.Test/AudioAnalysisTools/AcousticEventTests.cs +++ b/tests/Acoustics.Test/AudioAnalysisTools/AcousticEventTests.cs @@ -60,7 +60,6 @@ public void TestSonogramWithEventsOverlay() this.ActualImage = substituteSonogram; - // BUG: this asset is faulty. See https://github.com/QutEcoacoustics/audio-analysis/issues/300#issuecomment-601537263 this.ExpectedImage = Image.Load( PathHelper.ResolveAssetPath("AcousticEventTests_SuperimposeEventsOnImage.png")); diff --git a/tests/Acoustics.Test/Shared/Drawing/DrawLineTest.cs b/tests/Acoustics.Test/Shared/Drawing/DrawLineTest.cs index b5d8ee0e3..406ccb2af 100644 --- a/tests/Acoustics.Test/Shared/Drawing/DrawLineTest.cs +++ b/tests/Acoustics.Test/Shared/Drawing/DrawLineTest.cs @@ -48,8 +48,9 @@ public void DiagonalLineNotDrawnProperly() this.ActualImage = actual; // this should pass without bug - //this.AssertImagesEqual(); + this.AssertImagesEqual(); + // AT 2020-11: Update bug seems to be fixed. Leaving the passing test to ensure stable. /* Assert.Fail failed. Images are not equal - total delta 0.00019455253 is not less than tolerance 0. Difference are: @@ -69,8 +70,8 @@ Assert.Fail failed. Images are not equal - total delta 0.00019455253 is not less // . R . . . // . . . . . // . . . . . - Assert.AreEqual(color.ToPixel(), actual[2, 1]); - Assert.AreEqual(color.ToPixel(), actual[1, 2]); + //Assert.AreEqual(color.ToPixel(), actual[2, 1]); + //Assert.AreEqual(color.ToPixel(), actual[1, 2]); } [TestMethod] @@ -84,15 +85,19 @@ public void DiagonalLineNotDrawnProperlyCrossCheckBug28() var pen = new Pen(color, 1); // a 3-pixel line, bottom left to top right, 1px padding around edge + // because offsets drawn with a +0.5px vertical offset, line is drawn either side of middle point // . . . . . // . . . R . - // . . R . . + // . . R R . + // . R R . . // . R . . . - // . . . . . var expected = new Image(5, 5); expected[1, 3] = color; + expected[1, 4] = color; expected[2, 2] = color; + expected[2, 3] = color; expected[3, 1] = color; + expected[3, 2] = color; var actual = new Image(5, 5); actual.Mutate( @@ -106,8 +111,9 @@ public void DiagonalLineNotDrawnProperlyCrossCheckBug28() this.ActualImage = actual; // this should pass without bug - //this.AssertImagesEqual(); + this.AssertImagesEqual(); + // AT 2020-11: Update bug seems to be fixed. Leaving the passing test to ensure stable. /* Assert.Fail failed. Images are not equal - total delta 0.00015564202 is not less than tolerance 0. Difference are: @@ -127,8 +133,8 @@ Assert.Fail failed. Images are not equal - total delta 0.00015564202 is not less // . R . . . // . R . . . // . . . . . - Assert.AreEqual(color.ToPixel(), actual[2, 1]); - Assert.AreEqual(color.ToPixel(), actual[1, 2]); + //Assert.AreEqual(color.ToPixel(), actual[2, 1]); + //Assert.AreEqual(color.ToPixel(), actual[1, 2]); } [TestMethod] @@ -142,15 +148,16 @@ public void DiagonalLineNotDrawnProperlyCrossCheckBug28SecondAttempt() var pen = new Pen(color, 1); // a 3-pixel line, bottom left to top right, 1px padding around edge + // because offsets drawn with a +0.5px offsets, line is essentially drawn from points 1.5,3.5 to 3.5,1.5 + // which if you trace along pixel grid, produces chart below + // . . . . . // . . . . . // . . . R . // . . R . . - // . R . . . // . . . . . var expected = new Image(5, 5); - expected[1, 3] = color; - expected[2, 2] = color; - expected[3, 1] = color; + expected[3, 2] = color; + expected[2, 3] = color; var actual = new Image(5, 5); actual.Mutate( @@ -164,8 +171,9 @@ public void DiagonalLineNotDrawnProperlyCrossCheckBug28SecondAttempt() this.ActualImage = actual; // this should pass without bug - //this.AssertImagesEqual(); + this.AssertImagesEqual(); + // AT 2020-11: Update bug seems to be fixed. Leaving the passing test to ensure stable. /* Assert.Fail failed. Images are not equal - total delta 7.782101E-05 is not less than tolerance 0. Difference are: @@ -182,7 +190,7 @@ Assert.Fail failed. Images are not equal - total delta 7.782101E-05 is not less // . . R . . // . . R . . // . . . . . - Assert.AreEqual(color.ToPixel(), actual[2, 3]); + // Assert.AreEqual(color.ToPixel(), actual[2, 3]); } [TestMethod] @@ -234,7 +242,7 @@ public void TestNoAADrawLineDiagonalMultiplePoints() .Finish(); var path = Enumerable - .Range(1, 99) + .Range(1, 98) .Select(x => new PointF(x, x)) .ToArray(); diff --git a/tests/Acoustics.Test/Shared/Drawing/DrawingTests.cs b/tests/Acoustics.Test/Shared/Drawing/DrawingTests.cs index d5706be8c..a98a02c33 100644 --- a/tests/Acoustics.Test/Shared/Drawing/DrawingTests.cs +++ b/tests/Acoustics.Test/Shared/Drawing/DrawingTests.cs @@ -30,7 +30,7 @@ public class DrawingTests : GeneratedImageTest private readonly TestImage blankExpected; public DrawingTests() - : base() + : base(WriteTestOutput.Always) { this.ActualImage = new Image(Configuration.Default, 100, 100, Color.Black); this.blankExpected = new TestImage(100, 100, Color.Black); @@ -86,18 +86,29 @@ public void TestDrawingTextMissingTahomaFallsbackToRoboto() public void TestNoAADrawLine1Px() { // red line at top, 100px wide - this.ExpectedImage = this.blankExpected.FillPattern("R100").Finish(); + this.ExpectedImage = this.blankExpected.FillPattern("R100"); this.ActualImage.Mutate(x => x.NoAA().DrawLine(TestPenOne, 0, 0, 100, 0)); this.AssertImagesEqual(); } + [TestMethod] + public void TestNoAADrawLine1PxVertical() + { + // red line at left edge, 100px tall + this.ExpectedImage = this.blankExpected.FillPattern("100×R1E99"); + + this.ActualImage.Mutate(x => x.NoAA().DrawLine(TestPenOne, 0, 0, 0, 100)); + + this.AssertImagesEqual(); + } + [TestMethod] public void TestNoAADrawLineMiddle1Px() { // red line, 50px down, 100px wide - this.ExpectedImage = this.blankExpected.FillPattern("⬇50\nR100").Finish(); + this.ExpectedImage = this.blankExpected.FillPattern("⬇50\nR100"); this.ActualImage.Mutate(x => x.NoAA().DrawLine(TestPenOne, 0, 50, 100, 50)); @@ -108,7 +119,7 @@ public void TestNoAADrawLineMiddle1Px() public void TestNoAADrawLine2Px() { // red line at top, 1px high (because line spills over top border, 1px above, 1 below), 100px wide - this.ExpectedImage = this.blankExpected.FillPattern("R100").Finish(); + this.ExpectedImage = this.blankExpected.FillPattern("R100"); this.ActualImage.Mutate(x => x.NoAA().DrawLine(TestPenTwo, 0, 0, 100, 0)); @@ -119,7 +130,7 @@ public void TestNoAADrawLine2Px() public void TestNoAADrawLineMiddle2Px() { // red line, 49px down, 2px high, 100px wide - this.ExpectedImage = this.blankExpected.FillPattern("⬇49\n2×R100").Finish(); + this.ExpectedImage = this.blankExpected.FillPattern("⬇49\n2×R100"); this.ActualImage.Mutate(x => x.NoAA().DrawLine(TestPenTwo, 0, 50, 100, 50)); @@ -127,10 +138,11 @@ public void TestNoAADrawLineMiddle2Px() } [TestMethod] + [Ignore("https://github.com/SixLabors/ImageSharp.Drawing/discussions/110")] public void TestNoAADrawLine3Px() { // red line at top, 2px high (because line spills over top border, 1px above, 2 below), 100px wide - this.ExpectedImage = this.blankExpected.FillPattern("2×R100").Finish(); + this.ExpectedImage = this.blankExpected.FillPattern("2×R100"); this.ActualImage.Mutate(x => x.NoAA().DrawLine(TestPenThree, 0, 0, 100, 0)); @@ -141,7 +153,7 @@ public void TestNoAADrawLine3Px() public void TestNoAADrawLineMiddle3Px() { // red line, 49px down, 2px high, 100px wide - this.ExpectedImage = this.blankExpected.FillPattern("⬇49\n3×R100").Finish(); + this.ExpectedImage = this.blankExpected.FillPattern("⬇49\n3×R100"); this.ActualImage.Mutate(x => x.NoAA().DrawLine(TestPenThree, 0, 50, 100, 50)); @@ -158,7 +170,7 @@ public void TestNoAADrawBorderInset1Px() 1×ER98E ⬇1 "; - this.ExpectedImage = this.blankExpected.FillPattern(specification).Finish(); + this.ExpectedImage = this.blankExpected.FillPattern(specification); var rect = new Rectangle(1, 1, 98, 98); this.ActualImage.Mutate(x => x.NoAA().DrawBorderInset(TestPenOne, rect)); @@ -176,7 +188,7 @@ public void TestNoAADrawBorderInset2Px() 2×ERRR94RRE ⬇1 "; - this.ExpectedImage = this.blankExpected.FillPattern(specification).Finish(); + this.ExpectedImage = this.blankExpected.FillPattern(specification); var rect = new Rectangle(1, 1, 98, 98); this.ActualImage.Mutate(x => x.NoAA().DrawBorderInset(TestPenTwo, rect)); @@ -194,7 +206,7 @@ public void TestNoAADrawBorderInset3Px() 3×ERRRR92RRRE ⬇1 "; - this.ExpectedImage = this.blankExpected.FillPattern(specification).Finish(); + this.ExpectedImage = this.blankExpected.FillPattern(specification); var rect = new Rectangle(1, 1, 98, 98); this.ActualImage.Mutate(x => x.NoAA().DrawBorderInset(TestPenThree, rect)); diff --git a/tests/Acoustics.Test/Shared/Drawing/RectangleCornerBugTest.cs b/tests/Acoustics.Test/Shared/Drawing/RectangleCornerBugTest.cs index 45501e29c..6dd1d9c82 100644 --- a/tests/Acoustics.Test/Shared/Drawing/RectangleCornerBugTest.cs +++ b/tests/Acoustics.Test/Shared/Drawing/RectangleCornerBugTest.cs @@ -28,21 +28,21 @@ public void RectangleHasMissingBottomRightCorner() { var testImage = new Image(Configuration.Default, 100, 100, Color.Black); - var rectangle = new RectangleF(10.5f, 10.5f, 79, 79); + var rectangle = new RectangleF(10.0f, 10.0f, 79, 79); var pen = new Pen(Color.Red, 1f); var options = new ShapeGraphicsOptions() { GraphicsOptions = new GraphicsOptions() { - BlendPercentage = 1, + //BlendPercentage = 1, Antialias = false, - ColorBlendingMode = PixelColorBlendingMode.Normal, + //ColorBlendingMode = PixelColorBlendingMode.Normal, AntialiasSubpixelDepth = 0, }, }; - testImage.Mutate(x => x.Draw(pen, rectangle)); + testImage.Mutate(x => x.Draw(options, pen, rectangle)); testImage.Save(this.TestOutputDirectory.CombinePath("rectangle.png")); @@ -52,15 +52,16 @@ public void RectangleHasMissingBottomRightCorner() // 89 | R | R | B // 90 | B | B | B + // AT 2020-11: Update bug seems to be fixed. Leaving the passing test to ensure stable. // smoke test: when this test fails, bug in ImageSharp has been fixed // should be black Assert.AreEqual( Color.Black.ToPixel(), testImage[88, 88]); - // should be red (bug is that it is blended) + // should be red (bug *was* that it is blended) Assert.AreEqual( - new Rgb24(249, 0, 0), + new Rgb24(255, 0, 0), testImage[89, 89]); // should be black @@ -94,17 +95,17 @@ public void DrawTest() // the following two should be equivalent //x.NoAA().DrawRectangle(pen, border); - border.Offset(Drawing.NoAA.Bug28Offset); + //border.Offset(Drawing.NoAA.Bug28Offset); x.Draw(Drawing.NoAntiAlias, pen, border); }); // assert - this should pass without the delta if the bug was fixed Assert.That.ImageMatches(expected, actual, MissingCornerDelta); - // add smoke test - this should fail if bug fixed + // AT 2020-11: Update bug seems to be fixed. Leaving the passing test to ensure stable. // if there were no bug the expected pixel color is red Assert.AreEqual( - new Rgb24(0, 0, 0), + new Rgb24(255, 0, 0), actual[89, 89]); } } diff --git a/tests/Acoustics.Test/TestHelpers/GeneratedImageTest.cs b/tests/Acoustics.Test/TestHelpers/GeneratedImageTest.cs index 515bbf354..e8cb13704 100644 --- a/tests/Acoustics.Test/TestHelpers/GeneratedImageTest.cs +++ b/tests/Acoustics.Test/TestHelpers/GeneratedImageTest.cs @@ -5,11 +5,15 @@ namespace Acoustics.Test.TestHelpers { using System; + using System.Collections.Generic; using Acoustics.Shared.ImageSharp; + using global::TowseyLibrary; using Microsoft.VisualStudio.TestTools.UnitTesting; using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; + using Towel.DataStructures; + using TowseyLibrary; public abstract class GeneratedImageTest : OutputDirectoryTest where T : unmanaged, IPixel @@ -81,6 +85,13 @@ public void TestCleanup() x.ApplyProcessor(deltaProcessor); }); this.SaveImage("delta", delta); + + if (this.ShouldWrite(this.WriteImages)) + { + // create combined image for easier comparison + var combined = ImageTools.CombineImagesInLine(Color.Transparent, this.ExpectedImage, delta, this.ActualImage); + this.SaveImage("combined", combined); + } } } diff --git a/tests/Acoustics.Test/TestHelpers/TestImage.cs b/tests/Acoustics.Test/TestHelpers/TestImage.cs index 7d232d988..0e737bdf7 100644 --- a/tests/Acoustics.Test/TestHelpers/TestImage.cs +++ b/tests/Acoustics.Test/TestHelpers/TestImage.cs @@ -48,6 +48,8 @@ public TestImage(int width, int height, string specification, Rgb24? backgroundC this.FillPattern(specification); } + public static implicit operator Image(TestImage testImage) => testImage.Finish(); + public static Image Create(int width, int height, Color color, string specification) { return new TestImage(width, height, specification, color).Finish(); diff --git a/tests/Fixtures/AcousticEventTests_SuperimposeEventsOnImage.png b/tests/Fixtures/AcousticEventTests_SuperimposeEventsOnImage.png index f5c3aaa79..f0198d148 100644 --- a/tests/Fixtures/AcousticEventTests_SuperimposeEventsOnImage.png +++ b/tests/Fixtures/AcousticEventTests_SuperimposeEventsOnImage.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:db09b16c1b9f94bbf5fe03b1f73c6a1f1da06c419867309ef1b1e1c08f3bd4a6 -size 700 +oid sha256:6a863feefd6578d82adad4419bec50b1d1ee0dbc478a6b60bb464b7243ba27b5 +size 218 diff --git a/tests/Fixtures/roboto_font_test.png b/tests/Fixtures/roboto_font_test.png index da7306e25..91b91732c 100644 --- a/tests/Fixtures/roboto_font_test.png +++ b/tests/Fixtures/roboto_font_test.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:53888eaae34abb41012e403fe17e4ff66634cab9d0cc7e9ff6f02a889c10b2a4 -size 1979 +oid sha256:f23e94fdeaf0401728fb0b0b662783cd99c972df757381fa03520d6af083fe62 +size 1968