Browse Source

Merge pull request #195 from JimBobSquarePants/bugs/blur-loosing-surrounding-data

Fix for blur/pixelate loosing surrounding pixels when applied to rectangle
af/merge-core
James Jackson-South 9 years ago
committed by GitHub
parent
commit
5cb1e20be2
  1. 18
      src/ImageSharp/Processing/Effects/BackgroundColor.cs
  2. 6
      src/ImageSharp/Processing/Effects/Brightness.cs
  3. 2
      src/ImageSharp/Processing/Processors/ColorMatrix/ColorMatrixProcessor.cs
  4. 9
      src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs
  5. 13
      src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs
  6. 88
      src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs
  7. 122
      src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs
  8. 65
      src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs
  9. 8
      tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs
  10. 5
      tests/ImageSharp.Tests/ImageComparer.cs
  11. 2
      tests/ImageSharp.Tests/Processors/Filters/AlphaTest.cs
  12. 16
      tests/ImageSharp.Tests/Processors/Filters/BackgroundColorTest.cs
  13. 17
      tests/ImageSharp.Tests/Processors/Filters/BinaryThresholdTest.cs
  14. 16
      tests/ImageSharp.Tests/Processors/Filters/BlackWhiteTest.cs
  15. 17
      tests/ImageSharp.Tests/Processors/Filters/BoxBlurTest.cs
  16. 17
      tests/ImageSharp.Tests/Processors/Filters/BrightnessTest.cs
  17. 17
      tests/ImageSharp.Tests/Processors/Filters/ColorBlindnessTest.cs
  18. 17
      tests/ImageSharp.Tests/Processors/Filters/ContrastTest.cs
  19. 38
      tests/ImageSharp.Tests/Processors/Filters/GaussianBlurTest.cs
  20. 36
      tests/ImageSharp.Tests/Processors/Filters/GaussianSharpenTest.cs
  21. 28
      tests/ImageSharp.Tests/Processors/Filters/GrayscaleTest.cs
  22. 17
      tests/ImageSharp.Tests/Processors/Filters/HueTest.cs
  23. 16
      tests/ImageSharp.Tests/Processors/Filters/KodachromeTest.cs
  24. 79
      tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs
  25. 16
      tests/ImageSharp.Tests/Processors/Filters/PolaroidTest.cs
  26. 17
      tests/ImageSharp.Tests/Processors/Filters/SaturationTest.cs
  27. 16
      tests/ImageSharp.Tests/Processors/Filters/SepiaTest.cs
  28. 118
      tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs
  29. 16
      tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs
  30. 15
      tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs
  31. 19
      tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs
  32. 2
      tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs
  33. 14
      tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs
  34. 8
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs
  35. 4
      tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs
  36. 17
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

18
src/ImageSharp/Processing/Effects/BackgroundColor.cs

@ -26,7 +26,23 @@ namespace ImageSharp
public static Image<TPixel> BackgroundColor<TPixel>(this Image<TPixel> source, TPixel color)
where TPixel : struct, IPixel<TPixel>
{
source.ApplyProcessor(new BackgroundColorProcessor<TPixel>(color), source.Bounds);
return BackgroundColor(source, color, source.Bounds);
}
/// <summary>
/// Replaces the background color of image with the given one.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="color">The color to set as the background.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <returns>The <see cref="Image"/>.</returns>
public static Image<TPixel> BackgroundColor<TPixel>(this Image<TPixel> source, TPixel color, Rectangle rectangle)
where TPixel : struct, IPixel<TPixel>
{
source.ApplyProcessor(new BackgroundColorProcessor<TPixel>(color), rectangle);
return source;
}
}

6
src/ImageSharp/Processing/Effects/Brightness.cs

@ -20,12 +20,12 @@ namespace ImageSharp
/// Alters the brightness component of the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new brightness of the image. Must be between -100 and 100.</param>
/// <returns>The <see cref="Image{TPixel}"/>.</returns>
public static Image<TPixel> Brightness<TPixel>(this Image<TPixel> source, int amount)
where TPixel : struct, IPixel<TPixel>
{
{
return Brightness(source, amount, source.Bounds);
}
@ -33,7 +33,7 @@ namespace ImageSharp
/// Alters the brightness component of the image.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="source">The image this method extends.</param>
/// <param name="source">The image this method extends.</param>
/// <param name="amount">The new brightness of the image. Must be between -100 and 100.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.

2
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
/// <returns>
/// The <see cref="Rgba32"/>.
/// </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private TPixel ApplyMatrix(TPixel color, Matrix4x4 matrix, bool compand)
{
Vector4 vector = color.ToVector4();

9
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<TPixel> targetPixels = new PixelAccessor<TPixel>(source.Width, source.Height))
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
{
using (PixelAccessor<TPixel> 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);
}

13
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<TPixel> targetPixels = new PixelAccessor<TPixel>(width, height))
using (PixelAccessor<TPixel> firstPassPixels = new PixelAccessor<TPixel>(width, height))
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
{
using (PixelAccessor<TPixel> firstPassPixels = new PixelAccessor<TPixel>(width, height))
using (PixelAccessor<TPixel> 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);
}
}

88
src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs

@ -46,51 +46,51 @@ namespace ImageSharp.Processing.Processors
int maxX = endX - 1;
using (PixelAccessor<TPixel> targetPixels = new PixelAccessor<TPixel>(source.Width, source.Height))
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
{
using (PixelAccessor<TPixel> 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);
}

122
src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs

@ -70,88 +70,88 @@ namespace ImageSharp.Processing.Processors
}
using (PixelAccessor<TPixel> targetPixels = new PixelAccessor<TPixel>(source.Width, source.Height))
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
{
using (PixelAccessor<TPixel> 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);
}

65
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<int> range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size);
using (PixelAccessor<TPixel> targetPixels = new PixelAccessor<TPixel>(source.Width, source.Height))
using (PixelAccessor<TPixel> sourcePixels = source.Lock())
{
using (PixelAccessor<TPixel> 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);
}
}
});
}
}
}

8
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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
@ -41,7 +41,7 @@ namespace ImageSharp.Tests.Formats.Png
}
[Theory]
[WithTestPatternImages(100, 100, PixelTypes.All)]
[WithTestPatternImages(100, 100, PixelTypes.StandardImageClass)]
public void CanSaveIndexedPng<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
@ -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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{

5
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<byte> GetDifferences<TPixelA, TPixelB>(Image<TPixelA> source, Image<TPixelB> 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);
}
}

2
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}"))
{

16
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);
}
}
}
}
}

17
tests/ImageSharp.Tests/Processors/Filters/BinaryThreshold.cs → 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);
}
}
}
}
}

16
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);
}
}
}
}
}

17
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);
}
}
}
}
}

17
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);
}
}
}
}
}

17
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);
}
}
}
}
}

17
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);
}
}
}
}
}

38
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<int> GaussianBlurValues
= new TheoryData<int>
@ -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<TPixel>(TestImageProvider<TPixel> provider, int value)
where TPixel : struct, IPixel<TPixel>
{
string path = this.CreateOutputDirectory("GaussianBlur");
using (Image<TPixel> 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<TPixel>(TestImageProvider<TPixel> provider, int value)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> source = provider.GetImage())
using (Image<TPixel> image = new Image<TPixel>(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<TPixel>.HotPink, rect);
source.Fill(NamedColors<TPixel>.HotPink, rect);
ImageComparer.CheckSimilarity(image, source);
}
}
}

36
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<TPixel>(TestImageProvider<TPixel> provider, int value)
where TPixel : struct, IPixel<TPixel>
{
string path = this.CreateOutputDirectory("GaussianSharpen");
using (Image<TPixel> 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<TPixel>(TestImageProvider<TPixel> provider, int value)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> source = provider.GetImage())
using (Image<TPixel> image = new Image<TPixel>(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<TPixel>.HotPink, rect);
source.Fill(NamedColors<TPixel>.HotPink, rect);
ImageComparer.CheckSimilarity(image, source);
}
}
}

28
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.
/// </summary>
[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<TPixel>(TestImageProvider<TPixel> provider, GrayscaleMode value)
where TPixel : struct, IPixel<TPixel>
@ -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<TPixel>(TestImageProvider<TPixel> provider, GrayscaleMode value)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> source = provider.GetImage())
using (Image<TPixel> image = new Image<TPixel>(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<TPixel>.HotPink, rect);
source.Fill(NamedColors<TPixel>.HotPink, rect);
ImageComparer.CheckSimilarity(image, source);
}
}
}

17
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);
}
}
}
}
}

16
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);
}
}
}
}
}

79
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<int> PixelateValues
= new TheoryData<int>
@ -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<TPixel>(TestImageProvider<TPixel> provider, int value)
where TPixel : struct, IPixel<TPixel>
{
string path = CreateOutputDirectory("Pixelate");
foreach (TestFile file in Files)
using (Image<TPixel> 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<TPixel> 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<TPixel>(TestImageProvider<TPixel> provider, int value)
where TPixel : struct, IPixel<TPixel>
{
string path = this.CreateOutputDirectory("Pixelate");
foreach (TestFile file in Files)
using (Image<TPixel> source = provider.GetImage())
using (Image<TPixel> image = new Image<TPixel>(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<TPixel> pixels = image.Lock())
using (PixelAccessor<TPixel> 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]);
}
}
}
}
}

16
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);
}
}
}
}
}

17
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);
}
}
}
}
}

16
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);
}
}
}
}
}

118
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<object[]> GetData(MethodInfo testMethod)
{
TypeInfo type = testMethod.GetParameters().First().ParameterType.GetTypeInfo();
if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(TestImageProvider<>))
IEnumerable<object[]> addedRows = Enumerable.Empty<object[]>();
if (!string.IsNullOrWhiteSpace(this.MemberName))
{
Type type = this.MemberType ?? testMethod.DeclaringType;
Func<object> accessor = GetPropertyAccessor(type) ?? GetFieldAccessor(type);// ?? GetMethodAccessor(type);
if (accessor != null)
{
object obj = accessor();
if (obj is IEnumerable<object> 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<object[]> dataItems = Enumerable.Empty<object[]>();
if (firstIsprovider)
{
return InnerGetData(testMethod, addedRows);
}
else
{
foreach (KeyValuePair<PixelTypes, Type> 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<object[]> InnerGetData(MethodInfo testMethod, IEnumerable<object[]> memberData)
{
foreach (KeyValuePair<PixelTypes, Type> 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<object> 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<object> 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<object> 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);
}
}
}

16
tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs

@ -22,7 +22,21 @@ namespace ImageSharp.Tests
/// <param name="pixelTypes">The requested parameter</param>
/// <param name="additionalParameters">Additional theory parameter values</param>
public WithBlankImagesAttribute(int width, int height, PixelTypes pixelTypes, params object[] additionalParameters)
: base(pixelTypes, additionalParameters)
: base(null, pixelTypes, additionalParameters)
{
this.Width = width;
this.Height = height;
}
/// <summary>
/// Triggers passing an <see cref="TestImageProvider{TPixel}"/> that produces a blank image of size width * height
/// </summary>
/// <param name="width">The required width</param>
/// <param name="height">The required height</param>
/// <param name="pixelTypes">The requested parameter</param>
/// <param name="additionalParameters">Additional theory parameter values</param>
public WithBlankImagesAttribute(string memberData, int width, int height, PixelTypes pixelTypes, params object[] additionalParameters)
: base(memberData, pixelTypes, additionalParameters)
{
this.Width = width;
this.Height = height;

15
tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs

@ -24,7 +24,20 @@ namespace ImageSharp.Tests
/// <param name="pixelTypes">The requested pixel types</param>
/// <param name="additionalParameters">Additional theory parameter values</param>
public WithFileAttribute(string fileName, PixelTypes pixelTypes, params object[] additionalParameters)
: base(pixelTypes, additionalParameters)
: base(null, pixelTypes, additionalParameters)
{
this.fileName = fileName;
}
/// <summary>
/// Triggers passing <see cref="TestImageProvider{TPixel}"/> instances which read an image from the given file
/// One <see cref="TestImageProvider{TPixel}"/> instance will be passed for each the pixel format defined by the pixelTypes parameter
/// </summary>
/// <param name="fileName">The name of the file</param>
/// <param name="pixelTypes">The requested pixel types</param>
/// <param name="additionalParameters">Additional theory parameter values</param>
public WithFileAttribute(string fileName, string dataMemberName, PixelTypes pixelTypes, params object[] additionalParameters)
: base(dataMemberName, pixelTypes, additionalParameters)
{
this.fileName = fileName;
}

19
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;
}
/// <summary>
/// Triggers passing <see cref="TestImageProvider{TPixel}"/> instances which read an image for each file being enumerated by the (static) test class field/property defined by enumeratorMemberName
/// <see cref="TestImageProvider{TPixel}"/> instances will be passed for each the pixel format defined by the pixelTypes parameter
/// </summary>
/// <param name="enumeratorMemberName">The name of the static test class field/property enumerating the files</param>
/// <param name="pixelTypes">The requested pixel types</param>
/// <param name="additionalParameters">Additional theory parameter values</param>
public WithFileCollectionAttribute(
string enumeratorMemberName,
string DataMemberName,
PixelTypes pixelTypes,
params object[] additionalParameters)
: base(DataMemberName, pixelTypes, additionalParameters)
{
this.enumeratorMemberName = enumeratorMemberName;
}

2
tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs

@ -26,7 +26,7 @@ namespace ImageSharp.Tests
/// <param name="pixelTypes">The requested pixel types</param>
/// <param name="additionalParameters">Additional theory parameter values</param>
public WithMemberFactoryAttribute(string memberMethodName, PixelTypes pixelTypes, params object[] additionalParameters)
: base(pixelTypes, additionalParameters)
: base(null, pixelTypes, additionalParameters)
{
this.memberMethodName = memberMethodName;
}

14
tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs

@ -22,7 +22,19 @@ namespace ImageSharp.Tests
/// <param name="pixelTypes">The requested parameter</param>
/// <param name="additionalParameters">Additional theory parameter values</param>
public WithTestPatternImagesAttribute(int width, int height, PixelTypes pixelTypes, params object[] additionalParameters)
: base(pixelTypes, additionalParameters)
: this(null, width, height, pixelTypes,additionalParameters)
{
}
/// <summary>
/// Triggers passing an <see cref="TestImageProvider{TPixel}"/> that produces a test pattern image of size width * height
/// </summary>
/// <param name="width">The required width</param>
/// <param name="height">The required height</param>
/// <param name="pixelTypes">The requested parameter</param>
/// <param name="additionalParameters">Additional theory parameter values</param>
public WithTestPatternImagesAttribute(string memberData, int width, int height, PixelTypes pixelTypes, params object[] additionalParameters)
: base(memberData, pixelTypes, additionalParameters)
{
this.Width = width;
this.Height = height;

8
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; }
}
/// <summary>
/// Provides <see cref="Image{TPixel}" /> instances for parametric unit tests.
/// </summary>
/// <typeparam name="TPixel">The pixel format of the image</typeparam>
public abstract partial class TestImageProvider<TPixel>
public abstract partial class TestImageProvider<TPixel> : ITestImageProvider
where TPixel : struct, IPixel<TPixel>
{
public PixelTypes PixelType { get; private set; } = typeof(TPixel).GetPixelType();

4
tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs

@ -97,10 +97,10 @@ namespace ImageSharp.Tests
/// <param name="extension">The requested extension</param>
/// <param name="encoder">Optional encoder</param>
/// <param name="options">Optional encoder options</param>
public void SaveTestOutputFile<TPixel>(Image<TPixel> image, string extension = null, IImageEncoder encoder = null, IEncoderOptions options = null)
public void SaveTestOutputFile<TPixel>(Image<TPixel> image, string extension = null, IImageEncoder encoder = null, IEncoderOptions options = null, string tag = null)
where TPixel : struct, IPixel<TPixel>
{
string path = this.GetTestOutputFileName(extension);
string path = this.GetTestOutputFileName(extension: extension, tag:tag);
extension = Path.GetExtension(path);
IImageFormat format = GetImageFormatByExtension(extension);

17
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<TPixel>(this Image<TPixel> img, TestImageProvider<TPixel> provider, string extension = "png")
public static void DebugSave<TPixel>(this Image<TPixel> img, ITestImageProvider provider, object settings = null, string extension = "png")
where TPixel : struct, IPixel<TPixel>
{
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);
}
}
}

Loading…
Cancel
Save