mirror of https://github.com/SixLabors/ImageSharp
committed by
GitHub
16 changed files with 693 additions and 471 deletions
@ -0,0 +1,130 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
using SixLabors.Memory; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Holds the <see cref="ResizeKernel"/> values in an optimized contigous memory region.
|
||||
|
/// </summary>
|
||||
|
internal class KernelMap : IDisposable |
||||
|
{ |
||||
|
private readonly Buffer2D<float> data; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="KernelMap"/> class.
|
||||
|
/// </summary>
|
||||
|
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for allocations.</param>
|
||||
|
/// <param name="destinationSize">The size of the destination window</param>
|
||||
|
/// <param name="kernelRadius">The radius of the kernel</param>
|
||||
|
public KernelMap(MemoryAllocator memoryAllocator, int destinationSize, float kernelRadius) |
||||
|
{ |
||||
|
int width = (int)Math.Ceiling(kernelRadius * 2); |
||||
|
this.data = memoryAllocator.Allocate2D<float>(width, destinationSize, AllocationOptions.Clean); |
||||
|
this.Kernels = new ResizeKernel[destinationSize]; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the calculated <see cref="Kernels"/> values.
|
||||
|
/// </summary>
|
||||
|
public ResizeKernel[] Kernels { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Disposes <see cref="KernelMap"/> instance releasing it's backing buffer.
|
||||
|
/// </summary>
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
this.data.Dispose(); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Computes the weights to apply at each pixel when resizing.
|
||||
|
/// </summary>
|
||||
|
/// <param name="sampler">The <see cref="IResampler"/></param>
|
||||
|
/// <param name="destinationSize">The destination size</param>
|
||||
|
/// <param name="sourceSize">The source size</param>
|
||||
|
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for buffer allocations</param>
|
||||
|
/// <returns>The <see cref="KernelMap"/></returns>
|
||||
|
public static KernelMap Calculate( |
||||
|
IResampler sampler, |
||||
|
int destinationSize, |
||||
|
int sourceSize, |
||||
|
MemoryAllocator memoryAllocator) |
||||
|
{ |
||||
|
float ratio = (float)sourceSize / destinationSize; |
||||
|
float scale = ratio; |
||||
|
|
||||
|
if (scale < 1F) |
||||
|
{ |
||||
|
scale = 1F; |
||||
|
} |
||||
|
|
||||
|
float radius = MathF.Ceiling(scale * sampler.Radius); |
||||
|
var result = new KernelMap(memoryAllocator, destinationSize, radius); |
||||
|
|
||||
|
for (int i = 0; i < destinationSize; i++) |
||||
|
{ |
||||
|
float center = ((i + .5F) * ratio) - .5F; |
||||
|
|
||||
|
// Keep inside bounds.
|
||||
|
int left = (int)MathF.Ceiling(center - radius); |
||||
|
if (left < 0) |
||||
|
{ |
||||
|
left = 0; |
||||
|
} |
||||
|
|
||||
|
int right = (int)MathF.Floor(center + radius); |
||||
|
if (right > sourceSize - 1) |
||||
|
{ |
||||
|
right = sourceSize - 1; |
||||
|
} |
||||
|
|
||||
|
float sum = 0; |
||||
|
|
||||
|
ResizeKernel ws = result.CreateKernel(i, left, right); |
||||
|
result.Kernels[i] = ws; |
||||
|
|
||||
|
ref float weightsBaseRef = ref ws.GetStartReference(); |
||||
|
|
||||
|
for (int j = left; j <= right; j++) |
||||
|
{ |
||||
|
float weight = sampler.GetValue((j - center) / scale); |
||||
|
sum += weight; |
||||
|
|
||||
|
// weights[j - left] = weight:
|
||||
|
Unsafe.Add(ref weightsBaseRef, j - left) = weight; |
||||
|
} |
||||
|
|
||||
|
// Normalize, best to do it here rather than in the pixel loop later on.
|
||||
|
if (sum > 0) |
||||
|
{ |
||||
|
for (int w = 0; w < ws.Length; w++) |
||||
|
{ |
||||
|
// weights[w] = weights[w] / sum:
|
||||
|
ref float wRef = ref Unsafe.Add(ref weightsBaseRef, w); |
||||
|
wRef /= sum; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Slices a weights value at the given positions.
|
||||
|
/// </summary>
|
||||
|
/// <param name="destIdx">The index in destination buffer</param>
|
||||
|
/// <param name="leftIdx">The local left index value</param>
|
||||
|
/// <param name="rightIdx">The local right index value</param>
|
||||
|
/// <returns>The weights</returns>
|
||||
|
private ResizeKernel CreateKernel(int destIdx, int leftIdx, int rightIdx) |
||||
|
{ |
||||
|
return new ResizeKernel(destIdx, leftIdx, this.data, rightIdx - leftIdx + 1); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,95 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Buffers; |
||||
|
using System.Numerics; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
using SixLabors.ImageSharp.Memory; |
||||
|
using SixLabors.Memory; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Points to a collection of of weights allocated in <see cref="KernelMap"/>.
|
||||
|
/// </summary>
|
||||
|
internal struct ResizeKernel |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The local left index position
|
||||
|
/// </summary>
|
||||
|
public int Left; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The length of the weights window
|
||||
|
/// </summary>
|
||||
|
public int Length; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// The buffer containing the weights values.
|
||||
|
/// </summary>
|
||||
|
private readonly Memory<float> buffer; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Initializes a new instance of the <see cref="ResizeKernel"/> struct.
|
||||
|
/// </summary>
|
||||
|
/// <param name="index">The destination index in the buffer</param>
|
||||
|
/// <param name="left">The local left index</param>
|
||||
|
/// <param name="buffer">The span</param>
|
||||
|
/// <param name="length">The length of the window</param>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
internal ResizeKernel(int index, int left, Buffer2D<float> buffer, int length) |
||||
|
{ |
||||
|
int flatStartIndex = index * buffer.Width; |
||||
|
this.Left = left; |
||||
|
this.buffer = buffer.MemorySource.Memory.Slice(flatStartIndex, length); |
||||
|
this.Length = length; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a reference to the first item of the window.
|
||||
|
/// </summary>
|
||||
|
/// <returns>The reference to the first item of the window</returns>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public ref float GetStartReference() |
||||
|
{ |
||||
|
Span<float> span = this.buffer.Span; |
||||
|
return ref span[0]; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the span representing the portion of the <see cref="KernelMap"/> that this window covers
|
||||
|
/// </summary>
|
||||
|
/// <returns>The <see cref="Span{T}"/></returns>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public Span<float> GetSpan() => this.buffer.Span; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this <see cref="ResizeKernel"/> instance.
|
||||
|
/// </summary>
|
||||
|
/// <param name="rowSpan">The input span of vectors</param>
|
||||
|
/// <param name="sourceX">The source row position.</param>
|
||||
|
/// <returns>The weighted sum</returns>
|
||||
|
[MethodImpl(InliningOptions.ShortMethod)] |
||||
|
public Vector4 Convolve(Span<Vector4> rowSpan, int sourceX) |
||||
|
{ |
||||
|
ref float horizontalValues = ref this.GetStartReference(); |
||||
|
int left = this.Left; |
||||
|
ref Vector4 vecPtr = ref Unsafe.Add(ref MemoryMarshal.GetReference(rowSpan), left + sourceX); |
||||
|
|
||||
|
// Destination color components
|
||||
|
Vector4 result = Vector4.Zero; |
||||
|
|
||||
|
for (int i = 0; i < this.Length; i++) |
||||
|
{ |
||||
|
float weight = Unsafe.Add(ref horizontalValues, i); |
||||
|
Vector4 v = Unsafe.Add(ref vecPtr, i); |
||||
|
result += v * weight; |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,55 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
|
|
||||
using SixLabors.ImageSharp.Memory; |
|
||||
using SixLabors.Memory; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Holds the <see cref="WeightsWindow"/> values in an optimized contigous memory region.
|
|
||||
/// </summary>
|
|
||||
internal class WeightsBuffer : IDisposable |
|
||||
{ |
|
||||
private readonly Buffer2D<float> dataBuffer; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="WeightsBuffer"/> class.
|
|
||||
/// </summary>
|
|
||||
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use for allocations.</param>
|
|
||||
/// <param name="sourceSize">The size of the source window</param>
|
|
||||
/// <param name="destinationSize">The size of the destination window</param>
|
|
||||
public WeightsBuffer(MemoryAllocator memoryAllocator, int sourceSize, int destinationSize) |
|
||||
{ |
|
||||
this.dataBuffer = memoryAllocator.Allocate2D<float>(sourceSize, destinationSize, AllocationOptions.Clean); |
|
||||
this.Weights = new WeightsWindow[destinationSize]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the calculated <see cref="Weights"/> values.
|
|
||||
/// </summary>
|
|
||||
public WeightsWindow[] Weights { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Disposes <see cref="WeightsBuffer"/> instance releasing it's backing buffer.
|
|
||||
/// </summary>
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
this.dataBuffer.Dispose(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Slices a weights value at the given positions.
|
|
||||
/// </summary>
|
|
||||
/// <param name="destIdx">The index in destination buffer</param>
|
|
||||
/// <param name="leftIdx">The local left index value</param>
|
|
||||
/// <param name="rightIdx">The local right index value</param>
|
|
||||
/// <returns>The weights</returns>
|
|
||||
public WeightsWindow GetWeightsWindow(int destIdx, int leftIdx, int rightIdx) |
|
||||
{ |
|
||||
return new WeightsWindow(destIdx, leftIdx, this.dataBuffer, rightIdx - leftIdx + 1); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,154 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Buffers; |
|
||||
using System.Numerics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
using System.Runtime.InteropServices; |
|
||||
|
|
||||
using SixLabors.ImageSharp.Memory; |
|
||||
using SixLabors.Memory; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Processing.Processors.Transforms |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Points to a collection of of weights allocated in <see cref="WeightsBuffer"/>.
|
|
||||
/// </summary>
|
|
||||
internal struct WeightsWindow |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The local left index position
|
|
||||
/// </summary>
|
|
||||
public int Left; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The length of the weights window
|
|
||||
/// </summary>
|
|
||||
public int Length; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The index in the destination buffer
|
|
||||
/// </summary>
|
|
||||
private readonly int flatStartIndex; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The buffer containing the weights values.
|
|
||||
/// </summary>
|
|
||||
private readonly MemorySource<float> buffer; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="WeightsWindow"/> struct.
|
|
||||
/// </summary>
|
|
||||
/// <param name="index">The destination index in the buffer</param>
|
|
||||
/// <param name="left">The local left index</param>
|
|
||||
/// <param name="buffer">The span</param>
|
|
||||
/// <param name="length">The length of the window</param>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
internal WeightsWindow(int index, int left, Buffer2D<float> buffer, int length) |
|
||||
{ |
|
||||
this.flatStartIndex = (index * buffer.Width) + left; |
|
||||
this.Left = left; |
|
||||
this.buffer = buffer.MemorySource; |
|
||||
this.Length = length; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets a reference to the first item of the window.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The reference to the first item of the window</returns>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
public ref float GetStartReference() |
|
||||
{ |
|
||||
Span<float> span = this.buffer.GetSpan(); |
|
||||
return ref span[this.flatStartIndex]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the span representing the portion of the <see cref="WeightsBuffer"/> that this window covers
|
|
||||
/// </summary>
|
|
||||
/// <returns>The <see cref="Span{T}"/></returns>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
public Span<float> GetWindowSpan() => this.buffer.GetSpan().Slice(this.flatStartIndex, this.Length); |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this <see cref="WeightsWindow"/> instance.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rowSpan">The input span of vectors</param>
|
|
||||
/// <param name="sourceX">The source row position.</param>
|
|
||||
/// <returns>The weighted sum</returns>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
public Vector4 ComputeWeightedRowSum(Span<Vector4> rowSpan, int sourceX) |
|
||||
{ |
|
||||
ref float horizontalValues = ref this.GetStartReference(); |
|
||||
int left = this.Left; |
|
||||
ref Vector4 vecPtr = ref Unsafe.Add(ref MemoryMarshal.GetReference(rowSpan), left + sourceX); |
|
||||
|
|
||||
// Destination color components
|
|
||||
Vector4 result = Vector4.Zero; |
|
||||
|
|
||||
for (int i = 0; i < this.Length; i++) |
|
||||
{ |
|
||||
float weight = Unsafe.Add(ref horizontalValues, i); |
|
||||
Vector4 v = Unsafe.Add(ref vecPtr, i); |
|
||||
result += v.Premultiply() * weight; |
|
||||
} |
|
||||
|
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Computes the sum of vectors in 'rowSpan' weighted by weight values, pointed by this <see cref="WeightsWindow"/> instance.
|
|
||||
/// Applies <see cref="Vector4Extensions.Expand(float)"/> to all input vectors.
|
|
||||
/// </summary>
|
|
||||
/// <param name="rowSpan">The input span of vectors</param>
|
|
||||
/// <param name="sourceX">The source row position.</param>
|
|
||||
/// <returns>The weighted sum</returns>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
public Vector4 ComputeExpandedWeightedRowSum(Span<Vector4> rowSpan, int sourceX) |
|
||||
{ |
|
||||
ref float horizontalValues = ref this.GetStartReference(); |
|
||||
int left = this.Left; |
|
||||
ref Vector4 vecPtr = ref Unsafe.Add(ref MemoryMarshal.GetReference(rowSpan), left + sourceX); |
|
||||
|
|
||||
// Destination color components
|
|
||||
Vector4 result = Vector4.Zero; |
|
||||
|
|
||||
for (int i = 0; i < this.Length; i++) |
|
||||
{ |
|
||||
float weight = Unsafe.Add(ref horizontalValues, i); |
|
||||
Vector4 v = Unsafe.Add(ref vecPtr, i); |
|
||||
result += v.Premultiply().Expand() * weight; |
|
||||
} |
|
||||
|
|
||||
return result.UnPremultiply(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Computes the sum of vectors in 'firstPassPixels' at a row pointed by 'x',
|
|
||||
/// weighted by weight values, pointed by this <see cref="WeightsWindow"/> instance.
|
|
||||
/// </summary>
|
|
||||
/// <param name="firstPassPixels">The buffer of input vectors in row first order</param>
|
|
||||
/// <param name="x">The row position</param>
|
|
||||
/// <param name="sourceY">The source column position.</param>
|
|
||||
/// <returns>The weighted sum</returns>
|
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
public Vector4 ComputeWeightedColumnSum(Buffer2D<Vector4> firstPassPixels, int x, int sourceY) |
|
||||
{ |
|
||||
ref float verticalValues = ref this.GetStartReference(); |
|
||||
int left = this.Left; |
|
||||
|
|
||||
// Destination color components
|
|
||||
Vector4 result = Vector4.Zero; |
|
||||
|
|
||||
for (int i = 0; i < this.Length; i++) |
|
||||
{ |
|
||||
float yw = Unsafe.Add(ref verticalValues, i); |
|
||||
int index = left + i + sourceY; |
|
||||
result += firstPassPixels[x, index] * yw; |
|
||||
} |
|
||||
|
|
||||
return result.UnPremultiply(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,41 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using Xunit; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.Helpers |
||||
|
{ |
||||
|
public class ImageMathsTests |
||||
|
{ |
||||
|
[Theory] |
||||
|
[InlineData(1, 1, 1)] |
||||
|
[InlineData(1, 42, 1)] |
||||
|
[InlineData(10, 8, 2)] |
||||
|
[InlineData(12, 18, 6)] |
||||
|
[InlineData(4536, 1000, 8)] |
||||
|
[InlineData(1600, 1024, 64)] |
||||
|
public void GreatestCommonDivisor(int a, int b, int expected) |
||||
|
{ |
||||
|
int actual = ImageMaths.GreatestCommonDivisor(a, b); |
||||
|
|
||||
|
Assert.Equal(expected, actual); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(1, 1, 1)] |
||||
|
[InlineData(1, 42, 42)] |
||||
|
[InlineData(3, 4, 12)] |
||||
|
[InlineData(6, 4, 12)] |
||||
|
[InlineData(1600, 1024, 25600)] |
||||
|
[InlineData(3264, 100, 81600)] |
||||
|
public void LeastCommonMultiple(int a, int b, int expected) |
||||
|
{ |
||||
|
int actual = ImageMaths.LeastCommonMultiple(a, b); |
||||
|
|
||||
|
Assert.Equal(expected, actual); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// TODO: We need to test all ImageMaths methods!
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,76 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Numerics; |
||||
|
|
||||
|
using Xunit; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.Helpers |
||||
|
{ |
||||
|
public class Vector4ExtensionsTests |
||||
|
{ |
||||
|
[Theory] |
||||
|
[InlineData(0)] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(30)] |
||||
|
public void Premultiply_VectorSpan(int length) |
||||
|
{ |
||||
|
var rnd = new Random(42); |
||||
|
Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); |
||||
|
Vector4[] expected = source.Select(v => v.Premultiply()).ToArray(); |
||||
|
|
||||
|
Vector4Extensions.Premultiply(source); |
||||
|
|
||||
|
Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(0)] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(30)] |
||||
|
public void UnPremultiply_VectorSpan(int length) |
||||
|
{ |
||||
|
var rnd = new Random(42); |
||||
|
Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); |
||||
|
Vector4[] expected = source.Select(v => v.UnPremultiply()).ToArray(); |
||||
|
|
||||
|
Vector4Extensions.UnPremultiply(source); |
||||
|
|
||||
|
Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(0)] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(30)] |
||||
|
public void Expand_VectorSpan(int length) |
||||
|
{ |
||||
|
var rnd = new Random(42); |
||||
|
Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); |
||||
|
Vector4[] expected = source.Select(v => v.Expand()).ToArray(); |
||||
|
|
||||
|
Vector4Extensions.Expand(source); |
||||
|
|
||||
|
Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); |
||||
|
} |
||||
|
|
||||
|
[Theory] |
||||
|
[InlineData(0)] |
||||
|
[InlineData(1)] |
||||
|
[InlineData(30)] |
||||
|
public void Compress_VectorSpan(int length) |
||||
|
{ |
||||
|
var rnd = new Random(42); |
||||
|
Vector4[] source = rnd.GenerateRandomVectorArray(length, 0, 1); |
||||
|
Vector4[] expected = source.Select(v => v.Compress()).ToArray(); |
||||
|
|
||||
|
Vector4Extensions.Compress(source); |
||||
|
|
||||
|
Assert.Equal(expected, source, new ApproximateFloatComparer(1e-6f)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
using System; |
||||
|
using System.IO; |
||||
|
using System.Text; |
||||
|
|
||||
|
using SixLabors.ImageSharp.PixelFormats; |
||||
|
using SixLabors.ImageSharp.Processing; |
||||
|
using SixLabors.ImageSharp.Processing.Processors.Transforms; |
||||
|
using SixLabors.Primitives; |
||||
|
|
||||
|
using Xunit; |
||||
|
using Xunit.Abstractions; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms |
||||
|
{ |
||||
|
public class KernelMapTests |
||||
|
{ |
||||
|
private ITestOutputHelper Output { get; } |
||||
|
|
||||
|
public KernelMapTests(ITestOutputHelper output) |
||||
|
{ |
||||
|
this.Output = output; |
||||
|
} |
||||
|
|
||||
|
[Theory(Skip = "TODO: Add asserionts")] |
||||
|
[InlineData(500, 200, nameof(KnownResamplers.Bicubic))] |
||||
|
[InlineData(50, 40, nameof(KnownResamplers.Bicubic))] |
||||
|
[InlineData(40, 30, nameof(KnownResamplers.Bicubic))] |
||||
|
[InlineData(500, 200, nameof(KnownResamplers.Lanczos8))] |
||||
|
[InlineData(100, 80, nameof(KnownResamplers.Lanczos8))] |
||||
|
[InlineData(100, 10, nameof(KnownResamplers.Lanczos8))] |
||||
|
[InlineData(10, 100, nameof(KnownResamplers.Lanczos8))] |
||||
|
public void PrintKernelMap(int srcSize, int destSize, string resamplerName) |
||||
|
{ |
||||
|
var resampler = (IResampler)typeof(KnownResamplers).GetProperty(resamplerName).GetValue(null); |
||||
|
|
||||
|
var kernelMap = KernelMap.Calculate(resampler, destSize, srcSize, Configuration.Default.MemoryAllocator); |
||||
|
|
||||
|
var bld = new StringBuilder(); |
||||
|
|
||||
|
foreach (ResizeKernel window in kernelMap.Kernels) |
||||
|
{ |
||||
|
Span<float> span = window.GetSpan(); |
||||
|
for (int i = 0; i < window.Length; i++) |
||||
|
{ |
||||
|
float value = span[i]; |
||||
|
bld.Append($"{value,7:F4}"); |
||||
|
bld.Append("| "); |
||||
|
} |
||||
|
|
||||
|
bld.AppendLine(); |
||||
|
} |
||||
|
|
||||
|
string outDir = TestEnvironment.CreateOutputDirectory("." + nameof(this.PrintKernelMap)); |
||||
|
string fileName = $@"{outDir}\{resamplerName}_{srcSize}_{destSize}.MD"; |
||||
|
|
||||
|
File.WriteAllText(fileName, bld.ToString()); |
||||
|
|
||||
|
this.Output.WriteLine(bld.ToString()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,69 +1,47 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
// Copyright (c) Six Labors and contributors.
|
||||
// Licensed under the Apache License, Version 2.0.
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
using System; |
|
||||
using System.IO; |
|
||||
using System.Text; |
|
||||
|
|
||||
using SixLabors.ImageSharp.PixelFormats; |
using SixLabors.ImageSharp.PixelFormats; |
||||
using SixLabors.ImageSharp.Processing; |
using SixLabors.ImageSharp.Processing; |
||||
using SixLabors.ImageSharp.Processing.Processors.Transforms; |
|
||||
|
|
||||
using SixLabors.Primitives; |
using Xunit; |
||||
using Xunit.Abstractions; |
using Xunit.Abstractions; |
||||
|
|
||||
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms |
namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms |
||||
{ |
{ |
||||
public class ResizeProfilingBenchmarks : MeasureFixture |
public class ResizeProfilingBenchmarks : MeasureFixture |
||||
{ |
{ |
||||
|
public const string SkipText = |
||||
|
#if false
|
||||
|
null; |
||||
|
#else
|
||||
|
"Benchmark, enable manually!"; |
||||
|
#endif
|
||||
|
|
||||
|
private readonly Configuration configuration = Configuration.CreateDefaultInstance(); |
||||
|
|
||||
public ResizeProfilingBenchmarks(ITestOutputHelper output) |
public ResizeProfilingBenchmarks(ITestOutputHelper output) |
||||
: base(output) |
: base(output) |
||||
{ |
{ |
||||
|
this.configuration.MaxDegreeOfParallelism = 1; |
||||
} |
} |
||||
|
|
||||
public int ExecutionCount { get; set; } = 50; |
public int ExecutionCount { get; set; } = 50; |
||||
|
|
||||
// [Theory] // Benchmark, enable manually!
|
[Theory(Skip = SkipText)] |
||||
// [InlineData(100, 100)]
|
[InlineData(100, 100)] |
||||
// [InlineData(2000, 2000)]
|
[InlineData(2000, 2000)] |
||||
public void ResizeBicubic(int width, int height) |
public void ResizeBicubic(int width, int height) |
||||
{ |
{ |
||||
this.Measure(this.ExecutionCount, |
this.Measure(this.ExecutionCount, |
||||
() => |
() => |
||||
{ |
{ |
||||
using (var image = new Image<Rgba32>(width, height)) |
using (var image = new Image<Rgba32>(this.configuration, width, height)) |
||||
{ |
{ |
||||
image.Mutate(x => x.Resize(width / 4, height / 4)); |
image.Mutate(x => x.Resize(width / 5, height / 5)); |
||||
} |
} |
||||
}); |
}); |
||||
} |
} |
||||
|
|
||||
// [Fact]
|
|
||||
public void PrintWeightsData() |
|
||||
{ |
|
||||
var size = new Size(500, 500); |
|
||||
var proc = new ResizeProcessor<Rgba32>(KnownResamplers.Bicubic, 200, 200, size); |
|
||||
|
|
||||
WeightsBuffer weights = proc.PrecomputeWeights(Configuration.Default.MemoryAllocator, proc.Width, size.Width); |
|
||||
|
|
||||
var bld = new StringBuilder(); |
|
||||
|
|
||||
foreach (WeightsWindow window in weights.Weights) |
|
||||
{ |
|
||||
Span<float> span = window.GetWindowSpan(); |
|
||||
for (int i = 0; i < window.Length; i++) |
|
||||
{ |
|
||||
float value = span[i]; |
|
||||
bld.Append(value); |
|
||||
bld.Append("| "); |
|
||||
} |
|
||||
|
|
||||
bld.AppendLine(); |
|
||||
} |
|
||||
|
|
||||
File.WriteAllText("BicubicWeights.MD", bld.ToString()); |
|
||||
|
|
||||
// this.Output.WriteLine(bld.ToString());
|
|
||||
} |
|
||||
} |
} |
||||
} |
} |
||||
@ -1 +1 @@ |
|||||
Subproject commit c0627f384c1d3d2f8d914c9578ae31354c35fd2c |
Subproject commit 03c7fa7582dea75cea0d49514ccb7e1b6dc9e780 |
||||
Loading…
Reference in new issue