|
|
|
@ -39,8 +39,9 @@ namespace SixLabors.ImageSharp.Processing.Processors |
|
|
|
/// <summary>
|
|
|
|
/// Returns the processing matrix used for transforming the image.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="rectangle">The rectangle bounds</param>
|
|
|
|
/// <returns>The <see cref="Matrix3x2"/></returns>
|
|
|
|
protected abstract Matrix3x2 CreateProcessingMatrix(); |
|
|
|
protected abstract Matrix3x2 CreateProcessingMatrix(Rectangle rectangle); |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Creates a new target canvas to contain the results of the matrix transform.
|
|
|
|
@ -48,19 +49,9 @@ namespace SixLabors.ImageSharp.Processing.Processors |
|
|
|
/// <param name="sourceRectangle">The source rectangle.</param>
|
|
|
|
protected virtual void CreateNewCanvas(Rectangle sourceRectangle) |
|
|
|
{ |
|
|
|
if (this.ResizeRectangle == Rectangles.DefaultRectangle) |
|
|
|
{ |
|
|
|
if (this.Expand) |
|
|
|
{ |
|
|
|
this.ResizeRectangle = Matrix3x2.Invert(this.CreateProcessingMatrix(), out Matrix3x2 sizeMatrix) |
|
|
|
? ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix) |
|
|
|
: sourceRectangle; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
this.ResizeRectangle = sourceRectangle; |
|
|
|
} |
|
|
|
} |
|
|
|
this.ResizeRectangle = Matrix3x2.Invert(this.CreateProcessingMatrix(sourceRectangle), out Matrix3x2 sizeMatrix) |
|
|
|
? ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix) |
|
|
|
: sourceRectangle; |
|
|
|
|
|
|
|
this.Width = this.ResizeRectangle.Width; |
|
|
|
this.Height = this.ResizeRectangle.Height; |
|
|
|
@ -84,8 +75,8 @@ namespace SixLabors.ImageSharp.Processing.Processors |
|
|
|
{ |
|
|
|
int height = this.ResizeRectangle.Height; |
|
|
|
int width = this.ResizeRectangle.Width; |
|
|
|
Matrix3x2 matrix = this.GetCenteredMatrix(source); |
|
|
|
Rectangle sourceBounds = source.Bounds(); |
|
|
|
Matrix3x2 matrix = this.GetCenteredMatrix(source); |
|
|
|
|
|
|
|
if (this.Sampler is NearestNeighborResampler) |
|
|
|
{ |
|
|
|
@ -112,24 +103,24 @@ namespace SixLabors.ImageSharp.Processing.Processors |
|
|
|
|
|
|
|
int maxX = source.Width - 1; |
|
|
|
int maxY = source.Height - 1; |
|
|
|
int radius = Math.Max((int)this.Sampler.Radius, 1); |
|
|
|
|
|
|
|
Parallel.For( |
|
|
|
0, |
|
|
|
height, |
|
|
|
configuration.ParallelOptions, |
|
|
|
new ParallelOptions { MaxDegreeOfParallelism = 1 }, |
|
|
|
y => |
|
|
|
{ |
|
|
|
Span<TPixel> destRow = destination.GetPixelRowSpan(y); |
|
|
|
for (int x = 0; x < width; x++) |
|
|
|
{ |
|
|
|
var transformedPoint = Point.Transform(new Point(x, y), matrix); |
|
|
|
|
|
|
|
if (sourceBounds.Contains(transformedPoint.X, transformedPoint.Y)) |
|
|
|
{ |
|
|
|
WeightsWindow windowX = this.HorizontalWeights.Weights[transformedPoint.X]; |
|
|
|
WeightsWindow windowY = this.VerticalWeights.Weights[transformedPoint.Y]; |
|
|
|
|
|
|
|
Vector4 dXY = this.ComputeWeightedSumAtPosition(source, maxX, maxY, radius, ref windowX, ref windowY, ref transformedPoint); |
|
|
|
Vector4 dXY = this.ComputeWeightedSumAtPosition(source, maxX, maxY, ref windowX, ref windowY, ref transformedPoint); |
|
|
|
ref TPixel dest = ref destRow[x]; |
|
|
|
dest.PackFromVector4(dXY); |
|
|
|
} |
|
|
|
@ -148,7 +139,7 @@ namespace SixLabors.ImageSharp.Processing.Processors |
|
|
|
{ |
|
|
|
var translationToTargetCenter = Matrix3x2.CreateTranslation(-this.ResizeRectangle.Width * .5F, -this.ResizeRectangle.Height * .5F); |
|
|
|
var translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width * .5F, source.Height * .5F); |
|
|
|
return (translationToTargetCenter * this.CreateProcessingMatrix()) * translateToSourceCenter; |
|
|
|
return (translationToTargetCenter * this.CreateProcessingMatrix(this.ResizeRectangle)) * translateToSourceCenter; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -157,55 +148,87 @@ namespace SixLabors.ImageSharp.Processing.Processors |
|
|
|
/// <param name="source">The source image</param>
|
|
|
|
/// <param name="maxX">The maximum x value</param>
|
|
|
|
/// <param name="maxY">The maximum y value</param>
|
|
|
|
/// <param name="radius">The radius of the current sampling window</param>
|
|
|
|
/// <param name="windowX">The horizontal weights</param>
|
|
|
|
/// <param name="windowY">The vertical weights</param>
|
|
|
|
/// <param name="point">The transformed position</param>
|
|
|
|
/// <returns>The <see cref="Vector4"/></returns>
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
|
|
protected Vector4 ComputeWeightedSumAtPosition(ImageFrame<TPixel> source, int maxX, int maxY, int radius, ref WeightsWindow windowX, ref WeightsWindow windowY, ref Point point) |
|
|
|
protected Vector4 ComputeWeightedSumAtPosition( |
|
|
|
ImageFrame<TPixel> source, |
|
|
|
int maxX, |
|
|
|
int maxY, |
|
|
|
ref WeightsWindow windowX, |
|
|
|
ref WeightsWindow windowY, |
|
|
|
ref Point point) |
|
|
|
{ |
|
|
|
// What, in theory, is supposed to happen here is the following...
|
|
|
|
//
|
|
|
|
// We identify the maximum possible pixel offsets allowable by the current sampler
|
|
|
|
// clamping values to ensure that we do not go outwith the bounds of our image.
|
|
|
|
//
|
|
|
|
// Then we get the weights of that offset value from our pre-calculated vaues.
|
|
|
|
// First we grab the weight on the y-axis, then the x-axis and then we multiply
|
|
|
|
// them together to get the final weight.
|
|
|
|
//
|
|
|
|
// Unfortunately this simply does not seem to work!
|
|
|
|
// The output is rubbish and I cannot see why :(
|
|
|
|
ref float horizontalValues = ref windowX.GetStartReference(); |
|
|
|
ref float verticalValues = ref windowY.GetStartReference(); |
|
|
|
|
|
|
|
int left = point.X - radius; |
|
|
|
int right = point.X + radius; |
|
|
|
int top = point.Y - radius; |
|
|
|
int bottom = point.Y + radius; |
|
|
|
int yLength = windowY.Length; |
|
|
|
int xLength = windowX.Length; |
|
|
|
int yRadius = (int)MathF.Ceiling((yLength - 1) * .5F); |
|
|
|
int xRadius = (int)MathF.Ceiling((xLength - 1) * .5F); |
|
|
|
|
|
|
|
int left = point.X - xRadius; |
|
|
|
int right = point.X + xRadius; |
|
|
|
int top = point.Y - yRadius; |
|
|
|
int bottom = point.Y + yRadius; |
|
|
|
|
|
|
|
int yIndex = 0; |
|
|
|
int xIndex = 0; |
|
|
|
|
|
|
|
// Faster than clamping + we know we are only looking in one direction
|
|
|
|
if (left < 0) |
|
|
|
{ |
|
|
|
// Trim the length of our weights iterator across the x-axis.
|
|
|
|
// Offset our start index across the x-axis.
|
|
|
|
xIndex = ImageMaths.FastAbs(left); |
|
|
|
xLength -= xIndex; |
|
|
|
left = 0; |
|
|
|
} |
|
|
|
|
|
|
|
if (top < 0) |
|
|
|
{ |
|
|
|
// Trim the length of our weights iterator across the y-axis.
|
|
|
|
// Offset our start index across the y-axis.
|
|
|
|
yIndex = ImageMaths.FastAbs(top); |
|
|
|
yLength -= yIndex; |
|
|
|
top = 0; |
|
|
|
} |
|
|
|
|
|
|
|
if (right > maxX) |
|
|
|
if (right >= maxX) |
|
|
|
{ |
|
|
|
right = maxX; |
|
|
|
// Trim the length of our weights iterator across the x-axis.
|
|
|
|
xLength -= right - maxX; |
|
|
|
} |
|
|
|
|
|
|
|
if (bottom > maxY) |
|
|
|
if (bottom >= maxY) |
|
|
|
{ |
|
|
|
bottom = maxY; |
|
|
|
// Trim the length of our weights iterator across the y-axis.
|
|
|
|
yLength -= bottom - maxY; |
|
|
|
} |
|
|
|
|
|
|
|
Vector4 result = Vector4.Zero; |
|
|
|
|
|
|
|
// We calculate our sample by iterating up-down/left-right from our transformed point.
|
|
|
|
// Ignoring the weight of outlying pixels is better for shape preservation on transforms such as skew with samplers that use larger radii.
|
|
|
|
// We don't offset our window index so that the weight compensates for the missing values
|
|
|
|
for (int y = top, yl = 0; y <= bottom; y++, yl++) |
|
|
|
for (int y = top, yi = yIndex; yi < yLength; y++, yi++) |
|
|
|
{ |
|
|
|
float yweight = Unsafe.Add(ref verticalValues, yl); |
|
|
|
float yweight = Unsafe.Add(ref verticalValues, yi); |
|
|
|
|
|
|
|
for (int x = left, xl = 0; x <= right; x++, xl++) |
|
|
|
for (int x = left, xi = xIndex; xi < xLength; x++, xi++) |
|
|
|
{ |
|
|
|
float xweight = Unsafe.Add(ref horizontalValues, xl); |
|
|
|
float xweight = Unsafe.Add(ref horizontalValues, xi); |
|
|
|
float weight = yweight * xweight; |
|
|
|
|
|
|
|
result += source[x, y].ToVector4() * weight; |
|
|
|
|