diff --git a/src/ImageSharp/Common/Helpers/TolerantMath.cs b/src/ImageSharp/Common/Helpers/TolerantMath.cs index 5347efcc0..bef7eb161 100644 --- a/src/ImageSharp/Common/Helpers/TolerantMath.cs +++ b/src/ImageSharp/Common/Helpers/TolerantMath.cs @@ -16,6 +16,13 @@ namespace SixLabors.ImageSharp private readonly double negEpsilon; + /// + /// A read-only default instance for using 1e-8 as epsilon. + /// It is a field so it can be passed as an 'in' parameter. + /// Does not necessarily fit all use cases! + /// + public static readonly TolerantMath Default = new TolerantMath(1e-8); + public TolerantMath(double epsilon) { DebugGuard.MustBeGreaterThan(epsilon, 0, nameof(epsilon)); @@ -24,8 +31,6 @@ namespace SixLabors.ImageSharp this.negEpsilon = -epsilon; } - public static TolerantMath Default { get; } = new TolerantMath(1e-8); - /// /// == 0 /// diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs index 08b294913..5d3790f07 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs @@ -208,10 +208,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { var result = new TheoryData(); - string[] resamplerNames = typeof(KnownResamplers).GetProperties(BindingFlags.Public | BindingFlags.Static) - .Select(p => p.Name) - .Where(name => name != nameof(KnownResamplers.NearestNeighbor)) - .ToArray(); + string[] resamplerNames = TestUtils.GetAllResamplerNames(false); int[] dimensionVals = { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index b4aff53e8..034b66ae9 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -20,42 +20,82 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms public static readonly string[] CommonTestImages = { TestImages.Png.CalliphoraPartial }; private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F); - - public static readonly TheoryData AllResamplers = - new TheoryData + + public static readonly string[] AllResamplerNames = TestUtils.GetAllResamplerNames(); + + public static readonly string[] SmokeTestResamplerNames = { - { "Bicubic", KnownResamplers.Bicubic }, - { "Triangle", KnownResamplers.Triangle}, - { "NearestNeighbor", KnownResamplers.NearestNeighbor }, - { "Box", KnownResamplers.Box }, - // { "Lanczos2", KnownResamplers.Lanczos2 }, TODO: Add expected file - { "Lanczos3", KnownResamplers.Lanczos3 }, - { "Lanczos5", KnownResamplers.Lanczos5 }, - { "MitchellNetravali", KnownResamplers.MitchellNetravali }, - { "Lanczos8", KnownResamplers.Lanczos8 }, - { "Hermite", KnownResamplers.Hermite }, - { "Spline", KnownResamplers.Spline }, - { "Robidoux", KnownResamplers.Robidoux }, - { "RobidouxSharp", KnownResamplers.RobidouxSharp }, - { "Welch", KnownResamplers.Welch } + nameof(KnownResamplers.NearestNeighbor), + nameof(KnownResamplers.Bicubic), + nameof(KnownResamplers.Box), + nameof(KnownResamplers.Lanczos5), }; [Theory] - [WithTestPatternImages(nameof(AllResamplers), 100, 100, DefaultPixelType, 0.5f)] - [WithFileCollection(nameof(CommonTestImages), nameof(AllResamplers), DefaultPixelType, 0.5f)] - [WithFileCollection(nameof(CommonTestImages), nameof(AllResamplers), DefaultPixelType, 0.3f)] - public void Resize_WorksWithAllResamplers(TestImageProvider provider, string name, IResampler sampler, float ratio) + [WithFileCollection(nameof(CommonTestImages), nameof(AllResamplerNames), DefaultPixelType, 0.5f, null, null)] + [WithFileCollection(nameof(CommonTestImages), nameof(SmokeTestResamplerNames), DefaultPixelType, 0.3f, null, null)] + [WithFileCollection(nameof(CommonTestImages), nameof(SmokeTestResamplerNames), DefaultPixelType, 1.8f, null, null)] + [WithTestPatternImages(nameof(SmokeTestResamplerNames), 100, 100, DefaultPixelType, 0.5f, null, null)] + [WithTestPatternImages(nameof(SmokeTestResamplerNames), 100, 100, DefaultPixelType, 1f, null, null)] + [WithTestPatternImages(nameof(SmokeTestResamplerNames), 50, 50, DefaultPixelType, 8f, null, null)] + [WithTestPatternImages(nameof(SmokeTestResamplerNames), 201, 199, DefaultPixelType, null, 100, 99)] + [WithTestPatternImages(nameof(SmokeTestResamplerNames), 301, 1180, DefaultPixelType, null, 300, 480)] + [WithTestPatternImages(nameof(SmokeTestResamplerNames), 49, 80, DefaultPixelType, null, 301, 100)] + public void Resize_WorksWithAllResamplers( + TestImageProvider provider, + string samplerName, + float? ratio, + int? specificDestWidth, + int? specificDestHeight) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) - { - SizeF newSize = image.Size() * ratio; - image.Mutate(x => x.Resize((Size)newSize, sampler, false)); - FormattableString details = $"{name}-{ratio.ToString(System.Globalization.CultureInfo.InvariantCulture)}"; + IResampler sampler = TestUtils.GetResampler(samplerName); - image.DebugSave(provider, details); - image.CompareToReferenceOutput(ImageComparer.TolerantPercentage(0.02f), provider, details); - } + // NeirestNeighbourResampler is producing slightly different results With classic .NET framework on 32bit + // most likely because of differences in numeric behavior. + // The difference is well visible when comparing output for + // Resize_WorksWithAllResamplers_TestPattern301x1180_NearestNeighbor-300x480.png + // TODO: Should we investigate this? + bool allowHigherInaccuracy = !TestEnvironment.Is64BitProcess + && string.IsNullOrEmpty(TestEnvironment.NetCoreVersion) + && sampler is NearestNeighborResampler; + + var comparer = ImageComparer.TolerantPercentage(allowHigherInaccuracy ? 0.3f : 0.017f); + + provider.RunValidatingProcessorTest( + ctx => + { + + SizeF newSize; + string destSizeInfo; + if (ratio.HasValue) + { + newSize = ctx.GetCurrentSize() * ratio.Value; + destSizeInfo = ratio.Value.ToString(System.Globalization.CultureInfo.InvariantCulture); + } + else + { + if (!specificDestWidth.HasValue || !specificDestHeight.HasValue) + { + throw new InvalidOperationException( + "invalid dimensional input for Resize_WorksWithAllResamplers!"); + } + + newSize = new SizeF(specificDestWidth.Value, specificDestHeight.Value); + destSizeInfo = $"{newSize.Width}x{newSize.Height}"; + } + + FormattableString testOutputDetails = $"{samplerName}-{destSizeInfo}"; + ctx.Apply( + img => img.DebugSave( + provider, + $"{testOutputDetails}-ORIGINAL", + appendPixelTypeToFileName: false)); + ctx.Resize((Size)newSize, sampler, false); + return testOutputDetails; + }, + comparer, + appendPixelTypeToFileName: false); } [Theory] diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs index ec3254921..6107154d0 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs @@ -61,7 +61,7 @@ namespace SixLabors.ImageSharp.Tests addedRows = memberItems.Select(x => x as object[]); if (addedRows.Any(x => x == null)) { - throw new ArgumentException($"Property {this.MemberName} on {this.MemberType ?? testMethod.DeclaringType} yielded an item that is not an object[]"); + addedRows = memberItems.Select(x => new[] { x }); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index cc09dc057..17e5369d4 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -16,10 +16,9 @@ namespace SixLabors.ImageSharp.Tests /// /// A test image provider that produces test patterns. /// - /// private class TestPatternProvider : BlankProvider { - static Dictionary> testImages = new Dictionary>(); + static readonly Dictionary> TestImages = new Dictionary>(); public TestPatternProvider(int width, int height) : base(width, height) @@ -35,17 +34,17 @@ namespace SixLabors.ImageSharp.Tests public override Image GetImage() { - lock (testImages) + lock (TestImages) { - if (!testImages.ContainsKey(this.SourceFileOrDescription)) + if (!TestImages.ContainsKey(this.SourceFileOrDescription)) { Image image = new Image(this.Width, this.Height); DrawTestPattern(image); - testImages.Add(this.SourceFileOrDescription, image); + TestImages.Add(this.SourceFileOrDescription, image); } } - return testImages[this.SourceFileOrDescription].Clone(); + return TestImages[this.SourceFileOrDescription].Clone(); } /// @@ -202,6 +201,7 @@ namespace SixLabors.ImageSharp.Tests Rgba32 t = new Rgba32(0); for (int x = left; x < right; x++) + { for (int y = top; y < bottom; y++) { t.PackedValue += stepsPerPixel; @@ -210,6 +210,7 @@ namespace SixLabors.ImageSharp.Tests c.FromVector4(v); pixels[x, y] = c; } + } } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs index 5ea0bccf0..e51aa28d8 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestUtils.cs @@ -193,6 +193,45 @@ namespace SixLabors.ImageSharp.Tests } } + internal static void RunValidatingProcessorTest( + this TestImageProvider provider, + Func, FormattableString> processAndGetTestOutputDetails, + ImageComparer comparer = null, + bool appendPixelTypeToFileName = true, + bool appendSourceFileOrDescription = true) + where TPixel : struct, IPixel + { + if (comparer == null) + { + comparer = ImageComparer.TolerantPercentage(0.001f); + } + + using (Image image = provider.GetImage()) + { + FormattableString testOutputDetails = $""; + image.Mutate( + ctx => { testOutputDetails = processAndGetTestOutputDetails(ctx); } + ); + + image.DebugSave( + provider, + testOutputDetails, + appendPixelTypeToFileName: appendPixelTypeToFileName, + appendSourceFileOrDescription: appendSourceFileOrDescription); + + // TODO: Investigate the cause of pixel inaccuracies under Linux + if (TestEnvironment.IsWindows) + { + image.CompareToReferenceOutput( + comparer, + provider, + testOutputDetails, + appendPixelTypeToFileName: appendPixelTypeToFileName, + appendSourceFileOrDescription: appendSourceFileOrDescription); + } + } + } + public static void RunValidatingProcessorTestOnWrappedMemoryImage( this TestImageProvider provider, Action> process, @@ -297,5 +336,13 @@ namespace SixLabors.ImageSharp.Tests return (IResampler)property.GetValue(null); } + + public static string[] GetAllResamplerNames(bool includeNearestNeighbour = true) + { + return typeof(KnownResamplers).GetProperties(BindingFlags.Public | BindingFlags.Static) + .Select(p => p.Name) + .Where(name => includeNearestNeighbour || name != nameof(KnownResamplers.NearestNeighbor)) + .ToArray(); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index a8140e39d..cac7828e9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -9,6 +9,7 @@ using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using Xunit; using Xunit.Abstractions; +// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { @@ -313,6 +314,17 @@ namespace SixLabors.ImageSharp.Tests } + [Theory] + [WithTestPatternImages(49,20, PixelTypes.Rgba32)] + public void Use_WithTestPatternImages(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image img = provider.GetImage()) + { + img.DebugSave(provider); + } + } + public static readonly TheoryData BasicData = new TheoryData() { TestImageProvider.Blank(10, 20), diff --git a/tests/Images/External b/tests/Images/External index 5b18d8c95..1edb0f3e0 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 5b18d8c95acffb773012881870ba6f521ba13128 +Subproject commit 1edb0f3e04c18974821a3012a87f7c2e073c8019