|
|
@ -5,6 +5,7 @@ using System; |
|
|
using System.Collections.Concurrent; |
|
|
using System.Collections.Concurrent; |
|
|
using System.Numerics; |
|
|
using System.Numerics; |
|
|
using System.Runtime.CompilerServices; |
|
|
using System.Runtime.CompilerServices; |
|
|
|
|
|
using System.Runtime.InteropServices; |
|
|
using SixLabors.ImageSharp.PixelFormats; |
|
|
using SixLabors.ImageSharp.PixelFormats; |
|
|
|
|
|
|
|
|
namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
@ -17,27 +18,25 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
internal readonly struct EuclideanPixelMap<TPixel> : IPixelMap<TPixel>, IEquatable<EuclideanPixelMap<TPixel>> |
|
|
internal readonly struct EuclideanPixelMap<TPixel> : IPixelMap<TPixel>, IEquatable<EuclideanPixelMap<TPixel>> |
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
where TPixel : unmanaged, IPixel<TPixel> |
|
|
{ |
|
|
{ |
|
|
private readonly ConcurrentDictionary<int, Vector4> vectorCache; |
|
|
private readonly Vector4[] vectorCache; |
|
|
private readonly ConcurrentDictionary<TPixel, int> distanceCache; |
|
|
private readonly ConcurrentDictionary<TPixel, int> distanceCache; |
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// <summary>
|
|
|
/// Initializes a new instance of the <see cref="EuclideanPixelMap{TPixel}"/> struct.
|
|
|
/// Initializes a new instance of the <see cref="EuclideanPixelMap{TPixel}"/> struct.
|
|
|
/// </summary>
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="configuration">The configuration.</param>
|
|
|
/// <param name="palette">The color palette to map from.</param>
|
|
|
/// <param name="palette">The color palette to map from.</param>
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
public EuclideanPixelMap(ReadOnlyMemory<TPixel> palette) |
|
|
public EuclideanPixelMap(Configuration configuration, ReadOnlyMemory<TPixel> palette) |
|
|
{ |
|
|
{ |
|
|
Guard.MustBeGreaterThan(palette.Length, 0, nameof(palette)); |
|
|
Guard.MustBeGreaterThan(palette.Length, 0, nameof(palette)); |
|
|
|
|
|
|
|
|
this.Palette = palette; |
|
|
this.Palette = palette; |
|
|
ReadOnlySpan<TPixel> paletteSpan = this.Palette.Span; |
|
|
ReadOnlySpan<TPixel> paletteSpan = this.Palette.Span; |
|
|
this.vectorCache = new ConcurrentDictionary<int, Vector4>(); |
|
|
this.vectorCache = new Vector4[paletteSpan.Length]; |
|
|
this.distanceCache = new ConcurrentDictionary<TPixel, int>(); |
|
|
this.distanceCache = new ConcurrentDictionary<TPixel, int>(); |
|
|
|
|
|
|
|
|
for (int i = 0; i < paletteSpan.Length; i++) |
|
|
PixelOperations<TPixel>.Instance.ToVector4(configuration, paletteSpan, this.vectorCache); |
|
|
{ |
|
|
|
|
|
this.vectorCache[i] = paletteSpan[i].ToScaledVector4(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
/// <inheritdoc/>
|
|
|
@ -81,31 +80,32 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
int index = 0; |
|
|
int index = 0; |
|
|
float leastDistance = float.MaxValue; |
|
|
float leastDistance = float.MaxValue; |
|
|
Vector4 vector = color.ToScaledVector4(); |
|
|
Vector4 vector = color.ToScaledVector4(); |
|
|
|
|
|
ref TPixel paletteRef = ref MemoryMarshal.GetReference(palette); |
|
|
|
|
|
ref Vector4 vectorCacheRef = ref MemoryMarshal.GetReference<Vector4>(this.vectorCache); |
|
|
|
|
|
|
|
|
for (int i = 0; i < palette.Length; i++) |
|
|
for (int i = 0; i < palette.Length; i++) |
|
|
{ |
|
|
{ |
|
|
Vector4 candidate = this.vectorCache[i]; |
|
|
Vector4 candidate = Unsafe.Add(ref vectorCacheRef, i); |
|
|
float distance = Vector4.DistanceSquared(vector, candidate); |
|
|
float distance = Vector4.DistanceSquared(vector, candidate); |
|
|
|
|
|
|
|
|
if (!(distance < leastDistance)) |
|
|
// If it's an exact match, exit the loop
|
|
|
|
|
|
if (distance == 0) |
|
|
{ |
|
|
{ |
|
|
continue; |
|
|
index = i; |
|
|
|
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Less than... assign.
|
|
|
if (distance < leastDistance) |
|
|
index = i; |
|
|
|
|
|
leastDistance = distance; |
|
|
|
|
|
|
|
|
|
|
|
// And if it's an exact match, exit the loop
|
|
|
|
|
|
if (distance == 0) |
|
|
|
|
|
{ |
|
|
{ |
|
|
break; |
|
|
// Less than... assign.
|
|
|
|
|
|
index = i; |
|
|
|
|
|
leastDistance = distance; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Now I have the index, pop it into the cache for next time
|
|
|
// Now I have the index, pop it into the cache for next time
|
|
|
this.distanceCache[color] = index; |
|
|
this.distanceCache[color] = index; |
|
|
match = palette[index]; |
|
|
match = Unsafe.Add(ref paletteRef, index); |
|
|
return index; |
|
|
return index; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|