diff --git a/src/ImageSharp/Common/Helpers/ImageMaths.cs b/src/ImageSharp/Common/Helpers/ImageMaths.cs
index f07ccb03b..45cb52fd9 100644
--- a/src/ImageSharp/Common/Helpers/ImageMaths.cs
+++ b/src/ImageSharp/Common/Helpers/ImageMaths.cs
@@ -5,6 +5,7 @@ using System;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing.Processors.Transforms;
using SixLabors.Primitives;
namespace SixLabors.ImageSharp
@@ -98,7 +99,7 @@ namespace SixLabors.ImageSharp
///
/// Determine the Least Common Multiple (LCM) of two numbers.
- /// TODO: This method might be useful for building a more compact
+ /// TODO: This method might be useful for building a more compact
///
public static int LeastCommonMultiple(int a, int b)
{
diff --git a/src/ImageSharp/ImageSharp.csproj.DotSettings b/src/ImageSharp/ImageSharp.csproj.DotSettings
index cd75f91b7..a7337240a 100644
--- a/src/ImageSharp/ImageSharp.csproj.DotSettings
+++ b/src/ImageSharp/ImageSharp.csproj.DotSettings
@@ -5,4 +5,6 @@
True
True
True
- True
\ No newline at end of file
+ True
+ True
+ True
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/BicubicResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/BicubicResampler.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/BicubicResampler.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/BoxResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/BoxResampler.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/BoxResampler.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/CatmullRomResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/CatmullRomResampler.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/CatmullRomResampler.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/CatmullRomResampler.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/HermiteResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/HermiteResampler.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/HermiteResampler.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/HermiteResampler.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Lanczos2Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos2Resampler.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/Lanczos2Resampler.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos2Resampler.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Lanczos3Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos3Resampler.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/Lanczos3Resampler.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos3Resampler.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Lanczos5Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos5Resampler.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/Lanczos5Resampler.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos5Resampler.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Lanczos8Resampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos8Resampler.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/Lanczos8Resampler.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/Lanczos8Resampler.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/MitchellNetravaliResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/MitchellNetravaliResampler.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/MitchellNetravaliResampler.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/MitchellNetravaliResampler.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/NearestNeighborResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/NearestNeighborResampler.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/NearestNeighborResampler.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/RobidouxResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxResampler.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/RobidouxResampler.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxResampler.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/RobidouxSharpResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxSharpResampler.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/RobidouxSharpResampler.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/RobidouxSharpResampler.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/SplineResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/SplineResampler.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/SplineResampler.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/SplineResampler.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/TriangleResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/TriangleResampler.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/TriangleResampler.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/WelchResampler.cs b/src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs
similarity index 100%
rename from src/ImageSharp/Processing/Processors/Transforms/WelchResampler.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resamplers/WelchResampler.cs
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeKernel.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs
similarity index 81%
rename from src/ImageSharp/Processing/Processors/Transforms/ResizeKernel.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs
index 1ce9c9c91..1183de754 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/ResizeKernel.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs
@@ -11,18 +11,21 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
- /// Points to a collection of of weights allocated in .
+ /// Points to a collection of of weights allocated in .
///
- internal struct ResizeKernel
+ internal unsafe struct ResizeKernel
{
+ private readonly float* bufferPtr;
+
///
/// Initializes a new instance of the struct.
///
[MethodImpl(InliningOptions.ShortMethod)]
- internal ResizeKernel(int left, Memory bufferSlice)
+ internal ResizeKernel(int left, float* bufferPtr, int length)
{
this.Left = left;
- this.BufferSlice = bufferSlice;
+ this.bufferPtr = bufferPtr;
+ this.Length = length;
}
///
@@ -30,22 +33,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
public int Left { get; }
- ///
- /// Gets the slice of the buffer containing the weights values.
- ///
- public Memory BufferSlice { get; }
-
///
/// Gets the the length of the kernel
///
- public int Length => this.BufferSlice.Length;
+ public int Length { get; }
///
- /// Gets the span representing the portion of the that this window covers
+ /// Gets the span representing the portion of the that this window covers
///
/// The
[MethodImpl(InliningOptions.ShortMethod)]
- public Span GetValues() => this.BufferSlice.Span;
+ public Span GetValues() => new Span(this.bufferPtr, this.Length);
///
/// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this instance.
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.MosaicKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.MosaicKernelMap.cs
new file mode 100644
index 000000000..b815d05cb
--- /dev/null
+++ b/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
+{
+ ///
+ /// Contains
+ ///
+ internal partial class ResizeKernelMap
+ {
+ ///
+ /// Memory-optimized where repeating rows are stored only once.
+ ///
+ 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;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/KernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
similarity index 69%
rename from src/ImageSharp/Processing/Processors/Transforms/KernelMap.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
index 96eb97649..443db72d9 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/KernelMap.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0.
using System;
+using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -11,9 +12,10 @@ using SixLabors.Memory;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
- /// Holds the values in an optimized contigous memory region.
+ /// Provides values from an optimized,
+ /// contigous memory region.
///
- internal class KernelMap : IDisposable
+ internal partial class ResizeKernelMap : IDisposable
{
private readonly IResampler sampler;
@@ -25,11 +27,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
private readonly int radius;
+ private readonly MemoryHandle pinHandle;
+
private readonly Buffer2D data;
private readonly ResizeKernel[] kernels;
- private KernelMap(
+ private ResizeKernelMap(
MemoryAllocator memoryAllocator,
IResampler sampler,
int sourceSize,
@@ -47,16 +51,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
this.DestinationSize = destinationSize;
int maxWidth = (radius * 2) + 1;
this.data = memoryAllocator.Allocate2D(maxWidth, bufferHeight, AllocationOptions.Clean);
+ this.pinHandle = this.data.Memory.Pin();
this.kernels = new ResizeKernel[destinationSize];
}
public int DestinationSize { get; }
///
- /// Disposes instance releasing it's backing buffer.
+ /// Disposes instance releasing it's backing buffer.
///
public void Dispose()
{
+ this.pinHandle.Dispose();
this.data.Dispose();
}
@@ -73,8 +79,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// The destination size
/// The source size
/// The to use for buffer allocations
- /// The
- public static KernelMap Calculate(
+ /// The
+ public static ResizeKernelMap Calculate(
IResampler sampler,
int destinationSize,
int sourceSize,
@@ -88,25 +94,40 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
scale = 1F;
}
- int period = ImageMaths.LeastCommonMultiple(sourceSize, destinationSize) / sourceSize;
int radius = (int)MathF.Ceiling(scale * sampler.Radius);
-
- var result = new KernelMap(
- memoryAllocator,
- sampler,
- sourceSize,
- destinationSize,
- destinationSize,
- ratio,
- scale,
- radius);
-
- result.BasicInit();
+ int period = ImageMaths.LeastCommonMultiple(sourceSize, destinationSize) / sourceSize;
+ float center0 = (ratio - 1) * 0.5f;
+ int cornerInterval = (int)MathF.Ceiling((radius - center0 - 1) / ratio);
+
+ bool useMosaic = 2 * (cornerInterval + period) < destinationSize;
+
+ ResizeKernelMap result = useMosaic
+ ? new ResizeKernelMap(
+ memoryAllocator,
+ sampler,
+ sourceSize,
+ destinationSize,
+ destinationSize,
+ ratio,
+ scale,
+ radius)
+ : new MosaicKernelMap(
+ memoryAllocator,
+ sampler,
+ sourceSize,
+ destinationSize,
+ ratio,
+ scale,
+ radius,
+ period,
+ cornerInterval);
+
+ result.Init();
return result;
}
- private void BasicInit()
+ protected virtual void Init()
{
for (int i = 0; i < this.DestinationSize; i++)
{
@@ -157,7 +178,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
///
/// Slices a weights value at the given positions.
///
- private ResizeKernel CreateKernel(int destIdx, int left, int rightIdx)
+ private unsafe ResizeKernel CreateKernel(int destIdx, int left, int rightIdx)
{
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");
}
- int flatStartIndex = destIdx * this.data.Width;
+ Span rowSpan = this.data.GetRowSpan(destIdx);
- Memory bufferSlice = this.data.Memory.Slice(flatStartIndex, length);
- return new ResizeKernel(left, bufferSlice);
+ ref float rowReference = ref MemoryMarshal.GetReference(rowSpan);
+ float* rowPtr = (float*)Unsafe.AsPointer(ref rowReference);
+ return new ResizeKernel(left, rowPtr, length);
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
similarity index 98%
rename from src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
rename to src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
index 7c9d39fc5..189e21de7 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs
@@ -27,8 +27,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
where TPixel : struct, IPixel
{
// The following fields are not immutable but are optionally created on demand.
- private KernelMap horizontalKernelMap;
- private KernelMap verticalKernelMap;
+ private ResizeKernelMap horizontalKernelMap;
+ private ResizeKernelMap verticalKernelMap;
///
/// Initializes a new instance of the 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.
MemoryAllocator memoryAllocator = source.GetMemoryAllocator();
- this.horizontalKernelMap = KernelMap.Calculate(
+ this.horizontalKernelMap = ResizeKernelMap.Calculate(
this.Sampler,
this.ResizeRectangle.Width,
sourceRectangle.Width,
memoryAllocator);
- this.verticalKernelMap = KernelMap.Calculate(
+ this.verticalKernelMap = ResizeKernelMap.Calculate(
this.Sampler,
this.ResizeRectangle.Height,
sourceRectangle.Height,
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.ReferenceKernelMap.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.ReferenceKernelMap.cs
index 3786ec6e3..85a930fb9 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.ReferenceKernelMap.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.ReferenceKernelMap.cs
@@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
public partial class KernelMapTests
{
///
- /// Simplified reference implementation for functionality.
+ /// Simplified reference implementation for functionality.
///
internal class ReferenceKernelMap
{
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.cs
index 69de5dbbf..4d005576c 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.cs
@@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
IResampler resampler = TestUtils.GetResampler(resamplerName);
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
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));
private static string PrintKernelMap(ReferenceKernelMap kernelMap) =>
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
index 42cb083f1..839d26e71 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs
+++ b/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]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, nameof(KnownResamplers.Bicubic), 1)]
[WithTestPatternImages(100, 100, PixelTypes.Rgba32, nameof(KnownResamplers.Bicubic), 10)]