diff --git a/src/ImageProcessorCore/Samplers/Resampler.cs b/src/ImageProcessorCore/Samplers/Resampler.cs
index 4b7239af4..0554b18e0 100644
--- a/src/ImageProcessorCore/Samplers/Resampler.cs
+++ b/src/ImageProcessorCore/Samplers/Resampler.cs
@@ -11,6 +11,8 @@ namespace ImageProcessorCore.Samplers
///
/// Provides methods that allow the resampling of images using various algorithms.
+ ///
+ ///
///
public abstract class Resampler : ImageSampler
{
@@ -52,74 +54,171 @@ namespace ImageProcessorCore.Samplers
///
protected Weights[] PrecomputeWeights(int destinationSize, int sourceSize)
{
+ float xscale = destinationSize / (float)sourceSize;
+ float width;
IResampler sampler = this.Sampler;
- float ratio = sourceSize / (float)destinationSize;
- float scale = ratio;
+ float fwidth = sampler.Radius;
+ float fscale;
+ double left;
+ double right;
+ double weight = 0;
+ int n = 0;
+ int k;
- // When shrinking, broaden the effective kernel support so that we still
+ Weights[] result = new Weights[destinationSize];
+
+ // When expanding, broaden the effective kernel support so that we still
// visit every source pixel.
- if (scale < 1)
+ if (xscale < 0)
{
- scale = 1;
- }
+ width = sampler.Radius / xscale;
+ fscale = 1 / xscale;
- 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 =>
+ // Make the weights slices, one source for each column or row.
+ for (int i = 0; i < 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)
+ float centre = i / xscale;
+ left = Math.Ceiling(centre - width);
+ right = Math.Floor(centre + width);
+ float sum = 0;
+ result[i] = new Weights();
+ List builder = new List();
+ for (double j = left; j <= right; j++)
{
- end = sourceSize;
-
- if (end < start)
+ weight = centre - j;
+ weight = sampler.GetValue((float)weight / fscale) / fscale;
+ if (j < 0)
+ {
+ n = (int)-j;
+ }
+ else if (j >= sourceSize)
+ {
+ n = (int)((sourceSize - j) + sourceSize - 1);
+ }
+ else
{
- end = start;
+ n = (int)j;
}
+
+ sum++;
+ builder.Add(new Weight(n, (float)weight));
}
+ result[i].Values = builder.ToArray();
+ result[i].Sum = sum;
+ }
+ }
+ else
+ {
+ // 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();
List builder = new List();
- for (int a = start; a < end; a++)
+ for (double j = left; j <= right; j++)
{
- float w = sampler.GetValue((a - center) / scale);
-
- if (w < 0 || w > 0)
+ weight = centre - j;
+ weight = sampler.GetValue((float)weight);
+ if (j < 0)
{
- sum += w;
- builder.Add(new Weight(a, w));
+ n = (int)-j;
+ }
+ else if (j >= sourceSize)
+ {
+ n = (int)((sourceSize - j) + sourceSize - 1);
+ }
+ else
+ {
+ n = (int)j;
}
- }
- // Normalise the values
- if (sum > 0 || sum < 0)
- {
- builder.ForEach(w => w.Value /= sum);
+ sum++;
+ builder.Add(new Weight(n, (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.
///
diff --git a/src/ImageProcessorCore/Samplers/Resize.cs b/src/ImageProcessorCore/Samplers/Resize.cs
index 03aad0ec8..e7de40c44 100644
--- a/src/ImageProcessorCore/Samplers/Resize.cs
+++ b/src/ImageProcessorCore/Samplers/Resize.cs
@@ -54,6 +54,7 @@ namespace ImageProcessorCore.Samplers
int sourceBottom = source.Bounds.Bottom;
int targetY = targetRectangle.Y;
+ int targetBottom = targetRectangle.Bottom;
int startX = targetRectangle.X;
int endX = targetRectangle.Right;
bool compand = this.Compand;
@@ -69,18 +70,20 @@ namespace ImageProcessorCore.Samplers
endY,
y =>
{
- // Y coordinates of source points
- int originY = (int)((y - targetY) * heightFactor);
-
- for (int x = startX; x < endX; x++)
+ if (y >= targetY && y < targetBottom)
{
- // X coordinates of source points
- int originX = (int)((x - startX) * widthFactor);
+ // Y coordinates of source points
+ int originY = (int)((y - targetY) * heightFactor);
- target[x, y] = source[originX, originY];
- }
+ for (int x = startX; x < endX; x++)
+ {
+ // X coordinates of source points
+ int originX = (int)((x - startX) * widthFactor);
- this.OnRowProcessed();
+ target[x, y] = source[originX, originY];
+ }
+ this.OnRowProcessed();
+ }
});
// Break out now.
@@ -97,13 +100,15 @@ namespace ImageProcessorCore.Samplers
{
for (int x = startX; x < endX; x++)
{
+ float sum = this.HorizontalWeights[x].Sum;
Weight[] horizontalValues = this.HorizontalWeights[x].Values;
// Destination color components
Color destination = new Color();
- foreach (Weight xw in horizontalValues)
+ for (int i = 0; i < sum; i++)
{
+ Weight xw = horizontalValues[i];
int originX = xw.Index;
Color sourceColor = compand ? Color.Expand(source[originX, y]) : source[originX, y];
destination += sourceColor * xw.Value;
@@ -124,30 +129,34 @@ namespace ImageProcessorCore.Samplers
endY,
y =>
{
- Weight[] verticalValues = this.VerticalWeights[y].Values;
-
- for (int x = startX; x < endX; x++)
+ if (y >= targetY && y < targetBottom)
{
- // Destination color components
- Color destination = new Color();
+ float sum = this.VerticalWeights[y].Sum;
+ Weight[] verticalValues = this.VerticalWeights[y].Values;
- foreach (Weight yw in verticalValues)
+ for (int x = startX; x < endX; x++)
{
- int originY = yw.Index;
- int originX = x;
- Color sourceColor = compand ? Color.Expand(this.firstPass[originX, originY]) : this.firstPass[originX, originY];
- destination += sourceColor * yw.Value;
+ // Destination color components
+ Color destination = new Color();
+
+ for (int i = 0; i < sum; i++)
+ {
+ Weight yw = verticalValues[i];
+ int originY = yw.Index;
+ Color sourceColor = compand ? Color.Expand(this.firstPass[x, originY]) : this.firstPass[x, originY];
+ destination += sourceColor * yw.Value;
+ }
+
+ if (compand)
+ {
+ destination = Color.Compress(destination);
+ }
+
+ target[x, y] = destination;
}
- if (compand)
- {
- destination = Color.Compress(destination);
- }
-
- target[x, y] = destination;
+ this.OnRowProcessed();
}
-
- this.OnRowProcessed();
});
}
@@ -159,8 +168,6 @@ namespace ImageProcessorCore.Samplers
{
target.ClonePixels(target.Width, target.Height, source.Pixels);
}
-
- this.firstPass?.Dispose();
}
}
}
\ No newline at end of file
diff --git a/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs b/tests/ImageProcessorCore.Tests/Processors/Samplers/SamplerTests.cs
index 2e9eef9cf..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() },
@@ -60,17 +60,14 @@
{
Stopwatch watch = Stopwatch.StartNew();
Image image = new Image(stream);
-
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
- using (FileStream output = File.OpenWrite($"TestOutput/Sample/{Path.GetFileName(filename)}"))
+ using (FileStream output = File.OpenWrite($"TestOutput/Sample/{ Path.GetFileName(filename) }"))
{
processor.OnProgress += this.ProgressUpdate;
- // Not Chainable.
image = image.Process(image.Width / 2, image.Height / 2, processor);
image.Save(output);
processor.OnProgress -= this.ProgressUpdate;
}
- image.Dispose();
Trace.WriteLine($"{ name }: { watch.ElapsedMilliseconds}ms");
}
@@ -91,15 +88,14 @@
using (FileStream stream = File.OpenRead(file))
{
Stopwatch watch = Stopwatch.StartNew();
- using (Image image = new Image(stream))
+ Image image = new Image(stream);
+ string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
+ using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{
- 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)
- .Save(output);
- }
+ image.Resize(image.Width / 2, image.Height / 2, sampler, false, this.ProgressUpdate)
+ .Save(output);
}
+
Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms");
}
}
@@ -120,14 +116,12 @@
using (FileStream stream = File.OpenRead(file))
{
Stopwatch watch = Stopwatch.StartNew();
- using (Image image = new Image(stream))
+ Image image = new Image(stream);
+ string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
+ using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{
- string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
- using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
- {
- image.Resize(image.Width / 3, 0, new TriangleResampler(), false, this.ProgressUpdate)
- .Save(output);
- }
+ image.Resize(image.Width / 3, 0, new TriangleResampler(), false, this.ProgressUpdate)
+ .Save(output);
}
Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms");
@@ -150,14 +144,12 @@
using (FileStream stream = File.OpenRead(file))
{
Stopwatch watch = Stopwatch.StartNew();
- using (Image image = new Image(stream))
+ Image image = new Image(stream);
+ string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
+ using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
{
- string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
- using (FileStream output = File.OpenWrite($"TestOutput/Resize/{filename}"))
- {
- image.Resize(0, image.Height / 3, new TriangleResampler(), false, this.ProgressUpdate)
- .Save(output);
- }
+ image.Resize(0, image.Height / 3, new TriangleResampler(), false, this.ProgressUpdate)
+ .Save(output);
}
Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms");
@@ -179,14 +171,12 @@
using (FileStream stream = File.OpenRead(file))
{
Stopwatch watch = Stopwatch.StartNew();
- using (Image image = new Image(stream))
+ Image image = new Image(stream);
+ string filename = Path.GetFileNameWithoutExtension(file) + "-" + rotateType + flipType + Path.GetExtension(file);
+ using (FileStream output = File.OpenWrite($"TestOutput/RotateFlip/{filename}"))
{
- string filename = Path.GetFileNameWithoutExtension(file) + "-" + rotateType + flipType
- + Path.GetExtension(file);
- using (FileStream output = File.OpenWrite($"TestOutput/RotateFlip/{filename}"))
- {
- image.RotateFlip(rotateType, flipType, this.ProgressUpdate).Save(output);
- }
+ image.RotateFlip(rotateType, flipType, this.ProgressUpdate)
+ .Save(output);
}
Trace.WriteLine($"{rotateType + "-" + flipType}: {watch.ElapsedMilliseconds}ms");
@@ -194,9 +184,8 @@
}
}
- [Theory]
- [MemberData("ReSamplers")]
- public void ImageShouldRotate(string name, IResampler sampler)
+ [Fact]
+ public void ImageShouldRotate()
{
if (!Directory.Exists("TestOutput/Rotate"))
{
@@ -208,18 +197,16 @@
using (FileStream stream = File.OpenRead(file))
{
Stopwatch watch = Stopwatch.StartNew();
- using (Image image = new Image(stream))
+ Image image = new Image(stream);
+ string filename = Path.GetFileName(file);
+ using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}"))
{
- string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
- using (FileStream output = File.OpenWrite($"TestOutput/Rotate/{filename}"))
- {
- image.Rotate(45, sampler, false, this.ProgressUpdate)
- //.BackgroundColor(Color.Aqua)
- .Save(output);
- }
+ image.Rotate(45, this.ProgressUpdate)
+ .BackgroundColor(Color.Pink)
+ .Save(output);
}
- Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms");
+ Trace.WriteLine($"{watch.ElapsedMilliseconds}ms");
}
}
}
@@ -236,13 +223,11 @@
{
using (FileStream stream = File.OpenRead(file))
{
- using (Image image = new Image(stream))
+ Image image = new Image(stream);
+ string filename = Path.GetFileNameWithoutExtension(file) + "-EntropyCrop" + Path.GetExtension(file);
+ using (FileStream output = File.OpenWrite($"TestOutput/EntropyCrop/{filename}"))
{
- string filename = Path.GetFileNameWithoutExtension(file) + "-EntropyCrop" + Path.GetExtension(file);
- using (FileStream output = File.OpenWrite($"TestOutput/EntropyCrop/{filename}"))
- {
- image.EntropyCrop(.5f, this.ProgressUpdate).Save(output);
- }
+ image.EntropyCrop(.5f, this.ProgressUpdate).Save(output);
}
}
}
@@ -260,13 +245,11 @@
{
using (FileStream stream = File.OpenRead(file))
{
- using (Image image = new Image(stream))
+ Image image = new Image(stream);
+ string filename = Path.GetFileNameWithoutExtension(file) + "-Crop" + Path.GetExtension(file);
+ using (FileStream output = File.OpenWrite($"TestOutput/Crop/{filename}"))
{
- string filename = Path.GetFileNameWithoutExtension(file) + "-Crop" + Path.GetExtension(file);
- using (FileStream output = File.OpenWrite($"TestOutput/Crop/{filename}"))
- {
- image.Crop(image.Width / 2, image.Height / 2, this.ProgressUpdate).Save(output);
- }
+ image.Crop(image.Width / 2, image.Height / 2, this.ProgressUpdate).Save(output);
}
}
}