From 6e0c827f488ec2839d4524e76c83e43749d921f4 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 26 Jun 2018 00:40:23 +0200 Subject: [PATCH 01/12] make Guard.NotNull() and DebugGuard.NotNull() generic --- src/ImageSharp/Common/Helpers/DebugGuard.cs | 9 +++++---- src/ImageSharp/Common/Helpers/Guard.cs | 3 ++- tests/ImageSharp.Tests/Helpers/GuardTests.cs | 8 ++++++-- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Common/Helpers/DebugGuard.cs b/src/ImageSharp/Common/Helpers/DebugGuard.cs index 6dcd0fd27..5a1d3a2e3 100644 --- a/src/ImageSharp/Common/Helpers/DebugGuard.cs +++ b/src/ImageSharp/Common/Helpers/DebugGuard.cs @@ -17,13 +17,14 @@ namespace SixLabors.ImageSharp /// Verifies, that the method parameter with specified object value is not null /// and throws an exception if it is found to be so. /// - /// The target object, which cannot be null. + /// The target object, which cannot be null. /// The name of the parameter that is to be checked. - /// is null + /// is null [Conditional("DEBUG")] - public static void NotNull(object target, string parameterName) + public static void NotNull(T value, string parameterName) + where T : class { - if (target == null) + if (value == null) { throw new ArgumentNullException(parameterName); } diff --git a/src/ImageSharp/Common/Helpers/Guard.cs b/src/ImageSharp/Common/Helpers/Guard.cs index 011d7fdaa..d09079062 100644 --- a/src/ImageSharp/Common/Helpers/Guard.cs +++ b/src/ImageSharp/Common/Helpers/Guard.cs @@ -19,7 +19,8 @@ namespace SixLabors.ImageSharp /// The target object, which cannot be null. /// The name of the parameter that is to be checked. /// is null - public static void NotNull(object value, string parameterName) + public static void NotNull(T value, string parameterName) + where T : class { if (value == null) { diff --git a/tests/ImageSharp.Tests/Helpers/GuardTests.cs b/tests/ImageSharp.Tests/Helpers/GuardTests.cs index 42913e02d..0d1bb5ce9 100644 --- a/tests/ImageSharp.Tests/Helpers/GuardTests.cs +++ b/tests/ImageSharp.Tests/Helpers/GuardTests.cs @@ -12,13 +12,17 @@ namespace SixLabors.ImageSharp.Tests.Helpers /// public class GuardTests { + class Test + { + } + /// /// Tests that the method throws when the argument is null. /// [Fact] public void NotNullThrowsWhenArgIsNull() { - Assert.Throws(() => Guard.NotNull(null, "foo")); + Assert.Throws(() => Guard.NotNull((Test)null, "foo")); } /// @@ -27,7 +31,7 @@ namespace SixLabors.ImageSharp.Tests.Helpers [Fact] public void NotNullThrowsWhenArgNameEmpty() { - Assert.Throws(() => Guard.NotNull(null, string.Empty)); + Assert.Throws(() => Guard.NotNull((Test)null, string.Empty)); } /// From b5911782ad999ad808457341d0a7b28f7d6c85d5 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 26 Jun 2018 01:58:41 +0200 Subject: [PATCH 02/12] ImageMagick decoder works --- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 1 + .../ReferenceCodecs/MagickReferenceDecoder.cs | 51 +++++++++ .../ReferenceCodecs/MagickReferenceEncoder.cs | 60 ++++++++++ .../TestUtilities/TestImageExtensions.cs | 6 +- .../Tests/MagickReferenceCodecTests.cs | 106 ++++++++++++++++++ ...cs => SystemDrawingReferenceCodecTests.cs} | 10 +- 6 files changed, 228 insertions(+), 6 deletions(-) create mode 100644 tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceEncoder.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs rename tests/ImageSharp.Tests/TestUtilities/Tests/{ReferenceCodecTests.cs => SystemDrawingReferenceCodecTests.cs} (94%) diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index e7e2577a8..9e15b6aba 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -27,6 +27,7 @@ + diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs new file mode 100644 index 000000000..9b209137b --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -0,0 +1,51 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Runtime.InteropServices; + +using ImageMagick; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs +{ + public class MagickReferenceDecoder : IImageDecoder + { + public Image Decode(Configuration configuration, Stream stream) + where TPixel : struct, IPixel + { + using (var magickImage = new MagickImage(stream)) + { + var result = new Image(configuration, magickImage.Width, magickImage.Height); + Span resultPixels = result.GetPixelSpan(); + + using (IPixelCollection pixels = magickImage.GetPixelsUnsafe()) + { + if (magickImage.Depth == 8) + { + byte[] data = pixels.ToByteArray("RGBA"); + + PixelOperations.Instance.PackFromRgba32Bytes(data, resultPixels, resultPixels.Length); + } + else if (magickImage.Depth == 16) + { + ushort[] data = pixels.ToShortArray("RGBA"); + Span bytes = MemoryMarshal.Cast(data.AsSpan()); + + PixelOperations.Instance.PackFromRgba64Bytes(bytes, resultPixels, resultPixels.Length); + } + else + { + throw new NotImplementedException(); + } + } + + return result; + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceEncoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceEncoder.cs new file mode 100644 index 000000000..97e4a55a4 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceEncoder.cs @@ -0,0 +1,60 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using ImageMagick; + +using SixLabors.ImageSharp.Advanced; + +namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs +{ + public class MagickReferenceEncoder : IImageEncoder + { + public MagickReferenceEncoder(MagickFormat format) + { + this.Format = format; + } + + public MagickFormat Format { get; } + + public void Encode(Image image, Stream stream) + where TPixel : struct, IPixel + { + var black = MagickColor.FromRgba(0, 0, 0, 255); + using (var magickImage = new MagickImage(black, image.Width, image.Height)) + { + bool isDeep = Unsafe.SizeOf() > 32; + + magickImage.Depth = isDeep ? 16 : 8; + + Span allPixels = image.GetPixelSpan(); + + using (IPixelCollection magickPixels = magickImage.GetPixelsUnsafe()) + { + if (isDeep) + { + ushort[] data = new ushort[allPixels.Length * 4]; + Span dataSpan = MemoryMarshal.Cast(data); + PixelOperations.Instance.ToRgba64(allPixels, dataSpan, allPixels.Length); + magickPixels.SetPixels(data); + } + else + { + byte[] data = new byte[allPixels.Length * 4]; + PixelOperations.Instance.ToRgba32Bytes(allPixels, data, allPixels.Length); + magickPixels.SetPixels(data); + } + } + + magickImage.Write(stream, this.Format); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 016ae7ad2..79a0071ff 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -641,7 +641,8 @@ namespace SixLabors.ImageSharp.Tests IImageEncoder encoder, ImageComparer customComparer = null, bool appendPixelTypeToFileName = true, - string referenceImageExtension = null) + string referenceImageExtension = null, + IImageDecoder referenceDecoder = null) where TPixel : struct, IPixel { string actualOutputFile = provider.Utility.SaveTestOutputFile( @@ -650,7 +651,8 @@ namespace SixLabors.ImageSharp.Tests encoder, testOutputDetails, appendPixelTypeToFileName); - IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); + + referenceDecoder = referenceDecoder ?? TestEnvironment.GetReferenceDecoder(actualOutputFile); using (var actualImage = Image.Load(actualOutputFile, referenceDecoder)) { diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs new file mode 100644 index 000000000..a797fca0e --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs @@ -0,0 +1,106 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using ImageMagick; +using Xunit; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests +{ + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + + using Xunit.Abstractions; + + public class MagickReferenceCodecTests + { + public MagickReferenceCodecTests(ITestOutputHelper output) + { + this.Output = output; + } + + private ITestOutputHelper Output { get; } + + public const PixelTypes PixelTypesToTest32 = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24; + + public const PixelTypes PixelTypesToTest64 = + PixelTypes.Rgba32 | PixelTypes.Rgb24 | PixelTypes.Rgba64 | PixelTypes.Rgb48; + + public const PixelTypes PixelTypesToTest48 = + PixelTypes.Rgba32 | PixelTypes.Rgba64 | PixelTypes.Rgb48; + + [Theory] + [WithBlankImages(1, 1, PixelTypesToTest32, TestImages.Png.Splash)] + [WithBlankImages(1, 1, PixelTypesToTest32, TestImages.Png.Indexed)] + public void MagickDecode_8BitDepthImage_IsEquivalentTo_SystemDrawingResult(TestImageProvider dummyProvider, string testImage) + where TPixel : struct, IPixel + { + string path = TestFile.GetInputFileFullPath(testImage); + + var magickDecoder = new MagickReferenceDecoder(); + var sdDecoder = new SystemDrawingReferenceDecoder(); + + ImageComparer comparer = ImageComparer.Exact; + + using (var mImage = Image.Load(path, magickDecoder)) + using (var sdImage = Image.Load(path, sdDecoder)) + { + ImageSimilarityReport report = comparer.CompareImagesOrFrames(mImage, sdImage); + + mImage.DebugSave(dummyProvider); + + if (TestEnvironment.IsWindows) + { + Assert.True(report.IsEmpty); + } + } + } + + [Theory] + [WithBlankImages(1, 1, PixelTypesToTest64, TestImages.Png.Rgba64Bpp)] + [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.Rgb48Bpp)] + [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.Rgb48BppInterlaced)] + [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.Rgb48BppTrans)] + public void MagickDecode_16BitDepthImage_IsApproximatelyEquivalentTo_SystemDrawingResult(TestImageProvider dummyProvider, string testImage) + where TPixel : struct, IPixel + { + string path = TestFile.GetInputFileFullPath(testImage); + + var magickDecoder = new MagickReferenceDecoder(); + var sdDecoder = new SystemDrawingReferenceDecoder(); + + // 1020 == 4 * 255 (Equivalent to manhattan distance of 1+1+1+1=4 in Rgba32 space) + var comparer = ImageComparer.TolerantPercentage(1, 1020); + + using (var mImage = Image.Load(path, magickDecoder)) + using (var sdImage = Image.Load(path, sdDecoder)) + { + ImageSimilarityReport report = comparer.CompareImagesOrFrames(mImage, sdImage); + + mImage.DebugSave(dummyProvider); + + if (TestEnvironment.IsWindows) + { + Assert.True(report.IsEmpty); + } + } + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypesToTest32, MagickFormat.Png)] + [WithTestPatternImages(100, 100, PixelTypesToTest32, MagickFormat.Jpg)] + public void MagickEncode_8BitDepthImage(TestImageProvider provider, MagickFormat format) + where TPixel : struct, IPixel + { + string extension = format.ToString().ToLower(); + + var encoder = new MagickReferenceEncoder(format); + + using (Image image = provider.GetImage()) + { + image.VerifyEncoder(provider, extension, $"{format}", encoder, referenceDecoder: new SystemDrawingReferenceDecoder()); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs similarity index 94% rename from tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs rename to tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs index 3ad595b7e..3cdb67dbd 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/SystemDrawingReferenceCodecTests.cs @@ -1,4 +1,6 @@ -using System.IO; +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -8,13 +10,13 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit; using Xunit.Abstractions; -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests { - public class ReferenceCodecTests + public class SystemDrawingReferenceCodecTests { private ITestOutputHelper Output { get; } - public ReferenceCodecTests(ITestOutputHelper output) + public SystemDrawingReferenceCodecTests(ITestOutputHelper output) { this.Output = output; } From 817ac0b20475106162a5d786cf7540bad1dcb63c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 26 Jun 2018 02:06:27 +0200 Subject: [PATCH 03/12] oops --- .../TestUtilities/ReferenceCodecs/MagickReferenceEncoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceEncoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceEncoder.cs index 97e4a55a4..effd30ec6 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceEncoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceEncoder.cs @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs var black = MagickColor.FromRgba(0, 0, 0, 255); using (var magickImage = new MagickImage(black, image.Width, image.Height)) { - bool isDeep = Unsafe.SizeOf() > 32; + bool isDeep = Unsafe.SizeOf() > 4; magickImage.Depth = isDeep ? 16 : 8; From 79bba7b4e4d731a20634425afda918c8b0576ab3 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 26 Jun 2018 23:07:49 +0200 Subject: [PATCH 04/12] reference encoder is unnecessary --- .../ReferenceCodecs/MagickReferenceEncoder.cs | 60 ------------------- .../Tests/MagickReferenceCodecTests.cs | 17 ------ 2 files changed, 77 deletions(-) delete mode 100644 tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceEncoder.cs diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceEncoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceEncoder.cs deleted file mode 100644 index effd30ec6..000000000 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceEncoder.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; - -using SixLabors.ImageSharp.Formats; -using SixLabors.ImageSharp.PixelFormats; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -using ImageMagick; - -using SixLabors.ImageSharp.Advanced; - -namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs -{ - public class MagickReferenceEncoder : IImageEncoder - { - public MagickReferenceEncoder(MagickFormat format) - { - this.Format = format; - } - - public MagickFormat Format { get; } - - public void Encode(Image image, Stream stream) - where TPixel : struct, IPixel - { - var black = MagickColor.FromRgba(0, 0, 0, 255); - using (var magickImage = new MagickImage(black, image.Width, image.Height)) - { - bool isDeep = Unsafe.SizeOf() > 4; - - magickImage.Depth = isDeep ? 16 : 8; - - Span allPixels = image.GetPixelSpan(); - - using (IPixelCollection magickPixels = magickImage.GetPixelsUnsafe()) - { - if (isDeep) - { - ushort[] data = new ushort[allPixels.Length * 4]; - Span dataSpan = MemoryMarshal.Cast(data); - PixelOperations.Instance.ToRgba64(allPixels, dataSpan, allPixels.Length); - magickPixels.SetPixels(data); - } - else - { - byte[] data = new byte[allPixels.Length * 4]; - PixelOperations.Instance.ToRgba32Bytes(allPixels, data, allPixels.Length); - magickPixels.SetPixels(data); - } - } - - magickImage.Write(stream, this.Format); - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs index a797fca0e..db651886f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using ImageMagick; using Xunit; // ReSharper disable InconsistentNaming @@ -86,21 +85,5 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests } } } - - [Theory] - [WithTestPatternImages(100, 100, PixelTypesToTest32, MagickFormat.Png)] - [WithTestPatternImages(100, 100, PixelTypesToTest32, MagickFormat.Jpg)] - public void MagickEncode_8BitDepthImage(TestImageProvider provider, MagickFormat format) - where TPixel : struct, IPixel - { - string extension = format.ToString().ToLower(); - - var encoder = new MagickReferenceEncoder(format); - - using (Image image = provider.GetImage()) - { - image.VerifyEncoder(provider, extension, $"{format}", encoder, referenceDecoder: new SystemDrawingReferenceDecoder()); - } - } } } \ No newline at end of file From 507b2f8554281087322c02d49379f847345223a7 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 27 Jun 2018 00:30:23 +0200 Subject: [PATCH 05/12] using MagickReferenceDecoder everywhere, added ReferenceDecoderBenchmarks --- .../TestUtilities/ImagingTestCaseUtility.cs | 6 +- .../ReferenceCodecs/MagickReferenceDecoder.cs | 4 +- .../SystemDrawingReferenceEncoder.cs | 2 + .../TestUtilities/TestEnvironment.Formats.cs | 41 +++----- .../TestUtilities/TestEnvironment.cs | 8 +- .../TestUtilities/TestImageExtensions.cs | 19 ++-- .../Tests/ReferenceDecoderBenchmarks.cs | 96 +++++++++++++++++++ 7 files changed, 131 insertions(+), 45 deletions(-) create mode 100644 tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index 2c4eb6c33..65b32e088 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -146,7 +146,6 @@ namespace SixLabors.ImageSharp.Tests appendSourceFileOrDescription); } - /// /// Encodes image by the format matching the required extension, than saves it to the recommended output file. /// @@ -154,7 +153,9 @@ namespace SixLabors.ImageSharp.Tests /// The image instance /// The requested extension /// Optional encoder - /// /// A boolean indicating whether to append to the test output file name. + /// A value indicating whether to append the pixel type to the test output file name + /// A boolean indicating whether to append to the test output file name. + /// Additional information to append to the test output file name public string SaveTestOutputFile( Image image, string extension = null, @@ -176,6 +177,7 @@ namespace SixLabors.ImageSharp.Tests { image.Save(stream, encoder); } + return path; } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index 9b209137b..8cfc2472f 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -15,6 +15,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { public class MagickReferenceDecoder : IImageDecoder { + public static MagickReferenceDecoder Instance { get; } = new MagickReferenceDecoder(); + public Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel { @@ -40,7 +42,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs } else { - throw new NotImplementedException(); + throw new InvalidOperationException(); } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs index 912333695..46dae17a1 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceEncoder.cs @@ -20,6 +20,8 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs public static SystemDrawingReferenceEncoder Png { get; } = new SystemDrawingReferenceEncoder(ImageFormat.Png); + public static SystemDrawingReferenceEncoder Bmp { get; } = new SystemDrawingReferenceEncoder(ImageFormat.Bmp); + public void Encode(Image image, Stream stream) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 566c22342..30067ec2d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -14,9 +14,9 @@ namespace SixLabors.ImageSharp.Tests { public static partial class TestEnvironment { - private static Lazy configuration = new Lazy(CreateDefaultConfiguration); + private static readonly Lazy ConfigurationLazy = new Lazy(CreateDefaultConfiguration); - internal static Configuration Configuration => configuration.Value; + internal static Configuration Configuration => ConfigurationLazy.Value; internal static IImageDecoder GetReferenceDecoder(string filePath) { @@ -52,36 +52,25 @@ namespace SixLabors.ImageSharp.Tests private static Configuration CreateDefaultConfiguration() { - var configuration = new Configuration( - new PngConfigurationModule(), + var cfg = new Configuration( new JpegConfigurationModule(), new GifConfigurationModule() ); - if (!IsLinux) - { - // TODO: System.Drawing on Windows can decode 48bit and 64bit pngs but - // it doesn't preserve the accuracy we require for comparison. - // This makes CompareToOriginal method non-useful. - configuration.ConfigureCodecs( - ImageFormats.Png, - SystemDrawingReferenceDecoder.Instance, - SystemDrawingReferenceEncoder.Png, - new PngImageFormatDetector()); + // Magick codecs should work on all + cfg.ConfigureCodecs( + ImageFormats.Png, + MagickReferenceDecoder.Instance, + SystemDrawingReferenceEncoder.Png, + new PngImageFormatDetector()); - configuration.ConfigureCodecs( - ImageFormats.Bmp, - SystemDrawingReferenceDecoder.Instance, - SystemDrawingReferenceEncoder.Png, - new PngImageFormatDetector()); - } - else - { - configuration.Configure(new PngConfigurationModule()); - configuration.Configure(new BmpConfigurationModule()); - } + cfg.ConfigureCodecs( + ImageFormats.Bmp, + MagickReferenceDecoder.Instance, + SystemDrawingReferenceEncoder.Bmp, + new BmpImageFormatDetector()); - return configuration; + return cfg; } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs index 9a41e6602..f0b732998 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs @@ -21,9 +21,9 @@ namespace SixLabors.ImageSharp.Tests private const string ToolsDirectoryRelativePath = @"tests\Images\External\tools"; - private static Lazy solutionDirectoryFullPath = new Lazy(GetSolutionDirectoryFullPathImpl); + private static readonly Lazy SolutionDirectoryFullPathLazy = new Lazy(GetSolutionDirectoryFullPathImpl); - private static Lazy runsOnCi = new Lazy( + private static readonly Lazy RunsOnCiLazy = new Lazy( () => { bool isCi; @@ -41,9 +41,9 @@ namespace SixLabors.ImageSharp.Tests /// /// Gets a value indicating whether test execution runs on CI. /// - internal static bool RunsOnCI => runsOnCi.Value; + internal static bool RunsOnCI => RunsOnCiLazy.Value; - internal static string SolutionDirectoryFullPath => solutionDirectoryFullPath.Value; + internal static string SolutionDirectoryFullPath => SolutionDirectoryFullPathLazy.Value; private static string GetSolutionDirectoryFullPathImpl() { diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 79a0071ff..a1f97afb9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -499,16 +499,18 @@ namespace SixLabors.ImageSharp.Tests public static Image CompareToOriginal( this Image image, - ITestImageProvider provider) + ITestImageProvider provider, + IImageDecoder referenceDecoder = null) where TPixel : struct, IPixel { - return CompareToOriginal(image, provider, ImageComparer.Tolerant()); + return CompareToOriginal(image, provider, ImageComparer.Tolerant(), referenceDecoder); } public static Image CompareToOriginal( this Image image, ITestImageProvider provider, - ImageComparer comparer) + ImageComparer comparer, + IImageDecoder referenceDecoder = null) where TPixel : struct, IPixel { string path = TestImageProvider.GetFilePathOrNull(provider); @@ -519,15 +521,8 @@ namespace SixLabors.ImageSharp.Tests var testFile = TestFile.Create(path); - IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); - IImageFormat format = TestEnvironment.GetImageFormat(path); - IImageDecoder defaultDecoder = Configuration.Default.ImageFormatsManager.FindDecoder(format); - - //if (referenceDecoder.GetType() == defaultDecoder.GetType()) - //{ - // throw new InvalidOperationException($"Can't use CompareToOriginal(): no actual reference decoder registered for {format.Name}"); - //} - + referenceDecoder = referenceDecoder ?? TestEnvironment.GetReferenceDecoder(path); + using (var original = Image.Load(testFile.Bytes, referenceDecoder)) { comparer.VerifySimilarity(original, image); diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs new file mode 100644 index 000000000..724c2e414 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs @@ -0,0 +1,96 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; + +using Xunit; +using Xunit.Abstractions; + +namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests +{ + public class ReferenceDecoderBenchmarks + { + private ITestOutputHelper Output { get; } + + public const string SkipBenchmarks = +#if false + "Benchmark, enable manually!"; +#else + null; +#endif + + public const int DefaultExecutionCount = 50; + + public static readonly string[] PngBenchmarkFiles = + { + TestImages.Png.CalliphoraPartial, + TestImages.Png.Kaboom, + TestImages.Png.Bike, + TestImages.Png.Splash, + TestImages.Png.SplashInterlaced + }; + + public static readonly string[] BmpBenchmarkFiles = + { + TestImages.Bmp.NegHeight, + TestImages.Bmp.Car, + TestImages.Bmp.V5Header + }; + + public ReferenceDecoderBenchmarks(ITestOutputHelper output) + { + this.Output = output; + } + + [Theory(Skip = SkipBenchmarks)] + [WithFile(TestImages.Png.Kaboom, PixelTypes.Rgba32)] + public void BenchmarkMagickPngDecoder(TestImageProvider provider) + where TPixel : struct, IPixel + { + this.BenckmarkDecoderImpl(PngBenchmarkFiles, new MagickReferenceDecoder(), $@"Magick Decode Png"); + } + + [Theory(Skip = SkipBenchmarks)] + [WithFile(TestImages.Png.Kaboom, PixelTypes.Rgba32)] + public void BenchmarkSystemDrawingPngDecoder(TestImageProvider provider) + where TPixel : struct, IPixel + { + this.BenckmarkDecoderImpl(PngBenchmarkFiles, new SystemDrawingReferenceDecoder(), $@"System.Drawing Decode Png"); + } + + [Theory(Skip = SkipBenchmarks)] + [WithFile(TestImages.Png.Kaboom, PixelTypes.Rgba32)] + public void BenchmarkMagickBmpDecoder(TestImageProvider provider) + where TPixel : struct, IPixel + { + this.BenckmarkDecoderImpl(BmpBenchmarkFiles, new MagickReferenceDecoder(), $@"Magick Decode Bmp"); + } + + [Theory(Skip = SkipBenchmarks)] + [WithFile(TestImages.Png.Kaboom, PixelTypes.Rgba32)] + public void BenchmarkSystemDrawingBmpDecoder(TestImageProvider provider) + where TPixel : struct, IPixel + { + this.BenckmarkDecoderImpl(BmpBenchmarkFiles, new SystemDrawingReferenceDecoder(), $@"System.Drawing Decode Bmp"); + } + + private void BenckmarkDecoderImpl(IEnumerable testFiles, IImageDecoder decoder, string info, int times = DefaultExecutionCount) + { + var measure = new MeasureFixture(this.Output); + measure.Measure(times, + () => + { + foreach (string testFile in testFiles) + { + Image image = TestFile.Create(testFile).CreateImage(decoder); + image.Dispose(); + } + }, + info); + } + } +} \ No newline at end of file From ce0dd481313ef3baad6686cd820c903488d9c336 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 27 Jun 2018 00:39:43 +0200 Subject: [PATCH 06/12] keep using SystemDrawingReferenceDecoder for Bmp-s --- .../TestUtilities/TestEnvironment.Formats.cs | 4 ++-- tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs | 3 ++- .../TestUtilities/Tests/TestEnvironmentTests.cs | 6 +++--- .../TestUtilities/Tests/TestImageProviderTests.cs | 7 ++++++- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 30067ec2d..ccda71613 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Tests new GifConfigurationModule() ); - // Magick codecs should work on all + // Magick codecs should work on all platforms cfg.ConfigureCodecs( ImageFormats.Png, MagickReferenceDecoder.Instance, @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests cfg.ConfigureCodecs( ImageFormats.Bmp, - MagickReferenceDecoder.Instance, + SystemDrawingReferenceDecoder.Instance, SystemDrawingReferenceEncoder.Bmp, new BmpImageFormatDetector()); diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs index f0b732998..a5a3e332c 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.cs @@ -65,6 +65,7 @@ namespace SixLabors.ImageSharp.Tests $"Unable to find ImageSharp solution directory from {assemblyLocation} because of {ex.GetType().Name}!", ex); } + if (directory == null) { throw new Exception($"Unable to find ImageSharp solution directory from {assemblyLocation}!"); @@ -116,7 +117,7 @@ namespace SixLabors.ImageSharp.Tests /// internal static string CreateOutputDirectory(string path, params string[] pathParts) { - path = Path.Combine(TestEnvironment.ActualOutputDirectoryFullPath, path); + path = Path.Combine(ActualOutputDirectoryFullPath, path); if (pathParts != null && pathParts.Length > 0) { diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs index f6d3bdb7b..2c824729c 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs @@ -99,7 +99,7 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [InlineData("lol/foo.png", typeof(SystemDrawingReferenceDecoder))] + [InlineData("lol/foo.png", typeof(MagickReferenceDecoder))] [InlineData("lol/Rofl.bmp", typeof(SystemDrawingReferenceDecoder))] [InlineData("lol/Baz.JPG", typeof(JpegDecoder))] [InlineData("lol/Baz.gif", typeof(GifDecoder))] @@ -125,8 +125,8 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [InlineData("lol/foo.png", typeof(PngDecoder))] - [InlineData("lol/Rofl.bmp", typeof(BmpDecoder))] + [InlineData("lol/foo.png", typeof(MagickReferenceDecoder))] + [InlineData("lol/Rofl.bmp", typeof(SystemDrawingReferenceDecoder))] [InlineData("lol/Baz.JPG", typeof(JpegDecoder))] [InlineData("lol/Baz.gif", typeof(GifDecoder))] public void GetReferenceDecoder_ReturnsCorrectDecoders_Linux(string fileName, Type expectedDecoderType) diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 06c77235b..efc75773e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -241,7 +241,12 @@ namespace SixLabors.ImageSharp.Tests } - public static string[] AllBmpFiles => TestImages.Bmp.All; + public static string[] AllBmpFiles = + { + TestImages.Bmp.F, + TestImages.Bmp.CoreHeader, + TestImages.Bmp.Bit8 + }; [Theory] [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.Argb32)] From 7ac9ed62afb9a054eee1def9c1fd99a7cfae1e6e Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 27 Jun 2018 00:48:46 +0200 Subject: [PATCH 07/12] clanup + exact comparison for all PngDecoderTests --- .../Formats/Png/PngDecoderTests.cs | 97 ++++--------------- 1 file changed, 19 insertions(+), 78 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 53f71fb7b..586ca9bba 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -1,28 +1,24 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +// ReSharper disable InconsistentNaming + +using System.Buffers.Binary; using System.IO; using System.Text; + +using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + using Xunit; -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Png { - using System.Buffers.Binary; - using System.Linq; - - using SixLabors.ImageSharp.Formats.Png; - using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - - // TODO: Fix all bugs, and re enable Skipped and commented stuff !!! public class PngDecoderTests { private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; - - // TODO: Cannot use exact comparer since System.Drawing doesn't preserve more than 32bits. - private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.1302F, 2134); - + // Contains the png marker, IHDR and pHYs chunks of a 1x1 pixel 32bit png 1 a single black pixel. private static readonly byte[] raw1x1PngIHDRAndpHYs = { @@ -105,30 +101,6 @@ namespace SixLabors.ImageSharp.Tests TestImages.Png.GrayTrns16BitInterlaced }; - // This is a workaround for Mono-s decoder being incompatible with ours and GDI+. - // We shouldn't mix these with the Interleaved cases (which are also failing with Mono System.Drawing). Let's go AAA! - private static readonly string[] SkipOnMono = - { - TestImages.Png.Bad.ChunkLength2, - TestImages.Png.VimImage2, - TestImages.Png.Splash, - TestImages.Png.Indexed, - TestImages.Png.Bad.ChunkLength1, - TestImages.Png.VersioningImage1, - TestImages.Png.Banner7Adam7InterlaceMode, - TestImages.Png.GrayTrns16BitInterlaced, - TestImages.Png.Rgb48BppInterlaced - }; - - private static bool SkipVerification(ITestImageProvider provider) - { - string fn = provider.SourceFileOrDescription; - - // This is a workaround for Mono-s decoder being incompatible with ours and GDI+. - // We shouldn't mix these with the Interleaved cases (which are also failing with Mono System.Drawing). Let's go AAA! - return (TestEnvironment.IsLinux || TestEnvironment.IsMono) && SkipOnMono.Contains(fn); - } - [Theory] [WithFileCollection(nameof(CommonTestImages), PixelTypes.Rgba32)] public void Decode(TestImageProvider provider) @@ -137,22 +109,7 @@ namespace SixLabors.ImageSharp.Tests using (Image image = provider.GetImage(new PngDecoder())) { image.DebugSave(provider); - - if (!SkipVerification(provider)) - { - image.CompareToOriginal(provider, ImageComparer.Exact); - } - } - } - - [Theory] - [WithFile(TestImages.Png.Interlaced, PixelTypes.Rgba32)] - public void Decode_Interlaced_DoesNotThrow(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage(new PngDecoder())) - { - image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact); } } @@ -175,12 +132,8 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = provider.GetImage(new PngDecoder())) { - var encoder = new PngEncoder { ColorType = PngColorType.Rgb, BitDepth = PngBitDepth.Bit16 }; - - if (!SkipVerification(provider)) - { - image.VerifyEncoder(provider, "png", null, encoder, customComparer: ValidatorComparer); - } + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact); } } @@ -191,12 +144,8 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = provider.GetImage(new PngDecoder())) { - var encoder = new PngEncoder { ColorType = PngColorType.RgbWithAlpha, BitDepth = PngBitDepth.Bit16 }; - - if (!SkipVerification(provider)) - { - image.VerifyEncoder(provider, "png", null, encoder, customComparer: ValidatorComparer); - } + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact); } } @@ -207,12 +156,8 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = provider.GetImage(new PngDecoder())) { - var encoder = new PngEncoder { ColorType = PngColorType.Grayscale, BitDepth = PngBitDepth.Bit16 }; - - if (!SkipVerification(provider)) - { - image.VerifyEncoder(provider, "png", null, encoder, customComparer: ValidatorComparer); - } + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact); } } @@ -223,12 +168,8 @@ namespace SixLabors.ImageSharp.Tests { using (Image image = provider.GetImage(new PngDecoder())) { - var encoder = new PngEncoder { ColorType = PngColorType.GrayscaleWithAlpha, BitDepth = PngBitDepth.Bit16 }; - - if (!SkipVerification(provider)) - { - image.VerifyEncoder(provider, "png", null, encoder, customComparer: ValidatorComparer); - } + image.DebugSave(provider); + image.CompareToOriginal(provider, ImageComparer.Exact); } } @@ -303,7 +244,7 @@ namespace SixLabors.ImageSharp.Tests [InlineData(TestImages.Png.Blur, 32)] [InlineData(TestImages.Png.Rgb48Bpp, 48)] [InlineData(TestImages.Png.Rgb48BppInterlaced, 48)] - public void DetectPixelSize(string imagePath, int expectedPixelSize) + public void Identify(string imagePath, int expectedPixelSize) { var testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) From 055ca0c86a4ee39cc1316621c74add44779a1039 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 27 Jun 2018 00:51:29 +0200 Subject: [PATCH 08/12] PngDecoderTests.Chunks.cs --- .../Formats/Png/PngDecoderTests.Chunks.cs | 127 ++++++++++++++++++ .../Formats/Png/PngDecoderTests.cs | 110 +-------------- 2 files changed, 129 insertions(+), 108 deletions(-) create mode 100644 tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs new file mode 100644 index 000000000..6f04ba651 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs @@ -0,0 +1,127 @@ +using System.Buffers.Binary; +using System.IO; +using System.Text; + +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; +// ReSharper disable InconsistentNaming + +namespace SixLabors.ImageSharp.Tests.Formats.Png +{ + public partial class PngDecoderTests + { + // Contains the png marker, IHDR and pHYs chunks of a 1x1 pixel 32bit png 1 a single black pixel. + private static readonly byte[] Raw1X1PngIhdrAndpHYs = + { + // PNG Identifier + 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, + + // IHDR + 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, + 0x00, 0x00, 0x00, + // IHDR CRC + 0x90, 0x77, 0x53, 0xDE, + + // pHYS + 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, + 0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, + // pHYS CRC + 0xC7, 0x6F, 0xA8, 0x64 + }; + + // Contains the png marker, IDAT and IEND chunks of a 1x1 pixel 32bit png 1 a single black pixel. + private static readonly byte[] Raw1X1PngIdatAndIend = + { + // IDAT + 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x18, + 0x57, 0x63, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x01, + + // IDAT CRC + 0x5C, 0xCD, 0xFF, 0x69, + + // IEND + 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, + + // IEND CRC + 0xAE, 0x42, 0x60, 0x82 + }; + + [Theory] + [InlineData((uint)PngChunkType.Header)] // IHDR + [InlineData((uint)PngChunkType.Palette)] // PLTE + // [InlineData(PngChunkTypes.Data)] //TODO: Figure out how to test this + [InlineData((uint)PngChunkType.End)] // IEND + public void Decode_IncorrectCRCForCriticalChunk_ExceptionIsThrown(uint chunkType) + { + string chunkName = GetChunkTypeName(chunkType); + + using (var memStream = new MemoryStream()) + { + WriteHeaderChunk(memStream); + WriteChunk(memStream, chunkName); + WriteDataChunk(memStream); + + var decoder = new PngDecoder(); + + ImageFormatException exception = + Assert.Throws(() => decoder.Decode(null, memStream)); + + Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message); + } + } + + [Theory] + [InlineData((uint)PngChunkType.Gamma)] // gAMA + [InlineData((uint)PngChunkType.PaletteAlpha)] // tRNS + [InlineData( + (uint)PngChunkType.Physical)] // pHYs: It's ok to test physical as we don't throw for duplicate chunks. + //[InlineData(PngChunkTypes.Text)] //TODO: Figure out how to test this + public void Decode_IncorrectCRCForNonCriticalChunk_ExceptionIsThrown(uint chunkType) + { + string chunkName = GetChunkTypeName(chunkType); + + using (var memStream = new MemoryStream()) + { + WriteHeaderChunk(memStream); + WriteChunk(memStream, chunkName); + WriteDataChunk(memStream); + + var decoder = new PngDecoder(); + decoder.Decode(null, memStream); + } + } + + private static string GetChunkTypeName(uint value) + { + byte[] data = new byte[4]; + + BinaryPrimitives.WriteUInt32BigEndian(data, value); + + return Encoding.ASCII.GetString(data); + } + + private static void WriteHeaderChunk(MemoryStream memStream) + { + // Writes a 1x1 32bit png header chunk containing a single black pixel + memStream.Write(Raw1X1PngIhdrAndpHYs, 0, Raw1X1PngIhdrAndpHYs.Length); + } + + private static void WriteChunk(MemoryStream memStream, string chunkName) + { + memStream.Write(new byte[] { 0, 0, 0, 1 }, 0, 4); + memStream.Write(Encoding.GetEncoding("ASCII").GetBytes(chunkName), 0, 4); + memStream.Write(new byte[] { 0, 0, 0, 0, 0 }, 0, 5); + } + + private static void WriteDataChunk(MemoryStream memStream) + { + // Writes a 1x1 32bit png data chunk containing a single black pixel + memStream.Write(Raw1X1PngIdatAndIend, 0, Raw1X1PngIdatAndIend.Length); + memStream.Position = 0; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 586ca9bba..66e4f39fd 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -15,45 +15,11 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Formats.Png { - public class PngDecoderTests + public partial class PngDecoderTests { private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; - // Contains the png marker, IHDR and pHYs chunks of a 1x1 pixel 32bit png 1 a single black pixel. - private static readonly byte[] raw1x1PngIHDRAndpHYs = - { - // PNG Identifier - 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, - - // IHDR - 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x00, 0x00, 0x00, - // IHDR CRC - 0x90, 0x77, 0x53, 0xDE, - - // pHYS - 0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0E, 0xC3, 0x00, 0x00, 0x0E, 0xC3, 0x01, - // pHYS CRC - 0xC7, 0x6F, 0xA8, 0x64 - }; - - // Contains the png marker, IDAT and IEND chunks of a 1x1 pixel 32bit png 1 a single black pixel. - private static readonly byte[] raw1x1PngIDATAndIEND = - { - // IDAT - 0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54, 0x18, 0x57, 0x63, 0x60, 0x60, 0x60, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x01, - - // IDAT CRC - 0x5C, 0xCD, 0xFF, 0x69, - - // IEND - 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, - 0x4E, 0x44, - - // IEND CRC - 0xAE, 0x42, 0x60, 0x82 - }; + public static readonly string[] CommonTestImages = { @@ -252,77 +218,5 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); } } - - [Theory] - [InlineData((uint)PngChunkType.Header)] // IHDR - [InlineData((uint)PngChunkType.Palette)] // PLTE - // [InlineData(PngChunkTypes.Data)] //TODO: Figure out how to test this - [InlineData((uint)PngChunkType.End)] // IEND - public void Decode_IncorrectCRCForCriticalChunk_ExceptionIsThrown(uint chunkType) - { - string chunkName = GetChunkTypeName(chunkType); - - using (var memStream = new MemoryStream()) - { - WriteHeaderChunk(memStream); - WriteChunk(memStream, chunkName); - WriteDataChunk(memStream); - - var decoder = new PngDecoder(); - - ImageFormatException exception = Assert.Throws(() => decoder.Decode(null, memStream)); - - Assert.Equal($"CRC Error. PNG {chunkName} chunk is corrupt!", exception.Message); - } - } - - [Theory] - [InlineData((uint)PngChunkType.Gamma)] // gAMA - [InlineData((uint)PngChunkType.PaletteAlpha)] // tRNS - [InlineData((uint)PngChunkType.Physical)] // pHYs: It's ok to test physical as we don't throw for duplicate chunks. - //[InlineData(PngChunkTypes.Text)] //TODO: Figure out how to test this - public void Decode_IncorrectCRCForNonCriticalChunk_ExceptionIsThrown(uint chunkType) - { - string chunkName = GetChunkTypeName(chunkType); - - using (var memStream = new MemoryStream()) - { - WriteHeaderChunk(memStream); - WriteChunk(memStream, chunkName); - WriteDataChunk(memStream); - - var decoder = new PngDecoder(); - decoder.Decode(null, memStream); - } - } - - private static string GetChunkTypeName(uint value) - { - byte[] data = new byte[4]; - - BinaryPrimitives.WriteUInt32BigEndian(data, value); - - return Encoding.ASCII.GetString(data); - } - - private static void WriteHeaderChunk(MemoryStream memStream) - { - // Writes a 1x1 32bit png header chunk containing a single black pixel - memStream.Write(raw1x1PngIHDRAndpHYs, 0, raw1x1PngIHDRAndpHYs.Length); - } - - private static void WriteChunk(MemoryStream memStream, string chunkName) - { - memStream.Write(new byte[] { 0, 0, 0, 1 }, 0, 4); - memStream.Write(Encoding.GetEncoding("ASCII").GetBytes(chunkName), 0, 4); - memStream.Write(new byte[] { 0, 0, 0, 0, 0 }, 0, 5); - } - - private static void WriteDataChunk(MemoryStream memStream) - { - // Writes a 1x1 32bit png data chunk containing a single black pixel - memStream.Write(raw1x1PngIDATAndIEND, 0, raw1x1PngIDATAndIEND.Length); - memStream.Position = 0; - } } } \ No newline at end of file From 0b516fc4b9b0c61a735e54badbf3d2e0a604f10a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 27 Jun 2018 01:10:51 +0200 Subject: [PATCH 09/12] PngEncoderTests.WorksWithBitDepth16 --- .../Formats/Png/PngEncoderTests.cs | 86 ++++++++++++++++--- 1 file changed, 73 insertions(+), 13 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index eb046165d..a794bc03e 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -1,22 +1,21 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +// ReSharper disable InconsistentNaming using System.IO; using System.Linq; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Quantization; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Tests +namespace SixLabors.ImageSharp.Tests.Formats.Png { - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - public class PngEncoderTests { private const float ToleranceThresholdForPaletteEncoder = 0.2f / 100; @@ -70,7 +69,12 @@ namespace SixLabors.ImageSharp.Tests public void WorksWithDifferentSizes(TestImageProvider provider, PngColorType pngColorType) where TPixel : struct, IPixel { - TestPngEncoderCore(provider, pngColorType, PngFilterMethod.Adaptive, appendPngColorType: true); + TestPngEncoderCore( + provider, + pngColorType, + PngFilterMethod.Adaptive, + PngBitDepth.Bit8, + appendPngColorType: true); } [Theory] @@ -78,7 +82,13 @@ namespace SixLabors.ImageSharp.Tests public void IsNotBoundToSinglePixelType(TestImageProvider provider, PngColorType pngColorType) where TPixel : struct, IPixel { - TestPngEncoderCore(provider, pngColorType, PngFilterMethod.Adaptive, appendPixelType: true, appendPngColorType: true); + TestPngEncoderCore( + provider, + pngColorType, + PngFilterMethod.Adaptive, + PngBitDepth.Bit8, + appendPixelType: true, + appendPngColorType: true); } [Theory] @@ -86,7 +96,12 @@ namespace SixLabors.ImageSharp.Tests public void WorksWithAllFilterMethods(TestImageProvider provider, PngFilterMethod pngFilterMethod) where TPixel : struct, IPixel { - TestPngEncoderCore(provider, PngColorType.RgbWithAlpha, pngFilterMethod, appendPngFilterMethod: true); + TestPngEncoderCore( + provider, + PngColorType.RgbWithAlpha, + pngFilterMethod, + PngBitDepth.Bit8, + appendPngFilterMethod: true); } [Theory] @@ -94,7 +109,29 @@ namespace SixLabors.ImageSharp.Tests public void WorksWithAllCompressionLevels(TestImageProvider provider, int compressionLevel) where TPixel : struct, IPixel { - TestPngEncoderCore(provider, PngColorType.RgbWithAlpha, PngFilterMethod.Adaptive, compressionLevel, appendCompressionLevel: true); + TestPngEncoderCore( + provider, + PngColorType.RgbWithAlpha, + PngFilterMethod.Adaptive, + PngBitDepth.Bit8, + compressionLevel, + appendCompressionLevel: true); + } + + [Theory] + [WithTestPatternImages(24, 24, PixelTypes.Rgba64, PngColorType.Rgb)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba64, PngColorType.RgbWithAlpha)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.RgbWithAlpha)] + public void WorksWithBitDepth16(TestImageProvider provider, PngColorType pngColorType) + where TPixel : struct, IPixel + { + TestPngEncoderCore( + provider, + pngColorType, + PngFilterMethod.Adaptive, + PngBitDepth.Bit16, + appendPngColorType: true, + appendPixelType: true); } [Theory] @@ -102,7 +139,13 @@ namespace SixLabors.ImageSharp.Tests public void PaletteColorType_WuQuantizer(TestImageProvider provider, int paletteSize) where TPixel : struct, IPixel { - TestPngEncoderCore(provider, PngColorType.Palette, PngFilterMethod.Adaptive, paletteSize: paletteSize, appendPaletteSize: true); + TestPngEncoderCore( + provider, + PngColorType.Palette, + PngFilterMethod.Adaptive, + PngBitDepth.Bit8, + paletteSize: paletteSize, + appendPaletteSize: true); } private static bool HasAlpha(PngColorType pngColorType) => @@ -112,6 +155,7 @@ namespace SixLabors.ImageSharp.Tests TestImageProvider provider, PngColorType pngColorType, PngFilterMethod pngFilterMethod, + PngBitDepth bitDepth, int compressionLevel = 6, int paletteSize = 255, bool appendPngColorType = false, @@ -133,6 +177,7 @@ namespace SixLabors.ImageSharp.Tests ColorType = pngColorType, FilterMethod = pngFilterMethod, CompressionLevel = compressionLevel, + BitDepth = bitDepth, Quantizer = new WuQuantizer(paletteSize) }; @@ -155,16 +200,31 @@ namespace SixLabors.ImageSharp.Tests IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); string referenceOutputFile = ((ITestImageProvider)provider).Utility.GetReferenceOutputFileName("png", debugInfo, appendPixelType, true); + bool referenceOutputFileExists = File.Exists(referenceOutputFile); + using (var actualImage = Image.Load(actualOutputFile, referenceDecoder)) - using (var referenceImage = Image.Load(referenceOutputFile, referenceDecoder)) { + // TODO: Do we still need the reference output files? + Image referenceImage = referenceOutputFileExists + ? Image.Load(referenceOutputFile, referenceDecoder) + : image; + float paletteToleranceHack = 80f / paletteSize; paletteToleranceHack = paletteToleranceHack * paletteToleranceHack; ImageComparer comparer = pngColorType == PngColorType.Palette ? ImageComparer.Tolerant(ToleranceThresholdForPaletteEncoder * paletteToleranceHack) : ImageComparer.Exact; - - comparer.VerifySimilarity(referenceImage, actualImage); + try + { + comparer.VerifySimilarity(referenceImage, actualImage); + } + finally + { + if (referenceOutputFileExists) + { + referenceImage.Dispose(); + } + } } } } From 065a8b831c8c9a55eb09a8d0c3a6839ac1f32f02 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 27 Jun 2018 01:39:59 +0200 Subject: [PATCH 10/12] no BMP assertions on linux --- tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs | 8 ++++++-- .../TestUtilities/Tests/TestImageProviderTests.cs | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index d958278f6..b994af056 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -30,7 +30,11 @@ namespace SixLabors.ImageSharp.Tests using (Image image = provider.GetImage(new BmpDecoder())) { image.DebugSave(provider, "bmp"); - image.CompareToOriginal(provider); + + if (TestEnvironment.IsWindows) + { + image.CompareToOriginal(provider); + } } } @@ -52,7 +56,7 @@ namespace SixLabors.ImageSharp.Tests [InlineData(NegHeight, 24)] [InlineData(Bit8, 8)] [InlineData(Bit8Inverted, 8)] - public void DetectPixelSize(string imagePath, int expectedPixelSize) + public void Identify(string imagePath, int expectedPixelSize) { var testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index efc75773e..02acdfa18 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -244,7 +244,6 @@ namespace SixLabors.ImageSharp.Tests public static string[] AllBmpFiles = { TestImages.Bmp.F, - TestImages.Bmp.CoreHeader, TestImages.Bmp.Bit8 }; From 416bae672b22347345c3588b540ce34f773e368f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 27 Jun 2018 01:48:29 +0200 Subject: [PATCH 11/12] do not register SystemDrawing reference encoders on linux --- .../TestUtilities/TestEnvironment.Formats.cs | 7 +++++-- .../TestUtilities/Tests/TestEnvironmentTests.cs | 1 - 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index ccda71613..90c999f7c 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -58,16 +58,19 @@ namespace SixLabors.ImageSharp.Tests ); // Magick codecs should work on all platforms + IImageEncoder pngEncoder = IsWindows ? (IImageEncoder)SystemDrawingReferenceEncoder.Png : new PngEncoder(); + IImageEncoder bmpEncoder = IsWindows ? (IImageEncoder)SystemDrawingReferenceEncoder.Bmp : new BmpEncoder(); + cfg.ConfigureCodecs( ImageFormats.Png, MagickReferenceDecoder.Instance, - SystemDrawingReferenceEncoder.Png, + pngEncoder, new PngImageFormatDetector()); cfg.ConfigureCodecs( ImageFormats.Bmp, SystemDrawingReferenceDecoder.Instance, - SystemDrawingReferenceEncoder.Bmp, + bmpEncoder, new BmpImageFormatDetector()); return cfg; diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs index 2c824729c..8a3e69059 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs @@ -18,7 +18,6 @@ using Xunit.Abstractions; namespace SixLabors.ImageSharp.Tests { - public class TestEnvironmentTests { public TestEnvironmentTests(ITestOutputHelper output) From 2d76d3d05a1092a33e4a73c20c1ae8bff46a7c70 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 28 Jun 2018 00:58:23 +0200 Subject: [PATCH 12/12] Fix Sandbox46 --- tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index 245af5289..80cf162c5 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -19,6 +19,7 @@ +