diff --git a/src/ImageProcessor/ParallelImageProcessor.cs b/src/ImageProcessor/ParallelImageProcessor.cs
index 72722dd91..8d7ba13dc 100644
--- a/src/ImageProcessor/ParallelImageProcessor.cs
+++ b/src/ImageProcessor/ParallelImageProcessor.cs
@@ -21,7 +21,7 @@ namespace ImageProcessor
///
public void Apply(ImageBase target, ImageBase source, Rectangle sourceRectangle)
{
- this.OnApply();
+ this.OnApply(target.Bounds, sourceRectangle);
if (this.Parallelism > 1)
{
@@ -53,8 +53,6 @@ namespace ImageProcessor
///
public void Apply(ImageBase target, ImageBase source, int width, int height, Rectangle targetRectangle = default(Rectangle), Rectangle sourceRectangle = default(Rectangle))
{
- this.OnApply();
-
byte[] pixels = new byte[width * height * 4];
target.SetPixels(width, height, pixels);
@@ -68,6 +66,8 @@ namespace ImageProcessor
sourceRectangle = source.Bounds;
}
+ this.OnApply(target.Bounds, sourceRectangle);
+
if (this.Parallelism > 1)
{
int partitionCount = this.Parallelism;
@@ -98,7 +98,14 @@ namespace ImageProcessor
///
/// This method is called before the process is applied to prepare the processor.
///
- protected virtual void OnApply()
+ ///
+ /// The structure that specifies the location and size of the drawn image.
+ /// The image is scaled to fit the rectangle.
+ ///
+ ///
+ /// The structure that specifies the portion of the image object to draw.
+ ///
+ protected virtual void OnApply(Rectangle targetRectangle, Rectangle sourceRectangle)
{
}
diff --git a/src/ImageProcessor/Samplers/ImageSampleExtensions.cs b/src/ImageProcessor/Samplers/ImageSampleExtensions.cs
index ddd702c4a..a31355e6a 100644
--- a/src/ImageProcessor/Samplers/ImageSampleExtensions.cs
+++ b/src/ImageProcessor/Samplers/ImageSampleExtensions.cs
@@ -19,7 +19,7 @@ namespace ImageProcessor.Samplers
/// The
public static Image Resize(this Image source, int width, int height)
{
- return source.Process(width, height, default(Rectangle), default(Rectangle), new Resize(new BicubicResampler()));
+ return Resize(source, width, height, new RobidouxResampler());
}
///
@@ -32,7 +32,7 @@ namespace ImageProcessor.Samplers
/// The
public static Image Resize(this Image source, int width, int height, IResampler sampler)
{
- return source.Process(width, height, default(Rectangle), default(Rectangle), new Resize(sampler));
+ return Resize(source, width, height, sampler, source.Bounds, new Rectangle(0, 0, width, height));
}
///
diff --git a/src/ImageProcessor/Samplers/Resize.cs b/src/ImageProcessor/Samplers/Resize.cs
index da6207566..7e7e80557 100644
--- a/src/ImageProcessor/Samplers/Resize.cs
+++ b/src/ImageProcessor/Samplers/Resize.cs
@@ -10,12 +10,6 @@ namespace ImageProcessor.Samplers
///
/// Provides methods that allow the resizing of images using various resampling algorithms.
- ///
- /// TODO: There is a bug in this class. Whenever the processor is set to use parallel processing, the output image becomes distorted
- /// at the join points when startY is greater than 0. Uncomment the Parallelism overload and run the ImageShouldResize method in the SamplerTests
- /// class to see the error manifest.
- /// It is imperative that the issue is solved or resampling will be too slow to be practical and the project will have to cease.
- ///
///
public class Resize : ParallelImageProcessor
{
@@ -24,6 +18,16 @@ namespace ImageProcessor.Samplers
///
private const float Epsilon = 0.0001f;
+ ///
+ /// The horizontal weights.
+ ///
+ private Weights[] horizontalWeights;
+
+ ///
+ /// The vertical weights.
+ ///
+ private Weights[] verticalWeights;
+
///
/// Initializes a new instance of the class.
///
@@ -37,97 +41,74 @@ namespace ImageProcessor.Samplers
this.Sampler = sampler;
}
- ///
- public override int Parallelism => 1; // Uncomment this to see bug.
-
///
/// Gets the sampler to perform the resize operation.
///
public IResampler Sampler { get; }
///
- protected override void Apply(ImageBase target, ImageBase source, Rectangle targetRectangle, Rectangle sourceRectangle, int startY, int endY)
+ protected override void OnApply(Rectangle targetRectangle, Rectangle sourceRectangle)
{
- int sourceWidth = source.Width;
- int sourceHeight = source.Height;
-
- int width = target.Width;
- int height = target.Height;
+ 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)
+ {
int targetY = targetRectangle.Y;
+ int targetBottom = targetRectangle.Bottom;
int startX = targetRectangle.X;
int endX = targetRectangle.Right;
- // Scaling factors
- double heightFactor = sourceHeight / (double)targetRectangle.Height;
- int targetSectionHeight = endY - startY;
- int sourceSectionHeight = (int)((targetSectionHeight * heightFactor) + .5);
-
- int offsetY = this.CalculateOffset(startY, targetSectionHeight, sourceSectionHeight);
- int offsetX = this.CalculateOffset(startX, targetRectangle.Width, sourceRectangle.Width);
- Weights[] horizontalWeights = this.PrecomputeWeights(targetRectangle.Width, sourceRectangle.Width);
- Weights[] verticalWeights = this.PrecomputeWeights(targetSectionHeight, sourceSectionHeight);
-
- // Width and height decreased by 1
- int maxHeight = sourceHeight - 1;
- int maxWidth = sourceWidth - 1;
-
for (int y = startY; y < endY; y++)
{
- if (y >= 0 && y < height)
+ if (y >= targetY && y < targetBottom)
{
- List verticalValues = verticalWeights[y - startY].Values;
- double verticalSum = verticalWeights[y - startY].Sum;
+ List verticalValues = this.verticalWeights[y].Values;
+ double verticalSum = this.verticalWeights[y].Sum;
for (int x = startX; x < endX; x++)
{
- if (x >= 0 && x < width)
+ List horizontalValues = this.horizontalWeights[x].Values;
+ double horizontalSum = this.horizontalWeights[x].Sum;
+
+ // Destination color components
+ double r = 0;
+ double g = 0;
+ double b = 0;
+ double a = 0;
+
+ foreach (Weight yw in verticalValues)
{
- List horizontalValues = horizontalWeights[x - startX].Values;
- double horizontalSum = horizontalWeights[x - startX].Sum;
+ if (Math.Abs(yw.Value) < Epsilon)
+ {
+ continue;
+ }
- // Destination color components
- double r = 0;
- double g = 0;
- double b = 0;
- double a = 0;
+ int originY = yw.Index;
- foreach (Weight yw in verticalValues)
+ foreach (Weight xw in horizontalValues)
{
- if (Math.Abs(yw.Value) < Epsilon)
+ if (Math.Abs(xw.Value) < Epsilon)
{
continue;
}
- // TODO: This offset is wrong.
- int originY = offsetY == 0 ? yw.Index : yw.Index + offsetY;
- originY = originY.Clamp(0, maxHeight);
+ int originX = xw.Index;
+ Bgra sourceColor = source[originX, originY];
+ sourceColor = PixelOperations.ToLinear(sourceColor);
- foreach (Weight xw in horizontalValues)
- {
- if (Math.Abs(xw.Value) < Epsilon)
- {
- continue;
- }
-
- // TODO: This offset is wrong.
- int originX = xw.Index + offsetX;
- originX = originX.Clamp(0, maxWidth);
-
- Bgra sourceColor = source[originX, originY];
- sourceColor = PixelOperations.ToLinear(sourceColor);
-
- r += sourceColor.R * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
- g += sourceColor.G * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
- b += sourceColor.B * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
- a += sourceColor.A * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
- }
+ r += sourceColor.R * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
+ g += sourceColor.G * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
+ b += sourceColor.B * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
+ a += sourceColor.A * (yw.Value / verticalSum) * (xw.Value / horizontalSum);
}
-
- Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), a.ToByte());
- destinationColor = PixelOperations.ToSrgb(destinationColor);
- target[x, y] = destinationColor;
}
+
+ Bgra destinationColor = new Bgra(b.ToByte(), g.ToByte(), r.ToByte(), a.ToByte());
+ destinationColor = PixelOperations.ToSrgb(destinationColor);
+ target[x, y] = destinationColor;
}
}
}
@@ -192,44 +173,6 @@ namespace ImageProcessor.Samplers
return result;
}
- ///
- /// Calculates the scaled offset caused by parallelism.
- ///
- /// The offset position.
- /// The destination size.
- /// The source size.
- ///
- /// The .
- ///
- private int CalculateOffset(int offset, int destinationSize, int sourceSize)
- {
- if (offset == 0)
- {
- return 0;
- }
-
- IResampler sampler = this.Sampler;
- double du = sourceSize / (double)destinationSize;
- double scale = du;
-
- if (scale < 1)
- {
- scale = 1;
- }
-
- double ru = Math.Ceiling(scale * sampler.Radius);
-
- double fu = ((offset + .5) * du) - 0.5;
- int result = (int)Math.Ceiling(fu - ru);
-
- if (result < 0)
- {
- return 0;
- }
-
- return result;
- }
-
///
/// Represents the weight to be added to a scaled pixel.
///
diff --git a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs
index 0ce273346..9f5c9cccc 100644
--- a/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs
+++ b/tests/ImageProcessor.Tests/Processors/Samplers/SamplerTests.cs
@@ -45,7 +45,7 @@ namespace ImageProcessor.Tests
string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file);
using (FileStream output = File.OpenWrite($"Resized/{filename}"))
{
- image.Resize(500, 500, sampler).Save(output);
+ image.Resize(image.Width / 2, image.Height / 2, sampler).Save(output);
}
Trace.WriteLine($"{name}: {watch.ElapsedMilliseconds}ms");