Browse Source

Update pixelate processor

pull/1107/head
James Jackson-South 6 years ago
parent
commit
fcf7c44a63
  1. 14
      src/ImageSharp/Common/Extensions/EnumerableExtensions.cs
  2. 112
      src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs

14
src/ImageSharp/Common/Extensions/EnumerableExtensions.cs

@ -1,10 +1,10 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace SixLabors.ImageSharp.Common namespace SixLabors.ImageSharp
{ {
/// <summary> /// <summary>
/// Encapsulates a series of time saving extension methods to the <see cref="T:System.Collections.IEnumerable"/> interface. /// Encapsulates a series of time saving extension methods to the <see cref="T:System.Collections.IEnumerable"/> interface.
@ -34,15 +34,11 @@ namespace SixLabors.ImageSharp.Common
/// <summary> /// <summary>
/// Generates a sequence of integral numbers within a specified range. /// Generates a sequence of integral numbers within a specified range.
/// </summary> /// </summary>
/// <param name="fromInclusive"> /// <param name="fromInclusive">The start index, inclusive.</param>
/// The start index, inclusive.
/// </param>
/// <param name="toDelegate"> /// <param name="toDelegate">
/// A method that has one parameter and returns a <see cref="bool"/> calculating the end index. /// A method that has one parameter and returns a <see cref="bool"/> calculating the end index.
/// </param> /// </param>
/// <param name="step"> /// <param name="step">The incremental step.</param>
/// The incremental step.
/// </param>
/// <returns> /// <returns>
/// The <see cref="IEnumerable{Int32}"/> that contains a range of sequential integral numbers. /// The <see cref="IEnumerable{Int32}"/> that contains a range of sequential integral numbers.
/// </returns> /// </returns>
@ -56,4 +52,4 @@ namespace SixLabors.ImageSharp.Common
} }
} }
} }
} }

112
src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs

@ -3,10 +3,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Common;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Effects namespace SixLabors.ImageSharp.Processing.Processors.Effects
@ -38,77 +38,69 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source) protected override void OnFrameApply(ImageFrame<TPixel> source)
{ {
if (this.Size <= 0 || this.Size > source.Height || this.Size > source.Width) var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
{
throw new ArgumentOutOfRangeException(nameof(this.Size));
}
int startY = this.SourceRectangle.Y;
int endY = this.SourceRectangle.Bottom;
int startX = this.SourceRectangle.X;
int endX = this.SourceRectangle.Right;
int size = this.Size; int size = this.Size;
int offset = this.Size / 2;
// Align start/end positions. Guard.MustBeBetweenOrEqualTo(size, 0, interest.Width, nameof(size));
int minX = Math.Max(0, startX); Guard.MustBeBetweenOrEqualTo(size, 0, interest.Height, nameof(size));
int maxX = Math.Min(source.Width, endX);
int minY = Math.Max(0, startY); // Get the range on the y-plane to choose from.
int maxY = Math.Min(source.Height, endY); // TODO: It would be nice to be able to pool this somehow but neither Memory<T> nor Span<T>
// implement IEnumerable<T>.
IEnumerable<int> range = EnumerableExtensions.SteppedRange(interest.Y, i => i < interest.Bottom, size);
Parallel.ForEach(
range,
this.Configuration.GetParallelOptions(),
new RowAction(interest, size, source).Invoke);
}
// Reset offset if necessary. private readonly struct RowAction : IRowAction
if (minX > 0) {
private readonly int minX;
private readonly int maxX;
private readonly int maxXIndex;
private readonly int maxY;
private readonly int maxYIndex;
private readonly int size;
private readonly int radius;
private readonly ImageFrame<TPixel> source;
[MethodImpl(InliningOptions.ShortMethod)]
public RowAction(
Rectangle bounds,
int size,
ImageFrame<TPixel> source)
{ {
startX = 0; this.minX = bounds.X;
this.maxX = bounds.Right;
this.maxXIndex = bounds.Right - 1;
this.maxY = bounds.Bottom;
this.maxYIndex = bounds.Bottom - 1;
this.size = size;
this.radius = size >> 1;
this.source = source;
} }
if (minY > 0) [MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(int y)
{ {
startY = 0; Span<TPixel> rowSpan = this.source.GetPixelRowSpan(Math.Min(y + this.radius, this.maxYIndex));
}
// Get the range on the y-plane to choose from. for (int x = this.minX; x < this.maxX; x += this.size)
IEnumerable<int> range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size); {
// Get the pixel color in the centre of the soon to be pixelated area.
TPixel pixel = rowSpan[Math.Min(x + this.radius, this.maxXIndex)];
Parallel.ForEach( // For each pixel in the pixelate size, set it to the centre color.
range, for (int oY = y; oY < y + this.size && oY < this.maxY; oY++)
this.Configuration.GetParallelOptions(),
y =>
{ {
int offsetY = y - startY; for (int oX = x; oX < x + this.size && oX < this.maxX; oX++)
int offsetPy = offset;
// Make sure that the offset is within the boundary of the image.
while (offsetY + offsetPy >= maxY)
{
offsetPy--;
}
Span<TPixel> row = source.GetPixelRowSpan(offsetY + offsetPy);
for (int x = minX; x < maxX; x += size)
{ {
int offsetX = x - startX; this.source[oX, oY] = pixel;
int offsetPx = offset;
while (x + offsetPx >= maxX)
{
offsetPx--;
}
// Get the pixel color in the centre of the soon to be pixelated area.
TPixel pixel = row[offsetX + offsetPx];
// 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++)
{
source[k, l] = pixel;
}
}
} }
}); }
}
}
} }
} }
} }

Loading…
Cancel
Save