diff --git a/src/ImageSharp/Processing/Effects/BackgroundColor.cs b/src/ImageSharp/Processing/Effects/BackgroundColor.cs index cb189338e..a1914fee3 100644 --- a/src/ImageSharp/Processing/Effects/BackgroundColor.cs +++ b/src/ImageSharp/Processing/Effects/BackgroundColor.cs @@ -26,7 +26,23 @@ namespace ImageSharp public static Image BackgroundColor(this Image source, TPixel color) where TPixel : struct, IPixel { - source.ApplyProcessor(new BackgroundColorProcessor(color), source.Bounds); + return BackgroundColor(source, color, source.Bounds); + } + + /// + /// Replaces the background color of image with the given one. + /// + /// The pixel format. + /// The image this method extends. + /// The color to set as the background. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// The . + public static Image BackgroundColor(this Image source, TPixel color, Rectangle rectangle) + where TPixel : struct, IPixel + { + source.ApplyProcessor(new BackgroundColorProcessor(color), rectangle); return source; } } diff --git a/src/ImageSharp/Processing/Effects/Brightness.cs b/src/ImageSharp/Processing/Effects/Brightness.cs index 6b7477488..a28df82c0 100644 --- a/src/ImageSharp/Processing/Effects/Brightness.cs +++ b/src/ImageSharp/Processing/Effects/Brightness.cs @@ -20,12 +20,12 @@ namespace ImageSharp /// Alters the brightness component of the image. /// /// The pixel format. - /// The image this method extends. + /// The image this method extends. /// The new brightness of the image. Must be between -100 and 100. /// The . public static Image Brightness(this Image source, int amount) where TPixel : struct, IPixel - { + { return Brightness(source, amount, source.Bounds); } @@ -33,7 +33,7 @@ namespace ImageSharp /// Alters the brightness component of the image. /// /// The pixel format. - /// The image this method extends. + /// The image this method extends. /// The new brightness of the image. Must be between -100 and 100. /// /// The structure that specifies the portion of the image object to alter. diff --git a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs index b38093d63..cfe50150f 100644 --- a/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs +++ b/src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs @@ -7,6 +7,7 @@ namespace ImageSharp.Processing.Processors { using System; using System.Numerics; + using System.Runtime.CompilerServices; using System.Threading.Tasks; using ImageSharp.PixelFormats; @@ -79,6 +80,7 @@ namespace ImageSharp.Processing.Processors /// /// The . /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] private TPixel ApplyMatrix(TPixel color, Matrix4x4 matrix, bool compand) { Vector4 vector = color.ToVector4(); diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs index ac96c40ae..e6a42cc0c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs @@ -5,7 +5,6 @@ namespace ImageSharp.Processing.Processors { - using System; using System.Numerics; using System.Threading.Tasks; @@ -57,10 +56,11 @@ namespace ImageSharp.Processing.Processors int maxX = endX - 1; using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) + using (PixelAccessor sourcePixels = source.Lock()) { - using (PixelAccessor sourcePixels = source.Lock()) - { - Parallel.For( + sourcePixels.CopyTo(targetPixels); + + Parallel.For( startY, endY, this.ParallelOptions, @@ -119,7 +119,6 @@ namespace ImageSharp.Processing.Processors targetPixels[x, y] = packed; } }); - } source.SwapPixelsBuffers(targetPixels); } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs index 9b95cb1a3..965a725a1 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs @@ -45,16 +45,11 @@ namespace ImageSharp.Processing.Processors int width = source.Width; int height = source.Height; - using (PixelAccessor targetPixels = new PixelAccessor(width, height)) + using (PixelAccessor firstPassPixels = new PixelAccessor(width, height)) + using (PixelAccessor sourcePixels = source.Lock()) { - using (PixelAccessor firstPassPixels = new PixelAccessor(width, height)) - using (PixelAccessor sourcePixels = source.Lock()) - { - this.ApplyConvolution(firstPassPixels, sourcePixels, sourceRectangle, this.KernelX); - this.ApplyConvolution(targetPixels, firstPassPixels, sourceRectangle, this.KernelY); - } - - source.SwapPixelsBuffers(targetPixels); + this.ApplyConvolution(firstPassPixels, sourcePixels, source.Bounds, this.KernelX); + this.ApplyConvolution(sourcePixels, firstPassPixels, sourceRectangle, this.KernelY); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs index a0c140028..475f14ccf 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs @@ -46,51 +46,51 @@ namespace ImageSharp.Processing.Processors int maxX = endX - 1; using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) + using (PixelAccessor sourcePixels = source.Lock()) { - using (PixelAccessor sourcePixels = source.Lock()) - { - Parallel.For( - startY, - endY, - this.ParallelOptions, - y => - { - for (int x = startX; x < endX; x++) - { - float red = 0; - float green = 0; - float blue = 0; - - // Apply each matrix multiplier to the color components for each pixel. - for (int fy = 0; fy < kernelLength; fy++) - { - int fyr = fy - radius; - int offsetY = y + fyr; - - offsetY = offsetY.Clamp(0, maxY); - - for (int fx = 0; fx < kernelLength; fx++) - { - int fxr = fx - radius; - int offsetX = x + fxr; - - offsetX = offsetX.Clamp(0, maxX); - - Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); - currentColor *= this.KernelXY[fy, fx]; - - red += currentColor.X; - green += currentColor.Y; - blue += currentColor.Z; - } - } - - TPixel packed = default(TPixel); - packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); - targetPixels[x, y] = packed; - } - }); - } + sourcePixels.CopyTo(targetPixels); + + Parallel.For( + startY, + endY, + this.ParallelOptions, + y => + { + for (int x = startX; x < endX; x++) + { + float red = 0; + float green = 0; + float blue = 0; + + // Apply each matrix multiplier to the color components for each pixel. + for (int fy = 0; fy < kernelLength; fy++) + { + int fyr = fy - radius; + int offsetY = y + fyr; + + offsetY = offsetY.Clamp(0, maxY); + + for (int fx = 0; fx < kernelLength; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; + + offsetX = offsetX.Clamp(0, maxX); + + Vector4 currentColor = sourcePixels[offsetX, offsetY].ToVector4(); + currentColor *= this.KernelXY[fy, fx]; + + red += currentColor.X; + green += currentColor.Y; + blue += currentColor.Z; + } + } + + TPixel packed = default(TPixel); + packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); + targetPixels[x, y] = packed; + } + }); source.SwapPixelsBuffers(targetPixels); } diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index 73d956907..1f06924af 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -70,88 +70,88 @@ namespace ImageSharp.Processing.Processors } using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) + using (PixelAccessor sourcePixels = source.Lock()) { - using (PixelAccessor sourcePixels = source.Lock()) - { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => + sourcePixels.CopyTo(targetPixels); + + Parallel.For( + minY, + maxY, + this.ParallelOptions, + y => + { + for (int x = startX; x < endX; x++) { - for (int x = startX; x < endX; x++) + int maxIntensity = 0; + int maxIndex = 0; + + int[] intensityBin = new int[levels]; + float[] redBin = new float[levels]; + float[] blueBin = new float[levels]; + float[] greenBin = new float[levels]; + + for (int fy = 0; fy <= radius; fy++) { - int maxIntensity = 0; - int maxIndex = 0; + int fyr = fy - radius; + int offsetY = y + fyr; - int[] intensityBin = new int[levels]; - float[] redBin = new float[levels]; - float[] blueBin = new float[levels]; - float[] greenBin = new float[levels]; + // Skip the current row + if (offsetY < minY) + { + continue; + } - for (int fy = 0; fy <= radius; fy++) + // Outwith the current bounds so break. + if (offsetY >= maxY) { - int fyr = fy - radius; - int offsetY = y + fyr; + break; + } - // Skip the current row - if (offsetY < minY) - { - continue; - } + for (int fx = 0; fx <= radius; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; - // Outwith the current bounds so break. - if (offsetY >= maxY) + // Skip the column + if (offsetX < 0) { - break; + continue; } - for (int fx = 0; fx <= radius; fx++) + if (offsetX < maxX) { - int fxr = fx - radius; - int offsetX = x + fxr; + // ReSharper disable once AccessToDisposedClosure + Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); - // Skip the column - if (offsetX < 0) - { - continue; - } - - if (offsetX < maxX) - { - // ReSharper disable once AccessToDisposedClosure - Vector4 color = sourcePixels[offsetX, offsetY].ToVector4(); + float sourceRed = color.X; + float sourceBlue = color.Z; + float sourceGreen = color.Y; - float sourceRed = color.X; - float sourceBlue = color.Z; - float sourceGreen = color.Y; + int currentIntensity = (int)Math.Round((sourceBlue + sourceGreen + sourceRed) / 3.0 * (levels - 1)); - int currentIntensity = (int)Math.Round((sourceBlue + sourceGreen + sourceRed) / 3.0 * (levels - 1)); + intensityBin[currentIntensity] += 1; + blueBin[currentIntensity] += sourceBlue; + greenBin[currentIntensity] += sourceGreen; + redBin[currentIntensity] += sourceRed; - intensityBin[currentIntensity] += 1; - blueBin[currentIntensity] += sourceBlue; - greenBin[currentIntensity] += sourceGreen; - redBin[currentIntensity] += sourceRed; - - if (intensityBin[currentIntensity] > maxIntensity) - { - maxIntensity = intensityBin[currentIntensity]; - maxIndex = currentIntensity; - } + if (intensityBin[currentIntensity] > maxIntensity) + { + maxIntensity = intensityBin[currentIntensity]; + maxIndex = currentIntensity; } } + } - float red = MathF.Abs(redBin[maxIndex] / maxIntensity); - float green = MathF.Abs(greenBin[maxIndex] / maxIntensity); - float blue = MathF.Abs(blueBin[maxIndex] / maxIntensity); + float red = MathF.Abs(redBin[maxIndex] / maxIntensity); + float green = MathF.Abs(greenBin[maxIndex] / maxIntensity); + float blue = MathF.Abs(blueBin[maxIndex] / maxIntensity); - TPixel packed = default(TPixel); - packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); - targetPixels[x, y] = packed; - } + TPixel packed = default(TPixel); + packed.PackFromVector4(new Vector4(red, green, blue, sourcePixels[x, y].ToVector4().W)); + targetPixels[x, y] = packed; } - }); - } + } + }); source.SwapPixelsBuffers(targetPixels); } diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs index 7a57daa4e..0287eaab8 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs @@ -66,51 +66,46 @@ namespace ImageSharp.Processing.Processors // Get the range on the y-plane to choose from. IEnumerable range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size); - using (PixelAccessor targetPixels = new PixelAccessor(source.Width, source.Height)) + using (PixelAccessor sourcePixels = source.Lock()) { - using (PixelAccessor sourcePixels = source.Lock()) - { - Parallel.ForEach( - range, - this.ParallelOptions, - y => + Parallel.ForEach( + range, + this.ParallelOptions, + y => + { + int offsetY = y - startY; + int offsetPy = offset; + + for (int x = minX; x < maxX; x += size) { - int offsetY = y - startY; - int offsetPy = offset; + int offsetX = x - startX; + int offsetPx = offset; - for (int x = minX; x < maxX; x += size) + // Make sure that the offset is within the boundary of the image. + while (offsetY + offsetPy >= maxY) { - int offsetX = x - startX; - int offsetPx = offset; - - // Make sure that the offset is within the boundary of the image. - while (offsetY + offsetPy >= maxY) - { - offsetPy--; - } + offsetPy--; + } - while (x + offsetPx >= maxX) - { - offsetPx--; - } + while (x + offsetPx >= maxX) + { + offsetPx--; + } - // Get the pixel color in the centre of the soon to be pixelated area. - // ReSharper disable AccessToDisposedClosure - TPixel pixel = sourcePixels[offsetX + offsetPx, offsetY + offsetPy]; + // Get the pixel color in the centre of the soon to be pixelated area. + // ReSharper disable AccessToDisposedClosure + TPixel pixel = sourcePixels[offsetX + offsetPx, offsetY + offsetPy]; - // For each pixel in the pixelate size, set it to the centre color. - for (int l = offsetY; l < offsetY + size && l < maxY; l++) + // For each pixel in the pixelate size, set it to the centre color. + for (int l = offsetY; l < offsetY + size && l < maxY; l++) + { + for (int k = offsetX; k < offsetX + size && k < maxX; k++) { - for (int k = offsetX; k < offsetX + size && k < maxX; k++) - { - targetPixels[k, l] = pixel; - } + sourcePixels[k, l] = pixel; } } - }); - - source.SwapPixelsBuffers(targetPixels); - } + } + }); } } } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs index 46ade9f9a..be965160c 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs @@ -20,7 +20,7 @@ namespace ImageSharp.Tests.Formats.Png public class PngSmokeTests { [Theory] - [WithTestPatternImages(300, 300, PixelTypes.All)] + [WithTestPatternImages(300, 300, PixelTypes.StandardImageClass)] public void GeneralTest(TestImageProvider provider) where TPixel : struct, IPixel { @@ -41,7 +41,7 @@ namespace ImageSharp.Tests.Formats.Png } [Theory] - [WithTestPatternImages(100, 100, PixelTypes.All)] + [WithTestPatternImages(100, 100, PixelTypes.StandardImageClass)] public void CanSaveIndexedPng(TestImageProvider provider) where TPixel : struct, IPixel { @@ -56,7 +56,7 @@ namespace ImageSharp.Tests.Formats.Png using (Image img2 = Image.Load(ms, new PngDecoder())) { // img2.Save(provider.Utility.GetTestOutputFileName("bmp", "_loaded"), new BmpEncoder()); - ImageComparer.CheckSimilarity(image, img2); + ImageComparer.CheckSimilarity(image, img2, 0.03f); } } } @@ -105,7 +105,7 @@ namespace ImageSharp.Tests.Formats.Png //} [Theory] - [WithTestPatternImages(300, 300, PixelTypes.All)] + [WithTestPatternImages(300, 300, PixelTypes.StandardImageClass)] public void Resize(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/ImageComparer.cs b/tests/ImageSharp.Tests/ImageComparer.cs index 7d0a8377d..9b8a51fde 100644 --- a/tests/ImageSharp.Tests/ImageComparer.cs +++ b/tests/ImageSharp.Tests/ImageComparer.cs @@ -73,7 +73,7 @@ if (b > segmentThreshold) { diffPixels++; } } - return diffPixels / (scalingFactor * scalingFactor); + return (float)diffPixels / (float)(scalingFactor * scalingFactor); } private static Fast2DArray GetDifferences(Image source, Image target, int scalingFactor) @@ -88,7 +88,8 @@ { for (int x = 0; x < scalingFactor; x++) { - differences[x, y] = (byte)Math.Abs(firstGray[x, y] - secondGray[x, y]); + var diff = firstGray[x, y] - secondGray[x, y]; + differences[x, y] = (byte)Math.Abs(diff); } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs b/tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs index a8aeb3341..2b39086e3 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs @@ -43,7 +43,7 @@ namespace ImageSharp.Tests foreach (TestFile file in Files) { - string filename = file.GetFileName(value); + string filename = file.GetFileName(value + "-InBox"); using (Image image = file.CreateImage()) using (FileStream output = File.OpenWrite($"{path}/{filename}")) { diff --git a/tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs b/tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs index eccfc13af..4bc39f8ab 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs @@ -27,5 +27,21 @@ namespace ImageSharp.Tests } } } + + [Fact] + public void ImageShouldApplyBackgroundColorFilterInBox() + { + string path = this.CreateOutputDirectory("BackgroundColor"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName("-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.BackgroundColor(Rgba32.HotPink, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/BinaryThreshold.cs b/tests/ImageSharp.Tests/Processors/Filters/BinaryThresholdTest.cs similarity index 62% rename from tests/ImageSharp.Tests/Processors/Filters/BinaryThreshold.cs rename to tests/ImageSharp.Tests/Processors/Filters/BinaryThresholdTest.cs index d7d4eac05..f36014542 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/BinaryThreshold.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/BinaryThresholdTest.cs @@ -34,5 +34,22 @@ namespace ImageSharp.Tests } } } + + [Theory] + [MemberData(nameof(BinaryThresholdValues))] + public void ImageShouldApplyBinaryThresholdInBox(float value) + { + string path = this.CreateOutputDirectory("BinaryThreshold"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName(value + "-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.BinaryThreshold(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs index 6b9a48f72..377ae4719 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs @@ -25,5 +25,21 @@ namespace ImageSharp.Tests } } } + + [Fact] + public void ImageShouldApplyBlackWhiteFilterInBox() + { + string path = this.CreateOutputDirectory("BlackWhite"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName("-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.BlackWhite(new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs index 5d4f628ee..f4f5fb4bb 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs @@ -34,5 +34,22 @@ namespace ImageSharp.Tests } } } + + [Theory] + [MemberData(nameof(BoxBlurValues))] + public void ImageShouldApplyBoxBlurFilterInBox(int value) + { + string path = this.CreateOutputDirectory("BoxBlur"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName(value + "-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.BoxBlur(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs index e274ef041..f59d5be4c 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs @@ -34,5 +34,22 @@ namespace ImageSharp.Tests } } } + + [Theory] + [MemberData(nameof(BrightnessValues))] + public void ImageShouldApplyBrightnessFilterInBox(int value) + { + string path = this.CreateOutputDirectory("Brightness"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName(value + "-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Brightness(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs index d18f32caf..5564a77ef 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs @@ -41,5 +41,22 @@ namespace ImageSharp.Tests } } } + + [Theory] + [MemberData(nameof(ColorBlindnessFilters))] + public void ImageShouldApplyBrightnessFilterInBox(ColorBlindness colorBlindness) + { + string path = this.CreateOutputDirectory("ColorBlindness"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName(colorBlindness + "-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.ColorBlindness(colorBlindness, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs index 09376f2c0..5bbe2338c 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs @@ -33,5 +33,22 @@ namespace ImageSharp.Tests } } } + + [Theory] + [MemberData(nameof(ContrastValues))] + public void ImageShouldApplyContrastFilterInBox(int value) + { + string path = this.CreateOutputDirectory("Contrast"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName(value + "-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Contrast(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs index 809ffa2f5..4b2ac8b7c 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs @@ -6,10 +6,10 @@ namespace ImageSharp.Tests { using System.IO; - + using ImageSharp.PixelFormats; using Xunit; - public class GaussianBlurTest : FileTestBase + public class GaussianBlurTest { public static readonly TheoryData GaussianBlurValues = new TheoryData @@ -19,19 +19,33 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData(nameof(GaussianBlurValues))] - public void ImageShouldApplyGaussianBlurFilter(int value) + [WithTestPatternImages(nameof(GaussianBlurValues), 320, 240, PixelTypes.StandardImageClass)] + public void ImageShouldApplyGaussianBlurFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel { - string path = this.CreateOutputDirectory("GaussianBlur"); + using (Image image = provider.GetImage()) + { + image.GaussianBlur(value) + .DebugSave(provider, value.ToString()); + } + } - foreach (TestFile file in Files) + [Theory] + [WithTestPatternImages(nameof(GaussianBlurValues), 320, 240, PixelTypes.StandardImageClass)] + public void ImageShouldApplyGaussianBlurFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image image = new Image(source)) { - string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.GaussianBlur(value).Save(output); - } + Rectangle rect = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + image.GaussianBlur(value, rect) + .DebugSave(provider, value.ToString()); + + // lets draw identical shapes over the blured areas and ensure that it didn't change the outer area + image.Fill(NamedColors.HotPink, rect); + source.Fill(NamedColors.HotPink, rect); + ImageComparer.CheckSimilarity(image, source); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs index c1aa06941..1fa1ae15c 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs @@ -6,7 +6,7 @@ namespace ImageSharp.Tests { using System.IO; - + using ImageSharp.PixelFormats; using Xunit; public class GaussianSharpenTest : FileTestBase @@ -19,19 +19,33 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData(nameof(GaussianSharpenValues))] - public void ImageShouldApplyGaussianSharpenFilter(int value) + [WithTestPatternImages(nameof(GaussianSharpenValues), 320, 240, PixelTypes.StandardImageClass)] + public void ImageShouldApplyGaussianSharpenFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel { - string path = this.CreateOutputDirectory("GaussianSharpen"); + using (Image image = provider.GetImage()) + { + image.GaussianSharpen(value) + .DebugSave(provider, value.ToString()); + } + } - foreach (TestFile file in Files) + [Theory] + [WithTestPatternImages(nameof(GaussianSharpenValues), 320, 240, PixelTypes.StandardImageClass)] + public void ImageShouldApplyGaussianSharpenFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image image = new Image(source)) { - string filename = file.GetFileName(value); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) - { - image.GaussianSharpen(value).Save(output); - } + Rectangle rect = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + image.GaussianSharpen(value, rect) + .DebugSave(provider, value.ToString()); + + // lets draw identical shapes over the Sharpened areas and ensure that it didn't change the outer area + image.Fill(NamedColors.HotPink, rect); + source.Fill(NamedColors.HotPink, rect); + ImageComparer.CheckSimilarity(image, source); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs index 2b717a0b7..9a7d87854 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs @@ -5,12 +5,8 @@ namespace ImageSharp.Tests { - using System.IO; - using Xunit; using ImageSharp.Processing; - using ImageSharp.Tests; - using System.Numerics; using ImageSharp.PixelFormats; @@ -20,7 +16,7 @@ namespace ImageSharp.Tests /// Use test patterns over loaded images to save decode time. /// [Theory] - [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt709)] + [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt709)] [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt601)] public void ImageShouldApplyGrayscaleFilterAll(TestImageProvider provider, GrayscaleMode value) where TPixel : struct, IPixel @@ -36,7 +32,27 @@ namespace ImageSharp.Tests Assert.Equal(data[1], data[2]); } - image.DebugSave(provider); + image.DebugSave(provider, value.ToString()); + } + } + + [Theory] + [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt709)] + [WithTestPatternImages(50, 50, PixelTypes.StandardImageClass, GrayscaleMode.Bt601)] + public void ImageShouldApplyGrayscaleFilterInBox(TestImageProvider provider, GrayscaleMode value) + where TPixel : struct, IPixel + { + using (Image source = provider.GetImage()) + using (Image image = new Image(source)) + { + Rectangle rect = new Rectangle(image.Width / 4, image.Height / 4, image.Width / 2, image.Height / 2); + image.Grayscale(rect, value) + .DebugSave(provider, value.ToString()); + + // Let's draw identical shapes over the greyed areas and ensure that it didn't change the outer area + image.Fill(NamedColors.HotPink, rect); + source.Fill(NamedColors.HotPink, rect); + ImageComparer.CheckSimilarity(image, source); } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/HueTest.cs b/tests/ImageSharp.Tests/Processors/Filters/HueTest.cs index 4241dc833..3081c638c 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/HueTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/HueTest.cs @@ -34,5 +34,22 @@ namespace ImageSharp.Tests } } } + + [Theory] + [MemberData(nameof(HueValues))] + public void ImageShouldApplyHueFilterInBox(int value) + { + string path = this.CreateOutputDirectory("Hue"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName(value + "-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Hue(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs b/tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs index 40734e02a..870f813a1 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs @@ -25,5 +25,21 @@ namespace ImageSharp.Tests } } } + + [Fact] + public void ImageShouldApplyKodachromeFilterInBox() + { + string path = this.CreateOutputDirectory("Kodachrome"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName("InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Kodachrome(new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs b/tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs index 3a5fbc556..b21a8c969 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs @@ -6,10 +6,10 @@ namespace ImageSharp.Tests { using System.IO; - + using ImageSharp.PixelFormats; using Xunit; - public class PixelateTest : FileTestBase + public class PixelateTest { public static readonly TheoryData PixelateValues = new TheoryData @@ -19,37 +19,74 @@ namespace ImageSharp.Tests }; [Theory] - [MemberData(nameof(PixelateValues))] - public void ImageShouldApplyPixelateFilter(int value) + [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.StandardImageClass)] + public void ImageShouldApplyPixelateFilter(TestImageProvider provider, int value) + where TPixel : struct, IPixel { - string path = CreateOutputDirectory("Pixelate"); - - foreach (TestFile file in Files) + using (Image image = provider.GetImage()) { - string filename = file.GetFileName(value); - Image image = file.CreateImage(); + image.Pixelate(value) + .DebugSave(provider, new + { + size = value + }); - using (FileStream output = File.OpenWrite($"{path}/{filename}")) + using (PixelAccessor pixels = image.Lock()) { - image.Pixelate(value) - .Save(output); + for (int y = 0; y < pixels.Height; y += value) + { + for (int x = 0; x < pixels.Width; x += value) + { + TPixel source = pixels[x, y]; + for (int pixY = y; pixY < y + value && pixY < pixels.Height; pixY++) + { + for (int pixX = x; pixX < x + value && pixX < pixels.Width; pixX++) + { + Assert.Equal(source, pixels[pixX, pixY]); + } + } + } + } } } } [Theory] - [MemberData(nameof(PixelateValues))] - public void ImageShouldApplyPixelateFilterInBox(int value) + [WithTestPatternImages(nameof(PixelateValues), 320, 240, PixelTypes.StandardImageClass)] + public void ImageShouldApplyPixelateFilterInBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel { - string path = this.CreateOutputDirectory("Pixelate"); - - foreach (TestFile file in Files) + using (Image source = provider.GetImage()) + using (Image image = new Image(source)) { - string filename = file.GetFileName(value + "-InBox"); - using (Image image = file.CreateImage()) - using (FileStream output = File.OpenWrite($"{path}/{filename}")) + Rectangle rect = new Rectangle(image.Width/4, image.Height / 4, image.Width / 2, image.Height / 2); + + image.Pixelate(value, rect) + .DebugSave(provider, new + { + size = value + }); + + using (PixelAccessor pixels = image.Lock()) + using (PixelAccessor sourcePixels = source.Lock()) { - image.Pixelate(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + for (int y = 0; y < pixels.Height; y++) + { + for (int x = 0; x < pixels.Width; x++) + { + var tx = x; + var ty = y; + TPixel sourceColor = sourcePixels[tx, ty]; + if (rect.Contains(tx, ty)) + { + var sourceX = tx - ((tx - rect.Left) % value) + (value / 2); + var sourceY = ty - ((ty - rect.Top) % value) + (value / 2); + + sourceColor = pixels[sourceX, sourceY]; + } + Assert.Equal(sourceColor, pixels[tx, ty]); + } + } } } } diff --git a/tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs b/tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs index 040f8b4a2..e9938fb83 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs @@ -25,5 +25,21 @@ namespace ImageSharp.Tests } } } + + [Fact] + public void ImageShouldApplyPolaroidFilterInBox() + { + string path = this.CreateOutputDirectory("Polaroid"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName("InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Polaroid(new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs b/tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs index abd596d70..ee24f120c 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs @@ -34,5 +34,22 @@ namespace ImageSharp.Tests } } } + + [Theory] + [MemberData(nameof(SaturationValues))] + public void ImageShouldApplySaturationFilterInBox(int value) + { + string path = this.CreateOutputDirectory("Saturation"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName(value + "-InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Saturation(value, new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processors/Filters/SepiaTest.cs b/tests/ImageSharp.Tests/Processors/Filters/SepiaTest.cs index fbae10fa5..0e1583cc6 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/SepiaTest.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/SepiaTest.cs @@ -25,5 +25,21 @@ namespace ImageSharp.Tests } } } + + [Fact] + public void ImageShouldApplySepiaFilterInBox() + { + string path = this.CreateOutputDirectory("Sepia"); + + foreach (TestFile file in Files) + { + string filename = file.GetFileName("InBox"); + using (Image image = file.CreateImage()) + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + image.Sepia(new Rectangle(10, 10, image.Width / 2, image.Height / 2)).Save(output); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs index ffbd1b888..379ce3d05 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs @@ -21,26 +21,72 @@ namespace ImageSharp.Tests protected readonly PixelTypes PixelTypes; - protected ImageDataAttributeBase(PixelTypes pixelTypes, object[] additionalParameters) + protected ImageDataAttributeBase(string memberName, PixelTypes pixelTypes, object[] additionalParameters) { this.PixelTypes = pixelTypes; this.AdditionalParameters = additionalParameters; + this.MemberName = memberName; + } + public string MemberName { get; private set; } + + public Type MemberType { get; set; } + public override IEnumerable GetData(MethodInfo testMethod) { - TypeInfo type = testMethod.GetParameters().First().ParameterType.GetTypeInfo(); - if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(TestImageProvider<>)) + IEnumerable addedRows = Enumerable.Empty(); + if (!string.IsNullOrWhiteSpace(this.MemberName)) + { + Type type = this.MemberType ?? testMethod.DeclaringType; + Func accessor = GetPropertyAccessor(type) ?? GetFieldAccessor(type);// ?? GetMethodAccessor(type); + + if (accessor != null) + { + object obj = accessor(); + if (obj is IEnumerable memberItems) + { + addedRows = memberItems.Select(x => x as object[]); + if (addedRows.Any(x => x == null)) + { + throw new ArgumentException($"Property {MemberName} on {MemberType ?? testMethod.DeclaringType} yielded an item that is not an object[]"); + } + } + } + } + + if (!addedRows.Any()) { - yield return this.AdditionalParameters; + addedRows = new[] { new object[0] }; + } + + bool firstIsprovider = FirstIsProvider(testMethod); + IEnumerable dataItems = Enumerable.Empty(); + if (firstIsprovider) + { + return InnerGetData(testMethod, addedRows); } else { - foreach (KeyValuePair kv in this.PixelTypes.ExpandAllTypes()) - { - Type factoryType = typeof(TestImageProvider<>).MakeGenericType(kv.Value); + return addedRows.Select(x => x.Concat(this.AdditionalParameters).ToArray()); + } + } + + private bool FirstIsProvider(MethodInfo testMethod) + { + TypeInfo dataType = testMethod.GetParameters().First().ParameterType.GetTypeInfo(); + return dataType.IsGenericType && dataType.GetGenericTypeDefinition() == typeof(TestImageProvider<>); + } + + private IEnumerable InnerGetData(MethodInfo testMethod, IEnumerable memberData) + { + foreach (KeyValuePair kv in this.PixelTypes.ExpandAllTypes()) + { + Type factoryType = typeof(TestImageProvider<>).MakeGenericType(kv.Value); - foreach (object[] originalFacoryMethodArgs in this.GetAllFactoryMethodArgs(testMethod, factoryType)) + foreach (object[] originalFacoryMethodArgs in this.GetAllFactoryMethodArgs(testMethod, factoryType)) + { + foreach (object[] row in memberData) { object[] actualFactoryMethodArgs = new object[originalFacoryMethodArgs.Length + 2]; Array.Copy(originalFacoryMethodArgs, actualFactoryMethodArgs, originalFacoryMethodArgs.Length); @@ -50,9 +96,10 @@ namespace ImageSharp.Tests object factory = factoryType.GetMethod(this.GetFactoryMethodName(testMethod)) .Invoke(null, actualFactoryMethodArgs); - object[] result = new object[this.AdditionalParameters.Length + 1]; + object[] result = new object[this.AdditionalParameters.Length + 1 + row.Length]; result[0] = factory; - Array.Copy(this.AdditionalParameters, 0, result, 1, this.AdditionalParameters.Length); + Array.Copy(row, 0, result, 1, row.Length); + Array.Copy(this.AdditionalParameters, 0, result, 1 + row.Length, this.AdditionalParameters.Length); yield return result; } } @@ -71,5 +118,56 @@ namespace ImageSharp.Tests } protected abstract string GetFactoryMethodName(MethodInfo testMethod); + + Func GetFieldAccessor(Type type) + { + FieldInfo fieldInfo = null; + for (Type reflectionType = type; reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType) + { + fieldInfo = reflectionType.GetRuntimeField(MemberName); + if (fieldInfo != null) + break; + } + + if (fieldInfo == null || !fieldInfo.IsStatic) + return null; + + return () => fieldInfo.GetValue(null); + } + + //Func GetMethodAccessor(Type type) + //{ + // MethodInfo methodInfo = null; + // var parameterTypes = Parameters == null ? new Type[0] : Parameters.Select(p => p?.GetType()).ToArray(); + // for (var reflectionType = type; reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType) + // { + // methodInfo = reflectionType.GetRuntimeMethods() + // .FirstOrDefault(m => m.Name == MemberName && ParameterTypesCompatible(m.GetParameters(), parameterTypes)); + // if (methodInfo != null) + // break; + // } + + // if (methodInfo == null || !methodInfo.IsStatic) + // return null; + + // return () => methodInfo.Invoke(null, Parameters); + //} + + Func GetPropertyAccessor(Type type) + { + PropertyInfo propInfo = null; + for (Type reflectionType = type; reflectionType != null; reflectionType = reflectionType.GetTypeInfo().BaseType) + { + propInfo = reflectionType.GetRuntimeProperty(MemberName); + if (propInfo != null) + break; + } + + if (propInfo == null || propInfo.GetMethod == null || !propInfo.GetMethod.IsStatic) + return null; + + return () => propInfo.GetValue(null, null); + } + } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs index 25d3c8cac..32278d755 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs @@ -22,7 +22,21 @@ namespace ImageSharp.Tests /// The requested parameter /// Additional theory parameter values public WithBlankImagesAttribute(int width, int height, PixelTypes pixelTypes, params object[] additionalParameters) - : base(pixelTypes, additionalParameters) + : base(null, pixelTypes, additionalParameters) + { + this.Width = width; + this.Height = height; + } + + /// + /// Triggers passing an that produces a blank image of size width * height + /// + /// The required width + /// The required height + /// The requested parameter + /// Additional theory parameter values + public WithBlankImagesAttribute(string memberData, int width, int height, PixelTypes pixelTypes, params object[] additionalParameters) + : base(memberData, pixelTypes, additionalParameters) { this.Width = width; this.Height = height; diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs index 752c114e5..ec8421853 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs @@ -24,7 +24,20 @@ namespace ImageSharp.Tests /// The requested pixel types /// Additional theory parameter values public WithFileAttribute(string fileName, PixelTypes pixelTypes, params object[] additionalParameters) - : base(pixelTypes, additionalParameters) + : base(null, pixelTypes, additionalParameters) + { + this.fileName = fileName; + } + + /// + /// Triggers passing instances which read an image from the given file + /// One instance will be passed for each the pixel format defined by the pixelTypes parameter + /// + /// The name of the file + /// The requested pixel types + /// Additional theory parameter values + public WithFileAttribute(string fileName, string dataMemberName, PixelTypes pixelTypes, params object[] additionalParameters) + : base(dataMemberName, pixelTypes, additionalParameters) { this.fileName = fileName; } diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs index 3bd93e609..df8f8d090 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs @@ -29,7 +29,24 @@ namespace ImageSharp.Tests string enumeratorMemberName, PixelTypes pixelTypes, params object[] additionalParameters) - : base(pixelTypes, additionalParameters) + : base(null, pixelTypes, additionalParameters) + { + this.enumeratorMemberName = enumeratorMemberName; + } + + /// + /// Triggers passing instances which read an image for each file being enumerated by the (static) test class field/property defined by enumeratorMemberName + /// instances will be passed for each the pixel format defined by the pixelTypes parameter + /// + /// The name of the static test class field/property enumerating the files + /// The requested pixel types + /// Additional theory parameter values + public WithFileCollectionAttribute( + string enumeratorMemberName, + string DataMemberName, + PixelTypes pixelTypes, + params object[] additionalParameters) + : base(DataMemberName, pixelTypes, additionalParameters) { this.enumeratorMemberName = enumeratorMemberName; } diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs index 661640f66..6c6198c38 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs @@ -26,7 +26,7 @@ namespace ImageSharp.Tests /// The requested pixel types /// Additional theory parameter values public WithMemberFactoryAttribute(string memberMethodName, PixelTypes pixelTypes, params object[] additionalParameters) - : base(pixelTypes, additionalParameters) + : base(null, pixelTypes, additionalParameters) { this.memberMethodName = memberMethodName; } diff --git a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs index f2d2aeb88..77c60a943 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs @@ -22,7 +22,19 @@ namespace ImageSharp.Tests /// The requested parameter /// Additional theory parameter values public WithTestPatternImagesAttribute(int width, int height, PixelTypes pixelTypes, params object[] additionalParameters) - : base(pixelTypes, additionalParameters) + : this(null, width, height, pixelTypes,additionalParameters) + { + } + + /// + /// 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(string memberData, int width, int height, PixelTypes pixelTypes, params object[] additionalParameters) + : base(memberData, pixelTypes, additionalParameters) { this.Width = width; this.Height = height; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs index 9d6f46b72..bf30d4847 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs @@ -11,12 +11,16 @@ namespace ImageSharp.Tests using ImageSharp.PixelFormats; using Xunit.Abstractions; - + public interface ITestImageProvider + { + PixelTypes PixelType { get; } + ImagingTestCaseUtility Utility { get; } + } /// /// Provides instances for parametric unit tests. /// /// The pixel format of the image - public abstract partial class TestImageProvider + public abstract partial class TestImageProvider : ITestImageProvider where TPixel : struct, IPixel { public PixelTypes PixelType { get; private set; } = typeof(TPixel).GetPixelType(); diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index 6476c4ecf..290123885 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -97,10 +97,10 @@ namespace ImageSharp.Tests /// The requested extension /// Optional encoder /// Optional encoder options - public void SaveTestOutputFile(Image image, string extension = null, IImageEncoder encoder = null, IEncoderOptions options = null) + public void SaveTestOutputFile(Image image, string extension = null, IImageEncoder encoder = null, IEncoderOptions options = null, string tag = null) where TPixel : struct, IPixel { - string path = this.GetTestOutputFileName(extension); + string path = this.GetTestOutputFileName(extension: extension, tag:tag); extension = Path.GetExtension(path); IImageFormat format = GetImageFormatByExtension(extension); diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 5408d5362..dbd316423 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -3,19 +3,32 @@ namespace ImageSharp.Tests { using System; using System.Collections.Generic; + using System.Linq; + using System.Reflection; using System.Text; using ImageSharp.PixelFormats; public static class TestImageExtensions { - public static void DebugSave(this Image img, TestImageProvider provider, string extension = "png") + public static void DebugSave(this Image img, ITestImageProvider provider, object settings = null, string extension = "png") where TPixel : struct, IPixel { + string tag = null; + if (settings is string) + { + tag = (string)settings; + } + else if (settings != null) + { + var properties = settings.GetType().GetRuntimeProperties(); + + tag = string.Join("_", properties.ToDictionary(x => x.Name, x => x.GetValue(settings)).Select(x => $"{x.Key}-{x.Value}")); + } 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); + provider.Utility.SaveTestOutputFile(img, extension, tag: tag); } } }