Browse Source

Update pixelate processor

af/octree-no-pixelmap
James Jackson-South 6 years ago
parent
commit
d33061c613
  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.
using System;
using System.Collections.Generic;
namespace SixLabors.ImageSharp.Common
namespace SixLabors.ImageSharp
{
/// <summary>
/// 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>
/// Generates a sequence of integral numbers within a specified range.
/// </summary>
/// <param name="fromInclusive">
/// The start index, inclusive.
/// </param>
/// <param name="fromInclusive">The start index, inclusive.</param>
/// <param name="toDelegate">
/// A method that has one parameter and returns a <see cref="bool"/> calculating the end index.
/// </param>
/// <param name="step">
/// The incremental step.
/// </param>
/// <param name="step">The incremental step.</param>
/// <returns>
/// The <see cref="IEnumerable{Int32}"/> that contains a range of sequential integral numbers.
/// </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.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Common;
using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Effects
@ -38,77 +38,69 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects
/// <inheritdoc/>
protected override void OnFrameApply(ImageFrame<TPixel> source)
{
if (this.Size <= 0 || this.Size > source.Height || this.Size > source.Width)
{
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;
var interest = Rectangle.Intersect(this.SourceRectangle, source.Bounds());
int size = this.Size;
int offset = this.Size / 2;
// Align start/end positions.
int minX = Math.Max(0, startX);
int maxX = Math.Min(source.Width, endX);
int minY = Math.Max(0, startY);
int maxY = Math.Min(source.Height, endY);
Guard.MustBeBetweenOrEqualTo(size, 0, interest.Width, nameof(size));
Guard.MustBeBetweenOrEqualTo(size, 0, interest.Height, nameof(size));
// Get the range on the y-plane to choose from.
// 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.
if (minX > 0)
private readonly struct RowAction : IRowAction
{
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.
IEnumerable<int> range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size);
for (int x = this.minX; x < this.maxX; x += this.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(
range,
this.Configuration.GetParallelOptions(),
y =>
// For each pixel in the pixelate size, set it to the centre color.
for (int oY = y; oY < y + this.size && oY < this.maxY; oY++)
{
int offsetY = y - startY;
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)
for (int oX = x; oX < x + this.size && oX < this.maxX; oX++)
{
int offsetX = x - startX;
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;
}
}
this.source[oX, oY] = pixel;
}
});
}
}
}
}
}
}

Loading…
Cancel
Save