diff --git a/src/ImageProcessorCore/Samplers/ImageSamplerExtensions.cs b/src/ImageProcessorCore/Samplers/ImageSamplerExtensions.cs
index 542050ecd..d741ccfd3 100644
--- a/src/ImageProcessorCore/Samplers/ImageSamplerExtensions.cs
+++ b/src/ImageProcessorCore/Samplers/ImageSamplerExtensions.cs
@@ -176,34 +176,7 @@ namespace ImageProcessorCore.Samplers
/// The
public static Image Rotate(this Image source, float degrees, ProgressEventHandler progressHandler = null)
{
- return Rotate(source, degrees, new BicubicResampler(), false, progressHandler);
- }
-
- ///
- /// Rotates an image by the given angle in degrees.
- ///
- /// The image to resize.
- /// The angle in degrees to perform the rotation.
- /// Whether to compress and expand the image color-space to gamma correct the image during processing.
- /// A delegate which is called as progress is made processing the image.
- /// The
- public static Image Rotate(this Image source, float degrees, bool compand, ProgressEventHandler progressHandler = null)
- {
- return Rotate(source, degrees, new BicubicResampler(), compand, progressHandler);
- }
-
- ///
- /// Rotates an image by the given angle in degrees.
- ///
- /// The image to resize.
- /// The angle in degrees to perform the rotation.
- /// The to perform the resampling.
- /// Whether to compress and expand the image color-space to gamma correct the image during processing.
- /// A delegate which is called as progress is made processing the image.
- /// The
- public static Image Rotate(this Image source, float degrees, IResampler sampler, bool compand, ProgressEventHandler progressHandler = null)
- {
- Rotate processor = new Rotate(sampler) { Angle = degrees, Compand = compand };
+ Rotate processor = new Rotate { Angle = degrees };
processor.OnProgress += progressHandler;
try
diff --git a/src/ImageProcessorCore/Samplers/Resampler.cs b/src/ImageProcessorCore/Samplers/Resampler.cs
index 0554b18e0..368e03193 100644
--- a/src/ImageProcessorCore/Samplers/Resampler.cs
+++ b/src/ImageProcessorCore/Samplers/Resampler.cs
@@ -6,8 +6,6 @@
namespace ImageProcessorCore.Samplers
{
using System;
- using System.Collections.Generic;
- using System.Threading.Tasks;
///
/// Provides methods that allow the resampling of images using various algorithms.
@@ -54,58 +52,57 @@ namespace ImageProcessorCore.Samplers
///
protected Weights[] PrecomputeWeights(int destinationSize, int sourceSize)
{
- float xscale = destinationSize / (float)sourceSize;
- float width;
+ float scale = (float)destinationSize / sourceSize;
IResampler sampler = this.Sampler;
- float fwidth = sampler.Radius;
- float fscale;
+ float radius = sampler.Radius;
double left;
double right;
- double weight = 0;
- int n = 0;
- int k;
+ double weight;
+ int index;
+ int sum;
Weights[] result = new Weights[destinationSize];
- // When expanding, broaden the effective kernel support so that we still
+ // When shrinking, broaden the effective kernel support so that we still
// visit every source pixel.
- if (xscale < 0)
+ if (scale < 1)
{
- width = sampler.Radius / xscale;
- fscale = 1 / xscale;
+ float width = radius / scale;
+ float filterScale = 1 / scale;
// Make the weights slices, one source for each column or row.
for (int i = 0; i < destinationSize; i++)
{
- float centre = i / xscale;
+ float centre = i / scale;
left = Math.Ceiling(centre - width);
right = Math.Floor(centre + width);
- float sum = 0;
- result[i] = new Weights();
- List builder = new List();
+
+ result[i] = new Weights
+ {
+ Sum = 0,
+ Values = new Weight[(int)Math.Floor(2 * width + 1)]
+ };
+
for (double j = left; j <= right; j++)
{
weight = centre - j;
- weight = sampler.GetValue((float)weight / fscale) / fscale;
+ weight = sampler.GetValue((float)(weight / filterScale)) / filterScale;
if (j < 0)
{
- n = (int)-j;
+ index = (int)-j;
}
else if (j >= sourceSize)
{
- n = (int)((sourceSize - j) + sourceSize - 1);
+ index = (int)((sourceSize - j) + sourceSize - 1);
}
else
{
- n = (int)j;
+ index = (int)j;
}
- sum++;
- builder.Add(new Weight(n, (float)weight));
+ sum = (int)result[i].Sum++;
+ result[i].Values[sum] = new Weight(index, (float)weight);
}
-
- result[i].Values = builder.ToArray();
- result[i].Sum = sum;
}
}
else
@@ -113,122 +110,46 @@ namespace ImageProcessorCore.Samplers
// Make the weights slices, one source for each column or row.
for (int i = 0; i < destinationSize; i++)
{
- float centre = i / xscale;
- left = Math.Ceiling(centre - fwidth);
- right = Math.Floor(centre + fwidth);
- float sum = 0;
- result[i] = new Weights();
+ float centre = i / scale;
+ left = Math.Ceiling(centre - radius);
+ right = Math.Floor(centre + radius);
+ result[i] = new Weights
+ {
+ Sum = 0,
+ Values = new Weight[(int)(radius * 2 + 1)]
+ };
- List builder = new List();
for (double j = left; j <= right; j++)
{
weight = centre - j;
weight = sampler.GetValue((float)weight);
if (j < 0)
{
- n = (int)-j;
+ index = (int)-j;
}
else if (j >= sourceSize)
{
- n = (int)((sourceSize - j) + sourceSize - 1);
+ index = (int)((sourceSize - j) + sourceSize - 1);
}
else
{
- n = (int)j;
+ index = (int)j;
}
- sum++;
- builder.Add(new Weight(n, (float)weight));
+ sum = (int)result[i].Sum++;
+ result[i].Values[sum] = new Weight(index, (float)weight);
}
-
- result[i].Values = builder.ToArray();
- result[i].Sum = sum;
}
}
return result;
}
- //protected Weights[] PrecomputeWeights(int destinationSize, int sourceSize)
- //{
- // IResampler sampler = this.Sampler;
- // float ratio = sourceSize / (float)destinationSize;
- // float scale = ratio;
-
- // // When shrinking, broaden the effective kernel support so that we still
- // // visit every source pixel.
- // if (scale < 1)
- // {
- // scale = 1;
- // }
-
- // float scaledRadius = (float)Math.Ceiling(scale * sampler.Radius);
- // Weights[] result = new Weights[destinationSize];
-
- // // Make the weights slices, one source for each column or row.
- // Parallel.For(
- // 0,
- // destinationSize,
- // i =>
- // {
- // float center = ((i + .5f) * ratio) - 0.5f;
- // int start = (int)Math.Ceiling(center - scaledRadius);
-
- // if (start < 0)
- // {
- // start = 0;
- // }
-
- // int end = (int)Math.Floor(center + scaledRadius);
-
- // if (end > sourceSize)
- // {
- // end = sourceSize;
-
- // if (end < start)
- // {
- // end = start;
- // }
- // }
-
- // float sum = 0;
- // result[i] = new Weights();
-
- // List builder = new List();
- // for (int a = start; a < end; a++)
- // {
- // float w = sampler.GetValue((a - center) / scale);
-
- // if (w < 0 || w > 0)
- // {
- // sum += w;
- // builder.Add(new Weight(a, w));
- // }
- // }
-
- // // Normalise the values
- // if (sum > 0 || sum < 0)
- // {
- // builder.ForEach(w => w.Value /= sum);
- // }
-
- // result[i].Values = builder.ToArray();
- // result[i].Sum = sum;
- // });
-
- // return result;
- //}
-
///
/// Represents the weight to be added to a scaled pixel.
///
- protected class Weight
+ protected struct Weight
{
- ///
- /// The pixel index.
- ///
- public readonly int Index;
-
///
/// Initializes a new instance of the class.
///
@@ -241,9 +162,14 @@ namespace ImageProcessorCore.Samplers
}
///
- /// Gets or sets the result of the interpolation algorithm.
+ /// Gets the pixel index.
+ ///
+ public int Index { get; }
+
+ ///
+ /// Gets the result of the interpolation algorithm.
///
- public float Value { get; set; }
+ public float Value { get; }
}
///
@@ -262,4 +188,4 @@ namespace ImageProcessorCore.Samplers
public float Sum { get; set; }
}
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageProcessorCore/Samplers/Resamplers/BoxResampler.cs b/src/ImageProcessorCore/Samplers/Resamplers/BoxResampler.cs
index 6ef5c0e35..82c405114 100644
--- a/src/ImageProcessorCore/Samplers/Resamplers/BoxResampler.cs
+++ b/src/ImageProcessorCore/Samplers/Resamplers/BoxResampler.cs
@@ -17,12 +17,7 @@ namespace ImageProcessorCore.Samplers
///
public float GetValue(float x)
{
- if (x < 0)
- {
- x = -x;
- }
-
- if (x <= 0.5)
+ if (x > -0.5 && x <= 0.5)
{
return 1;
}
diff --git a/src/ImageProcessorCore/Samplers/Resize.cs b/src/ImageProcessorCore/Samplers/Resize.cs
index dc6a92f5e..e7de40c44 100644
--- a/src/ImageProcessorCore/Samplers/Resize.cs
+++ b/src/ImageProcessorCore/Samplers/Resize.cs
@@ -114,13 +114,6 @@ namespace ImageProcessorCore.Samplers
destination += sourceColor * xw.Value;
}
- //foreach (Weight xw in horizontalValues)
- //{
- // int originX = xw.Index;
- // Color sourceColor = compand ? Color.Expand(source[originX, y]) : source[originX, y];
- // destination += sourceColor * xw.Value;
- //}
-
if (compand)
{
destination = Color.Compress(destination);
@@ -150,19 +143,10 @@ namespace ImageProcessorCore.Samplers
{
Weight yw = verticalValues[i];
int originY = yw.Index;
- int originX = x;
- Color sourceColor = compand ? Color.Expand(this.firstPass[originX, originY]) : this.firstPass[originX, originY];
+ Color sourceColor = compand ? Color.Expand(this.firstPass[x, originY]) : this.firstPass[x, originY];
destination += sourceColor * yw.Value;
}
- //foreach (Weight yw in verticalValues)
- //{
- // int originY = yw.Index;
- // int originX = x;
- // Color sourceColor = compand ? Color.Expand(this.firstPass[originX, originY]) : this.firstPass[originX, originY];
- // destination += sourceColor * yw.Value;
- //}
-
if (compand)
{
destination = Color.Compress(destination);
@@ -170,6 +154,7 @@ namespace ImageProcessorCore.Samplers
target[x, y] = destination;
}
+
this.OnRowProcessed();
}
});
diff --git a/src/ImageProcessorCore/Samplers/Rotate.cs b/src/ImageProcessorCore/Samplers/Rotate.cs
index 77cc8e7e7..42032ae2a 100644
--- a/src/ImageProcessorCore/Samplers/Rotate.cs
+++ b/src/ImageProcessorCore/Samplers/Rotate.cs
@@ -10,24 +10,13 @@ namespace ImageProcessorCore.Samplers
///
/// Provides methods that allow the rotating of images using various algorithms.
///
- public class Rotate : Resampler
+ public class Rotate : ImageSampler
{
///
/// The angle of rotation.
///
private float angle;
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The sampler to perform the resize operation.
- ///
- public Rotate(IResampler sampler)
- : base(sampler)
- {
- }
-
///
/// Gets or sets the angle of rotation.
///
@@ -54,16 +43,6 @@ namespace ImageProcessorCore.Samplers
}
}
- ///
- protected override void OnApply(ImageBase source, ImageBase target, Rectangle targetRectangle, Rectangle sourceRectangle)
- {
- if (!(this.Sampler is NearestNeighborResampler))
- {
- this.HorizontalWeights = this.PrecomputeWeights(targetRectangle.Width, sourceRectangle.Width);
- this.VerticalWeights = this.PrecomputeWeights(targetRectangle.Height, sourceRectangle.Height);
- }
- }
-
///
protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
{
@@ -73,45 +52,11 @@ namespace ImageProcessorCore.Samplers
int endX = targetRectangle.Right;
float negativeAngle = -this.angle;
Point centre = Rectangle.Center(sourceRectangle);
- bool compand = this.Compand;
-
- if (this.Sampler is NearestNeighborResampler)
- {
- // Scaling factors
- float widthFactor = source.Width / (float)target.Width;
- float heightFactor = source.Height / (float)target.Height;
- Parallel.For(
- startY,
- endY,
- y =>
- {
- if (y >= targetY && y < targetBottom)
- {
- // Y coordinates of source points
- int originY = (int)((y - targetY) * heightFactor);
+ // Scaling factors
+ float widthFactor = source.Width / (float)target.Width;
+ float heightFactor = source.Height / (float)target.Height;
- for (int x = startX; x < endX; x++)
- {
- // X coordinates of source points
- int originX = (int)((x - startX) * widthFactor);
-
- // Rotate at the centre point
- Point rotated = Point.Rotate(new Point(originX, originY), centre, negativeAngle);
- if (sourceRectangle.Contains(rotated.X, rotated.Y))
- {
- target[x, y] = source[rotated.X, rotated.Y];
- }
- }
- this.OnRowProcessed();
- }
- });
-
- // Break out now.
- return;
- }
-
- // Interpolate the image using the calculated weights.
Parallel.For(
startY,
endY,
@@ -119,46 +64,21 @@ namespace ImageProcessorCore.Samplers
{
if (y >= targetY && y < targetBottom)
{
- Weight[] verticalValues = this.VerticalWeights[y].Values;
+ // Y coordinates of source points
+ int originY = (int)((y - targetY) * heightFactor);
for (int x = startX; x < endX; x++)
{
- Weight[] horizontalValues = this.HorizontalWeights[x].Values;
+ // X coordinates of source points
+ int originX = (int)((x - startX) * widthFactor);
- // Destination color components
- Color destination = new Color();
-
- foreach (Weight yw in verticalValues)
+ // Rotate at the centre point
+ Point rotated = Point.Rotate(new Point(originX, originY), centre, negativeAngle);
+ if (sourceRectangle.Contains(rotated.X, rotated.Y))
{
- int originY = yw.Index;
-
- foreach (Weight xw in horizontalValues)
- {
- int originX = xw.Index;
-
- // Rotate at the centre point
- Point rotated = Point.Rotate(new Point(originX, originY), centre, negativeAngle);
- if (sourceRectangle.Contains(rotated.X, rotated.Y))
- {
- target[x, y] = source[rotated.X, rotated.Y];
- }
-
- if (sourceRectangle.Contains(rotated.X, rotated.Y))
- {
- Color sourceColor = compand ? Color.Expand(source[rotated.X, rotated.Y]) : source[rotated.X, rotated.Y];
- destination += sourceColor * yw.Value * xw.Value;
- }
- }
+ target[x, y] = source[rotated.X, rotated.Y];
}
-
- if (compand)
- {
- destination = Color.Compress(destination);
- }
-
- target[x, y] = destination;
}
-
this.OnRowProcessed();
}
});
diff --git a/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs b/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs
index c419ae560..df1914c3e 100644
--- a/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs
+++ b/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs
@@ -15,13 +15,13 @@
{
{ "Bicubic", new BicubicResampler() },
{ "Triangle", new TriangleResampler() },
+ { "NearestNeighbor", new NearestNeighborResampler() },
// Perf: Enable for local testing only
//{ "Box", new BoxResampler() },
//{ "Lanczos3", new Lanczos3Resampler() },
//{ "Lanczos5", new Lanczos5Resampler() },
//{ "Lanczos8", new Lanczos8Resampler() },
//{ "MitchellNetravali", new MitchellNetravaliResampler() },
- { "NearestNeighbor", new NearestNeighborResampler() },
//{ "Hermite", new HermiteResampler() },
//{ "Spline", new SplineResampler() },
//{ "Robidoux", new RobidouxResampler() },
@@ -92,7 +92,7 @@
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{
- image.Resize(image.Width * 2, image.Height * 2, sampler, false, this.ProgressUpdate)
+ image.Resize(image.Width / 2, image.Height / 2, sampler, false, this.ProgressUpdate)
.Save(output);
}
@@ -184,9 +184,8 @@
}
}
- [Theory]
- [MemberData("ReSamplers")]
- public void ImageShouldRotate(string name, IResampler sampler)
+ [Fact]
+ public void ImageShouldRotate()
{
if (!Directory.Exists("TestOutput/Rotate"))
{
@@ -199,15 +198,15 @@
{
Stopwatch watch = Stopwatch.StartNew();
Image image = new Image(stream);
- string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
+ string filename = Path.GetFileName(file);
using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}"))
{
- image.Rotate(45, sampler, false, this.ProgressUpdate)
- //.BackgroundColor(Color.Aqua)
+ image.Rotate(45, this.ProgressUpdate)
+ .BackgroundColor(Color.Pink)
.Save(output);
}
- Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms");
+ Trace.WriteLine($"{watch.ElapsedMilliseconds}ms");
}
}
}