Browse Source

use double precision in KernelMap calculations

af/merge-core
Anton Firszov 8 years ago
parent
commit
b745ffaf5e
  1. 10
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernel.cs
  2. 8
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.PeriodicKernelMap.cs
  3. 66
      src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeKernelMap.cs

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

@ -80,5 +80,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
{ {
return new ResizeKernel(left, this.bufferPtr, this.Length); return new ResizeKernel(left, this.bufferPtr, this.Length);
} }
internal void Fill(Span<double> values)
{
DebugGuard.IsTrue(values.Length == this.Length, nameof(values), "ResizeKernel.Fill: values.Length != this.Length!");
for (int i = 0; i < this.Length; i++)
{
this.Values[i] = (float)values[i];
}
}
} }
} }

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

@ -26,8 +26,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
IResampler sampler, IResampler sampler,
int sourceLength, int sourceLength,
int destinationLength, int destinationLength,
float ratio, double ratio,
float scale, double scale,
int radius, int radius,
int period, int period,
int cornerInterval) int cornerInterval)
@ -62,8 +62,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int bottomStartDest = this.DestinationLength - this.cornerInterval; int bottomStartDest = this.DestinationLength - this.cornerInterval;
for (int i = startOfFirstRepeatedMosaic; i < bottomStartDest; i++) for (int i = startOfFirstRepeatedMosaic; i < bottomStartDest; i++)
{ {
float center = ((i + .5F) * this.ratio) - .5F; double center = ((i + .5) * this.ratio) - .5;
int left = (int)MathF.Ceiling(center - this.radius); int left = (int)Math.Ceiling(center - this.radius);
ResizeKernel kernel = this.kernels[i - this.period]; ResizeKernel kernel = this.kernels[i - this.period];
this.kernels[i] = kernel.AlterLeftValue(left); this.kernels[i] = kernel.AlterLeftValue(left);
} }

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

@ -17,13 +17,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// </summary> /// </summary>
internal partial class ResizeKernelMap : IDisposable internal partial class ResizeKernelMap : IDisposable
{ {
private readonly MemoryAllocator memoryAllocator;
private readonly IResampler sampler; private readonly IResampler sampler;
private readonly int sourceLength; private readonly int sourceLength;
private readonly float ratio; private readonly double ratio;
private readonly float scale; private readonly double scale;
private readonly int radius; private readonly int radius;
@ -39,10 +41,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int sourceLength, int sourceLength,
int destinationLength, int destinationLength,
int bufferHeight, int bufferHeight,
float ratio, double ratio,
float scale, double scale,
int radius) int radius)
{ {
this.memoryAllocator = memoryAllocator;
this.sampler = sampler; this.sampler = sampler;
this.ratio = ratio; this.ratio = ratio;
this.scale = scale; this.scale = scale;
@ -95,19 +98,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
int sourceSize, int sourceSize,
MemoryAllocator memoryAllocator) MemoryAllocator memoryAllocator)
{ {
float ratio = (float)sourceSize / destinationSize; double ratio = (double)sourceSize / destinationSize;
float scale = ratio; double scale = ratio;
if (scale < 1F) if (scale < 1F)
{ {
scale = 1F; scale = 1F;
} }
int radius = (int)MathF.Ceiling(scale * sampler.Radius); int radius = (int)Math.Ceiling(scale * sampler.Radius);
int period = ImageMaths.LeastCommonMultiple(sourceSize, destinationSize) / sourceSize; int period = ImageMaths.LeastCommonMultiple(sourceSize, destinationSize) / sourceSize;
float center0 = (ratio - 1) * 0.5f; double center0 = (ratio - 1) * 0.5f;
float firstNonNegativeLeftVal = (radius - center0 - 1) / ratio; double firstNonNegativeLeftVal = (radius - center0 - 1) / ratio;
int cornerInterval = (int)MathF.Ceiling(firstNonNegativeLeftVal); int cornerInterval = (int)Math.Ceiling(firstNonNegativeLeftVal);
// corner case for cornerInteval: // corner case for cornerInteval:
if (firstNonNegativeLeftVal == cornerInterval) if (firstNonNegativeLeftVal == cornerInterval)
@ -159,45 +162,48 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms
/// </summary> /// </summary>
private ResizeKernel BuildKernel(int destRowIndex, int dataRowIndex) private ResizeKernel BuildKernel(int destRowIndex, int dataRowIndex)
{ {
float center = ((destRowIndex + .5F) * this.ratio) - .5F; double center = ((destRowIndex + .5) * this.ratio) - .5;
// Keep inside bounds. // Keep inside bounds.
int left = (int)MathF.Ceiling(center - this.radius); int left = (int)Math.Ceiling(center - this.radius);
if (left < 0) if (left < 0)
{ {
left = 0; left = 0;
} }
int right = (int)MathF.Floor(center + this.radius); int right = (int)Math.Floor(center + this.radius);
if (right > this.sourceLength - 1) if (right > this.sourceLength - 1)
{ {
right = this.sourceLength - 1; right = this.sourceLength - 1;
} }
float sum = 0;
ResizeKernel kernel = this.CreateKernel(dataRowIndex, left, right); ResizeKernel kernel = this.CreateKernel(dataRowIndex, left, right);
ref float kernelBaseRef = ref MemoryMarshal.GetReference(kernel.Values); using (IMemoryOwner<double> tempBuffer = this.memoryAllocator.Allocate<double>(kernel.Length))
for (int j = left; j <= right; j++)
{ {
float value = this.sampler.GetValue((j - center) / this.scale); Span<double> kernelValues = tempBuffer.GetSpan();
sum += value; double sum = 0;
// weights[j - left] = weight: for (int j = left; j <= right; j++)
Unsafe.Add(ref kernelBaseRef, j - left) = value; {
} double value = this.sampler.GetValue((float)((j - center) / this.scale));
sum += value;
// Normalize, best to do it here rather than in the pixel loop later on. kernelValues[j - left] = value;
if (sum > 0) }
{
for (int w = 0; w < kernel.Length; w++) // Normalize, best to do it here rather than in the pixel loop later on.
if (sum > 0)
{ {
// weights[w] = weights[w] / sum: for (int j = 0; j < kernel.Length; j++)
ref float kRef = ref Unsafe.Add(ref kernelBaseRef, w); {
kRef /= sum; // weights[w] = weights[w] / sum:
ref double kRef = ref kernelValues[j];
kRef /= sum;
}
} }
kernel.Fill(kernelValues);
} }
return kernel; return kernel;

Loading…
Cancel
Save