From ec1971435b55510355f2dbf14891b69563b581c1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 18 Apr 2019 23:43:44 +0200 Subject: [PATCH] refactor - ResizeWindow -> ResizeWorker - Most logic moved to ResizeWorker --- .../Transforms/Resize/ResizeProcessor.cs | 42 ++--------- .../{ResizeWindow.cs => ResizeWorker.cs} | 72 +++++++++++++++---- 2 files changed, 62 insertions(+), 52 deletions(-) rename src/ImageSharp/Processing/Processors/Transforms/Resize/{ResizeWindow.cs => ResizeWorker.cs} (61%) diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index d61cc5e89c..63dd5efca0 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -237,11 +237,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms BufferArea sourceArea = source.PixelBuffer.GetArea(sourceRectangle); - // 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 (var resizeWindow = new ResizeWindow( + // If we want to reintroduce processing: + // it's possible to launch multiple workers for different regions of the image + using (var worker = new ResizeWorker( configuration, sourceArea, conversionModifiers, @@ -250,39 +248,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms width, destWorkingRect, startX)) - using (IMemoryOwner tempBuffer = source.MemoryAllocator.Allocate(width)) { - resizeWindow.Initialize(); - - // Now process the rows. - Span tempColSpan = tempBuffer.GetSpan(); - - for (int y = destWorkingRect.Top; y < destWorkingRect.Bottom; y++) - { - // Ensure offsets are normalized for cropping and padding. - ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); - - while (kernel.StartIndex + kernel.Length > resizeWindow.Bottom) - { - resizeWindow.Slide(); - } - - ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan); - - int top = kernel.StartIndex - resizeWindow.Top; - - for (int x = 0; x < width; x++) - { - Span firstPassColumn = resizeWindow.GetColumnSpan(x).Slice(top); - - // Destination color components - Unsafe.Add(ref tempRowBase, x) = kernel.ConvolveCore(firstPassColumn); - } - - Span targetRowSpan = destination.GetPixelRowSpan(y); - - PixelOperations.Instance.FromVector4Destructive(configuration, tempColSpan, targetRowSpan, conversionModifiers); - } + worker.Initialize(); + worker.FillDestinationPixels(destWorkingRect.Top, destWorkingRect.Bottom, startY, destination.PixelBuffer); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs similarity index 61% rename from src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs index afa9dac2d2..11b49b8631 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWindow.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; @@ -13,7 +14,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { - internal class ResizeWindow : IDisposable + internal class ResizeWorker : IDisposable where TPixel : struct, IPixel { private readonly Buffer2D buffer; @@ -32,15 +33,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly IMemoryOwner tempRowBuffer; + private readonly IMemoryOwner tempColumnBuffer; + private readonly ResizeKernelMap verticalKernelMap; + private readonly int destWidth; + private readonly Rectangle destWorkingRect; private readonly int diameter; private readonly int windowHeight; - public ResizeWindow( + public ResizeWorker( Configuration configuration, BufferArea source, PixelConversionModifiers conversionModifiers, @@ -56,6 +61,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.conversionModifiers = conversionModifiers; this.horizontalKernelMap = horizontalKernelMap; this.verticalKernelMap = verticalKernelMap; + this.destWidth = destWidth; this.destWorkingRect = destWorkingRect; this.startX = startX; @@ -67,22 +73,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.windowHeight, destWidth, AllocationOptions.Clean); + this.tempRowBuffer = configuration.MemoryAllocator.Allocate(this.sourceRectangle.Width); + this.tempColumnBuffer = configuration.MemoryAllocator.Allocate(destWidth); + + this.CurrentMinY = 0; + this.CurrentMaxY = this.windowHeight; } - public int Bottom { get; private set; } + public int CurrentMaxY { get; private set; } - public int Top { get; private set; } + public int CurrentMinY { get; private set; } public void Dispose() { this.buffer.Dispose(); + this.tempRowBuffer.Dispose(); + this.tempColumnBuffer.Dispose(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span GetColumnSpan(int x, int startY) { - return this.buffer.GetRowSpan(x).Slice(startY - this.Top); + return this.buffer.GetRowSpan(x).Slice(startY - this.CurrentMinY); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -93,23 +106,52 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public void Initialize() { - this.Initialize(0, this.windowHeight); + this.CalculateFirstPassValues(0, this.windowHeight); } - public void Slide() + public void FillDestinationPixels(int minY, int maxY, int startY, Buffer2D destination) { - int top = this.Top + this.diameter; - int bottom = Math.Min(this.Bottom + this.diameter, this.sourceRectangle.Height); - this.Initialize(top, bottom); + Span tempColSpan = this.tempColumnBuffer.GetSpan(); + + for (int y = minY; y < maxY; y++) + { + // Ensure offsets are normalized for cropping and padding. + ResizeKernel kernel = this.verticalKernelMap.GetKernel(y - startY); + + while (kernel.StartIndex + kernel.Length > this.CurrentMaxY) + { + this.Slide(); + } + + ref Vector4 tempRowBase = ref MemoryMarshal.GetReference(tempColSpan); + + int top = kernel.StartIndex - this.CurrentMinY; + + for (int x = 0; x < this.destWidth; x++) + { + Span firstPassColumn = this.GetColumnSpan(x).Slice(top); + + // Destination color components + Unsafe.Add(ref tempRowBase, x) = kernel.ConvolveCore(firstPassColumn); + } + + Span targetRowSpan = destination.GetRowSpan(y); + + PixelOperations.Instance.FromVector4Destructive(configuration, tempColSpan, targetRowSpan, conversionModifiers); + } } - private void Initialize(int top, int bottom) + public void Slide() { - this.Top = top; - this.Bottom = bottom; + this.CurrentMinY = this.CurrentMinY + this.diameter; + this.CurrentMaxY = Math.Min(this.CurrentMaxY + this.diameter, this.sourceRectangle.Height); + this.CalculateFirstPassValues(this.CurrentMinY, this.CurrentMaxY); + } + private void CalculateFirstPassValues(int minY, int maxY) + { Span tempRowSpan = this.tempRowBuffer.GetSpan(); - for (int y = top; y < bottom; y++) + for (int y = minY; y < maxY; y++) { Span sourceRow = this.source.GetRowSpan(y); @@ -120,7 +162,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.conversionModifiers); //ref Vector4 firstPassBaseRef = ref this.buffer.Span[y - top]; - Span firstPassSpan = this.buffer.Span.Slice(y - top); + Span firstPassSpan = this.buffer.Span.Slice(y - minY); for (int x = this.destWorkingRect.Left; x < this.destWorkingRect.Right; x++) {