diff --git a/src/ImageProcessorCore/Samplers/Processors/Matrix3x2Processor.cs b/src/ImageProcessorCore/Samplers/Processors/Matrix3x2Processor.cs
new file mode 100644
index 000000000..e9b0441e3
--- /dev/null
+++ b/src/ImageProcessorCore/Samplers/Processors/Matrix3x2Processor.cs
@@ -0,0 +1,47 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageProcessorCore.Processors
+{
+ using System.Numerics;
+
+ ///
+ /// Provides methods to transform an image using a .
+ ///
+ public abstract class Matrix3x2Processor : ImageSampler
+ {
+ ///
+ /// Creates a new target to contain the results of the matrix transform.
+ ///
+ /// Target image to apply the process to.
+ /// The source rectangle.
+ /// The processing matrix.
+ protected static void CreateNewTarget(ImageBase target, Rectangle sourceRectangle, Matrix3x2 processMatrix)
+ {
+ Matrix3x2 sizeMatrix;
+ if (Matrix3x2.Invert(processMatrix, out sizeMatrix))
+ {
+ Rectangle rectangle = ImageMaths.GetBoundingRectangle(sourceRectangle, sizeMatrix);
+ target.SetPixels(rectangle.Width, rectangle.Height, new float[rectangle.Width * rectangle.Height * 4]);
+ }
+ }
+
+ ///
+ /// Gets a transform matrix adjusted to center upon the target image bounds.
+ ///
+ /// Target image to apply the process to.
+ /// The source image.
+ /// The transform matrix.
+ ///
+ /// The .
+ ///
+ protected static Matrix3x2 GetCenteredMatrix(ImageBase target, ImageBase source, Matrix3x2 matrix)
+ {
+ Matrix3x2 translationToTargetCenter = Matrix3x2.CreateTranslation(-target.Width / 2f, -target.Height / 2f);
+ Matrix3x2 translateToSourceCenter = Matrix3x2.CreateTranslation(source.Width / 2f, source.Height / 2f);
+ return (translationToTargetCenter * matrix) * translateToSourceCenter;
+ }
+ }
+}
diff --git a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs
index 59d665d2e..d12bd3ec2 100644
--- a/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs
+++ b/src/ImageProcessorCore/Samplers/Processors/RotateProcessor.cs
@@ -11,85 +11,60 @@ namespace ImageProcessorCore.Processors
///
/// Provides methods that allow the rotating of images.
///
- public class RotateProcessor : ImageSampler
+ public class RotateProcessor : Matrix3x2Processor
{
+ ///
+ /// The tranform matrix to apply.
+ ///
+ private Matrix3x2 processMatrix;
+
///
public override int Parallelism { get; set; } = 1;
///
- /// Gets or sets the angle of rotation in degrees.
+ /// Gets or sets the angle of processMatrix in degrees.
///
public float Angle { get; set; }
- ///
- /// Gets or sets the center point.
- ///
- public Point Center { get; set; }
-
///
/// Gets or sets a value indicating whether to expand the canvas to fit the rotated image.
///
- public bool Expand { get; set; }
+ public bool Expand { get; set; } = true;
///
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
+ processMatrix = Point.CreateRotation(new Point(0, 0), -this.Angle);
if (this.Expand)
{
- Point centre = this.Center == Point.Empty ? Rectangle.Center(sourceRectangle) : this.Center;
- Matrix3x2 rotation = Point.CreateRotation(centre, -this.Angle);
- Matrix3x2 invertedRotation;
- Matrix3x2.Invert(rotation, out invertedRotation);
- Rectangle bounds = ImageMaths.GetBoundingRectangle(source.Bounds, invertedRotation);
- target.SetPixels(bounds.Width, bounds.Height, new float[bounds.Width * bounds.Height * 4]);
+ CreateNewTarget(target, sourceRectangle, processMatrix);
}
}
///
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
- int height = target.Height;
- int startX = 0;
- int endX = target.Width;
- Point centre = this.Center == Point.Empty ? Rectangle.Center(target.Bounds) : this.Center;
-
- //Matrix3x2 invertedRotation;
- Matrix3x2 rotation = Point.CreateRotation(centre, -this.Angle);
- //Matrix3x2.Invert(rotation, out invertedRotation);
- //Vector2 rightTop = Vector2.Transform(new Vector2(source.Width, 0), invertedRotation);
- //Vector2 leftBottom = Vector2.Transform(new Vector2(0, source.Height), invertedRotation);
-
- //if (this.Angle < 0)
- //{
- // rotation = Point.CreateRotation(new Point((int)-leftBottom.X, (int)leftBottom.Y), -this.Angle);
- //}
-
- //if (this.Angle > 0)
- //{
- // rotation = Point.CreateRotation(new Point((int)rightTop.X, (int)-rightTop.Y), -this.Angle);
- //}
+ Matrix3x2 matrix = GetCenteredMatrix(target, source, this.processMatrix);
- // Since we are not working in parallel we use full height and width
- // of the first pass image.
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
{
Parallel.For(
0,
- height,
+ target.Height,
y =>
+ {
+ for (int x = 0; x < target.Width; x++)
{
- for (int x = startX; x < endX; x++)
+ Point transformedPoint = Point.Rotate(new Point(x, y), matrix);
+ if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y))
{
- Point rotated = Point.Rotate(new Point(x, y), rotation);
- if (source.Bounds.Contains(rotated.X, rotated.Y))
- {
- targetPixels[x, y] = sourcePixels[rotated.X, rotated.Y];
- }
+ targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y];
}
+ }
- this.OnRowProcessed();
- });
+ OnRowProcessed();
+ });
}
}
}
diff --git a/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs
index 07760cf2a..43e54a1c7 100644
--- a/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs
+++ b/src/ImageProcessorCore/Samplers/Processors/SkewProcessor.cs
@@ -11,8 +11,13 @@ namespace ImageProcessorCore.Processors
///
/// Provides methods that allow the skewing of images.
///
- public class SkewProcessor : ImageSampler
+ public class SkewProcessor : Matrix3x2Processor
{
+ ///
+ /// The tranform matrix to apply.
+ ///
+ private Matrix3x2 processMatrix;
+
///
public override int Parallelism { get; set; } = 1;
@@ -26,77 +31,45 @@ namespace ImageProcessorCore.Processors
///
public float AngleY { get; set; }
- ///
- /// Gets or sets the center point.
- ///
- public Point Center { get; set; }
-
///
/// Gets or sets a value indicating whether to expand the canvas to fit the skewed image.
///
- public bool Expand { get; set; }
+ public bool Expand { get; set; } = true;
///
protected override void OnApply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
+ this.processMatrix = Point.CreateSkew(new Point(0, 0), -this.AngleX, -this.AngleY);
if (this.Expand)
{
- Point centre = this.Center;
- Matrix3x2 skew = Point.CreateSkew(centre, -this.AngleX, -this.AngleY);
- Matrix3x2 invertedSkew;
- Matrix3x2.Invert(skew, out invertedSkew);
- Rectangle bounds = ImageMaths.GetBoundingRectangle(source.Bounds, invertedSkew);
- target.SetPixels(bounds.Width, bounds.Height, new float[bounds.Width * bounds.Height * 4]);
+ CreateNewTarget(target, sourceRectangle, this.processMatrix);
}
}
///
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
- int height = target.Height;
- int startX = 0;
- int endX = target.Width;
- Point centre = this.Center;
-
- Matrix3x2 invertedSkew;
- Matrix3x2 skew = Point.CreateSkew(centre, -this.AngleX, -this.AngleY);
- Matrix3x2.Invert(skew, out invertedSkew);
- Vector2 rightTop = Vector2.Transform(new Vector2(source.Width, 0), invertedSkew);
- Vector2 leftBottom = Vector2.Transform(new Vector2(0, source.Height), invertedSkew);
-
- if (this.AngleX < 0 && this.AngleY > 0)
- {
- skew = Point.CreateSkew(new Point((int)-leftBottom.X, (int)leftBottom.Y), -this.AngleX, -this.AngleY);
- }
-
- if (this.AngleX > 0 && this.AngleY < 0)
- {
- skew = Point.CreateSkew(new Point((int)rightTop.X, (int)-rightTop.Y), -this.AngleX, -this.AngleY);
- }
-
- if (this.AngleX < 0 && this.AngleY < 0)
- {
- skew = Point.CreateSkew(new Point(target.Width - 1, target.Height - 1), -this.AngleX, -this.AngleY);
- }
+ Matrix3x2 matrix = GetCenteredMatrix(target, source, this.processMatrix);
using (PixelAccessor sourcePixels = source.Lock())
using (PixelAccessor targetPixels = target.Lock())
{
Parallel.For(
0,
- height,
+ target.Height,
y =>
- {
- for (int x = startX; x < endX; x++)
{
- Point skewed = Point.Skew(new Point(x, y), skew);
- if (source.Bounds.Contains(skewed.X, skewed.Y))
+ for (int x = 0; x < target.Width; x++)
{
- targetPixels[x, y] = sourcePixels[skewed.X, skewed.Y];
+ Point transformedPoint = Point.Skew(new Point(x, y), matrix);
+ if (source.Bounds.Contains(transformedPoint.X, transformedPoint.Y))
+ {
+ targetPixels[x, y] = sourcePixels[transformedPoint.X, transformedPoint.Y];
+ }
}
- }
- this.OnRowProcessed();
- });
+
+ OnRowProcessed();
+ });
}
}
}
diff --git a/src/ImageProcessorCore/Samplers/Rotate.cs b/src/ImageProcessorCore/Samplers/Rotate.cs
index ea24f9650..fa30d9d34 100644
--- a/src/ImageProcessorCore/Samplers/Rotate.cs
+++ b/src/ImageProcessorCore/Samplers/Rotate.cs
@@ -1,7 +1,7 @@
//
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
-// -------------------------------------------------------------------------------------------------------------------
+//
namespace ImageProcessorCore
{
@@ -21,21 +21,20 @@ namespace ImageProcessorCore
/// The
public static Image Rotate(this Image source, float degrees, ProgressEventHandler progressHandler = null)
{
- return Rotate(source, degrees, Point.Empty, true, progressHandler);
+ return Rotate(source, degrees, true, progressHandler);
}
///
- /// Rotates an image by the given angle in degrees around the given center point.
+ /// Rotates an image by the given angle in degrees.
///
/// The image to rotate.
/// The angle in degrees to perform the rotation.
- /// The center point at which to rotate the image.
/// Whether to expand the image to fit the rotated result.
/// A delegate which is called as progress is made processing the image.
/// The
- public static Image Rotate(this Image source, float degrees, Point center, bool expand, ProgressEventHandler progressHandler = null)
+ public static Image Rotate(this Image source, float degrees, bool expand, ProgressEventHandler progressHandler = null)
{
- RotateProcessor processor = new RotateProcessor { Angle = degrees, Center = center, Expand = expand };
+ RotateProcessor processor = new RotateProcessor { Angle = degrees, Expand = expand };
processor.OnProgress += progressHandler;
try
diff --git a/src/ImageProcessorCore/Samplers/Skew.cs b/src/ImageProcessorCore/Samplers/Skew.cs
index 7b0587a52..904f1d89d 100644
--- a/src/ImageProcessorCore/Samplers/Skew.cs
+++ b/src/ImageProcessorCore/Samplers/Skew.cs
@@ -1,7 +1,7 @@
//
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
-// -------------------------------------------------------------------------------------------------------------------
+//
namespace ImageProcessorCore
{
@@ -22,22 +22,21 @@ namespace ImageProcessorCore
/// The
public static Image Skew(this Image source, float degreesX, float degreesY, ProgressEventHandler progressHandler = null)
{
- return Skew(source, degreesX, degreesY, Point.Empty, true, progressHandler);
+ return Skew(source, degreesX, degreesY, true, progressHandler);
}
///
- /// Skews an image by the given angles in degrees around the given center point.
+ /// Skews an image by the given angles in degrees.
///
/// The image to skew.
/// The angle in degrees to perform the rotation along the x-axis.
/// The angle in degrees to perform the rotation along the y-axis.
- /// The center point at which to skew the image.
/// Whether to expand the image to fit the skewed result.
/// A delegate which is called as progress is made processing the image.
/// The
- public static Image Skew(this Image source, float degreesX, float degreesY, Point center, bool expand, ProgressEventHandler progressHandler = null)
+ public static Image Skew(this Image source, float degreesX, float degreesY, bool expand, ProgressEventHandler progressHandler = null)
{
- SkewProcessor processor = new SkewProcessor { AngleX = degreesX, AngleY = degreesY, Center = center, Expand = expand };
+ SkewProcessor processor = new SkewProcessor { AngleX = degreesX, AngleY = degreesY, Expand = expand };
processor.OnProgress += progressHandler;
try
diff --git a/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs b/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs
index 8c12e14f1..e81607b58 100644
--- a/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs
+++ b/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs
@@ -429,7 +429,7 @@ namespace ImageProcessorCore.Tests
Image image = new Image(stream);
using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}"))
{
- image.Rotate(63, this.ProgressUpdate)
+ image.Rotate(-170, this.ProgressUpdate)
.Save(output);
}
}
@@ -454,7 +454,7 @@ namespace ImageProcessorCore.Tests
Image image = new Image(stream);
using (FileStream output = File.OpenWrite($"TestOutput/Skew/{filename}"))
{
- image.Skew(-15, 12, this.ProgressUpdate)
+ image.Skew(-20, -10, this.ProgressUpdate)
.Save(output);
}
}