diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs
index b2d7d2116..e10afce2e 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs
@@ -6,14 +6,12 @@ using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using SixLabors.ImageSharp.Memory;
-
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{
///
/// Points to a collection of of weights allocated in .
///
- internal unsafe struct ResizeKernel
+ internal readonly unsafe struct ResizeKernel
{
private readonly float* bufferPtr;
@@ -73,5 +71,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
return result;
}
+
+ internal ResizeKernel AlterLeftValue(int left)
+ {
+ return new ResizeKernel(left, this.bufferPtr, this.Length);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.MosaicKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.MosaicKernelMap.cs
index 09ae79677..cf660a72f 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.MosaicKernelMap.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.MosaicKernelMap.cs
@@ -1,5 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+
+using System;
+
using SixLabors.Memory;
namespace SixLabors.ImageSharp.Processing.Processors.Transforms
@@ -42,9 +45,36 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
this.period = period;
}
+ internal override string Info => base.Info + $"|period:{this.period}|cornerInterval:{this.cornerInterval}";
+
protected override void Initialize()
{
- base.Initialize();
+ // Build top corner data + one period of the mosaic data:
+ int startOfFirstRepeatedMosaic = this.cornerInterval + this.period;
+
+ for (int i = 0; i < startOfFirstRepeatedMosaic; i++)
+ {
+ ResizeKernel kernel = this.BuildKernel(i, i);
+ this.kernels[i] = kernel;
+ }
+
+ // Copy the mosaics:
+ int bottomStartDest = this.DestinationSize - this.cornerInterval;
+ for (int i = startOfFirstRepeatedMosaic; i < bottomStartDest; i++)
+ {
+ float center = ((i + .5F) * this.ratio) - .5F;
+ int left = (int)MathF.Ceiling(center - this.radius);
+ ResizeKernel kernel = this.kernels[i - this.period];
+ this.kernels[i] = kernel.AlterLeftValue(left);
+ }
+
+ // Build bottom corner data:
+ int bottomStartData = this.cornerInterval + this.period;
+ for (int i = 0; i < this.cornerInterval; i++)
+ {
+ ResizeKernel kernel = this.BuildKernel(bottomStartDest + i, bottomStartData + i);
+ this.kernels[bottomStartDest + i] = kernel;
+ }
}
}
}
diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
index ebb15d376..262a39352 100644
--- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
+++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs
@@ -57,6 +57,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
public int DestinationSize { get; }
+ internal virtual string Info =>
+ $"radius:{this.radius}|sourceSize:{this.sourceSize}|destinationSize:{this.DestinationSize}|ratio:{this.ratio}|scale:{this.scale}";
+
///
/// Disposes instance releasing it's backing buffer.
///
@@ -97,11 +100,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int radius = (int)MathF.Ceiling(scale * sampler.Radius);
int period = ImageMaths.LeastCommonMultiple(sourceSize, destinationSize) / sourceSize;
float center0 = (ratio - 1) * 0.5f;
- int cornerInterval = (int)MathF.Ceiling((radius - center0 - 1) / ratio);
+ float firstNonNegativeLeftVal = (radius - center0 - 1) / ratio;
+ int cornerInterval = (int)MathF.Ceiling(firstNonNegativeLeftVal);
- bool useMosaic = 2 * (cornerInterval + period) < destinationSize;
+ // corner case for cornerInteval:
+ if (firstNonNegativeLeftVal == cornerInterval)
+ {
+ cornerInterval++;
+ }
- useMosaic = false;
+ bool useMosaic = 2 * (cornerInterval + period) < destinationSize;
ResizeKernelMap result = useMosaic
? new MosaicKernelMap(
@@ -131,14 +139,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
protected virtual void Initialize()
{
- for (int destRowIndex = 0; destRowIndex < this.DestinationSize; destRowIndex++)
+ for (int i = 0; i < this.DestinationSize; i++)
{
- ResizeKernel kernel = this.BuildKernelRow(destRowIndex, destRowIndex);
- this.kernels[destRowIndex] = kernel;
+ ResizeKernel kernel = this.BuildKernel(i, i);
+ this.kernels[i] = kernel;
}
}
- private ResizeKernel BuildKernelRow(int destRowIndex, int dataRowIndex)
+ private ResizeKernel BuildKernel(int destRowIndex, int dataRowIndex)
{
float center = ((destRowIndex + .5F) * this.ratio) - .5F;
@@ -157,7 +165,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
float sum = 0;
- ResizeKernel kernel = this.GetKernel(dataRowIndex, left, right);
+ ResizeKernel kernel = this.CreateKernel(dataRowIndex, left, right);
ref float kernelBaseRef = ref MemoryMarshal.GetReference(kernel.Values);
@@ -188,7 +196,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// Returns a referencing values of
/// at row .
///
- private unsafe ResizeKernel GetKernel(int dataRowIndex, int left, int right)
+ private unsafe ResizeKernel CreateKernel(int dataRowIndex, int left, int right)
{
int length = right - left + 1;
diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.cs
index 7b997e33f..f0a0d738b 100644
--- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.cs
+++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/KernelMapTests.cs
@@ -48,6 +48,11 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{ nameof(KnownResamplers.Lanczos3), 5, 25 },
{ nameof(KnownResamplers.Lanczos3), 5, 50 },
+ { nameof(KnownResamplers.Lanczos3), 25, 5 },
+ { nameof(KnownResamplers.Lanczos3), 50, 5 },
+ { nameof(KnownResamplers.Lanczos3), 49, 5 },
+ { nameof(KnownResamplers.Lanczos3), 31, 5 },
+
{ nameof(KnownResamplers.Lanczos8), 500, 200 },
{ nameof(KnownResamplers.Lanczos8), 100, 10 },
{ nameof(KnownResamplers.Lanczos8), 100, 80 },
@@ -75,8 +80,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
var kernelMap = ResizeKernelMap.Calculate(resampler, destSize, srcSize, Configuration.Default.MemoryAllocator);
#if DEBUG
+ this.Output.WriteLine($"Expected KernelMap:\n{PrintKernelMap(referenceMap)}\n");
this.Output.WriteLine($"Actual KernelMap:\n{PrintKernelMap(kernelMap)}\n");
- // this.Output.WriteLine($"Reference KernelMap:\n{PrintKernelMap(referenceMap)}\n");
#endif
for (int i = 0; i < kernelMap.DestinationSize; i++)
@@ -92,7 +97,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
Assert.Equal(expectedValues.Length, actualValues.Length);
- var comparer = new ApproximateFloatComparer(1e-6f);
+ var comparer = new ApproximateFloatComparer(1e-4f);
for (int x = 0; x < expectedValues.Length; x++)
{
@@ -116,12 +121,17 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms
{
var bld = new StringBuilder();
+ if (kernelMap is ResizeKernelMap actualMap)
+ {
+ bld.AppendLine(actualMap.Info);
+ }
+
int destinationSize = getDestinationSize(kernelMap);
for (int i = 0; i < destinationSize; i++)
{
ReferenceKernel kernel = getKernel(kernelMap, i);
- bld.Append($"({kernel.Left:D3}) || ");
+ bld.Append($"[{i:D3}] (L{kernel.Left:D3}) || ");
Span span = kernel.Values;
for (int j = 0; j < kernel.Length; j++)