mirror of https://github.com/SixLabors/ImageSharp
Browse Source
Former-commit-id: e1687a44c91ad29a9a20aa0ac0a5f6bf640c7202 Former-commit-id: d536480c39f7f575a40279ab96f7d7312a9dee91 Former-commit-id: 2ca77c98a64414096b01f4e41221ab17bea2ddebaf/merge-core
3 changed files with 208 additions and 0 deletions
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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…
Reference in new issue