From 740411a684cdffd3dee4af7216092e4b059a45c3 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 18 Mar 2017 19:47:22 +0100 Subject: [PATCH] merged ResizeProcessor and CompandingResizeProcessor --- .../Transforms/CompandingResizeProcessor.cs | 177 ------------------ .../ResamplingWeightedProcessor.Weights.cs | 24 +++ .../Transforms/ResamplingWeightedProcessor.cs | 1 - .../Processors/Transforms/ResizeProcessor.cs | 48 +++-- .../Processing/Transforms/Resize.cs | 12 +- .../Processors/Filters/ResizeTests.cs | 26 +++ 6 files changed, 88 insertions(+), 200 deletions(-) delete mode 100644 src/ImageSharp/Processing/Processors/Transforms/CompandingResizeProcessor.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/CompandingResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CompandingResizeProcessor.cs deleted file mode 100644 index c8a43b3d9..000000000 --- a/src/ImageSharp/Processing/Processors/Transforms/CompandingResizeProcessor.cs +++ /dev/null @@ -1,177 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp.Processing.Processors -{ - using System; - using System.Numerics; - using System.Threading.Tasks; - - /// - /// Provides methods that allow the resizing of images using various algorithms. - /// This version will expand and compress the image to and from a linear color space during processing. - /// - /// The pixel format. - internal class CompandingResizeProcessor : ResamplingWeightedProcessor - where TColor : struct, IPixel - { - /// - /// Initializes a new instance of the class. - /// - /// The sampler to perform the resize operation. - /// The target width. - /// The target height. - public CompandingResizeProcessor(IResampler sampler, int width, int height) - : base(sampler, width, height, new Rectangle(0, 0, width, height)) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The sampler to perform the resize operation. - /// The target width. - /// The target height. - /// - /// The structure that specifies the portion of the target image object to draw to. - /// - public CompandingResizeProcessor(IResampler sampler, int width, int height, Rectangle resizeRectangle) - : base(sampler, width, height, resizeRectangle) - { - } - - /// - public override bool Compand { get; set; } = true; - - /// - protected override unsafe void OnApply(ImageBase source, Rectangle sourceRectangle) - { - // Jump out, we'll deal with that later. - if (source.Width == this.Width && source.Height == this.Height && sourceRectangle == this.ResizeRectangle) - { - return; - } - - int width = this.Width; - int height = this.Height; - int sourceX = sourceRectangle.X; - int sourceY = sourceRectangle.Y; - int startY = this.ResizeRectangle.Y; - int endY = this.ResizeRectangle.Bottom; - int startX = this.ResizeRectangle.X; - int endX = this.ResizeRectangle.Right; - - int minX = Math.Max(0, startX); - int maxX = Math.Min(width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(height, endY); - - if (this.Sampler is NearestNeighborResampler) - { - // Scaling factors - float widthFactor = sourceRectangle.Width / (float)this.ResizeRectangle.Width; - float heightFactor = sourceRectangle.Height / (float)this.ResizeRectangle.Height; - - using (PixelAccessor targetPixels = new PixelAccessor(width, height)) - { - using (PixelAccessor sourcePixels = source.Lock()) - { - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - // Y coordinates of source points - int originY = (int)(((y - startY) * heightFactor) + sourceY); - - for (int x = minX; x < maxX; x++) - { - // X coordinates of source points - targetPixels[x, y] = sourcePixels[(int)(((x - startX) * widthFactor) + sourceX), originY]; - } - }); - } - - // Break out now. - source.SwapPixelsBuffers(targetPixels); - return; - } - } - - // Interpolate the image using the calculated weights. - // A 2-pass 1D algorithm appears to be faster than splitting a 1-pass 2D algorithm - // First process the columns. Since we are not using multiple threads startY and endY - // are the upper and lower bounds of the source rectangle. - using (PixelAccessor targetPixels = new PixelAccessor(width, height)) - { - using (PixelAccessor sourcePixels = source.Lock()) - using (PixelAccessor firstPassPixels = new PixelAccessor(width, source.Height)) - { - Parallel.For( - 0, - sourceRectangle.Bottom, - this.ParallelOptions, - y => - { - for (int x = minX; x < maxX; x++) - { - // Ensure offsets are normalised for cropping and padding. - WeightsWindow ws = this.HorizontalWeights.Weights[x - startX]; - float* horizontalValues = ws.Ptr; - int left = ws.Left; - - // Destination color components - Vector4 destination = Vector4.Zero; - - for (int i = 0; i < ws.Length; i++) - { - float xw = horizontalValues[i]; - int index = left + i; - destination += sourcePixels[index, y].ToVector4().Expand() * xw; - } - - TColor d = default(TColor); - d.PackFromVector4(destination.Compress()); - firstPassPixels[x, y] = d; - } - }); - - // Now process the rows. - Parallel.For( - minY, - maxY, - this.ParallelOptions, - y => - { - // Ensure offsets are normalised for cropping and padding. - WeightsWindow ws = this.VerticalWeights.Weights[y - startY]; - float* verticalValues = ws.Ptr; - int left = ws.Left; - - for (int x = 0; x < width; x++) - { - // Destination color components - Vector4 destination = Vector4.Zero; - - for (int i = 0; i < ws.Length; i++) - { - float yw = verticalValues[i]; - int index = left + i; - destination += firstPassPixels[x, index].ToVector4().Expand() * yw; - } - - TColor d = default(TColor); - d.PackFromVector4(destination.Compress()); - targetPixels[x, y] = d; - } - }); - } - - source.SwapPixelsBuffers(targetPixels); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs index 00a81100d..c7386487a 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs @@ -69,6 +69,30 @@ namespace ImageSharp.Processing.Processors return result; } + /// + /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance. + /// Applies to all input vectors. + /// + /// The input span of vectors + /// The weighted sum + public Vector4 ComputeExpandedWeightedRowSum(BufferSpan rowSpan) + { + float* horizontalValues = this.Ptr; + int left = this.Left; + + // Destination color components + Vector4 result = Vector4.Zero; + + for (int i = 0; i < this.Length; i++) + { + float xw = horizontalValues[i]; + int index = left + i; + result += rowSpan[index].Expand() * xw; + } + + return result; + } + /// /// Computes the sum of vectors in 'firstPassPixels' at a column pointed by 'x', /// weighted by weight values, pointed by this instance. diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs index f74bf7edd..d1a709384 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs @@ -156,6 +156,5 @@ namespace ImageSharp.Processing.Processors return result; } - } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs index 77b3f3b6f..944e245ac 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs @@ -12,9 +12,6 @@ namespace ImageSharp.Processing.Processors /// /// Provides methods that allow the resizing of images using various algorithms. /// - /// - /// This version and the have been separated out to improve performance. - /// /// The pixel format. internal class ResizeProcessor : ResamplingWeightedProcessor where TColor : struct, IPixel @@ -123,15 +120,27 @@ namespace ImageSharp.Processing.Processors using (PinnedBuffer tempRowBuffer = new PinnedBuffer(sourcePixels.Width)) { BufferSpan sourceRow = sourcePixels.GetRowSpan(y); + BulkPixelOperations.Instance.ToVector4( sourceRow, tempRowBuffer, sourceRow.Length); - for (int x = minX; x < maxX; x++) + if (this.Compand) + { + for (int x = minX; x < maxX; x++) + { + WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; + firstPassPixels[x, y] = window.ComputeExpandedWeightedRowSum(tempRowBuffer); + } + } + else { - WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; - firstPassPixels[x, y] = window.ComputeWeightedRowSum(tempRowBuffer); + for (int x = minX; x < maxX; x++) + { + WeightsWindow window = this.HorizontalWeights.Weights[x - startX]; + firstPassPixels[x, y] = window.ComputeWeightedRowSum(tempRowBuffer); + } } } }); @@ -146,14 +155,29 @@ namespace ImageSharp.Processing.Processors // Ensure offsets are normalised for cropping and padding. WeightsWindow window = this.VerticalWeights.Weights[y - startY]; - for (int x = 0; x < width; x++) + if (this.Compand) { - // Destination color components - Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x); + for (int x = 0; x < width; x++) + { + // Destination color components + Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x); + destination = destination.Compress(); + TColor d = default(TColor); + d.PackFromVector4(destination); + targetPixels[x, y] = d; + } + } + else + { + for (int x = 0; x < width; x++) + { + // Destination color components + Vector4 destination = window.ComputeWeightedColumnSum(firstPassPixels, x); - TColor d = default(TColor); - d.PackFromVector4(destination); - targetPixels[x, y] = d; + TColor d = default(TColor); + d.PackFromVector4(destination); + targetPixels[x, y] = d; + } } }); } diff --git a/src/ImageSharp/Processing/Transforms/Resize.cs b/src/ImageSharp/Processing/Transforms/Resize.cs index ab256c4ae..1952aa1a7 100644 --- a/src/ImageSharp/Processing/Transforms/Resize.cs +++ b/src/ImageSharp/Processing/Transforms/Resize.cs @@ -156,16 +156,8 @@ namespace ImageSharp Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - ResamplingWeightedProcessor processor; - - if (compand) - { - processor = new CompandingResizeProcessor(sampler, width, height, targetRectangle); - } - else - { - processor = new ResizeProcessor(sampler, width, height, targetRectangle); - } + ResizeProcessor processor = + new ResizeProcessor(sampler, width, height, targetRectangle) { Compand = compand }; source.ApplyProcessor(processor, sourceRectangle); return source; diff --git a/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs b/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs index 643033f4c..35994e028 100644 --- a/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processors/Filters/ResizeTests.cs @@ -8,6 +8,32 @@ namespace ImageSharp.Tests using System.IO; using Processing; using Xunit; + using Xunit.Abstractions; + + public class ResizeProfilingBenchmarks : MeasureFixture + { + public ResizeProfilingBenchmarks(ITestOutputHelper output) + : base(output) + { + } + + public int ExecutionCount { get; set; } = 50; + + [Theory] + [InlineData(100, 100)] + [InlineData(1000, 1000)] + public void ResizeBicubic(int width, int height) + { + this.Measure(this.ExecutionCount, + () => + { + using (Image image = new Image(width, height)) + { + image.Resize(width / 4, height / 4); + } + }); + } + } public class ResizeTests : FileTestBase {