Browse Source

Move Rotate and Skew

af/merge-core
James Jackson-South 9 years ago
parent
commit
8ea6ad70ab
  1. 25
      src/ImageSharp/Samplers/Processors/Transforms/Matrix3x2Processor.cs
  2. 62
      src/ImageSharp/Samplers/Processors/Transforms/RotateProcessor.cs
  3. 17
      src/ImageSharp/Samplers/Processors/Transforms/SkewProcessor.cs
  4. 2
      src/ImageSharp/Samplers/Transforms/Rotate.cs
  5. 2
      src/ImageSharp/Samplers/Transforms/Skew.cs
  6. 0
      tests/ImageSharp.Tests/Processors/Filters/RotateTest.cs
  7. 0
      tests/ImageSharp.Tests/Processors/Filters/SkewTest.cs

25
src/ImageSharp/Samplers/Processors/Transforms/Matrix3x2Processor.cs

@ -12,38 +12,39 @@ namespace ImageSharp.Processors
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
/// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam> /// <typeparam name="TPacked">The packed format. <example>uint, long, float.</example></typeparam>
public abstract class Matrix3x2Processor<TColor, TPacked> : ImageSamplingProcessor<TColor, TPacked> public abstract class Matrix3x2Processor<TColor, TPacked> : ImageFilteringProcessor<TColor, TPacked>
where TColor : struct, IPackedPixel<TPacked> where TColor : struct, IPackedPixel<TPacked>
where TPacked : struct where TPacked : struct
{ {
/// <summary> /// <summary>
/// Creates a new target to contain the results of the matrix transform. /// Gets the rectangle designating the target canvas.
/// </summary>
protected Rectangle CanvasRectangle { get; private set; }
/// <summary>
/// Creates a new target canvas to contain the results of the matrix transform.
/// </summary> /// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="sourceRectangle">The source rectangle.</param> /// <param name="sourceRectangle">The source rectangle.</param>
/// <param name="processMatrix">The processing matrix.</param> /// <param name="processMatrix">The processing matrix.</param>
protected static void CreateNewTarget(ImageBase<TColor, TPacked> target, Rectangle sourceRectangle, Matrix3x2 processMatrix) protected void CreateNewCanvas(Rectangle sourceRectangle, Matrix3x2 processMatrix)
{ {
Matrix3x2 sizeMatrix; Matrix3x2 sizeMatrix;
if (Matrix3x2.Invert(processMatrix, out sizeMatrix)) this.CanvasRectangle = Matrix3x2.Invert(processMatrix, out sizeMatrix)
{ ? ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix)
Rectangle rectangle = ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix); : sourceRectangle;
target.SetPixels(rectangle.Width, rectangle.Height, new TColor[rectangle.Width * rectangle.Height]);
}
} }
/// <summary> /// <summary>
/// Gets a transform matrix adjusted to center upon the target image bounds. /// Gets a transform matrix adjusted to center upon the target image bounds.
/// </summary> /// </summary>
/// <param name="target">Target image to apply the process to.</param>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <param name="matrix">The transform matrix.</param> /// <param name="matrix">The transform matrix.</param>
/// <returns> /// <returns>
/// The <see cref="Matrix3x2"/>. /// The <see cref="Matrix3x2"/>.
/// </returns> /// </returns>
protected static Matrix3x2 GetCenteredMatrix(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Matrix3x2 matrix) protected Matrix3x2 GetCenteredMatrix(ImageBase<TColor, TPacked> source, Matrix3x2 matrix)
{ {
Matrix3x2 translationToTargetCenter = Matrix3x2.CreateTranslation(-target.Width * .5F, -target.Height * .5F); Matrix3x2 translationToTargetCenter = Matrix3x2.CreateTranslation(-this.CanvasRectangle.Width * .5F, -this.CanvasRectangle.Height * .5F);
Matrix3x2 translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width * .5F, source.Height * .5F); Matrix3x2 translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width * .5F, source.Height * .5F);
return (translationToTargetCenter * matrix) * translateToSourceCenter; return (translationToTargetCenter * matrix) * translateToSourceCenter;
} }

62
src/ImageSharp/Samplers/Processors/Transforms/RotateProcessor.cs

@ -19,7 +19,7 @@ namespace ImageSharp.Processors
where TPacked : struct where TPacked : struct
{ {
/// <summary> /// <summary>
/// The tranform matrix to apply. /// The transform matrix to apply.
/// </summary> /// </summary>
private Matrix3x2 processMatrix; private Matrix3x2 processMatrix;
@ -34,19 +34,20 @@ namespace ImageSharp.Processors
public bool Expand { get; set; } = true; public bool Expand { get; set; } = true;
/// <inheritdoc/> /// <inheritdoc/>
public override void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle, int startY, int endY)
{ {
if (this.OptimizedApply(target, source)) if (this.OptimizedApply(source))
{ {
return; return;
} }
int height = target.Height; int height = this.CanvasRectangle.Height;
int width = target.Width; int width = this.CanvasRectangle.Width;
Matrix3x2 matrix = GetCenteredMatrix(target, source, this.processMatrix); Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix);
TColor[] target = new TColor[width * height];
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock()) using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock()) using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock<TColor, TPacked>(width, height))
{ {
Parallel.For( Parallel.For(
0, 0,
@ -64,10 +65,12 @@ namespace ImageSharp.Processors
} }
}); });
} }
source.SetPixels(width, height, target);
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle) protected override void OnApply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle)
{ {
const float Epsilon = .0001F; const float Epsilon = .0001F;
@ -79,40 +82,39 @@ namespace ImageSharp.Processors
this.processMatrix = Point.CreateRotation(new Point(0, 0), -this.Angle); this.processMatrix = Point.CreateRotation(new Point(0, 0), -this.Angle);
if (this.Expand) if (this.Expand)
{ {
CreateNewTarget(target, sourceRectangle, this.processMatrix); this.CreateNewCanvas(sourceRectangle, this.processMatrix);
} }
} }
/// <summary> /// <summary>
/// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees. /// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees.
/// </summary> /// </summary>
/// <param name="target">The target image.</param>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
/// <returns>The <see cref="bool"/></returns> /// <returns>The <see cref="bool"/></returns>
private bool OptimizedApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source) private bool OptimizedApply(ImageBase<TColor, TPacked> source)
{ {
const float Epsilon = .0001F; const float Epsilon = .0001F;
if (Math.Abs(this.Angle) < Epsilon) if (Math.Abs(this.Angle) < Epsilon)
{ {
target.ClonePixels(target.Width, target.Height, source.Pixels); // No need to do anything so return.
return true; return true;
} }
if (Math.Abs(this.Angle - 90) < Epsilon) if (Math.Abs(this.Angle - 90) < Epsilon)
{ {
this.Rotate90(target, source); this.Rotate90(source);
return true; return true;
} }
if (Math.Abs(this.Angle - 180) < Epsilon) if (Math.Abs(this.Angle - 180) < Epsilon)
{ {
this.Rotate180(target, source); this.Rotate180(source);
return true; return true;
} }
if (Math.Abs(this.Angle - 270) < Epsilon) if (Math.Abs(this.Angle - 270) < Epsilon)
{ {
this.Rotate270(target, source); this.Rotate270(source);
return true; return true;
} }
@ -122,16 +124,15 @@ namespace ImageSharp.Processors
/// <summary> /// <summary>
/// Rotates the image 270 degrees clockwise at the centre point. /// Rotates the image 270 degrees clockwise at the centre point.
/// </summary> /// </summary>
/// <param name="target">The target image.</param>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
private void Rotate270(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source) private void Rotate270(ImageBase<TColor, TPacked> source)
{ {
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
Image<TColor, TPacked> temp = new Image<TColor, TPacked>(height, width); TColor[] target = new TColor[width * height];
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock()) using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> tempPixels = temp.Lock()) using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock<TColor, TPacked>(height, width))
{ {
Parallel.For( Parallel.For(
0, 0,
@ -144,26 +145,26 @@ namespace ImageSharp.Processors
int newX = height - y - 1; int newX = height - y - 1;
newX = height - newX - 1; newX = height - newX - 1;
int newY = width - x - 1; int newY = width - x - 1;
tempPixels[newX, newY] = sourcePixels[x, y]; targetPixels[newX, newY] = sourcePixels[x, y];
} }
}); });
} }
target.SetPixels(height, width, temp.Pixels); source.SetPixels(height, width, target);
} }
/// <summary> /// <summary>
/// Rotates the image 180 degrees clockwise at the centre point. /// Rotates the image 180 degrees clockwise at the centre point.
/// </summary> /// </summary>
/// <param name="target">The target image.</param>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
private void Rotate180(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source) private void Rotate180(ImageBase<TColor, TPacked> source)
{ {
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
TColor[] target = new TColor[width * height];
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock()) using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock()) using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock<TColor, TPacked>(width, height))
{ {
Parallel.For( Parallel.For(
0, 0,
@ -179,21 +180,22 @@ namespace ImageSharp.Processors
} }
}); });
} }
source.SetPixels(width, height, target);
} }
/// <summary> /// <summary>
/// Rotates the image 90 degrees clockwise at the centre point. /// Rotates the image 90 degrees clockwise at the centre point.
/// </summary> /// </summary>
/// <param name="target">The target image.</param>
/// <param name="source">The source image.</param> /// <param name="source">The source image.</param>
private void Rotate90(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source) private void Rotate90(ImageBase<TColor, TPacked> source)
{ {
int width = source.Width; int width = source.Width;
int height = source.Height; int height = source.Height;
Image<TColor, TPacked> temp = new Image<TColor, TPacked>(height, width); TColor[] target = new TColor[width * height];
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock()) using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> tempPixels = temp.Lock()) using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock<TColor, TPacked>(height, width))
{ {
Parallel.For( Parallel.For(
0, 0,
@ -204,12 +206,12 @@ namespace ImageSharp.Processors
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
int newX = height - y - 1; int newX = height - y - 1;
tempPixels[newX, x] = sourcePixels[x, y]; targetPixels[newX, x] = sourcePixels[x, y];
} }
}); });
} }
target.SetPixels(height, width, temp.Pixels); source.SetPixels(height, width, target);
} }
} }
} }

17
src/ImageSharp/Samplers/Processors/Transforms/SkewProcessor.cs

@ -39,14 +39,15 @@ namespace ImageSharp.Processors
public bool Expand { get; set; } = true; public bool Expand { get; set; } = true;
/// <inheritdoc/> /// <inheritdoc/>
public override void Apply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY) protected override void Apply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle, int startY, int endY)
{ {
int height = target.Height; int height = this.CanvasRectangle.Height;
int width = target.Width; int width = this.CanvasRectangle.Width;
Matrix3x2 matrix = GetCenteredMatrix(target, source, this.processMatrix); Matrix3x2 matrix = this.GetCenteredMatrix(source, this.processMatrix);
TColor[] target = new TColor[width * height];
using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock()) using (PixelAccessor<TColor, TPacked> sourcePixels = source.Lock())
using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock()) using (PixelAccessor<TColor, TPacked> targetPixels = target.Lock<TColor, TPacked>(width, height))
{ {
Parallel.For( Parallel.For(
0, 0,
@ -64,15 +65,17 @@ namespace ImageSharp.Processors
} }
}); });
} }
source.SetPixels(width, height, target);
} }
/// <inheritdoc/> /// <inheritdoc/>
protected override void OnApply(ImageBase<TColor, TPacked> target, ImageBase<TColor, TPacked> source, Rectangle targetRectangle, Rectangle sourceRectangle) protected override void OnApply(ImageBase<TColor, TPacked> source, Rectangle sourceRectangle)
{ {
this.processMatrix = Point.CreateSkew(new Point(0, 0), -this.AngleX, -this.AngleY); this.processMatrix = Point.CreateSkew(new Point(0, 0), -this.AngleX, -this.AngleY);
if (this.Expand) if (this.Expand)
{ {
CreateNewTarget(target, sourceRectangle, this.processMatrix); this.CreateNewCanvas(sourceRectangle, this.processMatrix);
} }
} }
} }

2
src/ImageSharp/Samplers/Transforms/Rotate.cs

@ -56,7 +56,7 @@ namespace ImageSharp
where TPacked : struct where TPacked : struct
{ {
RotateProcessor<TColor, TPacked> processor = new RotateProcessor<TColor, TPacked> { Angle = degrees, Expand = expand }; RotateProcessor<TColor, TPacked> processor = new RotateProcessor<TColor, TPacked> { Angle = degrees, Expand = expand };
return source.Process(source.Width, source.Height, source.Bounds, source.Bounds, processor); return source.Process(source.Bounds, processor);
} }
} }
} }

2
src/ImageSharp/Samplers/Transforms/Skew.cs

@ -43,7 +43,7 @@ namespace ImageSharp
where TPacked : struct where TPacked : struct
{ {
SkewProcessor<TColor, TPacked> processor = new SkewProcessor<TColor, TPacked> { AngleX = degreesX, AngleY = degreesY, Expand = expand }; SkewProcessor<TColor, TPacked> processor = new SkewProcessor<TColor, TPacked> { AngleX = degreesX, AngleY = degreesY, Expand = expand };
return source.Process(source.Width, source.Height, source.Bounds, source.Bounds, processor); return source.Process(source.Bounds, processor);
} }
} }
} }

0
tests/ImageSharp.Tests/Processors/Samplers/RotateTest.cs → tests/ImageSharp.Tests/Processors/Filters/RotateTest.cs

0
tests/ImageSharp.Tests/Processors/Samplers/SkewTest.cs → tests/ImageSharp.Tests/Processors/Filters/SkewTest.cs

Loading…
Cancel
Save