Browse Source

Convert AffineTranformProcessor

af/octree-no-pixelmap
James Jackson-South 6 years ago
parent
commit
4dbff3da45
  1. 161
      src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs
  2. 11
      src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs

161
src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs

@ -3,7 +3,9 @@
using System; using System;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms namespace SixLabors.ImageSharp.Processing.Processors.Transforms
@ -48,7 +50,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
} }
int width = this.targetSize.Width; int width = this.targetSize.Width;
Rectangle sourceBounds = this.SourceRectangle;
var targetBounds = new Rectangle(Point.Empty, this.targetSize); var targetBounds = new Rectangle(Point.Empty, this.targetSize);
Configuration configuration = this.Configuration; Configuration configuration = this.Configuration;
@ -57,70 +58,124 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
if (this.resampler is NearestNeighborResampler) if (this.resampler is NearestNeighborResampler)
{ {
Rectangle sourceBounds = this.SourceRectangle;
var nearestRowAction = new NearestNeighborRowIntervalAction(ref sourceBounds, ref matrix, width, source, destination);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows(
targetBounds, targetBounds,
configuration, configuration,
rows => in nearestRowAction);
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> destRow = destination.GetPixelRowSpan(y);
for (int x = 0; x < width; x++)
{
var point = Point.Transform(new Point(x, y), matrix);
if (sourceBounds.Contains(point.X, point.Y))
{
destRow[x] = source[point.X, point.Y];
}
}
}
});
return; return;
} }
var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler); using var kernelMap = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.resampler);
var rowAction = new RowIntervalAction(configuration, kernelMap, ref matrix, width, source, destination);
ParallelRowIterator.IterateRows<RowIntervalAction, Vector4>(
targetBounds,
configuration,
in rowAction);
}
private readonly struct NearestNeighborRowIntervalAction : IRowIntervalAction
{
private readonly Rectangle bounds;
private readonly Matrix3x2 matrix;
private readonly int maxX;
private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination;
try [MethodImpl(InliningOptions.ShortMethod)]
public NearestNeighborRowIntervalAction(
ref Rectangle bounds,
ref Matrix3x2 matrix,
int maxX,
ImageFrame<TPixel> source,
ImageFrame<TPixel> destination)
{ {
ParallelRowIterator.IterateRowsWithTempBuffer<Vector4>( this.bounds = bounds;
targetBounds, this.matrix = matrix;
configuration, this.maxX = maxX;
(rows, vectorBuffer) => this.source = source;
this.destination = destination;
}
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows)
{
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> destRow = this.destination.GetPixelRowSpan(y);
for (int x = 0; x < this.maxX; x++)
{
var point = Point.Transform(new Point(x, y), this.matrix);
if (this.bounds.Contains(point.X, point.Y))
{ {
Span<Vector4> vectorSpan = vectorBuffer.Span; destRow[x] = this.source[point.X, point.Y];
for (int y = rows.Min; y < rows.Max; y++) }
{ }
Span<TPixel> targetRowSpan = destination.GetPixelRowSpan(y); }
PixelOperations<TPixel>.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); }
ref float ySpanRef = ref kernel.GetYStartReference(y); }
ref float xSpanRef = ref kernel.GetXStartReference(y);
private readonly struct RowIntervalAction : IRowIntervalAction<Vector4>
for (int x = 0; x < width; x++) {
{ private readonly Configuration configuration;
// Use the single precision position to calculate correct bounding pixels private readonly TransformKernelMap kernelMap;
// otherwise we get rogue pixels outside of the bounds. private readonly Matrix3x2 matrix;
var point = Vector2.Transform(new Vector2(x, y), matrix); private readonly int maxX;
kernel.Convolve( private readonly ImageFrame<TPixel> source;
point, private readonly ImageFrame<TPixel> destination;
x,
ref ySpanRef, [MethodImpl(InliningOptions.ShortMethod)]
ref xSpanRef, public RowIntervalAction(
source.PixelBuffer, Configuration configuration,
vectorSpan); TransformKernelMap kernelMap,
} ref Matrix3x2 matrix,
int maxX,
PixelOperations<TPixel>.Instance.FromVector4Destructive( ImageFrame<TPixel> source,
configuration, ImageFrame<TPixel> destination)
vectorSpan, {
targetRowSpan); this.configuration = configuration;
} this.kernelMap = kernelMap;
}); this.matrix = matrix;
this.maxX = maxX;
this.source = source;
this.destination = destination;
} }
finally
[MethodImpl(InliningOptions.ShortMethod)]
public void Invoke(in RowInterval rows, Memory<Vector4> memory)
{ {
kernel.Dispose(); Span<Vector4> vectorSpan = memory.Span;
for (int y = rows.Min; y < rows.Max; y++)
{
Span<TPixel> targetRowSpan = this.destination.GetPixelRowSpan(y);
PixelOperations<TPixel>.Instance.ToVector4(this.configuration, targetRowSpan, vectorSpan);
ref float ySpanRef = ref this.kernelMap.GetYStartReference(y);
ref float xSpanRef = ref this.kernelMap.GetXStartReference(y);
for (int x = 0; x < this.maxX; x++)
{
// Use the single precision position to calculate correct bounding pixels
// otherwise we get rogue pixels outside of the bounds.
var point = Vector2.Transform(new Vector2(x, y), this.matrix);
this.kernelMap.Convolve(
point,
x,
ref ySpanRef,
ref xSpanRef,
this.source.PixelBuffer,
vectorSpan);
}
PixelOperations<TPixel>.Instance.FromVector4Destructive(
this.configuration,
vectorSpan,
targetRowSpan);
}
} }
} }
} }

11
src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs

@ -48,10 +48,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
Rectangle bounds = this.cropRectangle; Rectangle bounds = this.cropRectangle;
// Copying is cheap, we should process more pixels per task: // Copying is cheap, we should process more pixels per task:
ParallelExecutionSettings parallelSettings = ParallelExecutionSettings.FromConfiguration(this.Configuration) ParallelExecutionSettings parallelSettings =
.MultiplyMinimumPixelsPerTask(4); ParallelExecutionSettings.FromConfiguration(this.Configuration).MultiplyMinimumPixelsPerTask(4);
var rowAction = new RowAction(ref bounds, source, destination); var rowAction = new RowIntervalAction(ref bounds, source, destination);
ParallelRowIterator.IterateRows( ParallelRowIterator.IterateRows(
bounds, bounds,
@ -59,13 +59,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
in rowAction); in rowAction);
} }
private readonly struct RowAction : IRowIntervalAction private readonly struct RowIntervalAction : IRowIntervalAction
{ {
private readonly Rectangle bounds; private readonly Rectangle bounds;
private readonly ImageFrame<TPixel> source; private readonly ImageFrame<TPixel> source;
private readonly ImageFrame<TPixel> destination; private readonly ImageFrame<TPixel> destination;
public RowAction(ref Rectangle bounds, ImageFrame<TPixel> source, ImageFrame<TPixel> destination) [MethodImpl(InliningOptions.ShortMethod)]
public RowIntervalAction(ref Rectangle bounds, ImageFrame<TPixel> source, ImageFrame<TPixel> destination)
{ {
this.bounds = bounds; this.bounds = bounds;
this.source = source; this.source = source;

Loading…
Cancel
Save