diff --git a/src/ImageProcessor/Common/Helpers/ImageMaths.cs b/src/ImageProcessor/Common/Helpers/ImageMaths.cs
index 0c748ebf1..fc4c42040 100644
--- a/src/ImageProcessor/Common/Helpers/ImageMaths.cs
+++ b/src/ImageProcessor/Common/Helpers/ImageMaths.cs
@@ -95,6 +95,44 @@ namespace ImageProcessor
return 1.0f;
}
+ ///
+ /// Returns the given degrees converted to radians.
+ ///
+ ///
+ /// The angle in degrees.
+ ///
+ ///
+ /// The representing the degree as radians.
+ ///
+ public static double DegreesToRadians(double angleInDegrees)
+ {
+ return angleInDegrees * (PI / 180);
+ }
+
+ ///
+ /// Rotates one point around another
+ ///
+ ///
+ /// The point to rotate.
+ /// The rotation angle in degrees.
+ /// The centre point of rotation. If not set the point will equal
+ ///
+ ///
+ /// Rotated point
+ public static Point RotatePoint(Point pointToRotate, double angleInDegrees, Point? centerPoint = null)
+ {
+ Point center = centerPoint ?? Point.Empty;
+
+ double angleInRadians = DegreesToRadians(angleInDegrees);
+ double cosTheta = Math.Cos(angleInRadians);
+ double sinTheta = Math.Sin(angleInRadians);
+ return new Point
+ {
+ X = (int)((cosTheta * (pointToRotate.X - center.X)) - (sinTheta * (pointToRotate.Y - center.Y)) + center.X),
+ Y = (int)((sinTheta * (pointToRotate.X - center.X)) + (cosTheta * (pointToRotate.Y - center.Y)) + center.Y)
+ };
+ }
+
///
/// Ensures that any passed double is correctly rounded to zero
///
diff --git a/src/ImageProcessor/Numerics/Rectangle.cs b/src/ImageProcessor/Numerics/Rectangle.cs
index 948b233af..2be098b51 100644
--- a/src/ImageProcessor/Numerics/Rectangle.cs
+++ b/src/ImageProcessor/Numerics/Rectangle.cs
@@ -155,6 +155,16 @@ namespace ImageProcessor
&& y < this.Y + this.Height;
}
+ ///
+ /// Returns the center point of the given
+ ///
+ /// The rectangle
+ ///
+ public static Point Center(Rectangle rectangle)
+ {
+ return new Point(rectangle.Left + rectangle.Width / 2, rectangle.Top + rectangle.Height / 2);
+ }
+
///
/// Indicates whether this instance and a specified object are equal.
///
diff --git a/src/ImageProcessor/Samplers/ImageSampleExtensions.cs b/src/ImageProcessor/Samplers/ImageSampleExtensions.cs
index eba1ff3fb..a73eb2b2c 100644
--- a/src/ImageProcessor/Samplers/ImageSampleExtensions.cs
+++ b/src/ImageProcessor/Samplers/ImageSampleExtensions.cs
@@ -49,7 +49,7 @@ namespace ImageProcessor.Samplers
/// The
public static Image Resize(this Image source, int width, int height, IResampler sampler, Rectangle sourceRectangle)
{
- return source.Process(width, height, sourceRectangle, new Rectangle(0, 0, width, height), new Resize(sampler));
+ return source.Process(width, height, sourceRectangle, new Rectangle(0, 0, width, height), new Resampler(sampler));
}
///
diff --git a/src/ImageProcessor/Samplers/Resize.cs b/src/ImageProcessor/Samplers/Resampler.cs
similarity index 71%
rename from src/ImageProcessor/Samplers/Resize.cs
rename to src/ImageProcessor/Samplers/Resampler.cs
index 6ea106724..ae3c00950 100644
--- a/src/ImageProcessor/Samplers/Resize.cs
+++ b/src/ImageProcessor/Samplers/Resampler.cs
@@ -1,4 +1,4 @@
-//
+//
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
//
@@ -10,14 +10,19 @@ namespace ImageProcessor.Samplers
using System.Threading.Tasks;
///
- /// Provides methods that allow the resizing of images using various resampling algorithms.
+ /// Provides methods that allow the resampling of images using various algorithms.
///
- public class Resize : ParallelImageProcessor
+ public class Resampler : ParallelImageProcessor
{
///
/// The epsilon for comparing floating point numbers.
///
- private const float Epsilon = 0.01f;
+ private const float Epsilon = 0.0001f;
+
+ ///
+ /// The angle of rotation.
+ ///
+ private double angle;
///
/// The horizontal weights.
@@ -30,12 +35,12 @@ namespace ImageProcessor.Samplers
private Weights[] verticalWeights;
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
///
/// The sampler to perform the resize operation.
///
- public Resize(IResampler sampler)
+ public Resampler(IResampler sampler)
{
Guard.NotNull(sampler, nameof(sampler));
@@ -47,6 +52,32 @@ namespace ImageProcessor.Samplers
///
public IResampler Sampler { get; }
+ ///
+ /// Gets or sets the angle of rotation.
+ ///
+ public double Angle
+ {
+ get
+ {
+ return this.angle;
+ }
+
+ set
+ {
+ if (value > 360)
+ {
+ value -= 360;
+ }
+
+ if (value < 0)
+ {
+ value += 360;
+ }
+
+ this.angle = value;
+ }
+ }
+
///
protected override void OnApply(ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle)
{
@@ -61,6 +92,8 @@ namespace ImageProcessor.Samplers
int targetBottom = targetRectangle.Bottom;
int startX = targetRectangle.X;
int endX = targetRectangle.Right;
+ Point centre = Rectangle.Center(sourceRectangle);
+ bool rotate = this.angle > 0 && this.angle < 360;
Parallel.For(
startY,
@@ -82,28 +115,38 @@ namespace ImageProcessor.Samplers
foreach (Weight yw in verticalValues)
{
- if (Math.Abs(yw.Value) < Epsilon)
- {
- continue;
- }
-
int originY = yw.Index;
foreach (Weight xw in horizontalValues)
{
- if (Math.Abs(xw.Value) < Epsilon)
- {
- continue;
- }
-
int originX = xw.Index;
- Color sourceColor = Color.InverseCompand(source[originX, originY]);
- if (Math.Abs(sourceColor.A) < Epsilon)
+ Color sourceColor;
+
+ float weight;
+
+ if (rotate)
{
- continue;
+ // Rotating at the centre point
+ Point rotated = ImageMaths.RotatePoint(new Point(originX, originY), this.angle, centre);
+ originX = rotated.X;
+ originY = rotated.Y;
+
+ // TODO: This can't work. We're not normalising properly since weights are skipped.
+ // Also... This is so slow!
+ if (sourceRectangle.Contains(originX, originY))
+ {
+ sourceColor = Color.InverseCompand(source[originX, originY]);
+ weight = (yw.Value / verticalSum) * (xw.Value / horizontalSum);
+
+ destination.R += sourceColor.R * weight;
+ destination.G += sourceColor.G * weight;
+ destination.B += sourceColor.B * weight;
+ destination.A += sourceColor.A * weight;
+ }
}
- float weight = (yw.Value / verticalSum) * (xw.Value / horizontalSum);
+ sourceColor = Color.InverseCompand(source[originX, originY]);
+ weight = (yw.Value / verticalSum) * (xw.Value / horizontalSum);
destination.R += sourceColor.R * weight;
destination.G += sourceColor.G * weight;
@@ -112,9 +155,12 @@ namespace ImageProcessor.Samplers
}
}
+ destination = Color.Compand(destination);
+
// Ensure are alpha values only reflect possible values to prevent bleed.
destination.A = (float)Math.Round(destination.A, 2);
- target[x, y] = Color.Compand(destination);
+
+ target[x, y] = destination;
}
}
});
@@ -176,6 +222,7 @@ namespace ImageProcessor.Samplers
builder.Add(new Weight(a, w));
}
}
+
result[i].Values = builder.ToImmutable();
result[i].Sum = sum;
});
diff --git a/src/ImageProcessor/project.json b/src/ImageProcessor/project.json
index dc11613af..57178e751 100644
--- a/src/ImageProcessor/project.json
+++ b/src/ImageProcessor/project.json
@@ -19,11 +19,11 @@
"System.Runtime.Extensions": "4.0.10",
"System.Reflection": "4.0.10",
"System.IO": "4.0.10",
- "StyleCop.Analyzers": "1.0.0-beta016",
"Microsoft.NETCore": "5.0.1-beta-23409",
- "Microsoft.NETCore.Platforms": "1.0.1-beta-23409"
+ "Microsoft.NETCore.Platforms": "1.0.1-beta-23409",
+ "StyleCop.Analyzers": "1.0.0-beta016"
},
"frameworks": {
- "dotnet": {}
+ "dotnet": { }
}
}
\ No newline at end of file
diff --git a/src/ImageProcessor/project.lock.json.REMOVED.git-id b/src/ImageProcessor/project.lock.json.REMOVED.git-id
index 8fc61db8b..365950bb8 100644
--- a/src/ImageProcessor/project.lock.json.REMOVED.git-id
+++ b/src/ImageProcessor/project.lock.json.REMOVED.git-id
@@ -1 +1 @@
-0b32043447c786c20468c00c3c0924348b389517
\ No newline at end of file
+e9c58eb8b9733d48d157d8bf0852daae4f60dab0
\ No newline at end of file
diff --git a/tests/ImageProcessor.Tests/project.lock.json.REMOVED.git-id b/tests/ImageProcessor.Tests/project.lock.json.REMOVED.git-id
index e7c0cc81c..a5a60bda7 100644
--- a/tests/ImageProcessor.Tests/project.lock.json.REMOVED.git-id
+++ b/tests/ImageProcessor.Tests/project.lock.json.REMOVED.git-id
@@ -1 +1 @@
-9ead0f07f8529db4e80eda95477601f6d3eb9583
\ No newline at end of file
+11091fe7d5fc9cff9aec6ef60bf1c47e0e01d066
\ No newline at end of file