Browse Source

rename KernelMap to ResizeKernelMap, introduce MosaicKernelMap, move source code

af/merge-core
Anton Firszov 8 years ago
parent
commit
ca61df8e64
  1. 3
      src/ImageSharp/Common/Helpers/ImageMaths.cs
  2. 4
      src/ImageSharp/ImageSharp.csproj.DotSettings
  3. 0
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs
  4. 0
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs
  5. 0
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/CatmullRomResampler.cs
  6. 0
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/HermiteResampler.cs
  7. 0
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos2Resampler.cs
  8. 0
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos3Resampler.cs
  9. 0
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos5Resampler.cs
  10. 0
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos8Resampler.cs
  11. 0
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/MitchellNetravaliResampler.cs
  12. 0
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs
  13. 0
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxResampler.cs
  14. 0
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxSharpResampler.cs
  15. 0
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/SplineResampler.cs
  16. 0
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs
  17. 0
      src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs
  18. 22
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs
  19. 46
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.MosaicKernelMap.cs
  20. 70
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
  21. 8
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
  22. 2
      tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.ReferenceKernelMap.cs
  23. 4
      tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.cs
  24. 1
      tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs

3
src/ImageSharp/Common/Helpers/ImageMaths.cs

@ -5,6 +5,7 @@ using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.Primitives; using SixLabors.Primitives;
namespace SixLabors.ImageSharp namespace SixLabors.ImageSharp
@ -98,7 +99,7 @@ namespace SixLabors.ImageSharp
/// <summary> /// <summary>
/// Determine the Least Common Multiple (LCM) of two numbers. /// Determine the Least Common Multiple (LCM) of two numbers.
/// TODO: This method might be useful for building a more compact <see cref="Processing.Processors.Transforms.KernelMap"/> /// TODO: This method might be useful for building a more compact <see cref="ResizeKernelMap"/>
/// </summary> /// </summary>
public static int LeastCommonMultiple(int a, int b) public static int LeastCommonMultiple(int a, int b)
{ {

4
src/ImageSharp/ImageSharp.csproj.DotSettings

@ -5,4 +5,6 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cpackedpixels/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cpackedpixels/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cpixelimplementations/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cpixelimplementations/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cpixeltypes/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cpixeltypes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cutils/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cutils/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=processing_005Cprocessors_005Ctransforms_005Cresamplers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=processing_005Cprocessors_005Ctransforms_005Cresize/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

0
src/ImageSharp/Processing/Processors/Transforms/BicubicResampler.cs → src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs

0
src/ImageSharp/Processing/Processors/Transforms/BoxResampler.cs → src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs

0
src/ImageSharp/Processing/Processors/Transforms/CatmullRomResampler.cs → src/ImageSharp/Processing/Processors/Transforms/Resamplers/CatmullRomResampler.cs

0
src/ImageSharp/Processing/Processors/Transforms/HermiteResampler.cs → src/ImageSharp/Processing/Processors/Transforms/Resamplers/HermiteResampler.cs

0
src/ImageSharp/Processing/Processors/Transforms/Lanczos2Resampler.cs → src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos2Resampler.cs

0
src/ImageSharp/Processing/Processors/Transforms/Lanczos3Resampler.cs → src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos3Resampler.cs

0
src/ImageSharp/Processing/Processors/Transforms/Lanczos5Resampler.cs → src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos5Resampler.cs

0
src/ImageSharp/Processing/Processors/Transforms/Lanczos8Resampler.cs → src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos8Resampler.cs

0
src/ImageSharp/Processing/Processors/Transforms/MitchellNetravaliResampler.cs → src/ImageSharp/Processing/Processors/Transforms/Resamplers/MitchellNetravaliResampler.cs

0
src/ImageSharp/Processing/Processors/Transforms/NearestNeighborResampler.cs → src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs

0
src/ImageSharp/Processing/Processors/Transforms/RobidouxResampler.cs → src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxResampler.cs

0
src/ImageSharp/Processing/Processors/Transforms/RobidouxSharpResampler.cs → src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxSharpResampler.cs

0
src/ImageSharp/Processing/Processors/Transforms/SplineResampler.cs → src/ImageSharp/Processing/Processors/Transforms/Resamplers/SplineResampler.cs

0
src/ImageSharp/Processing/Processors/Transforms/TriangleResampler.cs → src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs

0
src/ImageSharp/Processing/Processors/Transforms/WelchResampler.cs → src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs

22
src/ImageSharp/Processing/Processors/Transforms/ResizeKernel.cs → src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs

@ -11,18 +11,21 @@ using SixLabors.ImageSharp.Memory;
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="KernelMap"/>. /// Points to a collection of of weights allocated in <see cref="ResizeKernelMap"/>.
/// </summary> /// </summary>
internal struct ResizeKernel internal unsafe struct ResizeKernel
{ {
private readonly float* bufferPtr;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ResizeKernel"/> struct. /// Initializes a new instance of the <see cref="ResizeKernel"/> struct.
/// </summary> /// </summary>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
internal ResizeKernel(int left, Memory<float> bufferSlice) internal ResizeKernel(int left, float* bufferPtr, int length)
{ {
this.Left = left; this.Left = left;
this.BufferSlice = bufferSlice; this.bufferPtr = bufferPtr;
this.Length = length;
} }
/// <summary> /// <summary>
@ -30,22 +33,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// </summary> /// </summary>
public int Left { get; } public int Left { get; }
/// <summary>
/// Gets the slice of the buffer containing the weights values.
/// </summary>
public Memory<float> BufferSlice { get; }
/// <summary> /// <summary>
/// Gets the the length of the kernel /// Gets the the length of the kernel
/// </summary> /// </summary>
public int Length => this.BufferSlice.Length; public int Length { get; }
/// <summary> /// <summary>
/// Gets the span representing the portion of the <see cref="KernelMap"/> that this window covers /// Gets the span representing the portion of the <see cref="ResizeKernelMap"/> that this window covers
/// </summary> /// </summary>
/// <returns>The <see cref="Span{T}"/></returns> /// <returns>The <see cref="Span{T}"/></returns>
[MethodImpl(InliningOptions.ShortMethod)] [MethodImpl(InliningOptions.ShortMethod)]
public Span<float> GetValues() => this.BufferSlice.Span; public Span<float> GetValues() => new Span<float>(this.bufferPtr, this.Length);
/// <summary> /// <summary>
/// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this <see cref="ResizeKernel"/> instance. /// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this <see cref="ResizeKernel"/> instance.

46
src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.MosaicKernelMap.cs

@ -0,0 +1,46 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
/// <content>
/// Contains <see cref="MosaicKernelMap"/>
/// </content>
internal partial class ResizeKernelMap
{
/// <summary>
/// Memory-optimized <see cref="ResizeKernelMap"/> where repeating rows are stored only once.
/// </summary>
private sealed class MosaicKernelMap : ResizeKernelMap
{
private readonly int period;
private readonly int cornerInterval;
public MosaicKernelMap(
MemoryAllocator memoryAllocator,
IResampler sampler,
int sourceSize,
int destinationSize,
float ratio,
float scale,
int radius,
int period,
int cornerInterval)
: base(
memoryAllocator,
sampler,
sourceSize,
destinationSize,
(cornerInterval * 2) + period,
ratio,
scale,
radius)
{
this.cornerInterval = cornerInterval;
this.period = period;
}
}
}
}

70
src/ImageSharp/Processing/Processors/Transforms/KernelMap.cs → src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -11,9 +12,10 @@ using SixLabors.Memory;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
/// <summary> /// <summary>
/// Holds the <see cref="ResizeKernel"/> values in an optimized contigous memory region. /// Provides <see cref="ResizeKernel"/> values from an optimized,
/// contigous memory region.
/// </summary> /// </summary>
internal class KernelMap : IDisposable internal partial class ResizeKernelMap : IDisposable
{ {
private readonly IResampler sampler; private readonly IResampler sampler;
@ -25,11 +27,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly int radius; private readonly int radius;
private readonly MemoryHandle pinHandle;
private readonly Buffer2D<float> data; private readonly Buffer2D<float> data;
private readonly ResizeKernel[] kernels; private readonly ResizeKernel[] kernels;
private KernelMap( private ResizeKernelMap(
MemoryAllocator memoryAllocator, MemoryAllocator memoryAllocator,
IResampler sampler, IResampler sampler,
int sourceSize, int sourceSize,
@ -47,16 +51,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
this.DestinationSize = destinationSize; this.DestinationSize = destinationSize;
int maxWidth = (radius * 2) + 1; int maxWidth = (radius * 2) + 1;
this.data = memoryAllocator.Allocate2D<float>(maxWidth, bufferHeight, AllocationOptions.Clean); this.data = memoryAllocator.Allocate2D<float>(maxWidth, bufferHeight, AllocationOptions.Clean);
this.pinHandle = this.data.Memory.Pin();
this.kernels = new ResizeKernel[destinationSize]; this.kernels = new ResizeKernel[destinationSize];
} }
public int DestinationSize { get; } public int DestinationSize { get; }
/// <summary> /// <summary>
/// Disposes <see cref="KernelMap"/> 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.pinHandle.Dispose();
this.data.Dispose(); this.data.Dispose();
} }
@ -73,8 +79,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <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="KernelMap"/></returns> /// <returns>The <see cref="ResizeKernelMap"/></returns>
public static KernelMap Calculate( public static ResizeKernelMap Calculate(
IResampler sampler, IResampler sampler,
int destinationSize, int destinationSize,
int sourceSize, int sourceSize,
@ -88,25 +94,40 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
scale = 1F; scale = 1F;
} }
int period = ImageMaths.LeastCommonMultiple(sourceSize, destinationSize) / sourceSize;
int radius = (int)MathF.Ceiling(scale * sampler.Radius); int radius = (int)MathF.Ceiling(scale * sampler.Radius);
int period = ImageMaths.LeastCommonMultiple(sourceSize, destinationSize) / sourceSize;
var result = new KernelMap( float center0 = (ratio - 1) * 0.5f;
memoryAllocator, int cornerInterval = (int)MathF.Ceiling((radius - center0 - 1) / ratio);
sampler,
sourceSize, bool useMosaic = 2 * (cornerInterval + period) < destinationSize;
destinationSize,
destinationSize, ResizeKernelMap result = useMosaic
ratio, ? new ResizeKernelMap(
scale, memoryAllocator,
radius); sampler,
sourceSize,
result.BasicInit(); destinationSize,
destinationSize,
ratio,
scale,
radius)
: new MosaicKernelMap(
memoryAllocator,
sampler,
sourceSize,
destinationSize,
ratio,
scale,
radius,
period,
cornerInterval);
result.Init();
return result; return result;
} }
private void BasicInit() protected virtual void Init()
{ {
for (int i = 0; i < this.DestinationSize; i++) for (int i = 0; i < this.DestinationSize; i++)
{ {
@ -157,7 +178,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// <summary> /// <summary>
/// Slices a weights value at the given positions. /// Slices a weights value at the given positions.
/// </summary> /// </summary>
private ResizeKernel CreateKernel(int destIdx, int left, int rightIdx) private unsafe ResizeKernel CreateKernel(int destIdx, int left, int rightIdx)
{ {
int length = rightIdx - left + 1; int length = rightIdx - left + 1;
@ -166,10 +187,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
throw new InvalidOperationException($"Error in KernelMap.CreateKernel({destIdx},{left},{rightIdx}): left > this.data.Width"); throw new InvalidOperationException($"Error in KernelMap.CreateKernel({destIdx},{left},{rightIdx}): left > this.data.Width");
} }
int flatStartIndex = destIdx * this.data.Width; Span<float> rowSpan = this.data.GetRowSpan(destIdx);
Memory<float> bufferSlice = this.data.Memory.Slice(flatStartIndex, length); ref float rowReference = ref MemoryMarshal.GetReference(rowSpan);
return new ResizeKernel(left, bufferSlice); float* rowPtr = (float*)Unsafe.AsPointer(ref rowReference);
return new ResizeKernel(left, rowPtr, length);
} }
} }
} }

8
src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs → src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs

@ -27,8 +27,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
// The following fields are not immutable but are optionally created on demand. // The following fields are not immutable but are optionally created on demand.
private KernelMap horizontalKernelMap; private ResizeKernelMap horizontalKernelMap;
private KernelMap verticalKernelMap; private ResizeKernelMap verticalKernelMap;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ResizeProcessor{TPixel}"/> class. /// Initializes a new instance of the <see cref="ResizeProcessor{TPixel}"/> class.
@ -165,13 +165,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
// Since all image frame dimensions have to be the same we can calculate this for all frames. // Since all image frame dimensions have to be the same we can calculate this for all frames.
MemoryAllocator memoryAllocator = source.GetMemoryAllocator(); MemoryAllocator memoryAllocator = source.GetMemoryAllocator();
this.horizontalKernelMap = KernelMap.Calculate( this.horizontalKernelMap = ResizeKernelMap.Calculate(
this.Sampler, this.Sampler,
this.ResizeRectangle.Width, this.ResizeRectangle.Width,
sourceRectangle.Width, sourceRectangle.Width,
memoryAllocator); memoryAllocator);
this.verticalKernelMap = KernelMap.Calculate( this.verticalKernelMap = ResizeKernelMap.Calculate(
this.Sampler, this.Sampler,
this.ResizeRectangle.Height, this.ResizeRectangle.Height,
sourceRectangle.Height, sourceRectangle.Height,

2
tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.ReferenceKernelMap.cs

@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
public partial class KernelMapTests public partial class KernelMapTests
{ {
/// <summary> /// <summary>
/// Simplified reference implementation for <see cref="KernelMap"/> functionality. /// Simplified reference implementation for <see cref="ResizeKernelMap"/> functionality.
/// </summary> /// </summary>
internal class ReferenceKernelMap internal class ReferenceKernelMap
{ {

4
tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.cs

@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
IResampler resampler = TestUtils.GetResampler(resamplerName); IResampler resampler = TestUtils.GetResampler(resamplerName);
var referenceMap = ReferenceKernelMap.Calculate(resampler, destSize, srcSize); var referenceMap = ReferenceKernelMap.Calculate(resampler, destSize, srcSize);
var kernelMap = KernelMap.Calculate(resampler, destSize, srcSize, Configuration.Default.MemoryAllocator); var kernelMap = ResizeKernelMap.Calculate(resampler, destSize, srcSize, Configuration.Default.MemoryAllocator);
#if DEBUG #if DEBUG
this.Output.WriteLine($"Actual KernelMap:\n{PrintKernelMap(kernelMap)}\n"); this.Output.WriteLine($"Actual KernelMap:\n{PrintKernelMap(kernelMap)}\n");
@ -103,7 +103,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
} }
} }
private static string PrintKernelMap(KernelMap kernelMap) => private static string PrintKernelMap(ResizeKernelMap kernelMap) =>
PrintKernelMap(kernelMap, km => km.DestinationSize, (km, i) => km.GetKernel(i)); PrintKernelMap(kernelMap, km => km.DestinationSize, (km, i) => km.GetKernel(i));
private static string PrintKernelMap(ReferenceKernelMap kernelMap) => private static string PrintKernelMap(ReferenceKernelMap kernelMap) =>

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

@ -58,6 +58,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
} }
} }
// TODO: Merge with the previous theory + add test images
[Theory] [Theory]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, nameof(KnownResamplers.Bicubic), 1)] [WithTestPatternImages(100, 100, PixelTypes.Rgba32, nameof(KnownResamplers.Bicubic), 1)]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, nameof(KnownResamplers.Bicubic), 10)] [WithTestPatternImages(100, 100, PixelTypes.Rgba32, nameof(KnownResamplers.Bicubic), 10)]

Loading…
Cancel
Save