From e6e97ed4041cf2b6ef1aa6920be7cc4af97868d7 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 24 Mar 2017 07:49:10 +0000 Subject: [PATCH 1/9] Add image comparer test helper Add a couple of png smoke tests that encode and then decode an image in png then does a visual compare to verify they render the same. --- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 5 +- .../Formats/Png/PngInterlaceMode.cs | 4 +- .../Formats/Png/PngEncoderTests.cs | 64 +++++- .../Formats/Png/PngSmokeTests.cs | 62 ++++++ tests/ImageSharp.Tests/ImageComparer.cs | 84 ++++++++ .../WithTestPatternImageAttribute.cs | 38 ++++ .../ImageProviders/TestImageProvider.cs | 13 +- .../ImageProviders/TestPatternProvider.cs | 184 ++++++++++++++++++ .../TestUtilities/ImagingTestCaseUtility.cs | 34 +++- 9 files changed, 470 insertions(+), 18 deletions(-) create mode 100644 tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs create mode 100644 tests/ImageSharp.Tests/ImageComparer.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs create mode 100644 tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 7950d260c..e97eaed58 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -545,11 +545,10 @@ namespace ImageSharp.Formats /// The pixel format. /// The containing image data. /// The image base. - private void WritePhysicalChunk(Stream stream, ImageBase imageBase) + private void WritePhysicalChunk(Stream stream, Image image) where TColor : struct, IPixel { - Image image = imageBase as Image; - if (image != null && image.MetaData.HorizontalResolution > 0 && image.MetaData.VerticalResolution > 0) + if (image.MetaData.HorizontalResolution > 0 && image.MetaData.VerticalResolution > 0) { // 39.3700787 = inches in a meter. int dpmX = (int)Math.Round(image.MetaData.HorizontalResolution * 39.3700787D); diff --git a/src/ImageSharp/Formats/Png/PngInterlaceMode.cs b/src/ImageSharp/Formats/Png/PngInterlaceMode.cs index e32e808c1..ec3b8ebe7 100644 --- a/src/ImageSharp/Formats/Png/PngInterlaceMode.cs +++ b/src/ImageSharp/Formats/Png/PngInterlaceMode.cs @@ -13,11 +13,11 @@ namespace ImageSharp.Formats /// /// Non interlaced /// - None, + None = 0, /// /// Adam 7 interlacing. /// - Adam7 + Adam7 = 1 } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 49be75139..16906c1fa 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -9,12 +9,12 @@ namespace ImageSharp.Tests { using System.IO; using System.Threading.Tasks; - + using ImageSharp.IO; using Xunit; public class PngEncoderTests : FileTestBase { - [Fact] + [Fact(Skip ="Slow intergration test")] public void ImageCanSaveIndexedPng() { string path = CreateOutputDirectory("Png", "Indexed"); @@ -32,7 +32,7 @@ namespace ImageSharp.Tests } } - [Fact] + [Fact(Skip = "Slow intergration test")] public void ImageCanSavePngInParallel() { string path = this.CreateOutputDirectory("Png"); @@ -50,5 +50,63 @@ namespace ImageSharp.Tests } }); } + + [Theory] + [WithBlankImages(1, 1, PixelTypes.All)] + public void WritesFileMarker(TestImageProvider provider) + where TColor : struct, IPixel + { + using (Image image = provider.GetImage()) + using (EndianBinaryReader reader = Encode(image, null)) + { + + byte[] data = reader.ReadBytes(8); + byte[] expected = { + 0x89, // Set the high bit. + 0x50, // P + 0x4E, // N + 0x47, // G + 0x0D, // Line ending CRLF + 0x0A, // Line ending CRLF + 0x1A, // EOF + 0x0A // LF + }; + + Assert.Equal(expected, data); + } + } + + [Theory] + [WithBlankImages(1, 1, PixelTypes.All)] + [WithBlankImages(10, 10, PixelTypes.StandardImageClass)] + public void WritesFileHeaderHasHeightAndWidth(TestImageProvider provider) + where TColor : struct, IPixel + { + using (Image image = provider.GetImage()) + using (EndianBinaryReader reader = Encode(image, null)) + { + reader.ReadBytes(8); // throw away the file header + uint width = reader.ReadUInt32(); + uint height = reader.ReadUInt32(); + + byte bitDepth = reader.ReadByte(); + byte colorType = reader.ReadByte(); + byte compressionMethod = reader.ReadByte(); + byte filterMethod = reader.ReadByte(); + byte interlaceMethod = reader.ReadByte(); + + Assert.Equal(image.Width, (int)width); + Assert.Equal(image.Height, (int)height); + } + } + + private static EndianBinaryReader Encode(Image img, IEncoderOptions options) + where TColor : struct, IPixel + { + MemoryStream stream = new MemoryStream(); + new PngEncoder().Encode(img, stream, null); + stream.Position = 0; + return new EndianBinaryReader(Endianness.BigEndian, stream); + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs new file mode 100644 index 000000000..7a15e30e9 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -0,0 +1,62 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests.Formats.Png +{ + using System; + using System.Collections.Generic; + using System.Text; + using System.IO; + using Xunit; + using ImageSharp.Formats; + using System.Linq; + using ImageSharp.IO; + + public class PngSmokeTests + { + [Theory] + [WithTestPatternImages(300, 300, PixelTypes.All)] + public void WritesFileMarker(TestImageProvider provider) + where TColor : struct, IPixel + { + // does saving a file then repoening mean both files are identical??? + using (Image image = provider.GetImage()) + using (MemoryStream ms = new MemoryStream()) + { + image.Save(provider.Utility.GetTestOutputFileName("bmp")); + + image.Save(ms, new PngEncoder()); + ms.Position = 0; + using (Image img2 = new Image(ms, new Configuration(new PngFormat()))) + { + img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); + ImageComparer.VisualComparer(image, img2); + } + } + } + + [Theory] + [WithTestPatternImages(300, 300, PixelTypes.All)] + public void Resize(TestImageProvider provider) + where TColor : struct, IPixel + { + // does saving a file then repoening mean both files are identical??? + using (Image image = provider.GetImage()) + using (MemoryStream ms = new MemoryStream()) + { + // image.Save(provider.Utility.GetTestOutputFileName("png")); + image.Resize(100, 100); + // image.Save(provider.Utility.GetTestOutputFileName("png", "resize")); + + image.Save(ms, new PngEncoder()); + ms.Position = 0; + using (Image img2 = new Image(ms, new Configuration(new PngFormat()))) + { + ImageComparer.VisualComparer(image, img2); + } + } + } + } +} diff --git a/tests/ImageSharp.Tests/ImageComparer.cs b/tests/ImageSharp.Tests/ImageComparer.cs new file mode 100644 index 000000000..091edd343 --- /dev/null +++ b/tests/ImageSharp.Tests/ImageComparer.cs @@ -0,0 +1,84 @@ +namespace ImageSharp.Tests +{ + using System; + using ImageSharp; + using Xunit; + + /// + /// Class to perform simple image comparisons. + /// + public static class ImageComparer + { + const int DefaultScalingFactor = 32; + const int DefaultSegmentThreshold = 3; + const float DefaultImageThreshold = 0.000f; + + public static void VisualComparer(Image expected, Image actual, float imageTheshold = DefaultImageThreshold, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) + where TColorA : struct, IPixel + where TColorB : struct, IPixel + { + var percentage = expected.PercentageDifference(actual, segmentThreshold, scalingFactor); + + Assert.InRange(percentage, 0, imageTheshold); + } + + public static float PercentageDifference(this Image source, Image target, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) + where TColorA : struct, IPixel + where TColorB : struct, IPixel + { + // code adapted from https://www.codeproject.com/Articles/374386/Simple-image-comparison-in-NET + Fast2DArray differences = GetDifferences(source, target, scalingFactor); + + int diffPixels = 0; + + foreach (byte b in differences.Data) + { + if (b > segmentThreshold) { diffPixels++; } + } + + return diffPixels / (scalingFactor * scalingFactor); + } + + private static Fast2DArray GetDifferences(Image source, Image target, int scalingFactor) + where TColorA : struct, IPixel + where TColorB : struct, IPixel + { + Fast2DArray differences = new Fast2DArray(scalingFactor, scalingFactor); + Fast2DArray firstGray = source.GetGrayScaleValues(scalingFactor); + Fast2DArray secondGray = target.GetGrayScaleValues(scalingFactor); + + for (int y = 0; y < scalingFactor; y++) + { + for (int x = 0; x < scalingFactor; x++) + { + differences[x, y] = (byte)Math.Abs(firstGray[x, y] - secondGray[x, y]); + } + } + + return differences; + } + + private static Fast2DArray GetGrayScaleValues(this Image source, int scalingFactor) + where TColorA : struct, IPixel + { + byte[] buffer = new byte[4]; + using (Image img = new Image(source).Resize(scalingFactor, scalingFactor).Grayscale()) + { + using (PixelAccessor pixels = img.Lock()) + { + Fast2DArray grayScale = new Fast2DArray(scalingFactor, scalingFactor); + for (int y = 0; y < scalingFactor; y++) + { + for (int x = 0; x < scalingFactor; x++) + { + pixels[x, y].ToXyzBytes(buffer, 0); + grayScale[x, y] = buffer[1]; + } + } + + return grayScale; + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs new file mode 100644 index 000000000..98bc45f5b --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs @@ -0,0 +1,38 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Reflection; + + /// + /// Triggers passing instances which produce a blank image of size width * height. + /// One instance will be passed for each the pixel format defined by the pixelTypes parameter + /// + public class WithTestPatternImagesAttribute : ImageDataAttributeBase + { + /// + /// Triggers passing an that produces a test pattern image of size width * height + /// + /// The required width + /// The required height + /// The requested parameter + /// Additional theory parameter values + public WithTestPatternImagesAttribute(int width, int height, PixelTypes pixelTypes, params object[] additionalParameters) + : base(pixelTypes, additionalParameters) + { + this.Width = width; + this.Height = height; + } + + public int Width { get; } + public int Height { get; } + + protected override string GetFactoryMethodName(MethodInfo testMethod) => "TestPattern"; + + protected override object[] GetFactoryMethodArgs(MethodInfo testMethod, Type factoryType) => new object[] { this.Width, this.Height }; + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index cdb31ab69..4ec0fb507 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -25,13 +25,20 @@ namespace ImageSharp.Tests public ImagingTestCaseUtility Utility { get; private set; } public GenericFactory Factory { get; private set; } = new GenericFactory(); - - public static TestImageProvider Blank( + + public static TestImageProvider TestPattern( int width, int height, MethodInfo testMethod = null, PixelTypes pixelTypeOverride = PixelTypes.Undefined) - => new BlankProvider(width, height).Init(testMethod, pixelTypeOverride); + => new TestPatternProvider(width, height).Init(testMethod, pixelTypeOverride); + + public static TestImageProvider Blank( + int width, + int height, + MethodInfo testMethod = null, + PixelTypes pixelTypeOverride = PixelTypes.Undefined) + => new BlankProvider(width, height).Init(testMethod, pixelTypeOverride); public static TestImageProvider File( string filePath, diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs new file mode 100644 index 000000000..8d5e77554 --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -0,0 +1,184 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + using System.Numerics; + + public abstract partial class TestImageProvider + where TColor : struct, IPixel + { + private class TestPatternProvider : TestImageProvider + { + static Dictionary> testImages = new Dictionary>(); + + public TestPatternProvider(int width, int height) + { + this.Width = width; + this.Height = height; + } + + public override string SourceFileOrDescription => $"TestPattern{this.Width}x{this.Height}"; + + protected int Height { get; } + + protected int Width { get; } + + public override Image GetImage() + { + lock (testImages) + { + if (!testImages.ContainsKey(this.SourceFileOrDescription)) + { + var image = new Image(this.Width, this.Height); + DrawTestPattern(image); + testImages.Add(this.SourceFileOrDescription, image); + } + + return new Image(testImages[this.SourceFileOrDescription]); + } + } + + private static void DrawTestPattern(Image image) + { + // first lets split the image into 4 quadrants + using (var pixels = image.Lock()) + { + BlackWhiteChecker(pixels); // top left + HorizontalLines(pixels); // top right + TransparentGradients(pixels); // bottom left + Raninbow(pixels); // bottom right + } + } + + private static void HorizontalLines(PixelAccessor pixels) + { + // topLeft + int left = pixels.Width / 2; + int right = pixels.Width; + int top = 0; + int bottom = pixels.Height / 2; + int stride = pixels.Width / 12; + TColor[] c = { + NamedColors.HotPink, + NamedColors.Blue + }; + int p = 0; + for (var y = top; y < bottom; y++) + { + for (var x = left; x < right; x++) + { + if (x % stride == 0) + { + p++; + p = p % c.Length; + } + pixels[x, y] = c[p]; + } + } + } + + private static void BlackWhiteChecker(PixelAccessor pixels) + { + // topLeft + int left = 0; + int right = pixels.Width / 2; + int top = 0; + int bottom = pixels.Height / 2; + int stride = pixels.Width / 6; + TColor[] c = { + NamedColors.Black, + NamedColors.White + }; + + int p = 0; + for (var y = top; y < bottom; y++) + { + if (y % stride == 0) + { + p++; + p = p % c.Length; + } + var pstart = p; + for (var x = left; x < right; x++) + { + if (x % stride == 0) + { + p++; + p = p % c.Length; + } + pixels[x, y] = c[p]; + } + p = pstart; + } + } + + private static void TransparentGradients(PixelAccessor pixels) + { + // topLeft + int left = 0; + int right = pixels.Width / 2; + int top = pixels.Height / 2; + int bottom = pixels.Height; + int height = (int)Math.Ceiling(pixels.Height / 6f); + + Vector4 red = Color.Red.ToVector4(); // use real color so we can see har it translates in the test pattern + Vector4 green = Color.Green.ToVector4(); // use real color so we can see har it translates in the test pattern + Vector4 blue = Color.Blue.ToVector4(); // use real color so we can see har it translates in the test pattern + + TColor c = default(TColor); + + for (var x = left; x < right; x++) + { + blue.W = red.W = green.W = (float)x / (float)right; + + c.PackFromVector4(red); + var topBand = top; + for (var y = topBand; y < top + height; y++) + { + pixels[x, y] = c; + } + topBand = topBand + height; + c.PackFromVector4(green); + for (var y = topBand; y < topBand + height; y++) + { + pixels[x, y] = c; + } + topBand = topBand + height; + c.PackFromVector4(blue); + for (var y = topBand; y < bottom; y++) + { + pixels[x, y] = c; + } + } + + } + private static void Raninbow(PixelAccessor pixels) + { + int left = pixels.Width / 2; + int right = pixels.Width; + int top = pixels.Height / 2; + int bottom = pixels.Height; + + int pixelCount = left * top; + uint stepsPerPixel = (uint)(uint.MaxValue / pixelCount); + TColor c = default(TColor); + Color t = new Color(0); + uint inital = 0; + for (var x = left; x < right; x++) + for (var y = top; y < bottom; y++) + { + t.PackedValue += stepsPerPixel; + var v = t.ToVector4(); + //v.W = (x - left) / (float)left; + c.PackFromVector4(v); + pixels[x, y] = c; + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index bcccd1b44..1c960e0e8 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -44,13 +44,26 @@ namespace ImageSharp.Tests /// /// /// The required extension - public string GetTestOutputFileName(string extension = null) + public string GetTestOutputFileName(string extension = null, string tag = null) { string fn = string.Empty; + if (string.IsNullOrWhiteSpace(extension)) + { + extension = null; + } + fn = Path.GetFileNameWithoutExtension(this.SourceFileOrDescription); - extension = extension ?? Path.GetExtension(this.SourceFileOrDescription); - extension = extension ?? ".bmp"; + + if (string.IsNullOrWhiteSpace(extension)) + { + extension = Path.GetExtension(this.SourceFileOrDescription); + } + + if (string.IsNullOrWhiteSpace(extension)) + { + extension = ".bmp"; + } if (extension[0] != '.') { @@ -65,7 +78,14 @@ namespace ImageSharp.Tests pixName = '_' + pixName; } - return $"{this.GetTestOutputDir()}/{this.TestName}{pixName}{fn}{extension}"; + tag = tag ?? string.Empty; + if (tag != string.Empty) + { + tag= '_' + tag; + } + + + return $"{this.GetTestOutputDir()}/{this.TestName}{pixName}{fn}{tag}{extension}"; } /// @@ -80,7 +100,7 @@ namespace ImageSharp.Tests where TColor : struct, IPixel { string path = this.GetTestOutputFileName(extension); - + extension = Path.GetExtension(path); IImageFormat format = GetImageFormatByExtension(extension); encoder = encoder ?? format.Encoder; @@ -99,8 +119,8 @@ namespace ImageSharp.Tests private static IImageFormat GetImageFormatByExtension(string extension) { - extension = extension.ToLower(); - return Configuration.Default.ImageFormats.First(f => f.SupportedExtensions.Contains(extension)); + extension = extension?.TrimStart('.'); + return Configuration.Default.ImageFormats.First(f => f.SupportedExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase)); } private string GetTestOutputDir() From 1da6833a2a51beec93559c282b5ffe5bbf98d4f8 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 24 Mar 2017 08:08:02 +0000 Subject: [PATCH 2/9] fix stylecop issues --- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 2 +- .../Drawing/FillRegionProcessorTests.cs | 2 +- .../Formats/Png/PngEncoderTests.cs | 41 +++---------------- .../Formats/Png/PngSmokeTests.cs | 27 ++++++++++-- tests/ImageSharp.Tests/ImageComparer.cs | 2 +- .../ImageProviders/TestPatternProvider.cs | 32 +++++++-------- 6 files changed, 48 insertions(+), 58 deletions(-) diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index e97eaed58..498ae578c 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -544,7 +544,7 @@ namespace ImageSharp.Formats /// /// The pixel format. /// The containing image data. - /// The image base. + /// The image. private void WritePhysicalChunk(Stream stream, Image image) where TColor : struct, IPixel { diff --git a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs index db9686f3d..03994bc94 100644 --- a/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs +++ b/tests/ImageSharp.Tests/Drawing/FillRegionProcessorTests.cs @@ -27,7 +27,7 @@ namespace ImageSharp.Tests.Drawing [InlineData(false, 16, 4)] // we always do 4 sub=pixels when antialising is off. public void MinimumAntialiasSubpixelDepth(bool antialias, int antialiasSubpixelDepth, int expectedAntialiasSubpixelDepth) { - var bounds = new ImageSharp.Rectangle(0, 0, 1, 1); + ImageSharp.Rectangle bounds = new ImageSharp.Rectangle(0, 0, 1, 1); Mock> brush = new Mock>(); Mock region = new Mock(); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 16906c1fa..31b14601a 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -8,6 +8,7 @@ using ImageSharp.Formats; namespace ImageSharp.Tests { using System.IO; + using System.Linq; using System.Threading.Tasks; using ImageSharp.IO; using Xunit; @@ -57,10 +58,11 @@ namespace ImageSharp.Tests where TColor : struct, IPixel { using (Image image = provider.GetImage()) - using (EndianBinaryReader reader = Encode(image, null)) + using (MemoryStream ms = new MemoryStream()) { - - byte[] data = reader.ReadBytes(8); + image.Save(ms, new PngEncoder()); + + byte[] data = ms.ToArray().Take(8).ToArray(); byte[] expected = { 0x89, // Set the high bit. 0x50, // P @@ -75,38 +77,5 @@ namespace ImageSharp.Tests Assert.Equal(expected, data); } } - - [Theory] - [WithBlankImages(1, 1, PixelTypes.All)] - [WithBlankImages(10, 10, PixelTypes.StandardImageClass)] - public void WritesFileHeaderHasHeightAndWidth(TestImageProvider provider) - where TColor : struct, IPixel - { - using (Image image = provider.GetImage()) - using (EndianBinaryReader reader = Encode(image, null)) - { - reader.ReadBytes(8); // throw away the file header - uint width = reader.ReadUInt32(); - uint height = reader.ReadUInt32(); - - byte bitDepth = reader.ReadByte(); - byte colorType = reader.ReadByte(); - byte compressionMethod = reader.ReadByte(); - byte filterMethod = reader.ReadByte(); - byte interlaceMethod = reader.ReadByte(); - - Assert.Equal(image.Width, (int)width); - Assert.Equal(image.Height, (int)height); - } - } - - private static EndianBinaryReader Encode(Image img, IEncoderOptions options) - where TColor : struct, IPixel - { - MemoryStream stream = new MemoryStream(); - new PngEncoder().Encode(img, stream, null); - stream.Position = 0; - return new EndianBinaryReader(Endianness.BigEndian, stream); - } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index 7a15e30e9..d4e8534a7 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -18,20 +18,41 @@ namespace ImageSharp.Tests.Formats.Png { [Theory] [WithTestPatternImages(300, 300, PixelTypes.All)] - public void WritesFileMarker(TestImageProvider provider) + public void GeneralTest(TestImageProvider provider) where TColor : struct, IPixel { // does saving a file then repoening mean both files are identical??? using (Image image = provider.GetImage()) using (MemoryStream ms = new MemoryStream()) { - image.Save(provider.Utility.GetTestOutputFileName("bmp")); + // image.Save(provider.Utility.GetTestOutputFileName("bmp")); image.Save(ms, new PngEncoder()); ms.Position = 0; using (Image img2 = new Image(ms, new Configuration(new PngFormat()))) { - img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); + // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); + ImageComparer.VisualComparer(image, img2); + } + } + } + + [Theory] + [WithTestPatternImages(100, 100, PixelTypes.All)] + public void CanSaveIndexedPng(TestImageProvider provider) + where TColor : struct, IPixel + { + // does saving a file then repoening mean both files are identical??? + using (Image image = provider.GetImage()) + using (MemoryStream ms = new MemoryStream()) + { + // image.Save(provider.Utility.GetTestOutputFileName("bmp")); + image.MetaData.Quality = 256; + image.Save(ms, new PngEncoder()); + ms.Position = 0; + using (Image img2 = new Image(ms, new Configuration(new PngFormat()))) + { + // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); ImageComparer.VisualComparer(image, img2); } } diff --git a/tests/ImageSharp.Tests/ImageComparer.cs b/tests/ImageSharp.Tests/ImageComparer.cs index 091edd343..ee9359c2a 100644 --- a/tests/ImageSharp.Tests/ImageComparer.cs +++ b/tests/ImageSharp.Tests/ImageComparer.cs @@ -17,7 +17,7 @@ where TColorA : struct, IPixel where TColorB : struct, IPixel { - var percentage = expected.PercentageDifference(actual, segmentThreshold, scalingFactor); + float percentage = expected.PercentageDifference(actual, segmentThreshold, scalingFactor); Assert.InRange(percentage, 0, imageTheshold); } diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 8d5e77554..7e4401635 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -34,7 +34,7 @@ namespace ImageSharp.Tests { if (!testImages.ContainsKey(this.SourceFileOrDescription)) { - var image = new Image(this.Width, this.Height); + Image image = new Image(this.Width, this.Height); DrawTestPattern(image); testImages.Add(this.SourceFileOrDescription, image); } @@ -46,7 +46,7 @@ namespace ImageSharp.Tests private static void DrawTestPattern(Image image) { // first lets split the image into 4 quadrants - using (var pixels = image.Lock()) + using (PixelAccessor pixels = image.Lock()) { BlackWhiteChecker(pixels); // top left HorizontalLines(pixels); // top right @@ -68,9 +68,9 @@ namespace ImageSharp.Tests NamedColors.Blue }; int p = 0; - for (var y = top; y < bottom; y++) + for (int y = top; y < bottom; y++) { - for (var x = left; x < right; x++) + for (int x = left; x < right; x++) { if (x % stride == 0) { @@ -96,15 +96,15 @@ namespace ImageSharp.Tests }; int p = 0; - for (var y = top; y < bottom; y++) + for (int y = top; y < bottom; y++) { if (y % stride == 0) { p++; p = p % c.Length; } - var pstart = p; - for (var x = left; x < right; x++) + int pstart = p; + for (int x = left; x < right; x++) { if (x % stride == 0) { @@ -132,25 +132,25 @@ namespace ImageSharp.Tests TColor c = default(TColor); - for (var x = left; x < right; x++) + for (int x = left; x < right; x++) { blue.W = red.W = green.W = (float)x / (float)right; c.PackFromVector4(red); - var topBand = top; - for (var y = topBand; y < top + height; y++) + int topBand = top; + for (int y = topBand; y < top + height; y++) { pixels[x, y] = c; } topBand = topBand + height; c.PackFromVector4(green); - for (var y = topBand; y < topBand + height; y++) + for (int y = topBand; y < topBand + height; y++) { pixels[x, y] = c; } topBand = topBand + height; c.PackFromVector4(blue); - for (var y = topBand; y < bottom; y++) + for (int y = topBand; y < bottom; y++) { pixels[x, y] = c; } @@ -168,12 +168,12 @@ namespace ImageSharp.Tests uint stepsPerPixel = (uint)(uint.MaxValue / pixelCount); TColor c = default(TColor); Color t = new Color(0); - uint inital = 0; - for (var x = left; x < right; x++) - for (var y = top; y < bottom; y++) + + for (int x = left; x < right; x++) + for (int y = top; y < bottom; y++) { t.PackedValue += stepsPerPixel; - var v = t.ToVector4(); + Vector4 v = t.ToVector4(); //v.W = (x - left) / (float)left; c.PackFromVector4(v); pixels[x, y] = c; From fb0ecdbe0ca962f6c764ad453b62f6b3485b93dd Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Fri, 24 Mar 2017 18:57:38 +0000 Subject: [PATCH 3/9] Add explanatory comments --- tests/ImageSharp.Tests/ImageComparer.cs | 41 +++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/tests/ImageSharp.Tests/ImageComparer.cs b/tests/ImageSharp.Tests/ImageComparer.cs index ee9359c2a..0462506a6 100644 --- a/tests/ImageSharp.Tests/ImageComparer.cs +++ b/tests/ImageSharp.Tests/ImageComparer.cs @@ -9,10 +9,29 @@ /// public static class ImageComparer { - const int DefaultScalingFactor = 32; - const int DefaultSegmentThreshold = 3; - const float DefaultImageThreshold = 0.000f; + const int DefaultScalingFactor = 32; // this is means the images get scaled into a 32x32 image to sample pixels + const int DefaultSegmentThreshold = 3; // the greyscale difference between 2 segements my be > 3 before it influances the overall difference + const float DefaultImageThreshold = 0.000f; // after segment threasholds the images must have no differences + /// + /// Does a visual comparison between 2 images and then asserts the difference is less then a configurable threshold + /// + /// The color of the expected image + /// The color type fo the the actual image + /// The expected image + /// The actual image + /// + /// The threshold for the percentage difference where the images are asumed to be the same. + /// The default/undefined value is + /// + /// + /// The threashold of the individual segments before it acumulates towards the overall difference. + /// The default undefined value is + /// + /// + /// This is a sampling factor we sample a grid of average pixels width by high + /// The default undefined value is + /// public static void VisualComparer(Image expected, Image actual, float imageTheshold = DefaultImageThreshold, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) where TColorA : struct, IPixel where TColorB : struct, IPixel @@ -22,6 +41,22 @@ Assert.InRange(percentage, 0, imageTheshold); } + /// + /// Does a visual comparison between 2 images and then and returns the percentage diffence between the 2 + /// + /// The color of the source image + /// The color type for the target image + /// The source image + /// The target image + /// + /// The threashold of the individual segments before it acumulates towards the overall difference. + /// The default undefined value is + /// + /// + /// This is a sampling factor we sample a grid of average pixels width by high + /// The default undefined value is + /// + /// Returns a number from 0 - 1 which represents the diference focter between the images. public static float PercentageDifference(this Image source, Image target, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) where TColorA : struct, IPixel where TColorB : struct, IPixel From d6c31d95b161a1246218d8bde91efa44131f8144 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sat, 25 Mar 2017 07:07:58 +0000 Subject: [PATCH 4/9] Improve method name Added comments too --- .../Formats/Png/PngSmokeTests.cs | 6 +-- tests/ImageSharp.Tests/ImageComparer.cs | 2 +- .../ImageProviders/TestPatternProvider.cs | 39 +++++++++++++++---- 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index d4e8534a7..924903039 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -32,7 +32,7 @@ namespace ImageSharp.Tests.Formats.Png using (Image img2 = new Image(ms, new Configuration(new PngFormat()))) { // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); - ImageComparer.VisualComparer(image, img2); + ImageComparer.CheckSimilarity(image, img2); } } } @@ -53,7 +53,7 @@ namespace ImageSharp.Tests.Formats.Png using (Image img2 = new Image(ms, new Configuration(new PngFormat()))) { // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); - ImageComparer.VisualComparer(image, img2); + ImageComparer.CheckSimilarity(image, img2); } } } @@ -75,7 +75,7 @@ namespace ImageSharp.Tests.Formats.Png ms.Position = 0; using (Image img2 = new Image(ms, new Configuration(new PngFormat()))) { - ImageComparer.VisualComparer(image, img2); + ImageComparer.CheckSimilarity(image, img2); } } } diff --git a/tests/ImageSharp.Tests/ImageComparer.cs b/tests/ImageSharp.Tests/ImageComparer.cs index 0462506a6..41b884dd4 100644 --- a/tests/ImageSharp.Tests/ImageComparer.cs +++ b/tests/ImageSharp.Tests/ImageComparer.cs @@ -32,7 +32,7 @@ /// This is a sampling factor we sample a grid of average pixels width by high /// The default undefined value is /// - public static void VisualComparer(Image expected, Image actual, float imageTheshold = DefaultImageThreshold, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) + public static void CheckSimilarity(Image expected, Image actual, float imageTheshold = DefaultImageThreshold, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor) where TColorA : struct, IPixel where TColorB : struct, IPixel { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 7e4401635..89f46a947 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -12,6 +12,11 @@ namespace ImageSharp.Tests public abstract partial class TestImageProvider where TColor : struct, IPixel { + + /// + /// A test image provider that produces test patterns. + /// + /// private class TestPatternProvider : TestImageProvider { static Dictionary> testImages = new Dictionary>(); @@ -43,19 +48,26 @@ namespace ImageSharp.Tests } } + /// + /// Draws the test pattern on an image by drawing 4 other patterns in the for quadrants of the image. + /// + /// private static void DrawTestPattern(Image image) { // first lets split the image into 4 quadrants using (PixelAccessor pixels = image.Lock()) { BlackWhiteChecker(pixels); // top left - HorizontalLines(pixels); // top right + VirticalBars(pixels); // top right TransparentGradients(pixels); // bottom left - Raninbow(pixels); // bottom right + Rainbow(pixels); // bottom right } } - - private static void HorizontalLines(PixelAccessor pixels) + /// + /// Fills the top right quadrant with alternating solid vertical bars. + /// + /// + private static void VirticalBars(PixelAccessor pixels) { // topLeft int left = pixels.Width / 2; @@ -82,6 +94,10 @@ namespace ImageSharp.Tests } } + /// + /// fills the top left quadrant with a black and white checker board. + /// + /// private static void BlackWhiteChecker(PixelAccessor pixels) { // topLeft @@ -116,7 +132,11 @@ namespace ImageSharp.Tests p = pstart; } } - + + /// + /// Fills the bottom left quadrent with 3 horizental bars in Red, Green and Blue with a alpha gradient from left (transparent) to right (solid). + /// + /// private static void TransparentGradients(PixelAccessor pixels) { // topLeft @@ -155,9 +175,14 @@ namespace ImageSharp.Tests pixels[x, y] = c; } } - } - private static void Raninbow(PixelAccessor pixels) + + /// + /// Fills the bottom right quadrant with all the colors producable by converting itterating over a uint and unpacking it. + /// A better algorithm could be used but it works + /// + /// + private static void Rainbow(PixelAccessor pixels) { int left = pixels.Width / 2; int right = pixels.Width; From 72e88f3a33c8669b8a14b950ed7370e272910cd6 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 26 Mar 2017 22:26:03 +0100 Subject: [PATCH 5/9] allow test cases to be serialized to they show in VS --- .../ImageProviders/BlankProvider.cs | 25 ++++++++++- .../ImageProviders/FileProvider.cs | 20 ++++++++- .../ImageProviders/SolidProvider.cs | 38 +++++++++++++--- .../ImageProviders/TestImageProvider.cs | 44 +++++++++++++++---- .../ImageProviders/TestPatternProvider.cs | 21 ++++----- .../TestUtilities/ImagingTestCaseUtility.cs | 15 ++++--- 6 files changed, 131 insertions(+), 32 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs index ad4d2cc98..61067e53b 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs @@ -6,6 +6,7 @@ namespace ImageSharp.Tests { using System; + using Xunit.Abstractions; public abstract partial class TestImageProvider where TColor : struct, IPixel @@ -17,14 +18,34 @@ namespace ImageSharp.Tests this.Width = width; this.Height = height; } + public BlankProvider() + { + this.Width = 100; + this.Height = 100; + } public override string SourceFileOrDescription => $"Blank{this.Width}x{this.Height}"; - protected int Height { get; } + protected int Height { get; private set; } - protected int Width { get; } + protected int Width { get; private set; } public override Image GetImage() => this.Factory.CreateImage(this.Width, this.Height); + + + public override void Deserialize(IXunitSerializationInfo info) + { + this.Width = info.GetValue("width"); + this.Height = info.GetValue("height"); + base.Deserialize(info); + } + + public override void Serialize(IXunitSerializationInfo info) + { + info.AddValue("width", this.Width); + info.AddValue("height", this.Height); + base.Serialize(info); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index 7975f9b7e..a641cfa4d 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -7,11 +7,12 @@ namespace ImageSharp.Tests { using System; using System.Collections.Concurrent; + using Xunit.Abstractions; public abstract partial class TestImageProvider where TColor : struct, IPixel { - private class FileProvider : TestImageProvider + private class FileProvider : TestImageProvider, IXunitSerializable { // Need PixelTypes in the dictionary key, because result images of TestImageProvider.FileProvider // are shared between PixelTypes.Color & PixelTypes.StandardImageClass @@ -33,6 +34,10 @@ namespace ImageSharp.Tests this.filePath = filePath; } + public FileProvider() + { + } + public override string SourceFileOrDescription => this.filePath; public override Image GetImage() @@ -49,6 +54,19 @@ namespace ImageSharp.Tests return this.Factory.CreateImage(cachedImage); } + + public void Deserialize(IXunitSerializationInfo info) + { + this.filePath = info.GetValue("path"); + + base.Deserialize(info); // must be called last + } + + public void Serialize(IXunitSerializationInfo info) + { + base.Serialize(info); + info.AddValue("path", this.filePath); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs index 1593014ae..9a6750872 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/SolidProvider.cs @@ -6,6 +6,7 @@ namespace ImageSharp.Tests { using System; + using Xunit.Abstractions; /// /// Provides instances for parametric unit tests. @@ -14,15 +15,15 @@ namespace ImageSharp.Tests public abstract partial class TestImageProvider where TColor : struct, IPixel { - private class SolidProvider : BlankProvider + private class SolidProvider : BlankProvider { - private readonly byte a; + private byte a; - private readonly byte b; + private byte b; - private readonly byte g; + private byte g; - private readonly byte r; + private byte r; public SolidProvider(int width, int height, byte r, byte g, byte b, byte a) : base(width, height) @@ -33,6 +34,15 @@ namespace ImageSharp.Tests this.a = a; } + public SolidProvider() + : base() + { + this.r = 0; + this.g = 0; + this.b = 0; + this.a = 0; + } + public override string SourceFileOrDescription => $"Solid{this.Width}x{this.Height}_({this.r},{this.g},{this.b},{this.a})"; @@ -44,6 +54,24 @@ namespace ImageSharp.Tests return image.Fill(color); } + + public override void Serialize(IXunitSerializationInfo info) + { + info.AddValue("red", this.r); + info.AddValue("green", this.g); + info.AddValue("blue", this.b); + info.AddValue("alpha", this.a); + base.Serialize(info); + } + + public override void Deserialize(IXunitSerializationInfo info) + { + this.r = info.GetValue("red"); + this.g = info.GetValue("green"); + this.b = info.GetValue("blue"); + this.a = info.GetValue("alpha"); + base.Deserialize(info); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 4ec0fb507..5a73bbcc9 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -7,12 +7,13 @@ namespace ImageSharp.Tests { using System; using System.Reflection; + using Xunit.Abstractions; /// /// Provides instances for parametric unit tests. /// /// The pixel format of the image - public abstract partial class TestImageProvider + public abstract partial class TestImageProvider : IXunitSerializable where TColor : struct, IPixel { public PixelTypes PixelType { get; private set; } = typeof(TColor).GetPixelType(); @@ -25,7 +26,9 @@ namespace ImageSharp.Tests public ImagingTestCaseUtility Utility { get; private set; } public GenericFactory Factory { get; private set; } = new GenericFactory(); - + public string TypeName { get; private set; } + public string MethodName { get; private set; } + public static TestImageProvider TestPattern( int width, int height, @@ -72,12 +75,30 @@ namespace ImageSharp.Tests /// public abstract Image GetImage(); - protected TestImageProvider Init(MethodInfo testMethod, PixelTypes pixelTypeOverride) + public virtual void Deserialize(IXunitSerializationInfo info) + { + PixelTypes pixelType = info.GetValue("PixelType"); + string typeName = info.GetValue("TypeName"); + string methodName = info.GetValue("MethodName"); + + this.Init(typeName, methodName, pixelType); + } + + public virtual void Serialize(IXunitSerializationInfo info) + { + info.AddValue("PixelType", this.PixelType); + info.AddValue("TypeName", this.TypeName); + info.AddValue("MethodName", this.MethodName); + } + + protected TestImageProvider Init(string typeName, string methodName, PixelTypes pixelTypeOverride) { if (pixelTypeOverride != PixelTypes.Undefined) { this.PixelType = pixelTypeOverride; } + this.TypeName = typeName; + this.MethodName = methodName; if (pixelTypeOverride == PixelTypes.StandardImageClass) { @@ -85,19 +106,24 @@ namespace ImageSharp.Tests } this.Utility = new ImagingTestCaseUtility() - { - SourceFileOrDescription = this.SourceFileOrDescription, - PixelTypeName = this.PixelType.ToString() - }; + { + SourceFileOrDescription = this.SourceFileOrDescription, + PixelTypeName = this.PixelType.ToString() + }; - if (testMethod != null) + if (methodName != null) { - this.Utility.Init(testMethod); + this.Utility.Init(typeName, methodName); } return this; } + protected TestImageProvider Init(MethodInfo testMethod, PixelTypes pixelTypeOverride) + { + return Init(testMethod?.DeclaringType.Name, testMethod?.Name, pixelTypeOverride); + } + public override string ToString() { string provName = this.GetType().Name.Replace("Provider", ""); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs index 89f46a947..39ce61495 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs @@ -8,6 +8,7 @@ namespace ImageSharp.Tests using System; using System.Collections.Generic; using System.Numerics; + using Xunit.Abstractions; public abstract partial class TestImageProvider where TColor : struct, IPixel @@ -17,21 +18,21 @@ namespace ImageSharp.Tests /// A test image provider that produces test patterns. /// /// - private class TestPatternProvider : TestImageProvider + private class TestPatternProvider : BlankProvider { static Dictionary> testImages = new Dictionary>(); public TestPatternProvider(int width, int height) + : base(width, height) { - this.Width = width; - this.Height = height; } - public override string SourceFileOrDescription => $"TestPattern{this.Width}x{this.Height}"; - - protected int Height { get; } + public TestPatternProvider() + : base() + { + } - protected int Width { get; } + public override string SourceFileOrDescription => $"TestPattern{this.Width}x{this.Height}"; public override Image GetImage() { @@ -43,9 +44,9 @@ namespace ImageSharp.Tests DrawTestPattern(image); testImages.Add(this.SourceFileOrDescription, image); } - - return new Image(testImages[this.SourceFileOrDescription]); } + + return new Image(testImages[this.SourceFileOrDescription]); } /// @@ -132,7 +133,7 @@ namespace ImageSharp.Tests p = pstart; } } - + /// /// Fills the bottom left quadrent with 3 horizental bars in Red, Green and Blue with a alpha gradient from left (transparent) to right (solid). /// diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index 1c960e0e8..9fd33d90b 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -62,7 +62,7 @@ namespace ImageSharp.Tests if (string.IsNullOrWhiteSpace(extension)) { - extension = ".bmp"; + extension = ".bmp"; } if (extension[0] != '.') @@ -81,9 +81,9 @@ namespace ImageSharp.Tests tag = tag ?? string.Empty; if (tag != string.Empty) { - tag= '_' + tag; + tag = '_' + tag; } - + return $"{this.GetTestOutputDir()}/{this.TestName}{pixName}{fn}{tag}{extension}"; } @@ -111,10 +111,15 @@ namespace ImageSharp.Tests } } + internal void Init(string typeName, string methodName) + { + this.TestGroupName = typeName; + this.TestName = methodName; + } + internal void Init(MethodInfo method) { - this.TestGroupName = method.DeclaringType.Name; - this.TestName = method.Name; + this.Init(method.DeclaringType.Name, method.Name); } private static IImageFormat GetImageFormatByExtension(string extension) From 2e53515b98de61ecbf954d555756c9ede9de49e4 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 26 Mar 2017 22:27:26 +0100 Subject: [PATCH 6/9] Speed up greyscale tests by using test pattern. --- .../Processors/Filters/GrayscaleTest.cs | 34 ++++++++++--------- .../TestUtilities/TestImageExtensions.cs | 20 +++++++++++ 2 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs diff --git a/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs index 91f383dd2..97947a787 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs @@ -9,30 +9,32 @@ namespace ImageSharp.Tests using Xunit; using ImageSharp.Processing; + using ImageSharp.Tests; + using System.Numerics; public class GrayscaleTest : FileTestBase { - public static readonly TheoryData GrayscaleValues - = new TheoryData - { - GrayscaleMode.Bt709 , - GrayscaleMode.Bt601 , - }; - + /// + /// Use test patterns over loaded images to save decode time. + /// [Theory] - [MemberData(nameof(GrayscaleValues))] - public void ImageShouldApplyGrayscaleFilter(GrayscaleMode value) + [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt709)] + [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt601)] + public void ImageShouldApplyGrayscaleFilterAll(TestImageProvider provider, GrayscaleMode value) + where TColor : struct, IPixel { - string path = this.CreateOutputDirectory("Grayscale"); - - foreach (TestFile file in Files) + using (Image image = provider.GetImage()) { - string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) + image.Grayscale(value); + byte[] data = new byte[3]; + foreach (TColor p in image.Pixels) { - image.Grayscale(value).Save(output); + p.ToXyzBytes(data, 0); + Assert.Equal(data[0], data[1]); + Assert.Equal(data[1], data[2]); } + + image.DebugSave(provider); } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs new file mode 100644 index 000000000..e2bc2bd2d --- /dev/null +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -0,0 +1,20 @@ + +namespace ImageSharp.Tests +{ + using System; + using System.Collections.Generic; + using System.Text; + + public static class TestImageExtensions + { + public static void DebugSave(this Image img, TestImageProvider provider, string extension = "png") + where TColor : struct, IPixel + { + if(!bool.TryParse(Environment.GetEnvironmentVariable("CI"), out bool isCI) || !isCI) + { + // we are running locally then we want to save it out + provider.Utility.SaveTestOutputFile(img, extension); + } + } + } +} From 5763412df59fb19e5de1816f08758288bd08bc95 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Sun, 26 Mar 2017 22:46:17 +0100 Subject: [PATCH 7/9] fix broken tests --- .../TestUtilities/ImageProviders/BlankProvider.cs | 2 +- .../TestUtilities/ImageProviders/FileProvider.cs | 4 ++-- .../TestUtilities/ImageProviders/TestImageProvider.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs index 61067e53b..6dc0d89c5 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs @@ -11,7 +11,7 @@ namespace ImageSharp.Tests public abstract partial class TestImageProvider where TColor : struct, IPixel { - private class BlankProvider : TestImageProvider + private class BlankProvider : TestImageProvider, IXunitSerializable { public BlankProvider(int width, int height) { diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs index a641cfa4d..bc18209f3 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -55,14 +55,14 @@ namespace ImageSharp.Tests return this.Factory.CreateImage(cachedImage); } - public void Deserialize(IXunitSerializationInfo info) + public override void Deserialize(IXunitSerializationInfo info) { this.filePath = info.GetValue("path"); base.Deserialize(info); // must be called last } - public void Serialize(IXunitSerializationInfo info) + public override void Serialize(IXunitSerializationInfo info) { base.Serialize(info); info.AddValue("path", this.filePath); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 5a73bbcc9..26192ba1e 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -13,7 +13,7 @@ namespace ImageSharp.Tests /// Provides instances for parametric unit tests. /// /// The pixel format of the image - public abstract partial class TestImageProvider : IXunitSerializable + public abstract partial class TestImageProvider where TColor : struct, IPixel { public PixelTypes PixelType { get; private set; } = typeof(TColor).GetPixelType(); From fac9b469ef948bc0a42e8afa90da42f70c1e4db2 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Mon, 27 Mar 2017 07:42:06 +0100 Subject: [PATCH 8/9] fix Image.Load api changes --- tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index 924903039..882f903d6 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -29,7 +29,7 @@ namespace ImageSharp.Tests.Formats.Png image.Save(ms, new PngEncoder()); ms.Position = 0; - using (Image img2 = new Image(ms, new Configuration(new PngFormat()))) + using (Image img2 = Image.Load(ms, new PngDecoder())) { // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); ImageComparer.CheckSimilarity(image, img2); @@ -50,7 +50,7 @@ namespace ImageSharp.Tests.Formats.Png image.MetaData.Quality = 256; image.Save(ms, new PngEncoder()); ms.Position = 0; - using (Image img2 = new Image(ms, new Configuration(new PngFormat()))) + using (Image img2 = Image.Load(ms, new PngDecoder())) { // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); ImageComparer.CheckSimilarity(image, img2); @@ -73,7 +73,7 @@ namespace ImageSharp.Tests.Formats.Png image.Save(ms, new PngEncoder()); ms.Position = 0; - using (Image img2 = new Image(ms, new Configuration(new PngFormat()))) + using (Image img2 = Image.Load(ms, new PngDecoder())) { ImageComparer.CheckSimilarity(image, img2); } From 5b02e9a8d4fa3a576c3009163fba970c8c523242 Mon Sep 17 00:00:00 2001 From: Scott Williams Date: Mon, 27 Mar 2017 07:42:16 +0100 Subject: [PATCH 9/9] remove ignored tests --- .../Formats/Png/PngEncoderTests.cs | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 31b14601a..51cb0cdc0 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -15,43 +15,6 @@ namespace ImageSharp.Tests public class PngEncoderTests : FileTestBase { - [Fact(Skip ="Slow intergration test")] - public void ImageCanSaveIndexedPng() - { - string path = CreateOutputDirectory("Png", "Indexed"); - - foreach (TestFile file in Files) - { - using (Image image = file.CreateImage()) - { - using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png")) - { - image.MetaData.Quality = 256; - image.Save(output, new PngFormat()); - } - } - } - } - - [Fact(Skip = "Slow intergration test")] - public void ImageCanSavePngInParallel() - { - string path = this.CreateOutputDirectory("Png"); - - Parallel.ForEach( - Files, - file => - { - using (Image image = file.CreateImage()) - { - using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png")) - { - image.SaveAsPng(output); - } - } - }); - } - [Theory] [WithBlankImages(1, 1, PixelTypes.All)] public void WritesFileMarker(TestImageProvider provider)