Browse Source

ResizeWindowOld

pull/888/head
Anton Firszov 7 years ago
parent
commit
4d38d7c426
  1. 122
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
  2. 7
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs
  3. 13
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

122
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs

@ -244,26 +244,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
// 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 (Buffer2D<Vector4> firstPassPixelsTransposed = source.MemoryAllocator.Allocate2D<Vector4>(sourceHeight, width))
//using (Buffer2D<Vector4> firstPassPixelsTransposed = source.MemoryAllocator.Allocate2D<Vector4>(sourceHeight, width))
using (var resizeWindow = new ResizeWindowOld(
configuration,
sourceRectangle,
conversionModifiers,
this.horizontalKernelMap,
this.verticalKernelMap,
sourceHeight,
width,
minX,
maxX,
startX))
using (IMemoryOwner<Vector4> tempBuffer = source.MemoryAllocator.Allocate<Vector4>(Math.Max(source.Width, width)))
{
firstPassPixelsTransposed.MemorySource.Clear();
Span<Vector4> tempRowSpan = tempBuffer.GetSpan().Slice(sourceX, source.Width - sourceX);
for (int y = 0; y < sourceRectangle.Bottom; y++)
{
Span<TPixel> sourceRow = source.GetPixelRowSpan(y).Slice(sourceX);
Span<Vector4> tempRowSpan = tempBuffer.GetSpan().Slice(sourceX, source.Width - sourceX);
PixelOperations<TPixel>.Instance.ToVector4(configuration, sourceRow, tempRowSpan, conversionModifiers);
ref Vector4 firstPassBaseRef = ref firstPassPixelsTransposed.Span[y];
for (int x = minX; x < maxX; x++)
{
ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - startX);
Unsafe.Add(ref firstPassBaseRef, x * sourceHeight) = kernel.Convolve(tempRowSpan);
}
}
resizeWindow.Initialize(source.PixelBuffer, tempRowSpan);
// Now process the rows.
Span<Vector4> tempColSpan = tempBuffer.GetSpan().Slice(0, width);
@ -277,7 +275,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
for (int x = 0; x < width; x++)
{
Span<Vector4> firstPassColumn = firstPassPixelsTransposed.GetRowSpan(x).Slice(sourceY);
Span<Vector4> firstPassColumn = resizeWindow.GetColumnSpan(x);
// Destination color components
Unsafe.Add(ref tempRowBase, x) = kernel.Convolve(firstPassColumn);
@ -301,4 +299,92 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
this.verticalKernelMap = null;
}
}
class ResizeWindowOld : IDisposable
{
private readonly Buffer2D<Vector4> buffer;
private readonly Configuration configuration;
private readonly Rectangle sourceRectangle;
private readonly PixelConversionModifiers conversionModifiers;
private readonly ResizeKernelMap horizontalKernelMap;
private readonly ResizeKernelMap verticalKernelMap;
private readonly int minX;
private readonly int maxX;
private readonly int startX;
public ResizeWindowOld(
Configuration configuration,
Rectangle sourceRectangle,
PixelConversionModifiers conversionModifiers,
ResizeKernelMap horizontalKernelMap,
ResizeKernelMap verticalKernelMap,
int sourceHeight,
int destWidth,
int minX,
int maxX,
int startX)
{
this.configuration = configuration;
this.sourceRectangle = sourceRectangle;
this.conversionModifiers = conversionModifiers;
this.horizontalKernelMap = horizontalKernelMap;
this.verticalKernelMap = verticalKernelMap;
this.minX = minX;
this.maxX = maxX;
this.startX = startX;
this.buffer = configuration.MemoryAllocator.Allocate2D<Vector4>(sourceRectangle.Height, destWidth, AllocationOptions.Clean);
this.Top = sourceRectangle.Top;
this.Bottom = sourceRectangle.Bottom;
}
public int Top { get; private set; }
public int Bottom { get; private set; }
public void Initialize<TPixel>(
Buffer2D<TPixel> source,
Span<Vector4> tempRowSpan)
where TPixel : struct, IPixel<TPixel>
{
for (int y = this.sourceRectangle.Top; y < this.sourceRectangle.Bottom; y++)
{
Span<TPixel> sourceRow = source.GetRowSpan(y).Slice(this.sourceRectangle.X);
PixelOperations<TPixel>.Instance.ToVector4(
this.configuration,
sourceRow,
tempRowSpan,
this.conversionModifiers);
ref Vector4 firstPassBaseRef = ref this.buffer.Span[y - this.sourceRectangle.Y];
for (int x = this.minX; x < this.maxX; x++)
{
ResizeKernel kernel = this.horizontalKernelMap.GetKernel(x - this.startX);
Unsafe.Add(ref firstPassBaseRef, x * this.sourceRectangle.Height) = kernel.Convolve(tempRowSpan);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<Vector4> GetColumnSpan(int x)
{
return this.buffer.GetRowSpan(x);
}
public void Dispose()
{
this.buffer.Dispose();
}
}
}

7
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs

@ -93,7 +93,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
GenerateImageResizeData();
[Theory(Skip = "Only for debugging and development")]
[Theory(
Skip = "Only for debugging and development"
)]
[MemberData(nameof(KernelMapData))]
public void PrintNonNormalizedKernelMap(string resamplerName, int srcSize, int destSize)
{
@ -130,7 +132,10 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
var referenceMap = ReferenceKernelMap.Calculate(resampler, destSize, srcSize);
var kernelMap = ResizeKernelMap.Calculate(resampler, destSize, srcSize, Configuration.Default.MemoryAllocator);
#if DEBUG
this.Output.WriteLine(kernelMap.Info);
this.Output.WriteLine($"Expected KernelMap:\n{PrintKernelMap(referenceMap)}\n");
this.Output.WriteLine($"Actual KernelMap:\n{PrintKernelMap(kernelMap)}\n");
#endif

13
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

@ -5,6 +5,7 @@ using System;
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Primitives;
using SixLabors.ImageSharp.Processing;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
@ -37,19 +38,23 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector;
[Theory]
[WithBasicTestPatternImages(15, 12, PixelTypes.Rgba32)]
public void Resize_BasicSmall<TPixel>(TestImageProvider<TPixel> provider)
[WithBasicTestPatternImages(15, 12, PixelTypes.Rgba32, 2, 3, 1, 2)]
[WithBasicTestPatternImages(2, 256, PixelTypes.Rgba32, 1, 1, 1, 8)]
[WithBasicTestPatternImages(2, 32, PixelTypes.Rgba32, 1, 1, 1, 2)]
public void Resize_BasicSmall<TPixel>(TestImageProvider<TPixel> provider, int wN, int wD, int hN, int hD)
where TPixel : struct, IPixel<TPixel>
{
// Basic test case, very helpful for debugging
// [WithBasicTestPatternImages(15, 12, PixelTypes.Rgba32, 2, 3, 1, 2)] means:
// resizing: (15, 12) -> (10, 6)
// kernel dimensions: (3, 4)
using (Image<TPixel> image = provider.GetImage())
{
var destSize = new Size(image.Width * 2 / 3, image.Height / 2);
var destSize = new Size(image.Width * wN / wD, image.Height * hN / hD);
image.Mutate(x => x.Resize(destSize, KnownResamplers.Bicubic, false));
image.DebugSave(provider);
FormattableString outputInfo = $"({wN}÷{wD},{hN}÷{hD})";
image.DebugSave(provider, outputInfo);
}
}

Loading…
Cancel
Save