From 0f412fc88b51ffb30b7d6e7c7398c9be90935785 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 27 Sep 2018 13:00:42 +0100 Subject: [PATCH] Add tests for all supported png bit depths. --- .../Formats/Png/PngEncoderTests.cs | 160 ++++++++---------- .../ReferenceCodecs/MagickReferenceDecoder.cs | 6 +- .../Tests/MagickReferenceCodecTests.cs | 8 +- 3 files changed, 74 insertions(+), 100 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 0508ac8c26..5d328db361 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -9,7 +9,6 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.MetaData; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Quantization; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -19,10 +18,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png { public class PngEncoderTests { - // This is bull. Failing online for no good reason. - // The images are an exact match. Maybe the submodule isn't updating? - private const float ToleranceThresholdForPaletteEncoder = 1.3F / 100; - public static readonly TheoryData PngBitDepthFiles = new TheoryData { @@ -137,19 +132,32 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } [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) + [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.Rgb, PngBitDepth.Bit8)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba64, PngColorType.Rgb, PngBitDepth.Bit16)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.RgbWithAlpha, PngBitDepth.Bit8)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba64, PngColorType.RgbWithAlpha, PngBitDepth.Bit16)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.Palette, PngBitDepth.Bit1)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.Palette, PngBitDepth.Bit2)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.Palette, PngBitDepth.Bit4)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.Palette, PngBitDepth.Bit8)] + [WithTestPatternImages(24, 24, PixelTypes.Rgb24, PngColorType.Grayscale, PngBitDepth.Bit1)] + [WithTestPatternImages(24, 24, PixelTypes.Rgb24, PngColorType.Grayscale, PngBitDepth.Bit2)] + [WithTestPatternImages(24, 24, PixelTypes.Rgb24, PngColorType.Grayscale, PngBitDepth.Bit4)] + [WithTestPatternImages(24, 24, PixelTypes.Rgb24, PngColorType.Grayscale, PngBitDepth.Bit8)] + [WithTestPatternImages(24, 24, PixelTypes.Rgb48, PngColorType.Grayscale, PngBitDepth.Bit16)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba32, PngColorType.GrayscaleWithAlpha, PngBitDepth.Bit8)] + [WithTestPatternImages(24, 24, PixelTypes.Rgba64, PngColorType.GrayscaleWithAlpha, PngBitDepth.Bit16)] + public void WorksWithAllBitDepths(TestImageProvider provider, PngColorType pngColorType, PngBitDepth pngBitDepth) where TPixel : struct, IPixel { TestPngEncoderCore( provider, pngColorType, PngFilterMethod.Adaptive, - PngBitDepth.Bit16, + pngBitDepth, appendPngColorType: true, - appendPixelType: true); + appendPixelType: true, + appendPngBitDepth: true); } [Theory] @@ -166,87 +174,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png appendPaletteSize: true); } - private static bool HasAlpha(PngColorType pngColorType) => - pngColorType == PngColorType.GrayscaleWithAlpha || pngColorType == PngColorType.RgbWithAlpha; - - private static void TestPngEncoderCore( - TestImageProvider provider, - PngColorType pngColorType, - PngFilterMethod pngFilterMethod, - PngBitDepth bitDepth, - int compressionLevel = 6, - int paletteSize = 255, - bool appendPngColorType = false, - bool appendPngFilterMethod = false, - bool appendPixelType = false, - bool appendCompressionLevel = false, - bool appendPaletteSize = false) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - if (!HasAlpha(pngColorType)) - { - image.Mutate(c => c.MakeOpaque()); - } - - var encoder = new PngEncoder - { - ColorType = pngColorType, - FilterMethod = pngFilterMethod, - CompressionLevel = compressionLevel, - BitDepth = bitDepth, - Quantizer = new WuQuantizer(paletteSize) - }; - - string pngColorTypeInfo = appendPngColorType ? pngColorType.ToString() : string.Empty; - string pngFilterMethodInfo = appendPngFilterMethod ? pngFilterMethod.ToString() : string.Empty; - string compressionLevelInfo = appendCompressionLevel ? $"_C{compressionLevel}" : string.Empty; - string paletteSizeInfo = appendPaletteSize ? $"_PaletteSize-{paletteSize}" : string.Empty; - string debugInfo = $"{pngColorTypeInfo}{pngFilterMethodInfo}{compressionLevelInfo}{paletteSizeInfo}"; - //string referenceInfo = $"{pngColorTypeInfo}"; - - // Does DebugSave & load reference CompareToReferenceInput(): - string actualOutputFile = ((ITestImageProvider)provider).Utility.SaveTestOutputFile(image, "png", encoder, debugInfo, appendPixelType); - - if (TestEnvironment.IsMono) - { - // There are bugs in mono's System.Drawing implementation, reference decoders are not always reliable! - return; - } - - 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)) - { - // TODO: Do we still need the reference output files? - Image referenceImage = referenceOutputFileExists - ? Image.Load(referenceOutputFile, referenceDecoder) - : image; - - float paletteToleranceHack = 80f / paletteSize; - paletteToleranceHack *= paletteToleranceHack; - ImageComparer comparer = pngColorType == PngColorType.Palette - ? ImageComparer.Tolerant(ToleranceThresholdForPaletteEncoder * paletteToleranceHack) - : ImageComparer.Exact; - try - { - comparer.VerifySimilarity(referenceImage, actualImage); - } - finally - { - if (referenceOutputFileExists) - { - referenceImage.Dispose(); - } - } - } - } - } - [Theory] [WithBlankImages(1, 1, PixelTypes.Rgba32)] public void WritesFileMarker(TestImageProvider provider) @@ -321,5 +248,54 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png } } } + + private static void TestPngEncoderCore( + TestImageProvider provider, + PngColorType pngColorType, + PngFilterMethod pngFilterMethod, + PngBitDepth bitDepth, + int compressionLevel = 6, + int paletteSize = 255, + bool appendPngColorType = false, + bool appendPngFilterMethod = false, + bool appendPixelType = false, + bool appendCompressionLevel = false, + bool appendPaletteSize = false, + bool appendPngBitDepth = false) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + var encoder = new PngEncoder + { + ColorType = pngColorType, + FilterMethod = pngFilterMethod, + CompressionLevel = compressionLevel, + BitDepth = bitDepth, + Quantizer = new WuQuantizer(paletteSize) + }; + + string pngColorTypeInfo = appendPngColorType ? pngColorType.ToString() : string.Empty; + string pngFilterMethodInfo = appendPngFilterMethod ? pngFilterMethod.ToString() : string.Empty; + string compressionLevelInfo = appendCompressionLevel ? $"_C{compressionLevel}" : string.Empty; + string paletteSizeInfo = appendPaletteSize ? $"_PaletteSize-{paletteSize}" : string.Empty; + string pngBitDepthInfo = appendPngBitDepth ? bitDepth.ToString() : string.Empty; + string debugInfo = $"{pngColorTypeInfo}{pngFilterMethodInfo}{compressionLevelInfo}{paletteSizeInfo}{pngBitDepthInfo}"; + + string actualOutputFile = provider.Utility.SaveTestOutputFile(image, "png", encoder, debugInfo, appendPixelType); + + // Compare to the Magick reference decoder. + IImageDecoder referenceDecoder = TestEnvironment.GetReferenceDecoder(actualOutputFile); + + // We compare using both our decoder and the reference decoder as pixel transformation + // occurrs within the encoder itself leaving the input image unaffected. + // This means we are benefiting from testing our decoder also. + using (var imageSharpImage = Image.Load(actualOutputFile, new PngDecoder())) + using (var referenceImage = Image.Load(actualOutputFile, referenceDecoder)) + { + ImageComparer.Exact.VerifySimilarity(referenceImage, imageSharpImage); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index 8cfc2472f5..7e942691e9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -29,13 +29,13 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs { if (magickImage.Depth == 8) { - byte[] data = pixels.ToByteArray("RGBA"); - + byte[] data = pixels.ToByteArray(PixelMapping.RGBA); + PixelOperations.Instance.PackFromRgba32Bytes(data, resultPixels, resultPixels.Length); } else if (magickImage.Depth == 16) { - ushort[] data = pixels.ToShortArray("RGBA"); + ushort[] data = pixels.ToShortArray(PixelMapping.RGBA); Span bytes = MemoryMarshal.Cast(data.AsSpan()); PixelOperations.Instance.PackFromRgba64Bytes(bytes, resultPixels, resultPixels.Length); diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs index db651886f2..b60439b488 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/MagickReferenceCodecTests.cs @@ -14,10 +14,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests public class MagickReferenceCodecTests { - public MagickReferenceCodecTests(ITestOutputHelper output) - { - this.Output = output; - } + public MagickReferenceCodecTests(ITestOutputHelper output) => this.Output = output; private ITestOutputHelper Output { get; } @@ -61,6 +58,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.Rgb48Bpp)] [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.Rgb48BppInterlaced)] [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.Rgb48BppTrans)] + [WithBlankImages(1, 1, PixelTypesToTest48, TestImages.Png.Gray16Bit)] public void MagickDecode_16BitDepthImage_IsApproximatelyEquivalentTo_SystemDrawingResult(TestImageProvider dummyProvider, string testImage) where TPixel : struct, IPixel { @@ -71,7 +69,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests // 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)) {