Browse Source

pixelate no longer blanks out background

pull/195/head
Scott Williams 9 years ago
parent
commit
a999a0b33b
  1. 65
      src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs
  2. 8
      tests/ImageSharp.Tests/Formats/Png/PngSmokeTests.cs
  3. 5
      tests/ImageSharp.Tests/ImageComparer.cs
  4. 79
      tests/ImageSharp.Tests/Processors/Filters/PixelateTest.cs
  5. 118
      tests/ImageSharp.Tests/TestUtilities/Attributes/ImageDataAttributeBase.cs
  6. 16
      tests/ImageSharp.Tests/TestUtilities/Attributes/WithBlankImageAttribute.cs
  7. 15
      tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileAttribute.cs
  8. 19
      tests/ImageSharp.Tests/TestUtilities/Attributes/WithFileCollectionAttribute.cs
  9. 2
      tests/ImageSharp.Tests/TestUtilities/Attributes/WithMemberFactoryAttribute.cs
  10. 14
      tests/ImageSharp.Tests/TestUtilities/Attributes/WithTestPatternImageAttribute.cs
  11. 8
      tests/ImageSharp.Tests/TestUtilities/ImageProviders/TestImageProvider.cs
  12. 4
      tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs
  13. 17
      tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs

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

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

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