From b8cc804a531398fa18c656e201ad47343a92be02 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sat, 30 Jul 2016 10:35:53 +1000 Subject: [PATCH] Pixelate Former-commit-id: e1687a44c91ad29a9a20aa0ac0a5f6bf640c7202 Former-commit-id: d536480c39f7f575a40279ab96f7d7312a9dee91 Former-commit-id: 2ca77c98a64414096b01f4e41221ab17bea2ddeb --- src/ImageProcessorCore/Filters/Pixelate.cs | 64 ++++++++++++ .../Filters/Processors/PixelateProcessor.cs | 97 +++++++++++++++++++ .../Processors/Filters/PixelateTest.cs | 47 +++++++++ 3 files changed, 208 insertions(+) create mode 100644 src/ImageProcessorCore/Filters/Pixelate.cs create mode 100644 src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs create mode 100644 tests/ImageProcessorCore.Tests/Processors/Filters/PixelateTest.cs diff --git a/src/ImageProcessorCore/Filters/Pixelate.cs b/src/ImageProcessorCore/Filters/Pixelate.cs new file mode 100644 index 000000000..8bfb7cd2d --- /dev/null +++ b/src/ImageProcessorCore/Filters/Pixelate.cs @@ -0,0 +1,64 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore +{ + using Processors; + using System; + + /// + /// Extension methods for the type. + /// + /// The pixel format. + /// The packed format. long, float. + public static partial class ImageExtensions + { + /// + /// Pixelates and image with the given pixel size. + /// + /// The image this method extends. + /// The size of the pixels. + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Pixelate(this Image source, int size = 4, ProgressEventHandler progressHandler = null) + where T : IPackedVector + where TP : struct + { + return Pixelate(source, size, source.Bounds, progressHandler); + } + + /// + /// Pixelates and image with the given pixel size. + /// + /// The image this method extends. + /// The size of the pixels. + /// + /// The structure that specifies the portion of the image object to alter. + /// + /// A delegate which is called as progress is made processing the image. + /// The . + public static Image Pixelate(this Image source, int size, Rectangle rectangle, ProgressEventHandler progressHandler = null) + where T : IPackedVector + where TP : struct + { + if (size <= 0 || size > source.Height || size > source.Width) + { + throw new ArgumentOutOfRangeException(nameof(size)); + } + + PixelateProcessor processor = new PixelateProcessor(size); + processor.OnProgress += progressHandler; + + try + { + return source.Process(rectangle, processor); + } + finally + { + processor.OnProgress -= progressHandler; + } + } + } +} diff --git a/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs b/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs new file mode 100644 index 000000000..816ce2f42 --- /dev/null +++ b/src/ImageProcessorCore/Filters/Processors/PixelateProcessor.cs @@ -0,0 +1,97 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Processors +{ + using System.Collections.Generic; + using System.Threading.Tasks; + + /// + /// An to invert the colors of an . + /// + /// The pixel format. + /// The packed format. long, float. + public class PixelateProcessor : ImageProcessor + where T : IPackedVector + where TP : struct + { + /// + /// Initializes a new instance of the class. + /// + /// The size of the pixels. Must be greater than 0. + /// + /// is less than 0 or equal to 0. + /// + public PixelateProcessor(int size) + { + Guard.MustBeGreaterThan(size, 0, nameof(size)); + this.Value = size; + } + + /// + /// Gets or the pixel size. + /// + public int Value { get; } + + /// + protected override void Apply(ImageBase target, ImageBase 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 range = EnumerableExtensions.SteppedRange(startY, i => i < endY, size); + + using (IPixelAccessor sourcePixels = source.Lock()) + using (IPixelAccessor 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(); + } + }); + } + } + } +} diff --git a/tests/ImageProcessorCore.Tests/Processors/Filters/PixelateTest.cs b/tests/ImageProcessorCore.Tests/Processors/Filters/PixelateTest.cs new file mode 100644 index 000000000..b2ef2fb38 --- /dev/null +++ b/tests/ImageProcessorCore.Tests/Processors/Filters/PixelateTest.cs @@ -0,0 +1,47 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageProcessorCore.Tests +{ + using System.IO; + + using Xunit; + + public class PixelateTest : FileTestBase + { + public static readonly TheoryData PixelateValues + = new TheoryData + { + 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); + } + } + } + } + } +} \ No newline at end of file