Browse Source

Make ResizeKernelMap non-generic.

pull/1118/head
James Jackson-South 6 years ago
parent
commit
f9fb732e22
  1. 13
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResamplerExtensions.cs
  2. 4
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs
  3. 15
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs
  4. 34
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
  5. 12
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs
  6. 8
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs
  7. 21
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.cs
  8. 2
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

13
src/ImageSharp/Processing/Processors/Transforms/Resize/ResamplerExtensions.cs

@ -78,13 +78,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
// Since all image frame dimensions have to be the same we can calculate // Since all image frame dimensions have to be the same we can calculate
// the kernel maps and reuse for all frames. // the kernel maps and reuse for all frames.
MemoryAllocator allocator = configuration.MemoryAllocator; MemoryAllocator allocator = configuration.MemoryAllocator;
using var horizontalKernelMap = ResizeKernelMap<TResampler>.Calculate( using var horizontalKernelMap = ResizeKernelMap.Calculate(
in sampler, in sampler,
destinationRectangle.Width, destinationRectangle.Width,
sourceRectangle.Width, sourceRectangle.Width,
allocator); allocator);
using var verticalKernelMap = ResizeKernelMap<TResampler>.Calculate( using var verticalKernelMap = ResizeKernelMap.Calculate(
in sampler, in sampler,
destinationRectangle.Height, destinationRectangle.Height,
sourceRectangle.Height, sourceRectangle.Height,
@ -135,17 +135,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
in operation); in operation);
} }
private static void ApplyResizeFrameTransform<TResampler, TPixel>( private static void ApplyResizeFrameTransform<TPixel>(
Configuration configuration, Configuration configuration,
ImageFrame<TPixel> source, ImageFrame<TPixel> source,
ImageFrame<TPixel> destination, ImageFrame<TPixel> destination,
ResizeKernelMap<TResampler> horizontalKernelMap, ResizeKernelMap horizontalKernelMap,
ResizeKernelMap<TResampler> verticalKernelMap, ResizeKernelMap verticalKernelMap,
Rectangle sourceRectangle, Rectangle sourceRectangle,
Rectangle destinationRectangle, Rectangle destinationRectangle,
Rectangle interest, Rectangle interest,
bool compand) bool compand)
where TResampler : unmanaged, IResampler
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
PixelConversionModifiers conversionModifiers = PixelConversionModifiers conversionModifiers =
@ -155,7 +154,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
// To reintroduce parallel processing, we would launch multiple workers // To reintroduce parallel processing, we would launch multiple workers
// for different row intervals of the image. // for different row intervals of the image.
using (var worker = new ResizeWorker<TResampler, TPixel>( using (var worker = new ResizeWorker<TPixel>(
configuration, configuration,
sourceArea, sourceArea,
conversionModifiers, conversionModifiers,

4
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs

@ -8,7 +8,7 @@ using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
/// <summary> /// <summary>
/// Points to a collection of of weights allocated in <see cref="ResizeKernelMap{T}"/>. /// Points to a collection of of weights allocated in <see cref="ResizeKernelMap"/>.
/// </summary> /// </summary>
internal readonly unsafe struct ResizeKernel internal readonly unsafe struct ResizeKernel
{ {
@ -44,7 +44,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
} }
/// <summary> /// <summary>
/// Gets the span representing the portion of the <see cref="ResizeKernelMap{T}"/> that this window covers. /// Gets the span representing the portion of the <see cref="ResizeKernelMap"/> that this window covers.
/// </summary> /// </summary>
/// <value>The <see cref="Span{T}"/>. /// <value>The <see cref="Span{T}"/>.
/// </value> /// </value>

15
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs

@ -5,13 +5,12 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
internal partial class ResizeKernelMap<TResampler> internal partial class ResizeKernelMap
where TResampler : unmanaged, IResampler
{ {
/// <summary> /// <summary>
/// Memory-optimized <see cref="ResizeKernelMap{TResampler}"/> where repeating rows are stored only once. /// Memory-optimized <see cref="ResizeKernelMap"/> where repeating rows are stored only once.
/// </summary> /// </summary>
private sealed class PeriodicKernelMap : ResizeKernelMap<TResampler> private sealed class PeriodicKernelMap : ResizeKernelMap
{ {
private readonly int period; private readonly int period;
@ -19,7 +18,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public PeriodicKernelMap( public PeriodicKernelMap(
MemoryAllocator memoryAllocator, MemoryAllocator memoryAllocator,
TResampler sampler,
int sourceLength, int sourceLength,
int destinationLength, int destinationLength,
double ratio, double ratio,
@ -29,7 +27,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int cornerInterval) int cornerInterval)
: base( : base(
memoryAllocator, memoryAllocator,
sampler,
sourceLength, sourceLength,
destinationLength, destinationLength,
(cornerInterval * 2) + period, (cornerInterval * 2) + period,
@ -43,14 +40,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
internal override string Info => base.Info + $"|period:{this.period}|cornerInterval:{this.cornerInterval}"; internal override string Info => base.Info + $"|period:{this.period}|cornerInterval:{this.cornerInterval}";
protected internal override void Initialize() protected internal override void Initialize<TResampler>(in TResampler sampler)
{ {
// Build top corner data + one period of the mosaic data: // Build top corner data + one period of the mosaic data:
int startOfFirstRepeatedMosaic = this.cornerInterval + this.period; int startOfFirstRepeatedMosaic = this.cornerInterval + this.period;
for (int i = 0; i < startOfFirstRepeatedMosaic; i++) for (int i = 0; i < startOfFirstRepeatedMosaic; i++)
{ {
this.kernels[i] = this.BuildKernel(i, i); this.kernels[i] = this.BuildKernel(in sampler, i, i);
} }
// Copy the mosaics: // Copy the mosaics:
@ -67,7 +64,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int bottomStartData = this.cornerInterval + this.period; int bottomStartData = this.cornerInterval + this.period;
for (int i = 0; i < this.cornerInterval; i++) for (int i = 0; i < this.cornerInterval; i++)
{ {
this.kernels[bottomStartDest + i] = this.BuildKernel(bottomStartDest + i, bottomStartData + i); this.kernels[bottomStartDest + i] = this.BuildKernel(in sampler, bottomStartDest + i, bottomStartData + i);
} }
} }
} }

34
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs

@ -12,14 +12,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <summary> /// <summary>
/// Provides resize kernel values from an optimized contiguous memory region. /// Provides resize kernel values from an optimized contiguous memory region.
/// </summary> /// </summary>
/// <typeparam name="TResampler">The type of sampler.</typeparam> internal partial class ResizeKernelMap : IDisposable
internal partial class ResizeKernelMap<TResampler> : IDisposable
where TResampler : unmanaged, IResampler
{ {
private static readonly TolerantMath TolerantMath = TolerantMath.Default; private static readonly TolerantMath TolerantMath = TolerantMath.Default;
private readonly TResampler sampler;
private readonly int sourceLength; private readonly int sourceLength;
private readonly double ratio; private readonly double ratio;
@ -41,7 +37,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private ResizeKernelMap( private ResizeKernelMap(
MemoryAllocator memoryAllocator, MemoryAllocator memoryAllocator,
TResampler sampler,
int sourceLength, int sourceLength,
int destinationLength, int destinationLength,
int bufferHeight, int bufferHeight,
@ -49,7 +44,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
double scale, double scale,
int radius) int radius)
{ {
this.sampler = sampler;
this.ratio = ratio; this.ratio = ratio;
this.scale = scale; this.scale = scale;
this.radius = radius; this.radius = radius;
@ -79,7 +73,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
$"radius:{this.radius}|sourceSize:{this.sourceLength}|destinationSize:{this.DestinationLength}|ratio:{this.ratio}|scale:{this.scale}"; $"radius:{this.radius}|sourceSize:{this.sourceLength}|destinationSize:{this.DestinationLength}|ratio:{this.ratio}|scale:{this.scale}";
/// <summary> /// <summary>
/// Disposes <see cref="ResizeKernelMap{TResampler}"/> instance releasing it's backing buffer. /// Disposes <see cref="ResizeKernelMap"/> instance releasing it's backing buffer.
/// </summary> /// </summary>
public void Dispose() public void Dispose()
=> this.Dispose(true); => this.Dispose(true);
@ -111,16 +105,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <summary> /// <summary>
/// Computes the weights to apply at each pixel when resizing. /// Computes the weights to apply at each pixel when resizing.
/// </summary> /// </summary>
/// <typeparam name="TResampler">The type of sampler.</typeparam>
/// <param name="sampler">The <see cref="IResampler"/></param> /// <param name="sampler">The <see cref="IResampler"/></param>
/// <param name="destinationSize">The destination size</param> /// <param name="destinationSize">The destination size</param>
/// <param name="sourceSize">The source size</param> /// <param name="sourceSize">The source size</param>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations</param> /// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations</param>
/// <returns>The <see cref="ResizeKernelMap{IResampler}"/></returns> /// <returns>The <see cref="ResizeKernelMap"/></returns>
public static ResizeKernelMap<TResampler> Calculate( public static ResizeKernelMap Calculate<TResampler>(
in TResampler sampler, in TResampler sampler,
int destinationSize, int destinationSize,
int sourceSize, int sourceSize,
MemoryAllocator memoryAllocator) MemoryAllocator memoryAllocator)
where TResampler : unmanaged, IResampler
{ {
double ratio = (double)sourceSize / destinationSize; double ratio = (double)sourceSize / destinationSize;
double scale = ratio; double scale = ratio;
@ -158,10 +154,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
// If we don't have at least 2 periods, we go with the basic implementation: // If we don't have at least 2 periods, we go with the basic implementation:
bool hasAtLeast2Periods = 2 * (cornerInterval + period) < destinationSize; bool hasAtLeast2Periods = 2 * (cornerInterval + period) < destinationSize;
ResizeKernelMap<TResampler> result = hasAtLeast2Periods ResizeKernelMap result = hasAtLeast2Periods
? new PeriodicKernelMap( ? new PeriodicKernelMap(
memoryAllocator, memoryAllocator,
sampler,
sourceSize, sourceSize,
destinationSize, destinationSize,
ratio, ratio,
@ -169,9 +164,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
radius, radius,
period, period,
cornerInterval) cornerInterval)
: new ResizeKernelMap<TResampler>( : new ResizeKernelMap(
memoryAllocator, memoryAllocator,
sampler,
sourceSize, sourceSize,
destinationSize, destinationSize,
destinationSize, destinationSize,
@ -179,7 +173,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
scale, scale,
radius); radius);
result.Initialize(); result.Initialize(in sampler);
return result; return result;
} }
@ -187,11 +181,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <summary> /// <summary>
/// Initializes the kernel map. /// Initializes the kernel map.
/// </summary> /// </summary>
protected internal virtual void Initialize() protected internal virtual void Initialize<TResampler>(in TResampler sampler)
where TResampler : unmanaged, IResampler
{ {
for (int i = 0; i < this.DestinationLength; i++) for (int i = 0; i < this.DestinationLength; i++)
{ {
this.kernels[i] = this.BuildKernel(i, i); this.kernels[i] = this.BuildKernel(in sampler, i, i);
} }
} }
@ -200,7 +195,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// referencing the data at row <paramref name="dataRowIndex"/> within <see cref="data"/>, /// referencing the data at row <paramref name="dataRowIndex"/> within <see cref="data"/>,
/// so the data reusable by other data rows. /// so the data reusable by other data rows.
/// </summary> /// </summary>
private ResizeKernel BuildKernel(int destRowIndex, int dataRowIndex) private ResizeKernel BuildKernel<TResampler>(in TResampler sampler, int destRowIndex, int dataRowIndex)
where TResampler : unmanaged, IResampler
{ {
double center = ((destRowIndex + .5) * this.ratio) - .5; double center = ((destRowIndex + .5) * this.ratio) - .5;
@ -224,7 +220,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
for (int j = left; j <= right; j++) for (int j = left; j <= right; j++)
{ {
double value = this.sampler.GetValue((float)((j - center) / this.scale)); double value = sampler.GetValue((float)((j - center) / this.scale));
sum += value; sum += value;
kernelValues[j - left] = value; kernelValues[j - left] = value;

12
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeWorker.cs

@ -6,7 +6,6 @@ using System.Buffers;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
@ -19,8 +18,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// When sliding the window, the contents of the bottom window band are copied to the new top band. /// When sliding the window, the contents of the bottom window band are copied to the new top band.
/// For more details, and visual explanation, see "ResizeWorker.pptx". /// For more details, and visual explanation, see "ResizeWorker.pptx".
/// </summary> /// </summary>
internal sealed class ResizeWorker<TResampler, TPixel> : IDisposable internal sealed class ResizeWorker<TPixel> : IDisposable
where TResampler : unmanaged, IResampler
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
private readonly Buffer2D<Vector4> transposedFirstPassBuffer; private readonly Buffer2D<Vector4> transposedFirstPassBuffer;
@ -29,7 +27,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly PixelConversionModifiers conversionModifiers; private readonly PixelConversionModifiers conversionModifiers;
private readonly ResizeKernelMap<TResampler> horizontalKernelMap; private readonly ResizeKernelMap horizontalKernelMap;
private readonly BufferArea<TPixel> source; private readonly BufferArea<TPixel> source;
@ -39,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly IMemoryOwner<Vector4> tempColumnBuffer; private readonly IMemoryOwner<Vector4> tempColumnBuffer;
private readonly ResizeKernelMap<TResampler> verticalKernelMap; private readonly ResizeKernelMap verticalKernelMap;
private readonly int destWidth; private readonly int destWidth;
@ -57,8 +55,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
Configuration configuration, Configuration configuration,
BufferArea<TPixel> source, BufferArea<TPixel> source,
PixelConversionModifiers conversionModifiers, PixelConversionModifiers conversionModifiers,
ResizeKernelMap<TResampler> horizontalKernelMap, ResizeKernelMap horizontalKernelMap,
ResizeKernelMap<TResampler> verticalKernelMap, ResizeKernelMap verticalKernelMap,
int destWidth, int destWidth,
Rectangle targetWorkingRect, Rectangle targetWorkingRect,
Point targetOrigin) Point targetOrigin)

8
tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeKernelMapTests.ReferenceKernelMap.cs

@ -13,8 +13,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
/// <summary> /// <summary>
/// Simplified reference implementation for <see cref="ResizeKernelMap"/> functionality. /// Simplified reference implementation for <see cref="ResizeKernelMap"/> functionality.
/// </summary> /// </summary>
internal class ReferenceKernelMap<TResampler> internal class ReferenceKernelMap
where TResampler : unmanaged, IResampler
{ {
private readonly ReferenceKernel[] kernels; private readonly ReferenceKernel[] kernels;
@ -27,7 +26,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
public ReferenceKernel GetKernel(int destinationIndex) => this.kernels[destinationIndex]; public ReferenceKernel GetKernel(int destinationIndex) => this.kernels[destinationIndex];
public static ReferenceKernelMap<TResampler> Calculate(TResampler sampler, int destinationSize, int sourceSize, bool normalize = true) public static ReferenceKernelMap Calculate<TResampler>(in TResampler sampler, int destinationSize, int sourceSize, bool normalize = true)
where TResampler : unmanaged, IResampler
{ {
double ratio = (double)sourceSize / destinationSize; double ratio = (double)sourceSize / destinationSize;
double scale = ratio; double scale = ratio;
@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
result.Add(new ReferenceKernel(left, floatVals)); result.Add(new ReferenceKernel(left, floatVals));
} }
return new ReferenceKernelMap<TResampler>(result.ToArray()); return new ReferenceKernelMap(result.ToArray());
} }
} }

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

@ -89,7 +89,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
public void PrintNonNormalizedKernelMap<TResampler>(TResampler resampler, int srcSize, int destSize) public void PrintNonNormalizedKernelMap<TResampler>(TResampler resampler, int srcSize, int destSize)
where TResampler : unmanaged, IResampler where TResampler : unmanaged, IResampler
{ {
var kernelMap = ReferenceKernelMap<TResampler>.Calculate(resampler, destSize, srcSize, false); var kernelMap = ReferenceKernelMap.Calculate<TResampler>(in resampler, destSize, srcSize, false);
this.Output.WriteLine($"Actual KernelMap:\n{PrintKernelMap(kernelMap)}\n"); this.Output.WriteLine($"Actual KernelMap:\n{PrintKernelMap(kernelMap)}\n");
} }
@ -117,8 +117,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
private void VerifyKernelMapContentIsCorrect<TResampler>(TResampler resampler, int srcSize, int destSize) private void VerifyKernelMapContentIsCorrect<TResampler>(TResampler resampler, int srcSize, int destSize)
where TResampler : unmanaged, IResampler where TResampler : unmanaged, IResampler
{ {
var referenceMap = ReferenceKernelMap<TResampler>.Calculate(resampler, destSize, srcSize); var referenceMap = ReferenceKernelMap.Calculate(in resampler, destSize, srcSize);
var kernelMap = ResizeKernelMap<TResampler>.Calculate(resampler, destSize, srcSize, Configuration.Default.MemoryAllocator); var kernelMap = ResizeKernelMap.Calculate(in resampler, destSize, srcSize, Configuration.Default.MemoryAllocator);
#if DEBUG #if DEBUG
this.Output.WriteLine(kernelMap.Info); this.Output.WriteLine(kernelMap.Info);
@ -153,23 +153,20 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
} }
} }
private static string PrintKernelMap<TResampler>(ResizeKernelMap<TResampler> kernelMap) private static string PrintKernelMap(ResizeKernelMap kernelMap)
where TResampler : unmanaged, IResampler => PrintKernelMap(kernelMap, km => km.DestinationLength, (km, i) => km.GetKernel(i));
=> PrintKernelMap<TResampler, ResizeKernelMap<TResampler>>(kernelMap, km => km.DestinationLength, (km, i) => km.GetKernel(i));
private static string PrintKernelMap<TResampler>(ReferenceKernelMap<TResampler> kernelMap) private static string PrintKernelMap(ReferenceKernelMap kernelMap)
where TResampler : unmanaged, IResampler => PrintKernelMap(kernelMap, km => km.DestinationSize, (km, i) => km.GetKernel(i));
=> PrintKernelMap<TResampler, ReferenceKernelMap<TResampler>>(kernelMap, km => km.DestinationSize, (km, i) => km.GetKernel(i));
private static string PrintKernelMap<TResampler, TKernelMap>( private static string PrintKernelMap<TKernelMap>(
TKernelMap kernelMap, TKernelMap kernelMap,
Func<TKernelMap, int> getDestinationSize, Func<TKernelMap, int> getDestinationSize,
Func<TKernelMap, int, ReferenceKernel> getKernel) Func<TKernelMap, int, ReferenceKernel> getKernel)
where TResampler : unmanaged, IResampler
{ {
var bld = new StringBuilder(); var bld = new StringBuilder();
if (kernelMap is ResizeKernelMap<TResampler> actualMap) if (kernelMap is ResizeKernelMap actualMap)
{ {
bld.AppendLine(actualMap.Info); bld.AppendLine(actualMap.Info);
} }

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

@ -121,7 +121,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
configuration.MemoryAllocator = allocator; configuration.MemoryAllocator = allocator;
configuration.WorkingBufferSizeHintInBytes = workingBufferSizeHintInBytes; configuration.WorkingBufferSizeHintInBytes = workingBufferSizeHintInBytes;
var verticalKernelMap = ResizeKernelMap<BicubicResampler>.Calculate( var verticalKernelMap = ResizeKernelMap.Calculate<BicubicResampler>(
default, default,
destSize.Height, destSize.Height,
image0.Height, image0.Height,

Loading…
Cancel
Save