Browse Source

Pixelate

Former-commit-id: e1687a44c91ad29a9a20aa0ac0a5f6bf640c7202
Former-commit-id: d536480c39f7f575a40279ab96f7d7312a9dee91
Former-commit-id: 2ca77c98a64414096b01f4e41221ab17bea2ddeb
pull/1/head
James Jackson-South 10 years ago
parent
commit
b8cc804a53
  1. 64
      src/ImageProcessorCore/Filters/Pixelate.cs
  2. 97
      src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs
  3. 47
      tests/ImageProcessorCore.Tests/Processors/Filters/PixelateTest.cs

64
src/ImageProcessorCore/Filters/Pixelate.cs

@ -0,0 +1,64 @@
// <copyright file="Pixelate.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore
{
using Processors;
using System;
/// <summary>
/// Extension methods for the <see cref="Image{T,TP}"/> type.
/// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public static partial class ImageExtensions
{
/// <summary>
/// Pixelates and image with the given pixel size.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="size">The size of the pixels.</param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image<T, TP> Pixelate<T, TP>(this Image<T, TP> source, int size = 4, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{
return Pixelate(source, size, source.Bounds, progressHandler);
}
/// <summary>
/// Pixelates and image with the given pixel size.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="size">The size of the pixels.</param>
/// <param name="rectangle">
/// The <see cref="Rectangle"/> structure that specifies the portion of the image object to alter.
/// </param>
/// <param name="progressHandler">A delegate which is called as progress is made processing the image.</param>
/// <returns>The <see cref="Image{T,TP}"/>.</returns>
public static Image<T, TP> Pixelate<T, TP>(this Image<T, TP> source, int size, Rectangle rectangle, ProgressEventHandler progressHandler = null)
where T : IPackedVector<TP>
where TP : struct
{
if (size <= 0 || size > source.Height || size > source.Width)
{
throw new ArgumentOutOfRangeException(nameof(size));
}
PixelateProcessor<T, TP> processor = new PixelateProcessor<T, TP>(size);
processor.OnProgress += progressHandler;
try
{
return source.Process(rectangle, processor);
}
finally
{
processor.OnProgress -= progressHandler;
}
}
}
}

97
src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs

@ -0,0 +1,97 @@
// <copyright file="PixelateProcessor.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Processors
{
using System.Collections.Generic;
using System.Threading.Tasks;
/// <summary>
/// An <see cref="IImageProcessor{T,TP}"/> to invert the colors of an <see cref="Image{T,TP}"/>.
/// </summary>
/// <typeparam name="T">The pixel format.</typeparam>
/// <typeparam name="TP">The packed format. <example>long, float.</example></typeparam>
public class PixelateProcessor<T, TP> : ImageProcessor<T, TP>
where T : IPackedVector<TP>
where TP : struct
{
/// <summary>
/// Initializes a new instance of the <see cref="PixelateProcessor{T,TP}"/> class.
/// </summary>
/// <param name="size">The size of the pixels. Must be greater than 0.</param>
/// <exception cref="System.ArgumentException">
/// <paramref name="size"/> is less than 0 or equal to 0.
/// </exception>
public PixelateProcessor(int size)
{
Guard.MustBeGreaterThan(size, 0, nameof(size));
this.Value = size;
}
/// <summary>
/// Gets or the pixel size.
/// </summary>
public int Value { get; }
/// <inheritdoc/>
protected override void Apply(ImageBase<T, TP> target, ImageBase<T, TP> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
int sourceY = sourceRectangle.Y;
int sourceBottom = sourceRectangle.Bottom;
int startX = sourceRectangle.X;
int endX = sourceRectangle.Right;
int size = this.Value;
int offset = this.Value / 2;
// Get the range on the y-plane to choose from.
IEnumerable<int> range = EnumerableExtensions.SteppedRange(startY, i => i < endY, size);
using (IPixelAccessor<T, TP> sourcePixels = source.Lock())
using (IPixelAccessor<T, TP> targetPixels = target.Lock())
{
Parallel.ForEach(
range,
y =>
{
if (y >= sourceY && y < sourceBottom)
{
for (int x = startX; x < endX; x += size)
{
int offsetX = offset;
int offsetY = offset;
// Make sure that the offset is within the boundary of the
// image.
while (y + offsetY >= sourceBottom)
{
offsetY--;
}
while (x + offsetX >= endX)
{
offsetX--;
}
// Get the pixel color in the centre of the soon to be pixelated area.
// ReSharper disable AccessToDisposedClosure
T pixel = sourcePixels[x + offsetX, y + offsetY];
// For each pixel in the pixelate size, set it to the centre color.
for (int l = y; l < y + size && l < sourceBottom; l++)
{
for (int k = x; k < x + size && k < endX; k++)
{
targetPixels[k, l] = pixel;
}
}
}
this.OnRowProcessed();
}
});
}
}
}
}

47
tests/ImageProcessorCore.Tests/Processors/Filters/PixelateTest.cs

@ -0,0 +1,47 @@
// <copyright file="PixelateTest.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageProcessorCore.Tests
{
using System.IO;
using Xunit;
public class PixelateTest : FileTestBase
{
public static readonly TheoryData<int> PixelateValues
= new TheoryData<int>
{
4 ,
8 ,
};
[Theory]
[MemberData("PixelateValues")]
public void ImageShouldApplyPixelateFilter(int value)
{
const string path = "TestOutput/Pixelate";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
foreach (string file in Files)
{
using (FileStream stream = File.OpenRead(file))
{
string filename = Path.GetFileNameWithoutExtension(file) + "-" + value + Path.GetExtension(file);
Image image = new Image(stream);
using (FileStream output = File.OpenWrite($"{path}/{filename}"))
{
image.Pixelate(value)
.Save(output);
}
}
}
}
}
}
Loading…
Cancel
Save