From c75ebb308a6f1b518a6993e2702c9b72aa4d40b5 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 27 Apr 2017 19:19:23 +1000 Subject: [PATCH] Fix source rectangle regression in resize #118 --- .../ResamplingWeightedProcessor.Weights.cs | 19 ++++++++++-------- .../Processors/Transforms/ResizeProcessor.cs | 8 ++++---- tests/ImageSharp.Tests/FileTestBase.cs | 1 + tests/ImageSharp.Tests/TestImages.cs | 1 + .../TestImages/Formats/Png/cross.png | Bin 0 -> 15227 bytes 5 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Png/cross.png diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs index 5d29924e11..f9c78c12fe 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs @@ -52,13 +52,14 @@ namespace ImageSharp.Processing.Processors /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance. /// /// The input span of vectors + /// The source row position. /// The weighted sum [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ComputeWeightedRowSum(BufferSpan rowSpan) + public Vector4 ComputeWeightedRowSum(BufferSpan rowSpan, int sourceX) { ref float horizontalValues = ref this.Ptr; int left = this.Left; - ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left); + ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX); // Destination color components Vector4 result = Vector4.Zero; @@ -78,13 +79,14 @@ namespace ImageSharp.Processing.Processors /// Applies to all input vectors. /// /// The input span of vectors + /// The source row position. /// The weighted sum [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ComputeExpandedWeightedRowSum(BufferSpan rowSpan) + public Vector4 ComputeExpandedWeightedRowSum(BufferSpan rowSpan, int sourceX) { ref float horizontalValues = ref this.Ptr; int left = this.Left; - ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left); + ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left + sourceX); // Destination color components Vector4 result = Vector4.Zero; @@ -100,14 +102,15 @@ namespace ImageSharp.Processing.Processors } /// - /// Computes the sum of vectors in 'firstPassPixels' at a column pointed by 'x', + /// Computes the sum of vectors in 'firstPassPixels' at a row pointed by 'x', /// weighted by weight values, pointed by this instance. /// /// The buffer of input vectors in row first order - /// The column position + /// The row position + /// The source column position. /// The weighted sum [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector4 ComputeWeightedColumnSum(Buffer2D firstPassPixels, int x) + public Vector4 ComputeWeightedColumnSum(Buffer2D firstPassPixels, int x, int sourceY) { ref float verticalValues = ref this.Ptr; int left = this.Left; @@ -118,7 +121,7 @@ namespace ImageSharp.Processing.Processors for (int i = 0; i < this.Length; i++) { float yw = Unsafe.Add(ref verticalValues, i); - int index = left + i; + int index = left + i + sourceY; result += firstPassPixels[x, index] * yw; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 23166fd3ab..ecff3da2cd 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -133,7 +133,7 @@ namespace ImageSharp.Processing.Processors for (int x = minX; x < maxX; x++) { WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; - firstPassPixels[x, y] = window.ComputeExpandedWeightedRowSum(tempRowBuffer); + firstPassPixels[x, y] = window.ComputeExpandedWeightedRowSum(tempRowBuffer, sourceX); } } else @@ -141,7 +141,7 @@ namespace ImageSharp.Processing.Processors for (int x = minX; x < maxX; x++) { WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; - firstPassPixels[x, y] = window.ComputeWeightedRowSum(tempRowBuffer); + firstPassPixels[x, y] = window.ComputeWeightedRowSum(tempRowBuffer, sourceX); } } } @@ -162,7 +162,7 @@ namespace ImageSharp.Processing.Processors for (int x = 0; x < width; x++) { // Destination color components - Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x); + Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels,x, sourceY); destination = destination.Compress(); TPixel d = default(TPixel); d.PackFromVector4(destination); @@ -174,7 +174,7 @@ namespace ImageSharp.Processing.Processors for (int x = 0; x < width; x++) { // Destination color components - Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x); + Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels,x, sourceY); TPixel d = default(TPixel); d.PackFromVector4(destination); diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index 1fa26063da..12c7d51541 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -30,6 +30,7 @@ namespace ImageSharp.Tests TestFile.Create(TestImages.Bmp.Car), // TestFile.Create(TestImages.Bmp.NegHeight), // Perf: Enable for local testing only TestFile.Create(TestImages.Png.Splash), + // TestFile.Create(TestImages.Png.Cross), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.ChunkLength1), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.ChunkLength2), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Powerpoint), // Perf: Enable for local testing only diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 5be1240efc..44c8c34ee3 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -21,6 +21,7 @@ namespace ImageSharp.Tests public const string Blur = "Png/blur.png"; public const string Indexed = "Png/indexed.png"; public const string Splash = "Png/splash.png"; + public const string Cross = "Png/cross.png"; public const string Powerpoint = "Png/pp.png"; public const string SplashInterlaced = "Png/splash-interlaced.png"; public const string Interlaced = "Png/interlaced.png"; diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Png/cross.png b/tests/ImageSharp.Tests/TestImages/Formats/Png/cross.png new file mode 100644 index 0000000000000000000000000000000000000000..e0c9e1dd794c668c09838b983b76466258f1c00b GIT binary patch literal 15227 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 literal 0 HcmV?d00001