Browse Source

Original maths was totally incorrect

This, however, is no better if not, worse.
af/merge-core
James Jackson-South 9 years ago
parent
commit
5201a2238c
  1. 2
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  2. 93
      src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs
  3. 33
      src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs
  4. 2
      src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs

2
src/ImageSharp/Common/Helpers/ImageMaths.cs

@ -157,7 +157,7 @@ namespace SixLabors.ImageSharp
Vector2[] allCorners = { leftTop, rightTop, leftBottom, rightBottom };
float extentX = allCorners.Select(v => v.X).Max() - allCorners.Select(v => v.X).Min();
float extentY = allCorners.Select(v => v.Y).Max() - allCorners.Select(v => v.Y).Min();
return new Rectangle(0, 0, (int)extentX, (int)extentY);
return new Rectangle(0, 0, (int)MathF.Ceiling(extentX), (int)MathF.Ceiling(extentY));
}
/// <summary>

93
src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs

@ -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;

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

@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public float Angle { get; set; }
/// <inheritdoc/>
protected override Matrix3x2 CreateProcessingMatrix()
protected override Matrix3x2 CreateProcessingMatrix(Rectangle rectangle)
{
if (this.transformMatrix == default(Matrix3x2))
{
@ -54,25 +54,6 @@ namespace SixLabors.ImageSharp.Processing.Processors
return this.transformMatrix;
}
/// <inheritdoc/>
protected override void CreateNewCanvas(Rectangle sourceRectangle)
{
if (MathF.Abs(this.Angle) < Constants.Epsilon ||
MathF.Abs(this.Angle - 180) < Constants.Epsilon)
{
this.ResizeRectangle = sourceRectangle;
}
if (MathF.Abs(this.Angle - 90) < Constants.Epsilon ||
MathF.Abs(this.Angle - 270) < Constants.Epsilon)
{
// We always expand enumerated rectangle values
this.ResizeRectangle = new Rectangle(0, 0, sourceRectangle.Height, sourceRectangle.Width);
}
base.CreateNewCanvas(sourceRectangle);
}
/// <inheritdoc/>
protected override void OnApply(ImageFrame<TPixel> source, ImageFrame<TPixel> destination, Rectangle sourceRectangle, Configuration configuration)
{
@ -157,6 +138,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
int width = source.Width;
int height = source.Height;
Rectangle destinationBounds = destination.Bounds();
Parallel.For(
0,
@ -171,7 +153,10 @@ namespace SixLabors.ImageSharp.Processing.Processors
newX = height - newX - 1;
int newY = width - x - 1;
destination[newX, newY] = sourceRow[x];
if (destinationBounds.Contains(newX, newY))
{
destination[newX, newY] = sourceRow[x];
}
}
});
}
@ -213,6 +198,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
{
int width = source.Width;
int height = source.Height;
Rectangle destinationBounds = destination.Bounds();
Parallel.For(
0,
@ -224,7 +210,10 @@ namespace SixLabors.ImageSharp.Processing.Processors
int newX = height - y - 1;
for (int x = 0; x < width; x++)
{
destination[newX, x] = sourceRow[x];
if (destinationBounds.Contains(newX, x))
{
destination[newX, x] = sourceRow[x];
}
}
});
}

2
src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs

@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Processors
public float AngleY { get; set; }
/// <inheritdoc/>
protected override Matrix3x2 CreateProcessingMatrix()
protected override Matrix3x2 CreateProcessingMatrix(Rectangle rectangle)
{
if (this.transformMatrix == default(Matrix3x2))
{

Loading…
Cancel
Save