From 6a7dfccbc85418f842eee5d6e08a1e830cfa3f75 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 18 Feb 2018 16:57:09 +0100 Subject: [PATCH 01/11] IImage : IDisposable --- src/ImageSharp/Image/IImage.cs | 4 +++- src/ImageSharp/Image/Image{TPixel}.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Image/IImage.cs b/src/ImageSharp/Image/IImage.cs index 7355dc1fe..b9e2cee61 100644 --- a/src/ImageSharp/Image/IImage.cs +++ b/src/ImageSharp/Image/IImage.cs @@ -1,12 +1,14 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; + namespace SixLabors.ImageSharp { /// /// Encapsulates the properties and methods that describe an image. /// - public interface IImage : IImageInfo + public interface IImage : IImageInfo, IDisposable { } } \ No newline at end of file diff --git a/src/ImageSharp/Image/Image{TPixel}.cs b/src/ImageSharp/Image/Image{TPixel}.cs index be38b41f2..f264d8a59 100644 --- a/src/ImageSharp/Image/Image{TPixel}.cs +++ b/src/ImageSharp/Image/Image{TPixel}.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. /// /// The pixel format. - public sealed partial class Image : IImage, IDisposable, IConfigurable + public sealed partial class Image : IImage, IConfigurable where TPixel : struct, IPixel { private Configuration configuration; From 8e1a4f34b1f98709cae0f2eff1bfd06cf981099c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 18 Feb 2018 19:31:21 +0100 Subject: [PATCH 02/11] better BmpEncoderTests --- .../Formats/Bmp/BmpEncoderTests.cs | 61 ++++++++++++++----- .../ImageProviders/TestPatternProvider.cs | 3 +- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index d96d3def5..ff7e34733 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -8,28 +8,59 @@ using Xunit; namespace SixLabors.ImageSharp.Tests { + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + + using Xunit.Abstractions; + public class BmpEncoderTests : FileTestBase { - public static readonly TheoryData BitsPerPixel - = new TheoryData + private const PixelTypes PixelTypesToTest = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24; + + public static readonly TheoryData BitsPerPixel = + new TheoryData + { + BmpBitsPerPixel.Pixel24, + BmpBitsPerPixel.Pixel32 + }; + + public BmpEncoderTests(ITestOutputHelper output) { - BmpBitsPerPixel.Pixel24, - BmpBitsPerPixel.Pixel32 - }; + this.Output = output; + } + + private ITestOutputHelper Output { get; } [Theory] - [MemberData(nameof(BitsPerPixel))] - public void BitmapCanEncodeDifferentBitRates(BmpBitsPerPixel bitsPerPixel) + [WithTestPatternImages(nameof(BitsPerPixel), 48, 24, PixelTypesToTest)] + [WithTestPatternImages(nameof(BitsPerPixel), 47, 8, PixelTypesToTest)] + [WithTestPatternImages(nameof(BitsPerPixel), 49, 7, PixelTypesToTest)] + [WithSolidFilledImages(nameof(BitsPerPixel), 1, 1, 255, 100, 50, 255, PixelTypesToTest)] + [WithTestPatternImages(nameof(BitsPerPixel), 7, 5, PixelTypesToTest)] + public void Encode(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel { - string path = TestEnvironment.CreateOutputDirectory("Bmp"); - - foreach (TestFile file in Files) + + using (Image image = provider.GetImage()) { - string filename = file.GetFileNameWithoutExtension(bitsPerPixel); - using (Image image = file.CreateImage()) - { - image.Save($"{path}/{filename}.bmp", new BmpEncoder { BitsPerPixel = bitsPerPixel }); - } + // there is no alpha in bmp! + image.Mutate( + c => c.Opacity(1) + ); + + var encoder = new BmpEncoder { BitsPerPixel = bitsPerPixel }; + string path = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, testOutputDetails:bitsPerPixel); + + this.Output.WriteLine(path); + + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); + string referenceOutputFile = provider.Utility.GetReferenceOutputFileName("bmp", bitsPerPixel, true); + + this.Output.WriteLine(referenceOutputFile); + + //using (var encodedImage = Image.Load(referenceOutputFile, referenceDecoder)) + //{ + // ImageComparer.Exact.CompareImagesOrFrames(image, encodedImage); + //} } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 0b4817087..7fc9e58d4 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -86,9 +86,10 @@ namespace SixLabors.ImageSharp.Tests NamedColors.HotPink, NamedColors.Blue }; - int p = 0; + for (int y = top; y < bottom; y++) { + int p = 0; for (int x = left; x < right; x++) { if (x % stride == 0) From 8ea447d94cfd1d51c0b547d93b09f055ffcfa2f5 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 18 Feb 2018 19:50:02 +0100 Subject: [PATCH 03/11] BmpEncoderTests using reference output --- .../Formats/Bmp/BmpEncoderTests.cs | 48 ++++++++++--------- .../TestUtilities/TestImageExtensions.cs | 28 +++++++++++ tests/Images/External | 2 +- 3 files changed, 54 insertions(+), 24 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index ff7e34733..b1eea79d8 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -5,6 +5,7 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.PixelFormats; using Xunit; +// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { @@ -31,36 +32,37 @@ namespace SixLabors.ImageSharp.Tests private ITestOutputHelper Output { get; } [Theory] - [WithTestPatternImages(nameof(BitsPerPixel), 48, 24, PixelTypesToTest)] - [WithTestPatternImages(nameof(BitsPerPixel), 47, 8, PixelTypesToTest)] - [WithTestPatternImages(nameof(BitsPerPixel), 49, 7, PixelTypesToTest)] - [WithSolidFilledImages(nameof(BitsPerPixel), 1, 1, 255, 100, 50, 255, PixelTypesToTest)] - [WithTestPatternImages(nameof(BitsPerPixel), 7, 5, PixelTypesToTest)] - public void Encode(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + [WithTestPatternImages(nameof(BitsPerPixel), 24, 24, PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24)] + public void Encode_IsNotBoundToSinglePixelType(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel + { + TestBmpEncoderCore(provider, bitsPerPixel); + } + + [Theory] + [WithTestPatternImages(nameof(BitsPerPixel), 48, 24, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel), 47, 8, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel), 49, 7, PixelTypes.Rgba32)] + [WithSolidFilledImages(nameof(BitsPerPixel), 1, 1, 255, 100, 50, 255, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel), 7, 5, PixelTypes.Rgba32)] + public void Encode_WorksWithDifferentSizes(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel + { + TestBmpEncoderCore(provider, bitsPerPixel); + } + + private static void TestBmpEncoderCore(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel { - using (Image image = provider.GetImage()) { // there is no alpha in bmp! - image.Mutate( - c => c.Opacity(1) - ); - - var encoder = new BmpEncoder { BitsPerPixel = bitsPerPixel }; - string path = provider.Utility.SaveTestOutputFile(image, "bmp", encoder, testOutputDetails:bitsPerPixel); + image.Mutate(c => c.Opacity(1)); - this.Output.WriteLine(path); - - IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); - string referenceOutputFile = provider.Utility.GetReferenceOutputFileName("bmp", bitsPerPixel, true); - - this.Output.WriteLine(referenceOutputFile); + var encoder = new BmpEncoder { BitsPerPixel = bitsPerPixel }; - //using (var encodedImage = Image.Load(referenceOutputFile, referenceDecoder)) - //{ - // ImageComparer.Exact.CompareImagesOrFrames(image, encodedImage); - //} + // Does DebugSave & load reference CompareToReferenceInput(): + image.VerifyEncoder(provider, "bmp", bitsPerPixel, encoder); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 2b3cb1bcc..5d43816a5 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -227,6 +227,33 @@ namespace SixLabors.ImageSharp.Tests return image; } + /// + /// Loads the expected image with a reference decoder + compares it to . + /// Also performs a debug save using . + /// + internal static void VerifyEncoder(this Image image, + ITestImageProvider provider, + string extension, + object testOutputDetails, + IImageEncoder encoder, + ImageComparer customComparer = null, + bool appendPixelTypeToFileName = true + ) + where TPixel : struct, IPixel + { + + string path = provider.Utility.SaveTestOutputFile(image, extension, encoder, testOutputDetails, appendPixelTypeToFileName); + + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); + string referenceOutputFile = provider.Utility.GetReferenceOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName); + + using (var encodedImage = Image.Load(referenceOutputFile, referenceDecoder)) + { + ImageComparer comparer = customComparer ?? ImageComparer.Exact; + comparer.CompareImagesOrFrames(image, encodedImage); + } + } + internal static Image ToGrayscaleImage(this Buffer2D buffer, float scale) { var image = new Image(buffer.Width, buffer.Height); @@ -242,5 +269,6 @@ namespace SixLabors.ImageSharp.Tests return image; } + } } \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External index 376605e05..6508e097a 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 376605e05bb704d425b2d17bf5b310f5376da22e +Subproject commit 6508e097adf1ef6c38a8df17465c6868ed133256 From 46368a9fa7ece0ed7d1dafaee90beeac9ae13300 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 18 Feb 2018 19:55:29 +0100 Subject: [PATCH 04/11] improve information locality for BmpDecoderTests --- .../Formats/Bmp/BmpDecoderTests.cs | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 2c0121803..2209f223b 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -1,19 +1,28 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Formats; +using System.IO; +using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.PixelFormats; using Xunit; + // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { - using System.IO; - - using SixLabors.ImageSharp.Formats.Bmp; + + using static TestImages.Bmp; - public class BmpDecoderTests : FileTestBase + public class BmpDecoderTests { + public const PixelTypes CommonNonDefaultPixelTypes = + PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; + + public static readonly string[] AllBmpFiles = + { + Car, F, NegHeight, CoreHeader, V5Header, RLE, RLEInverted, Bit8, Bit8Inverted, Bit16, Bit16Inverted + }; + [Theory] [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32)] public void DecodeBmp(TestImageProvider provider) @@ -27,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [WithFile(TestImages.Bmp.F, CommonNonDefaultPixelTypes)] + [WithFile(F, CommonNonDefaultPixelTypes)] public void BmpDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { @@ -39,18 +48,18 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [InlineData(TestImages.Bmp.Car, 24)] - [InlineData(TestImages.Bmp.F, 24)] - [InlineData(TestImages.Bmp.NegHeight, 24)] - [InlineData(TestImages.Bmp.Bit8, 8)] - [InlineData(TestImages.Bmp.Bit8Inverted, 8)] + [InlineData(Car, 24)] + [InlineData(F, 24)] + [InlineData(NegHeight, 24)] + [InlineData(Bit8, 8)] + [InlineData(Bit8Inverted, 8)] public void DetectPixelSize(string imagePath, int expectedPixelSize) { - TestFile testFile = TestFile.Create(imagePath); + var testFile = TestFile.Create(imagePath); using (var stream = new MemoryStream(testFile.Bytes, false)) { Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); } } } -} +} \ No newline at end of file From 79808201f1e17c1c95bacb76f6b9cbdec7f4f674 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 18 Feb 2018 21:21:01 +0100 Subject: [PATCH 05/11] DebugSaveMultiFrame() works --- .../Formats/Bmp/BmpDecoderTests.cs | 1 - .../Formats/Gif/GifDecoderTests.cs | 43 +++++++-- .../TestUtilities/ImagingTestCaseUtility.cs | 65 +++++++++++++- .../TestUtilities/TestImageExtensions.cs | 87 +++++++++++++++++++ .../Tests/TestImageProviderTests.cs | 24 ++++- 5 files changed, 208 insertions(+), 12 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 2209f223b..d958278f6 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -10,7 +10,6 @@ using Xunit; namespace SixLabors.ImageSharp.Tests { - using static TestImages.Bmp; public class BmpDecoderTests diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 9a095548a..f92d5da81 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -6,23 +6,52 @@ using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; using Xunit; +using System.IO; +using SixLabors.ImageSharp.Advanced; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { - using System.IO; - using SixLabors.ImageSharp.Advanced; - public class GifDecoderTests { - private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; + private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; + + public static readonly string[] TestFiles = + { + TestImages.Gif.Giphy, TestImages.Gif.Rings, TestImages.Gif.Trans, TestImages.Gif.Kumin + }; - public static readonly string[] TestFiles = { TestImages.Gif.Giphy, TestImages.Gif.Rings, TestImages.Gif.Trans }; + public static readonly string[] MultiFrameTestFiles = + { + TestImages.Gif.Giphy, TestImages.Gif.Kumin + }; public static readonly string[] BadAppExtFiles = { TestImages.Gif.Issues.BadAppExtLength, TestImages.Gif.Issues.BadAppExtLength_2 }; [Theory] - [WithFileCollection(nameof(TestFiles), PixelTypes)] + [WithFileCollection(nameof(MultiFrameTestFiles), PixelTypes.Rgba32)] + public void AllFramesDecoded(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.DebugSaveMultiFrame(provider); + } + } + + [Theory] + [WithFile(TestImages.Gif.Trans, TestPixelTypes)] + public void IsNotBoundToSinglePixelType(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + image.DebugSave(provider); + } + } + + [Theory] + [WithFileCollection(nameof(TestFiles), TestPixelTypes)] public void DecodeAndReSave(TestImageProvider imageProvider) where TPixel : struct, IPixel { @@ -34,7 +63,7 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [WithFileCollection(nameof(TestFiles), PixelTypes)] + [WithFileCollection(nameof(TestFiles), TestPixelTypes)] public void DecodeResizeAndSave(TestImageProvider imageProvider) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index e7dfe5488..cde8ec9e4 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -157,16 +157,77 @@ namespace SixLabors.ImageSharp.Tests return path; } + public IEnumerable GetTestOutputFileNamesMultiFrame( + int frameCount, + string extension = null, + object testOutputDetails = null, + bool appendPixelTypeToFileName = true) + { + string baseDir = this.GetTestOutputFileName("", testOutputDetails, appendPixelTypeToFileName); + + if (!Directory.Exists(baseDir)) + { + Directory.CreateDirectory(baseDir); + } + + for (int i = 0; i < frameCount; i++) + { + string filePath = $"{baseDir}/{i:D2}.{extension}"; + yield return filePath; + } + } + + public string[] SaveTestOutputFileMultiFrame( + Image image, + string extension = "png", + IImageEncoder encoder = null, + object testOutputDetails = null, + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + encoder = encoder ?? TestEnvironment.GetReferenceEncoder($"foo.{extension}"); + + string[] files = this.GetTestOutputFileNamesMultiFrame( + image.Frames.Count, + extension, + testOutputDetails, + appendPixelTypeToFileName).ToArray(); + + for (int i = 0; i < image.Frames.Count; i++) + { + using (Image frameImage = image.Frames.CloneFrame(i)) + { + string filePath = files[i]; + using (FileStream stream = File.OpenWrite(filePath)) + { + frameImage.Save(stream, encoder); + } + } + } + + return files; + } + internal string GetReferenceOutputFileName( string extension, - object settings, + object testOutputDetails, bool appendPixelTypeToFileName) { return TestEnvironment.GetReferenceOutputFileName( - this.GetTestOutputFileName(extension, settings, appendPixelTypeToFileName) + this.GetTestOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName) ); } + public string[] GetReferenceOutputFileNamesMultiFrame( + int frameCount, + string extension, + object testOutputDetails, + bool appendPixelTypeToFileName = true) + { + return this.GetTestOutputFileNamesMultiFrame(frameCount, extension, testOutputDetails) + .Select(TestEnvironment.GetReferenceOutputFileName).ToArray(); + } + internal void Init(string typeName, string methodName, string outputSubfolderName) { this.TestGroupName = typeName; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 5d43816a5..c95ebc424 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -16,6 +16,7 @@ namespace SixLabors.ImageSharp.Tests using System.Numerics; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Memory; + using SixLabors.ImageSharp.MetaData; using Xunit; @@ -52,6 +53,28 @@ namespace SixLabors.ImageSharp.Tests return image; } + public static Image DebugSaveMultiFrame( + this Image image, + ITestImageProvider provider, + object testOutputDetails = null, + string extension = "png", + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + if (TestEnvironment.RunsOnCI) + { + return image; + } + + // We are running locally then we want to save it out + provider.Utility.SaveTestOutputFileMultiFrame( + image, + extension, + testOutputDetails: testOutputDetails, + appendPixelTypeToFileName: appendPixelTypeToFileName); + return image; + } + /// /// Compares the image against the expected Reference output, throws an exception if the images are not similar enough. /// The output file should be named identically to the output produced by . @@ -118,6 +141,29 @@ namespace SixLabors.ImageSharp.Tests return image; } + public static Image CompareToReferenceOutputMultiFrame( + this Image image, + ITestImageProvider provider, + ImageComparer comparer, + object testOutputDetails = null, + string extension = "png", + bool grayscale = false, + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + using (Image referenceImage = GetReferenceOutputImageMultiFrame( + provider, + image.Frames.Count, + testOutputDetails, + extension, + appendPixelTypeToFileName)) + { + comparer.VerifySimilarity(referenceImage, image); + } + + return image; + } + public static Image GetReferenceOutputImage(this ITestImageProvider provider, object testOutputDetails = null, string extension = "png", @@ -136,6 +182,47 @@ namespace SixLabors.ImageSharp.Tests return Image.Load(referenceOutputFile, decoder); } + public static Image GetReferenceOutputImageMultiFrame(this ITestImageProvider provider, + int frameCount, + object testOutputDetails = null, + string extension = "png", + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + string[] frameFiles = provider.Utility.GetReferenceOutputFileNamesMultiFrame( + frameCount, + extension, + testOutputDetails, + appendPixelTypeToFileName); + + var temporalFrameImages = new List>(); + + IImageDecoder decoder = TestEnvironment.GetReferenceDecoder(frameFiles[0]); + + foreach (string path in frameFiles) + { + if (!File.Exists(path)) + { + throw new Exception("Reference output file missing: " + path); + } + + var tempImage = Image.Load(path, decoder); + temporalFrameImages.Add(tempImage); + } + + var result = new Image( + Configuration.Default, + new ImageMetaData(), + temporalFrameImages.Select(fi => fi.Frames.RootFrame)); + + foreach (Image fi in temporalFrameImages) + { + fi.Dispose(); + } + + return result; + } + public static IEnumerable GetReferenceOutputSimilarityReports( this Image image, ITestImageProvider provider, diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index f0adeb753..2f306e949 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -239,8 +239,28 @@ namespace SixLabors.ImageSharp.Tests where TPixel : struct, IPixel { Assert.NotNull(provider.Utility.SourceFileOrDescription); - Image image = provider.GetImage(); - provider.Utility.SaveTestOutputFile(image, "png"); + using (Image image = provider.GetImage()) + { + provider.Utility.SaveTestOutputFile(image, "png"); + } + } + + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] + public void SaveTestOutputFileMultiFrame(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + string[] files = provider.Utility.SaveTestOutputFileMultiFrame(image); + + Assert.True(files.Length > 2); + foreach (string path in files) + { + this.Output.WriteLine(path); + Assert.True(File.Exists(path)); + } + } } [Theory] From b389ce646a50f3db18cc39add207b4f330cb7045 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 18 Feb 2018 22:30:12 +0100 Subject: [PATCH 06/11] GifDecoderTests using reference images --- .../Formats/Gif/GifDecoderTests.cs | 78 +++++++++---------- .../TestUtilities/TestImageExtensions.cs | 36 ++++++++- tests/Images/External | 2 +- 3 files changed, 70 insertions(+), 46 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index f92d5da81..3802a2c27 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -12,6 +12,10 @@ using SixLabors.ImageSharp.Advanced; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { + using System.Collections.Generic; + + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + public class GifDecoderTests { private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; @@ -26,56 +30,70 @@ namespace SixLabors.ImageSharp.Tests TestImages.Gif.Giphy, TestImages.Gif.Kumin }; + public static readonly string[] BasicVerificationFiles = + { + TestImages.Gif.Cheers, + TestImages.Gif.Rings, + + // previously DecodeBadApplicationExtensionLength: + TestImages.Gif.Issues.BadAppExtLength, + TestImages.Gif.Issues.BadAppExtLength_2, + + // previously DecodeBadDescriptorDimensionsLength: + TestImages.Gif.Issues.BadDescriptorWidth + }; + + private static readonly Dictionary BasicVerificationFrameCount = + new Dictionary + { + [TestImages.Gif.Cheers] = 93, + [TestImages.Gif.Issues.BadDescriptorWidth] = 36, + }; + public static readonly string[] BadAppExtFiles = { TestImages.Gif.Issues.BadAppExtLength, TestImages.Gif.Issues.BadAppExtLength_2 }; [Theory] [WithFileCollection(nameof(MultiFrameTestFiles), PixelTypes.Rgba32)] - public void AllFramesDecoded(TestImageProvider provider) + public void Decode_VerifyAllFrames(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { image.DebugSaveMultiFrame(provider); + image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); } } [Theory] [WithFile(TestImages.Gif.Trans, TestPixelTypes)] - public void IsNotBoundToSinglePixelType(TestImageProvider provider) + public void GifDecoder_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { image.DebugSave(provider); + image.CompareFirstFrameToReferenceOutput(provider, ImageComparer.Exact); } } [Theory] - [WithFileCollection(nameof(TestFiles), TestPixelTypes)] - public void DecodeAndReSave(TestImageProvider imageProvider) + [WithFileCollection(nameof(BasicVerificationFiles), PixelTypes.Rgba32)] + public void Decode_VerifyRootFrameAndFrameCount(TestImageProvider provider) where TPixel : struct, IPixel { - using (Image image = imageProvider.GetImage()) + if (!BasicVerificationFrameCount.TryGetValue(provider.SourceFileOrDescription, out int expectedFrameCount)) { - imageProvider.Utility.SaveTestOutputFile(image, "bmp"); - imageProvider.Utility.SaveTestOutputFile(image, "gif"); + expectedFrameCount = 1; } - } - [Theory] - [WithFileCollection(nameof(TestFiles), TestPixelTypes)] - public void DecodeResizeAndSave(TestImageProvider imageProvider) - where TPixel : struct, IPixel - { - using (Image image = imageProvider.GetImage()) + using (Image image = provider.GetImage()) { - image.Mutate(x => x.Resize(new Size(image.Width / 2, image.Height / 2))); - - imageProvider.Utility.SaveTestOutputFile(image, "bmp"); - imageProvider.Utility.SaveTestOutputFile(image, "gif"); + Assert.Equal(expectedFrameCount, image.Frames.Count); + image.DebugSave(provider); + image.CompareFirstFrameToReferenceOutput(provider, ImageComparer.Exact); } } - + [Fact] public void Decode_IgnoreMetadataIsFalse_CommentsAreRead() { @@ -178,27 +196,5 @@ namespace SixLabors.ImageSharp.Tests } } } - - [Theory] - [WithFileCollection(nameof(BadAppExtFiles), PixelTypes.Rgba32)] - public void DecodeBadApplicationExtensionLength(TestImageProvider imageProvider) - where TPixel : struct, IPixel - { - using (Image image = imageProvider.GetImage()) - { - imageProvider.Utility.SaveTestOutputFile(image, "bmp"); - } - } - - [Theory] - [WithFile(TestImages.Gif.Issues.BadDescriptorWidth, PixelTypes.Rgba32)] - public void DecodeBadDescriptorDimensionsLength(TestImageProvider provider) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - provider.Utility.SaveTestOutputFile(image, "bmp"); - } - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index c95ebc424..fcc28bf4e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -141,6 +141,32 @@ namespace SixLabors.ImageSharp.Tests return image; } + public static Image CompareFirstFrameToReferenceOutput( + this Image image, + ITestImageProvider provider, + ImageComparer comparer, + object testOutputDetails = null, + string extension = "png", + bool grayscale = false, + bool appendPixelTypeToFileName = true) + where TPixel : struct, IPixel + { + using (Image firstFrameOnlyImage = new Image(image.Width, image.Height)) + using (Image referenceImage = GetReferenceOutputImage( + provider, + testOutputDetails, + extension, + appendPixelTypeToFileName)) + { + firstFrameOnlyImage.Frames.AddFrame(image.Frames.RootFrame); + firstFrameOnlyImage.Frames.RemoveFrame(0); + + comparer.VerifySimilarity(referenceImage, firstFrameOnlyImage); + } + + return image; + } + public static Image CompareToReferenceOutputMultiFrame( this Image image, ITestImageProvider provider, @@ -209,17 +235,19 @@ namespace SixLabors.ImageSharp.Tests var tempImage = Image.Load(path, decoder); temporalFrameImages.Add(tempImage); } + + Image firstTemp = temporalFrameImages[0]; - var result = new Image( - Configuration.Default, - new ImageMetaData(), - temporalFrameImages.Select(fi => fi.Frames.RootFrame)); + var result = new Image(firstTemp.Width, firstTemp.Height); foreach (Image fi in temporalFrameImages) { + result.Frames.AddFrame(fi.Frames.RootFrame); fi.Dispose(); } + // remove the initial empty frame: + result.Frames.RemoveFrame(0); return result; } diff --git a/tests/Images/External b/tests/Images/External index 6508e097a..65db1d045 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 6508e097adf1ef6c38a8df17465c6868ed133256 +Subproject commit 65db1d045e74e7702ec33b44f88f97b4bf86f1ea From ad3eee53641dbeffb11e449cc83cb713840dc80b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 18 Feb 2018 23:50:15 +0100 Subject: [PATCH 07/11] PngEncoderTests using reference images --- .../Formats/Gif/GifEncoderTests.cs | 7 +- .../Formats/Png/PngEncoderTests.cs | 119 +++++++++++++++--- tests/Images/External | 2 +- 3 files changed, 110 insertions(+), 18 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index a06e36e2a..a2f4806f3 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -7,15 +7,16 @@ using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; using Xunit; +// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { public class GifEncoderTests { - private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; + private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; [Theory] - [WithTestPatternImages(100, 100, PixelTypes)] + [WithTestPatternImages(100, 100, TestPixelTypes)] public void EncodeGeneratedPatterns(TestImageProvider provider) where TPixel : struct, IPixel { @@ -78,7 +79,7 @@ namespace SixLabors.ImageSharp.Tests } [Fact] - public void Encode_CommentIsToLong_CommentIsTrimmed() + public void Encode_WhenCommentIsTooLong_CommentIsTrimmed() { using (Image input = new Image(1, 1)) { diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 1566ddf44..ef261aac9 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -10,36 +10,127 @@ using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.PixelFormats; using Xunit; +// ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests { + using SixLabors.ImageSharp.Quantizers; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + public class PngEncoderTests : FileTestBase { - private const PixelTypes PixelTypes = Tests.PixelTypes.Rgba32 | Tests.PixelTypes.RgbaVector | Tests.PixelTypes.Argb32; + /// + /// All types except Palette + /// + public static readonly TheoryData PngColorTypes = new TheoryData() + { + PngColorType.RgbWithAlpha, + PngColorType.Rgb, + PngColorType.Grayscale, + PngColorType.GrayscaleWithAlpha, + }; + + /// + /// All types except Palette + /// + public static readonly TheoryData CompressionLevels = new TheoryData() + { + 1, 2, 3, 4, 5, 6, 7, 8, 9 + }; + + public static readonly TheoryData PaletteSizes = new TheoryData() + { + 30, 55, 100, 201, 255 + }; + + [Theory] + [WithTestPatternImages(nameof(PngColorTypes), 48, 24, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(PngColorTypes), 47, 8, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(PngColorTypes), 49, 7, PixelTypes.Rgba32)] + [WithSolidFilledImages(nameof(PngColorTypes), 1, 1, 255, 100, 50, 255, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(PngColorTypes), 7, 5, PixelTypes.Rgba32)] + public void WorksWithDifferentSizes(TestImageProvider provider, PngColorType pngColorType) + where TPixel : struct, IPixel + { + TestPngEncoderCore(provider, pngColorType, appendPngColorType: true); + } + + [Theory] + [WithTestPatternImages(nameof(PngColorTypes), 24, 24, PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24)] + public void IsNotBoundToSinglePixelType(TestImageProvider provider, PngColorType pngColorType) + where TPixel : struct, IPixel + { + TestPngEncoderCore(provider, pngColorType, appendPixelType: true); + } + + [Theory] + [WithTestPatternImages(nameof(CompressionLevels), 24, 24, PixelTypes.Rgba32)] + public void WorksWithAllCompressionLevels(TestImageProvider provider, int compressionLevel) + where TPixel : struct, IPixel + { + TestPngEncoderCore(provider, PngColorType.RgbWithAlpha, compressionLevel, appendCompressionLevel: true); + } [Theory] - [WithTestPatternImages(100, 100, PixelTypes, PngColorType.RgbWithAlpha)] - [WithTestPatternImages(100, 100, PixelTypes, PngColorType.Rgb)] - [WithTestPatternImages(100, 100, PixelTypes, PngColorType.Palette)] - [WithTestPatternImages(100, 100, PixelTypes, PngColorType.Grayscale)] - [WithTestPatternImages(100, 100, PixelTypes, PngColorType.GrayscaleWithAlpha)] - public void EncodeGeneratedPatterns(TestImageProvider provider, PngColorType pngColorType) + [WithTestPatternImages(nameof(PaletteSizes), 72, 72, PixelTypes.Rgba32)] + public void PaletteColorType_WuQuantizer(TestImageProvider provider, int paletteSize) where TPixel : struct, IPixel { using (Image image = provider.GetImage()) { - var options = new PngEncoder() - { - PngColorType = pngColorType - }; - provider.Utility.TestName += "_" + pngColorType; + var encoder = new PngEncoder + { + PngColorType = PngColorType.Palette, + PaletteSize = paletteSize, + Quantizer = new WuQuantizer() + }; - provider.Utility.SaveTestOutputFile(image, "png", options); + image.VerifyEncoder(provider, "png", $"PaletteSize-{paletteSize}", encoder, appendPixelTypeToFileName: false); } } + private static bool HasAlpha(PngColorType pngColorType) => + pngColorType == PngColorType.GrayscaleWithAlpha || pngColorType == PngColorType.RgbWithAlpha; + + private static void TestPngEncoderCore( + TestImageProvider provider, + PngColorType pngColorType, + int compressionLevel = 6, + bool appendPngColorType = false, + bool appendPixelType = false, + bool appendCompressionLevel = false) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + if (!HasAlpha(pngColorType)) + { + image.Mutate(c => c.Opacity(1)); + } + + var encoder = new PngEncoder { PngColorType = pngColorType, CompressionLevel = compressionLevel}; + + string pngColorTypeInfo = appendPixelType ? pngColorType.ToString() : ""; + string compressionLevelInfo = appendCompressionLevel ? $"_C{compressionLevel}" : ""; + string debugInfo = $"{pngColorTypeInfo}{compressionLevelInfo}"; + string referenceInfo = $"{pngColorTypeInfo}"; + + // Does DebugSave & load reference CompareToReferenceInput(): + string path = ((ITestImageProvider)provider).Utility.SaveTestOutputFile(image, "png", encoder, debugInfo, appendPixelType); + + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); + string referenceOutputFile = ((ITestImageProvider)provider).Utility.GetReferenceOutputFileName("png", referenceInfo, appendPixelType); + + using (var encodedImage = Image.Load(referenceOutputFile, referenceDecoder)) + { + ImageComparer comparer = null ?? ImageComparer.Exact; + comparer.CompareImagesOrFrames(image, encodedImage); + } + } + } + [Theory] - [WithBlankImages(1, 1, PixelTypes.All)] + [WithBlankImages(1, 1, PixelTypes.Rgba32)] public void WritesFileMarker(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/Images/External b/tests/Images/External index 65db1d045..06809c1f2 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 65db1d045e74e7702ec33b44f88f97b4bf86f1ea +Subproject commit 06809c1f2462332731f2a88bd866d5222f533aa5 From 72e465d27204e850f1443be62c3445c5ff57e235 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 19 Feb 2018 01:00:44 +0100 Subject: [PATCH 08/11] use ImageComparer.Tolerant() when testing Palette PNG encoding --- tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index ef261aac9..7e24f41df 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -19,6 +19,8 @@ namespace SixLabors.ImageSharp.Tests public class PngEncoderTests : FileTestBase { + private const float ToleranceThresholdForPaletteEncoder = 0.01f / 100; + /// /// All types except Palette /// @@ -123,7 +125,7 @@ namespace SixLabors.ImageSharp.Tests using (var encodedImage = Image.Load(referenceOutputFile, referenceDecoder)) { - ImageComparer comparer = null ?? ImageComparer.Exact; + ImageComparer comparer = pngColorType== PngColorType.Palette ? ImageComparer.Tolerant(ToleranceThresholdForPaletteEncoder) : ImageComparer.Exact; comparer.CompareImagesOrFrames(image, encodedImage); } } From 15a995dd5cf19fe9c5fb70daa8385b434df4f587 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 19 Feb 2018 02:15:48 +0100 Subject: [PATCH 09/11] JpegEncoderTests using reference images --- .../ComplexIntegrationTests.cs | 35 ++++ .../Formats/Jpg/JpegEncoderTests.cs | 155 +++++++++--------- .../TestUtilities/TestImageExtensions.cs | 13 +- tests/Images/External | 2 +- 4 files changed, 126 insertions(+), 79 deletions(-) create mode 100644 tests/ImageSharp.Tests/ComplexIntegrationTests.cs diff --git a/tests/ImageSharp.Tests/ComplexIntegrationTests.cs b/tests/ImageSharp.Tests/ComplexIntegrationTests.cs new file mode 100644 index 000000000..ad4676872 --- /dev/null +++ b/tests/ImageSharp.Tests/ComplexIntegrationTests.cs @@ -0,0 +1,35 @@ +namespace SixLabors.ImageSharp.Tests +{ + using SixLabors.ImageSharp.Formats.Jpeg; + using SixLabors.ImageSharp.PixelFormats; + using SixLabors.ImageSharp.Processing; + using SixLabors.Primitives; + + using Xunit; + + /// + /// Might be useful to catch complex bugs + /// + public class ComplexIntegrationTests + { + [Theory] + [WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)] + [WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)] + [WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)] + [WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)] + public void LoadResizeSave(TestImageProvider provider, int quality, JpegSubsample subsample) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(x => x.Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max }))) + { + + image.MetaData.ExifProfile = null; // Reduce the size of the file + JpegEncoder options = new JpegEncoder { Subsample = subsample, Quality = quality }; + + provider.Utility.TestName += $"{subsample}_Q{quality}"; + provider.Utility.SaveTestOutputFile(image, "png"); + provider.Utility.SaveTestOutputFile(image, "jpg", options); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index c8d416bea..8610356b5 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -10,117 +10,126 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg using System.Collections.Generic; using System.IO; + using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.PixelFormats; - using SixLabors.ImageSharp.Processing; - using SixLabors.Primitives; + using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; + using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using Xunit; using Xunit.Abstractions; - public class JpegEncoderTests : MeasureFixture + public class JpegEncoderTests { - public static IEnumerable AllBmpFiles => TestImages.Bmp.All; + public static readonly TheoryData BitsPerPixel_Quality = + new TheoryData + { + { JpegSubsample.Ratio420, 40 }, + { JpegSubsample.Ratio420, 60 }, + { JpegSubsample.Ratio420, 100 }, + + { JpegSubsample.Ratio444, 40 }, + { JpegSubsample.Ratio444, 60 }, + { JpegSubsample.Ratio444, 100 }, + }; - public JpegEncoderTests(ITestOutputHelper output) - : base(output) + [Theory] + [WithFile(TestImages.Png.CalliphoraPartial, nameof(BitsPerPixel_Quality), PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel_Quality), 73, 71, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel_Quality), 48, 24, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel_Quality), 46, 8, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel_Quality), 51, 7, PixelTypes.Rgba32)] + [WithSolidFilledImages(nameof(BitsPerPixel_Quality), 1, 1, 255, 100, 50, 255, PixelTypes.Rgba32)] + [WithTestPatternImages(nameof(BitsPerPixel_Quality), 7, 5, PixelTypes.Rgba32)] + public void EncodeBaseline_WorksWithDifferentSizes(TestImageProvider provider, JpegSubsample subsample, int quality) + where TPixel : struct, IPixel { + TestJpegEncoderCore(provider, subsample, quality); } [Theory] - [WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)] - [WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio420)] - [WithFile(TestImages.Jpeg.Baseline.Snake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)] - [WithFile(TestImages.Jpeg.Baseline.Lake, PixelTypes.Rgba32, 75, JpegSubsample.Ratio444)] - public void LoadResizeSave(TestImageProvider provider, int quality, JpegSubsample subsample) + [WithTestPatternImages(nameof(BitsPerPixel_Quality), 48, 48, PixelTypes.Rgba32 | PixelTypes.Bgra32)] + public void EncodeBaseline_IsNotBoundToSinglePixelType(TestImageProvider provider, JpegSubsample subsample, int quality) where TPixel : struct, IPixel { - using (Image image = provider.GetImage(x => x.Resize(new ResizeOptions { Size = new Size(150, 100), Mode = ResizeMode.Max }))) - { - - image.MetaData.ExifProfile = null; // Reduce the size of the file - JpegEncoder options = new JpegEncoder { Subsample = subsample, Quality = quality }; - - provider.Utility.TestName += $"{subsample}_Q{quality}"; - provider.Utility.SaveTestOutputFile(image, "png"); - provider.Utility.SaveTestOutputFile(image, "jpg", options); - } + TestJpegEncoderCore(provider, subsample, quality); } - [Theory] - [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.Rgba32 | PixelTypes.Argb32, JpegSubsample.Ratio420, 75)] - [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32 | PixelTypes.Rgba32 | PixelTypes.Argb32, JpegSubsample.Ratio444, 75)] - public void OpenBmp_SaveJpeg(TestImageProvider provider, JpegSubsample subSample, int quality) - where TPixel : struct, IPixel + private static ImageComparer GetComparer(int quality) { - using (Image image = provider.GetImage()) + if (quality > 90) { - ImagingTestCaseUtility utility = provider.Utility; - utility.TestName += "_" + subSample + "_Q" + quality; - - using (FileStream outputStream = File.OpenWrite(utility.GetTestOutputFileName("jpg"))) - { - image.Save(outputStream, new JpegEncoder() - { - Subsample = subSample, - Quality = quality - }); - } + return ImageComparer.Tolerant(0.0005f / 100); + } + else if (quality > 50) + { + return ImageComparer.Tolerant(0.005f / 100); + } + else + { + return ImageComparer.Tolerant(0.01f / 100); } } - [Fact] - public void Encode_IgnoreMetadataIsFalse_ExifProfileIsWritten() + private static void TestJpegEncoderCore( + TestImageProvider provider, + JpegSubsample subsample, + int quality = 100) + where TPixel : struct, IPixel { - JpegEncoder options = new JpegEncoder() - { - IgnoreMetadata = false - }; - - TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); - - using (Image input = testFile.CreateImage()) + using (Image image = provider.GetImage()) { - using (MemoryStream memStream = new MemoryStream()) - { - input.Save(memStream, options); - - memStream.Position = 0; - using (Image output = Image.Load(memStream)) - { - Assert.NotNull(output.MetaData.ExifProfile); - } - } + // There is no alpha in Jpeg! + image.Mutate(c => c.Opacity(1)); + + var encoder = new JpegEncoder() + { + Subsample = subsample, + Quality = quality + }; + string info = $"{subsample}-Q{quality}"; + ImageComparer comparer = GetComparer(quality); + + // Does DebugSave & load reference CompareToReferenceInput(): + image.VerifyEncoder(provider, "jpeg", info, encoder, comparer, referenceImageExtension: "png"); } } + - [Fact] - public void Encode_IgnoreMetadataIsTrue_ExifProfileIgnored() + [Theory] + [InlineData(false)] + [InlineData(true)] + public void IgnoreMetadata_ControlsIfExifProfileIsWritten(bool ignoreMetaData) { - JpegEncoder options = new JpegEncoder() + var encoder = new JpegEncoder() { - IgnoreMetadata = true + IgnoreMetadata = ignoreMetaData }; - - TestFile testFile = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan); - - using (Image input = testFile.CreateImage()) + + using (Image input = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage()) { - using (MemoryStream memStream = new MemoryStream()) + using (var memStream = new MemoryStream()) { - input.SaveAsJpeg(memStream, options); + input.Save(memStream, encoder); memStream.Position = 0; - using (Image output = Image.Load(memStream)) + using (var output = Image.Load(memStream)) { - Assert.Null(output.MetaData.ExifProfile); + if (ignoreMetaData) + { + Assert.Null(output.MetaData.ExifProfile); + } + else + { + Assert.NotNull(output.MetaData.ExifProfile); + } } } } } - + [Fact] - public void Encode_Quality_0_And_1_Are_Identical() + public void Quality_0_And_1_Are_Identical() { var options = new JpegEncoder { @@ -143,7 +152,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg } [Fact] - public void Encode_Quality_0_And_100_Are_Not_Identical() + public void Quality_0_And_100_Are_Not_Identical() { var options = new JpegEncoder { diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index fcc28bf4e..33dbc911e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -352,16 +352,19 @@ namespace SixLabors.ImageSharp.Tests object testOutputDetails, IImageEncoder encoder, ImageComparer customComparer = null, - bool appendPixelTypeToFileName = true + bool appendPixelTypeToFileName = true, + string referenceImageExtension = null ) where TPixel : struct, IPixel { - string path = provider.Utility.SaveTestOutputFile(image, extension, encoder, testOutputDetails, appendPixelTypeToFileName); - - IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(path); - string referenceOutputFile = provider.Utility.GetReferenceOutputFileName(extension, testOutputDetails, appendPixelTypeToFileName); + provider.Utility.SaveTestOutputFile(image, extension, encoder, testOutputDetails, appendPixelTypeToFileName); + referenceImageExtension = referenceImageExtension ?? extension; + string referenceOutputFile = provider.Utility.GetReferenceOutputFileName(referenceImageExtension, testOutputDetails, appendPixelTypeToFileName); + + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(referenceOutputFile); + using (var encodedImage = Image.Load(referenceOutputFile, referenceDecoder)) { ImageComparer comparer = customComparer ?? ImageComparer.Exact; diff --git a/tests/Images/External b/tests/Images/External index 06809c1f2..550a157d8 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 06809c1f2462332731f2a88bd866d5222f533aa5 +Subproject commit 550a157d8af7a6883646a010c609f9c7c5c015ac From 079f46c437883b4a19120e65ab7fb6836a9eee89 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 19 Feb 2018 02:45:42 +0100 Subject: [PATCH 10/11] adding a few more cases to PngEncoderTests --- .../Formats/Png/PngEncoderTests.cs | 18 +++++++++++++++++- tests/Images/External | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 7e24f41df..b1c419990 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -45,7 +45,13 @@ namespace SixLabors.ImageSharp.Tests 30, 55, 100, 201, 255 }; + public static readonly TheoryData PaletteLargeOnly = new TheoryData() + { + 80, 100, 120, 230 + }; + [Theory] + [WithFile(TestImages.Png.Palette8Bpp, nameof(PngColorTypes), PixelTypes.Rgba32)] [WithTestPatternImages(nameof(PngColorTypes), 48, 24, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(PngColorTypes), 47, 8, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(PngColorTypes), 49, 7, PixelTypes.Rgba32)] @@ -56,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests { TestPngEncoderCore(provider, pngColorType, appendPngColorType: true); } - + [Theory] [WithTestPatternImages(nameof(PngColorTypes), 24, 24, PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24)] public void IsNotBoundToSinglePixelType(TestImageProvider provider, PngColorType pngColorType) @@ -73,6 +79,16 @@ namespace SixLabors.ImageSharp.Tests TestPngEncoderCore(provider, PngColorType.RgbWithAlpha, compressionLevel, appendCompressionLevel: true); } + [Theory] + [WithFile(TestImages.Png.Palette8Bpp, nameof(PaletteLargeOnly), PixelTypes.Rgba32)] + public void PaletteColorType_WuQuantizer_File( + TestImageProvider provider, + int paletteSize) + where TPixel : struct, IPixel + { + this.PaletteColorType_WuQuantizer(provider, paletteSize); + } + [Theory] [WithTestPatternImages(nameof(PaletteSizes), 72, 72, PixelTypes.Rgba32)] public void PaletteColorType_WuQuantizer(TestImageProvider provider, int paletteSize) diff --git a/tests/Images/External b/tests/Images/External index 550a157d8..b3be1178d 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 550a157d8af7a6883646a010c609f9c7c5c015ac +Subproject commit b3be1178d4e970efc624181480094e50b0d57a90 From 525124d849840c746dbc2b60a4e8466d9937c3ba Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 19 Feb 2018 03:09:19 +0100 Subject: [PATCH 11/11] removing unused stuff --- tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs | 5 ----- tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index 3802a2c27..9cdb9f8b1 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -20,11 +20,6 @@ namespace SixLabors.ImageSharp.Tests { private const PixelTypes TestPixelTypes = PixelTypes.Rgba32 | PixelTypes.RgbaVector | PixelTypes.Argb32; - public static readonly string[] TestFiles = - { - TestImages.Gif.Giphy, TestImages.Gif.Rings, TestImages.Gif.Trans, TestImages.Gif.Kumin - }; - public static readonly string[] MultiFrameTestFiles = { TestImages.Gif.Giphy, TestImages.Gif.Kumin diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index b1c419990..28f156279 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests using SixLabors.ImageSharp.Quantizers; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - public class PngEncoderTests : FileTestBase + public class PngEncoderTests { private const float ToleranceThresholdForPaletteEncoder = 0.01f / 100;