From 562bb03b43da3bff444c2cd9b858fce8bf2ee180 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 9 Aug 2016 12:48:42 +1000 Subject: [PATCH] Resize now 10% faster Former-commit-id: 8d123c508edef580da7e7226d9dc94b42e0261c2 Former-commit-id: 3fb2cf0b1318711665772efbd37a1e05ea8c39bd Former-commit-id: 89e4d4123421180721162e5159406483c574bbb2 --- .../Processors/CompandingResizeProcessor.cs | 100 +++++++++--------- .../Samplers/Processors/ResizeProcessor.cs | 100 +++++++++--------- .../Processors/Samplers/ResizeTests.cs | 37 ++++++- 3 files changed, 133 insertions(+), 104 deletions(-) diff --git a/src/ImageProcessorCore/Samplers/Processors/CompandingResizeProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/CompandingResizeProcessor.cs index 409c26878..882a864db 100644 --- a/src/ImageProcessorCore/Samplers/Processors/CompandingResizeProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/CompandingResizeProcessor.cs @@ -5,6 +5,7 @@ namespace ImageProcessorCore.Processors { + using System; using System.Numerics; using System.Threading.Tasks; @@ -50,7 +51,11 @@ namespace ImageProcessorCore.Processors int targetBottom = target.Bounds.Bottom; int startX = targetRectangle.X; int endX = targetRectangle.Right; - bool compand = this.Compand; + + int minX = Math.Max(targetX, startX); + int maxX = Math.Min(targetRight, endX); + int minY = Math.Max(targetY, startY); + int maxY = Math.Min(targetBottom, endY); if (this.Sampler is NearestNeighborResampler) { @@ -62,27 +67,21 @@ namespace ImageProcessorCore.Processors using (IPixelAccessor targetPixels = target.Lock()) { Parallel.For( - startY, - endY, + minY, + maxY, this.ParallelOptions, y => { - if (targetY <= y && y < targetBottom) + // Y coordinates of source points + int originY = (int)((y - startY) * heightFactor); + + for (int x = minX; x < maxX; x++) { - // Y coordinates of source points - int originY = (int)((y - startY) * heightFactor); - - for (int x = startX; x < endX; x++) - { - if (targetX <= x && x < targetRight) - { - // X coordinates of source points - targetPixels[x, y] = sourcePixels[(int)((x - startX) * widthFactor), originY]; - } - } - - this.OnRowProcessed(); + // X coordinates of source points + targetPixels[x, y] = sourcePixels[(int)((x - startX) * widthFactor), originY]; } + + this.OnRowProcessed(); }); } @@ -99,62 +98,61 @@ namespace ImageProcessorCore.Processors using (IPixelAccessor firstPassPixels = firstPass.Lock()) using (IPixelAccessor targetPixels = target.Lock()) { + minX = Math.Max(0, startX); + maxX = Math.Min(width, endX); + minY = Math.Max(0, startY); + maxY = Math.Min(height, endY); + Parallel.For( 0, sourceHeight, this.ParallelOptions, y => { - for (int x = startX; x < endX; x++) + for (int x = minX; x < maxX; x++) { - if (x >= 0 && x < width) - { - // Ensure offsets are normalised for cropping and padding. - Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values; - - // Destination color components - Vector4 destination = Vector4.Zero; + // Ensure offsets are normalised for cropping and padding. + Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values; - for (int i = 0; i < horizontalValues.Length; i++) - { - Weight xw = horizontalValues[i]; - destination += sourcePixels[xw.Index, y].ToVector4().Expand() * xw.Value; - } + // Destination color components + Vector4 destination = Vector4.Zero; - T d = default(T); - d.PackVector(destination.Compress()); - firstPassPixels[x, y] = d; + for (int i = 0; i < horizontalValues.Length; i++) + { + Weight xw = horizontalValues[i]; + destination += sourcePixels[xw.Index, y].ToVector4().Expand() * xw.Value; } + + T d = default(T); + d.PackVector(destination.Compress()); + firstPassPixels[x, y] = d; } }); // Now process the rows. Parallel.For( - startY, - endY, + minY, + maxY, this.ParallelOptions, y => { - if (y >= 0 && y < height) + // Ensure offsets are normalised for cropping and padding. + Weight[] verticalValues = this.VerticalWeights[y - startY].Values; + + for (int x = 0; x < width; x++) { - // Ensure offsets are normalised for cropping and padding. - Weight[] verticalValues = this.VerticalWeights[y - startY].Values; + // Destination color components + Vector4 destination = Vector4.Zero; - for (int x = 0; x < width; x++) + for (int i = 0; i < verticalValues.Length; i++) { - // Destination color components - Vector4 destination = Vector4.Zero; - - for (int i = 0; i < verticalValues.Length; i++) - { - Weight yw = verticalValues[i]; - destination += firstPassPixels[x, yw.Index].ToVector4().Expand() * yw.Value; - } - - T d = default(T); - d.PackVector(destination.Compress()); - targetPixels[x, y] = d; + Weight yw = verticalValues[i]; + destination += firstPassPixels[x, yw.Index].ToVector4().Expand() * yw.Value; } + + T d = default(T); + d.PackVector(destination.Compress()); + targetPixels[x, y] = d; } this.OnRowProcessed(); diff --git a/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs b/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs index 0dc72dee3..191fc0739 100644 --- a/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs +++ b/src/ImageProcessorCore/Samplers/Processors/ResizeProcessor.cs @@ -5,6 +5,7 @@ namespace ImageProcessorCore.Processors { + using System; using System.Numerics; using System.Threading.Tasks; @@ -50,6 +51,11 @@ namespace ImageProcessorCore.Processors int startX = targetRectangle.X; int endX = targetRectangle.Right; + int minX = Math.Max(targetX, startX); + int maxX = Math.Min(targetRight, endX); + int minY = Math.Max(targetY, startY); + int maxY = Math.Min(targetBottom, endY); + if (this.Sampler is NearestNeighborResampler) { // Scaling factors @@ -60,27 +66,21 @@ namespace ImageProcessorCore.Processors using (IPixelAccessor targetPixels = target.Lock()) { Parallel.For( - startY, - endY, + minY, + maxY, this.ParallelOptions, y => { - if (targetY <= y && y < targetBottom) + // Y coordinates of source points + int originY = (int)((y - startY) * heightFactor); + + for (int x = minX; x < maxX; x++) { - // Y coordinates of source points - int originY = (int)((y - startY) * heightFactor); - - for (int x = startX; x < endX; x++) - { - if (targetX <= x && x < targetRight) - { - // X coordinates of source points - targetPixels[x, y] = sourcePixels[(int)((x - startX) * widthFactor), originY]; - } - } - - this.OnRowProcessed(); + // X coordinates of source points + targetPixels[x, y] = sourcePixels[(int)((x - startX) * widthFactor), originY]; } + + this.OnRowProcessed(); }); } @@ -97,67 +97,65 @@ namespace ImageProcessorCore.Processors using (IPixelAccessor firstPassPixels = firstPass.Lock()) using (IPixelAccessor targetPixels = target.Lock()) { + minX = Math.Max(0, startX); + maxX = Math.Min(width, endX); + minY = Math.Max(0, startY); + maxY = Math.Min(height, endY); + Parallel.For( 0, sourceHeight, this.ParallelOptions, y => { - for (int x = startX; x < endX; x++) + for (int x = minX; x < maxX; x++) { - if (x >= 0 && x < width) - { - // Ensure offsets are normalised for cropping and padding. - Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values; - - // Destination color components - Vector4 destination = Vector4.Zero; + // Ensure offsets are normalised for cropping and padding. + Weight[] horizontalValues = this.HorizontalWeights[x - startX].Values; - for (int i = 0; i < horizontalValues.Length; i++) - { - Weight xw = horizontalValues[i]; - destination += sourcePixels[xw.Index, y].ToVector4() * xw.Value; - } + // Destination color components + Vector4 destination = Vector4.Zero; - T d = default(T); - d.PackVector(destination); - firstPassPixels[x, y] = d; + for (int i = 0; i < horizontalValues.Length; i++) + { + Weight xw = horizontalValues[i]; + destination += sourcePixels[xw.Index, y].ToVector4() * xw.Value; } + + T d = default(T); + d.PackVector(destination); + firstPassPixels[x, y] = d; } }); // Now process the rows. Parallel.For( - startY, - endY, + minY, + maxY, this.ParallelOptions, y => { - if (y >= 0 && y < height) + // Ensure offsets are normalised for cropping and padding. + Weight[] verticalValues = this.VerticalWeights[y - startY].Values; + + for (int x = 0; x < width; x++) { - // Ensure offsets are normalised for cropping and padding. - Weight[] verticalValues = this.VerticalWeights[y - startY].Values; + // Destination color components + Vector4 destination = Vector4.Zero; - for (int x = 0; x < width; x++) + for (int i = 0; i < verticalValues.Length; i++) { - // Destination color components - Vector4 destination = Vector4.Zero; - - for (int i = 0; i < verticalValues.Length; i++) - { - Weight yw = verticalValues[i]; - destination += firstPassPixels[x, yw.Index].ToVector4() * yw.Value; - } - - T d = default(T); - d.PackVector(destination); - targetPixels[x, y] = d; + Weight yw = verticalValues[i]; + destination += firstPassPixels[x, yw.Index].ToVector4() * yw.Value; } + + T d = default(T); + d.PackVector(destination); + targetPixels[x, y] = d; } this.OnRowProcessed(); }); - } } } diff --git a/tests/ImageProcessorCore.Tests/Processors/Samplers/ResizeTests.cs b/tests/ImageProcessorCore.Tests/Processors/Samplers/ResizeTests.cs index 03812a886..997f30995 100644 --- a/tests/ImageProcessorCore.Tests/Processors/Samplers/ResizeTests.cs +++ b/tests/ImageProcessorCore.Tests/Processors/Samplers/ResizeTests.cs @@ -115,9 +115,9 @@ namespace ImageProcessorCore.Tests [Theory] [MemberData("ReSamplers")] - public void ImageShouldResizeWithCropMode(string name, IResampler sampler) + public void ImageShouldResizeWithCropWidthMode(string name, IResampler sampler) { - name = name + "-Crop"; + name = name + "-CropWidth"; if (!Directory.Exists(path)) { @@ -146,6 +146,39 @@ namespace ImageProcessorCore.Tests } } + [Theory] + [MemberData("ReSamplers")] + public void ImageShouldResizeWithCropHeightMode(string name, IResampler sampler) + { + name = name + "-CropHeight"; + + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + foreach (string file in Files) + { + using (FileStream stream = File.OpenRead(file)) + { + string filename = Path.GetFileNameWithoutExtension(file) + "-" + name + Path.GetExtension(file); + + Image image = new Image(stream); + using (FileStream output = File.OpenWrite($"{path}/{filename}")) + { + ResizeOptions options = new ResizeOptions() + { + Sampler = sampler, + Size = new Size(image.Width, image.Height / 2) + }; + + image.Resize(options, this.ProgressUpdate) + .Save(output); + } + } + } + } + [Theory] [MemberData("ReSamplers")] public void ImageShouldResizeWithPadMode(string name, IResampler sampler)