Browse Source

ParallelHelper -> AffineTransformProcessor, RotateProcessor

af/merge-core
Anton Firszov 8 years ago
parent
commit
3e24c577f7
  1. 186
      src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs
  2. 86
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs

186
src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs

@ -10,6 +10,7 @@ using System.Runtime.InteropServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.Memory; using SixLabors.Memory;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -78,23 +79,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
if (this.Sampler is NearestNeighborResampler) if (this.Sampler is NearestNeighborResampler)
{ {
ParallelFor.WithConfiguration( ParallelHelper.IterateRows(
0, targetBounds,
height,
configuration, configuration,
y => rows =>
{
Span<TPixel> destRow = destination.GetPixelRowSpan(y);
for (int x = 0; x < width; x++)
{ {
var point = Point.Transform(new Point(x, y), matrix); for (int y = rows.Min; y < rows.Max; y++)
if (sourceBounds.Contains(point.X, point.Y))
{ {
destRow[x] = source[point.X, point.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;
} }
@ -116,86 +119,107 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
using (Buffer2D<float> yBuffer = memoryAllocator.Allocate2D<float>(yLength, height)) using (Buffer2D<float> yBuffer = memoryAllocator.Allocate2D<float>(yLength, height))
using (Buffer2D<float> xBuffer = memoryAllocator.Allocate2D<float>(xLength, height)) using (Buffer2D<float> xBuffer = memoryAllocator.Allocate2D<float>(xLength, height))
{ {
ParallelFor.WithConfiguration( ParallelHelper.IterateRows(
0, targetBounds,
height,
configuration, configuration,
y => rows =>
{ {
ref TPixel destRowRef = ref MemoryMarshal.GetReference(destination.GetPixelRowSpan(y)); for (int y = rows.Min; y < rows.Max; y++)
ref float ySpanRef = ref MemoryMarshal.GetReference(yBuffer.GetRowSpan(y));
ref float xSpanRef = ref MemoryMarshal.GetReference(xBuffer.GetRowSpan(y));
for (int x = 0; x < width; x++)
{ {
// Use the single precision position to calculate correct bounding pixels ref TPixel destRowRef = ref MemoryMarshal.GetReference(destination.GetPixelRowSpan(y));
// otherwise we get rogue pixels outside of the bounds. ref float ySpanRef = ref MemoryMarshal.GetReference(yBuffer.GetRowSpan(y));
var point = Vector2.Transform(new Vector2(x, y), matrix); ref float xSpanRef = ref MemoryMarshal.GetReference(xBuffer.GetRowSpan(y));
// Clamp sampling pixel radial extents to the source image edges
Vector2 maxXY = point + radius;
Vector2 minXY = point - radius;
// max, maxY, minX, minY
var extents = new Vector4(
MathF.Floor(maxXY.X + .5F),
MathF.Floor(maxXY.Y + .5F),
MathF.Ceiling(minXY.X - .5F),
MathF.Ceiling(minXY.Y - .5F));
int right = (int)extents.X;
int bottom = (int)extents.Y;
int left = (int)extents.Z;
int top = (int)extents.W;
extents = Vector4.Clamp(extents, Vector4.Zero, maxSource);
int maxX = (int)extents.X;
int maxY = (int)extents.Y;
int minX = (int)extents.Z;
int minY = (int)extents.W;
if (minX == maxX || minY == maxY)
{
continue;
}
// It appears these have to be calculated on-the-fly. for (int x = 0; x < width; x++)
// Precalulating transformed weights would require prior knowledge of every transformed pixel location
// since they can be at sub-pixel positions on both axis.
// I've optimized where I can but am always open to suggestions.
if (yScale > 1 && xScale > 1)
{
CalculateWeightsDown(top, bottom, minY, maxY, point.Y, sampler, yScale, ref ySpanRef, yLength);
CalculateWeightsDown(left, right, minX, maxX, point.X, sampler, xScale, ref xSpanRef, xLength);
}
else
{ {
CalculateWeightsScaleUp(minY, maxY, point.Y, sampler, ref ySpanRef); // Use the single precision position to calculate correct bounding pixels
CalculateWeightsScaleUp(minX, maxX, point.X, sampler, ref xSpanRef); // otherwise we get rogue pixels outside of the bounds.
} var point = Vector2.Transform(new Vector2(x, y), matrix);
// Clamp sampling pixel radial extents to the source image edges
Vector2 maxXY = point + radius;
Vector2 minXY = point - radius;
// max, maxY, minX, minY
var extents = new Vector4(
MathF.Floor(maxXY.X + .5F),
MathF.Floor(maxXY.Y + .5F),
MathF.Ceiling(minXY.X - .5F),
MathF.Ceiling(minXY.Y - .5F));
int right = (int)extents.X;
int bottom = (int)extents.Y;
int left = (int)extents.Z;
int top = (int)extents.W;
extents = Vector4.Clamp(extents, Vector4.Zero, maxSource);
int maxX = (int)extents.X;
int maxY = (int)extents.Y;
int minX = (int)extents.Z;
int minY = (int)extents.W;
if (minX == maxX || minY == maxY)
{
continue;
}
// Now multiply the results against the offsets // It appears these have to be calculated on-the-fly.
Vector4 sum = Vector4.Zero; // Precalculating transformed weights would require prior knowledge of every transformed pixel location
for (int yy = 0, j = minY; j <= maxY; j++, yy++) // since they can be at sub-pixel positions on both axis.
{ // I've optimized where I can but am always open to suggestions.
float yWeight = Unsafe.Add(ref ySpanRef, yy); if (yScale > 1 && xScale > 1)
{
CalculateWeightsDown(
top,
bottom,
minY,
maxY,
point.Y,
sampler,
yScale,
ref ySpanRef,
yLength);
CalculateWeightsDown(
left,
right,
minX,
maxX,
point.X,
sampler,
xScale,
ref xSpanRef,
xLength);
}
else
{
CalculateWeightsScaleUp(minY, maxY, point.Y, sampler, ref ySpanRef);
CalculateWeightsScaleUp(minX, maxX, point.X, sampler, ref xSpanRef);
}
for (int xx = 0, i = minX; i <= maxX; i++, xx++) // Now multiply the results against the offsets
Vector4 sum = Vector4.Zero;
for (int yy = 0, j = minY; j <= maxY; j++, yy++)
{ {
float xWeight = Unsafe.Add(ref xSpanRef, xx); float yWeight = Unsafe.Add(ref ySpanRef, yy);
var vector = source[i, j].ToVector4();
for (int xx = 0, i = minX; i <= maxX; i++, xx++)
{
float xWeight = Unsafe.Add(ref xSpanRef, xx);
var vector = source[i, j].ToVector4();
// Values are first premultiplied to prevent darkening of edge pixels // Values are first premultiplied to prevent darkening of edge pixels
Vector4 multiplied = vector.Premultiply(); Vector4 multiplied = vector.Premultiply();
sum += multiplied * xWeight * yWeight; sum += multiplied * xWeight * yWeight;
}
} }
}
ref TPixel dest = ref Unsafe.Add(ref destRowRef, x); ref TPixel dest = ref Unsafe.Add(ref destRowRef, x);
// Reverse the premultiplication // Reverse the premultiplication
dest.PackFromVector4(sum.UnPremultiply()); dest.PackFromVector4(sum.UnPremultiply());
}
} }
}); });
} }

86
src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs

@ -5,6 +5,7 @@ using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.MetaData.Profiles.Exif; using SixLabors.ImageSharp.MetaData.Profiles.Exif;
using SixLabors.ImageSharp.ParallelUtils;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.Primitives; using SixLabors.Primitives;
@ -147,25 +148,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int height = source.Height; int height = source.Height;
Rectangle destinationBounds = destination.Bounds(); Rectangle destinationBounds = destination.Bounds();
ParallelFor.WithConfiguration( ParallelHelper.IterateRows(
0, source.Bounds(),
height,
configuration, configuration,
y => rows =>
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
for (int x = 0; x < width; x++)
{ {
int newX = height - y - 1; for (int y = rows.Min; y < rows.Max; y++)
newX = height - newX - 1;
int newY = width - x - 1;
if (destinationBounds.Contains(newX, newY))
{ {
destination[newX, newY] = sourceRow[x]; Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
for (int x = 0; x < width; x++)
{
int newX = height - y - 1;
newX = height - newX - 1;
int newY = width - x - 1;
if (destinationBounds.Contains(newX, newY))
{
destination[newX, newY] = sourceRow[x];
}
}
} }
} });
});
} }
/// <summary> /// <summary>
@ -179,20 +182,22 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
ParallelFor.WithConfiguration( ParallelHelper.IterateRows(
0, source.Bounds(),
height,
configuration, configuration,
y => rows =>
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
Span<TPixel> targetRow = destination.GetPixelRowSpan(height - y - 1);
for (int x = 0; x < width; x++)
{ {
targetRow[width - x - 1] = sourceRow[x]; for (int y = rows.Min; y < rows.Max; y++)
} {
}); Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
Span<TPixel> targetRow = destination.GetPixelRowSpan(height - y - 1);
for (int x = 0; x < width; x++)
{
targetRow[width - x - 1] = sourceRow[x];
}
}
});
} }
/// <summary> /// <summary>
@ -207,22 +212,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int height = source.Height; int height = source.Height;
Rectangle destinationBounds = destination.Bounds(); Rectangle destinationBounds = destination.Bounds();
ParallelFor.WithConfiguration( ParallelHelper.IterateRows(
0, source.Bounds(),
height,
configuration, configuration,
y => rows =>
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
int newX = height - y - 1;
for (int x = 0; x < width; x++)
{ {
if (destinationBounds.Contains(newX, x)) for (int y = rows.Min; y < rows.Max; y++)
{ {
destination[newX, x] = sourceRow[x]; Span<TPixel> sourceRow = source.GetPixelRowSpan(y);
int newX = height - y - 1;
for (int x = 0; x < width; x++)
{
// TODO: Optimize this:
if (destinationBounds.Contains(newX, x))
{
destination[newX, x] = sourceRow[x];
}
}
} }
} });
});
} }
} }
} }
Loading…
Cancel
Save