diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
index 7950d260c..498ae578c 100644
--- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs
+++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs
@@ -544,12 +544,11 @@ namespace ImageSharp.Formats
///
/// The pixel format.
/// The containing image data.
- /// The image base.
- private void WritePhysicalChunk(Stream stream, ImageBase imageBase)
+ /// The image.
+ 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/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 49be75139..51cb0cdc0 100644
--- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs
@@ -8,47 +8,37 @@ using ImageSharp.Formats;
namespace ImageSharp.Tests
{
using System.IO;
+ using System.Linq;
using System.Threading.Tasks;
-
+ using ImageSharp.IO;
using Xunit;
public class PngEncoderTests : FileTestBase
{
- [Fact]
- public void ImageCanSaveIndexedPng()
+ [Theory]
+ [WithBlankImages(1, 1, PixelTypes.All)]
+ public void WritesFileMarker(TestImageProvider provider)
+ where TColor : struct, IPixel
{
- string path = CreateOutputDirectory("Png", "Indexed");
-
- foreach (TestFile file in Files)
+ using (Image image = provider.GetImage())
+ using (MemoryStream ms = new MemoryStream())
{
- using (Image image = file.CreateImage())
- {
- using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.png"))
- {
- image.MetaData.Quality = 256;
- image.Save(output, new PngFormat());
- }
- }
- }
- }
+ image.Save(ms, new PngEncoder());
+
+ byte[] data = ms.ToArray().Take(8).ToArray();
+ 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
+ };
- [Fact]
- 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);
- }
- }
- });
+ Assert.Equal(expected, data);
+ }
}
}
}
\ 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..882f903d6
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs
@@ -0,0 +1,83 @@
+//
+// 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 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(ms, new PngEncoder());
+ ms.Position = 0;
+ using (Image img2 = Image.Load(ms, new PngDecoder()))
+ {
+ // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder());
+ ImageComparer.CheckSimilarity(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 = Image.Load(ms, new PngDecoder()))
+ {
+ // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder());
+ ImageComparer.CheckSimilarity(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 = Image.Load(ms, new PngDecoder()))
+ {
+ ImageComparer.CheckSimilarity(image, img2);
+ }
+ }
+ }
+ }
+}
diff --git a/tests/ImageSharp.Tests/ImageComparer.cs b/tests/ImageSharp.Tests/ImageComparer.cs
new file mode 100644
index 000000000..41b884dd4
--- /dev/null
+++ b/tests/ImageSharp.Tests/ImageComparer.cs
@@ -0,0 +1,119 @@
+namespace ImageSharp.Tests
+{
+ using System;
+ using ImageSharp;
+ using Xunit;
+
+ ///
+ /// Class to perform simple image comparisons.
+ ///
+ public static class ImageComparer
+ {
+ 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 CheckSimilarity(Image expected, Image actual, float imageTheshold = DefaultImageThreshold, byte segmentThreshold = DefaultSegmentThreshold, int scalingFactor = DefaultScalingFactor)
+ where TColorA : struct, IPixel
+ where TColorB : struct, IPixel
+ {
+ float percentage = expected.PercentageDifference(actual, segmentThreshold, scalingFactor);
+
+ 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
+ {
+ // 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/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/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/BlankProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs
index ad4d2cc98..6dc0d89c5 100644
--- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs
+++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/BlankProvider.cs
@@ -6,25 +6,46 @@
namespace ImageSharp.Tests
{
using System;
+ using Xunit.Abstractions;
public abstract partial class TestImageProvider
where TColor : struct, IPixel
{
- private class BlankProvider : TestImageProvider
+ private class BlankProvider : TestImageProvider, IXunitSerializable
{
public BlankProvider(int width, int height)
{
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..bc18209f3 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 override void Deserialize(IXunitSerializationInfo info)
+ {
+ this.filePath = info.GetValue("path");
+
+ base.Deserialize(info); // must be called last
+ }
+
+ public override 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 cdb31ab69..26192ba1e 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
where TColor : struct, IPixel
{
public PixelTypes PixelType { get; private set; } = typeof(TColor).GetPixelType();
@@ -25,13 +26,22 @@ 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 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,
@@ -65,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)
{
@@ -78,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
new file mode 100644
index 000000000..39ce61495
--- /dev/null
+++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestPatternProvider.cs
@@ -0,0 +1,210 @@
+//
+// 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;
+ using Xunit.Abstractions;
+
+ public abstract partial class TestImageProvider
+ where TColor : struct, IPixel
+ {
+
+ ///
+ /// A test image provider that produces test patterns.
+ ///
+ ///
+ private class TestPatternProvider : BlankProvider
+ {
+ static Dictionary> testImages = new Dictionary>();
+
+ public TestPatternProvider(int width, int height)
+ : base(width, height)
+ {
+ }
+
+ public TestPatternProvider()
+ : base()
+ {
+ }
+
+ public override string SourceFileOrDescription => $"TestPattern{this.Width}x{this.Height}";
+
+ public override Image GetImage()
+ {
+ lock (testImages)
+ {
+ if (!testImages.ContainsKey(this.SourceFileOrDescription))
+ {
+ Image image = new Image(this.Width, this.Height);
+ DrawTestPattern(image);
+ testImages.Add(this.SourceFileOrDescription, image);
+ }
+ }
+
+ return new Image(testImages[this.SourceFileOrDescription]);
+ }
+
+ ///
+ /// 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
+ VirticalBars(pixels); // top right
+ TransparentGradients(pixels); // bottom left
+ Rainbow(pixels); // bottom right
+ }
+ }
+ ///
+ /// Fills the top right quadrant with alternating solid vertical bars.
+ ///
+ ///
+ private static void VirticalBars(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 (int y = top; y < bottom; y++)
+ {
+ for (int x = left; x < right; x++)
+ {
+ if (x % stride == 0)
+ {
+ p++;
+ p = p % c.Length;
+ }
+ pixels[x, y] = c[p];
+ }
+ }
+ }
+
+ ///
+ /// fills the top left quadrant with a black and white checker board.
+ ///
+ ///
+ 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 (int y = top; y < bottom; y++)
+ {
+ if (y % stride == 0)
+ {
+ p++;
+ p = p % c.Length;
+ }
+ int pstart = p;
+ for (int x = left; x < right; x++)
+ {
+ if (x % stride == 0)
+ {
+ p++;
+ p = p % c.Length;
+ }
+ pixels[x, y] = c[p];
+ }
+ 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
+ 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 (int x = left; x < right; x++)
+ {
+ blue.W = red.W = green.W = (float)x / (float)right;
+
+ c.PackFromVector4(red);
+ int topBand = top;
+ for (int y = topBand; y < top + height; y++)
+ {
+ pixels[x, y] = c;
+ }
+ topBand = topBand + height;
+ c.PackFromVector4(green);
+ for (int y = topBand; y < topBand + height; y++)
+ {
+ pixels[x, y] = c;
+ }
+ topBand = topBand + height;
+ c.PackFromVector4(blue);
+ for (int y = topBand; y < bottom; y++)
+ {
+ pixels[x, y] = c;
+ }
+ }
+ }
+
+ ///
+ /// 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;
+ 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);
+
+ for (int x = left; x < right; x++)
+ for (int y = top; y < bottom; y++)
+ {
+ t.PackedValue += stepsPerPixel;
+ Vector4 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..9fd33d90b 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;
@@ -91,16 +111,21 @@ 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)
{
- 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()
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);
+ }
+ }
+ }
+}