|
|
|
@ -2,11 +2,13 @@ |
|
|
|
// Licensed under the Apache License, Version 2.0.
|
|
|
|
|
|
|
|
using System; |
|
|
|
using System.Buffers; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Numerics; |
|
|
|
using System.Runtime.CompilerServices; |
|
|
|
|
|
|
|
using System.Runtime.InteropServices; |
|
|
|
using SixLabors.ImageSharp.PixelFormats; |
|
|
|
using SixLabors.Memory; |
|
|
|
using SixLabors.Primitives; |
|
|
|
|
|
|
|
namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|
|
|
@ -19,13 +21,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|
|
|
where TPixel : struct, IPixel<TPixel> |
|
|
|
{ |
|
|
|
private readonly Dictionary<TPixel, PixelPair<TPixel>> cache = new Dictionary<TPixel, PixelPair<TPixel>>(); |
|
|
|
|
|
|
|
private TPixel[] palette; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// The vector representation of the image palette.
|
|
|
|
/// </summary>
|
|
|
|
private Vector4[] paletteVector; |
|
|
|
private IMemoryOwner<TPixel> palette; |
|
|
|
private IMemoryOwner<Vector4> paletteVector; |
|
|
|
private bool palleteVectorMapped; |
|
|
|
private bool isDisposed; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Initializes a new instance of the <see cref="PaletteDitherProcessor{TPixel}"/> class.
|
|
|
|
@ -37,6 +36,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|
|
|
: base(source, sourceRectangle) |
|
|
|
{ |
|
|
|
this.Definition = definition; |
|
|
|
this.palette = this.Configuration.MemoryAllocator.Allocate<TPixel>(definition.Palette.Length); |
|
|
|
this.paletteVector = this.Configuration.MemoryAllocator.Allocate<Vector4>(definition.Palette.Length); |
|
|
|
} |
|
|
|
|
|
|
|
protected PaletteDitherProcessor Definition { get; } |
|
|
|
@ -44,28 +45,45 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|
|
|
/// <inheritdoc/>
|
|
|
|
protected override void BeforeFrameApply(ImageFrame<TPixel> source) |
|
|
|
{ |
|
|
|
// Lazy init palette:
|
|
|
|
if (this.palette is null) |
|
|
|
// Lazy init palettes:
|
|
|
|
if (!this.palleteVectorMapped) |
|
|
|
{ |
|
|
|
ReadOnlySpan<Color> sourcePalette = this.Definition.Palette.Span; |
|
|
|
this.palette = new TPixel[sourcePalette.Length]; |
|
|
|
Color.ToPixel<TPixel>(this.Configuration, sourcePalette, this.palette); |
|
|
|
} |
|
|
|
Color.ToPixel(this.Configuration, sourcePalette, this.palette.Memory.Span); |
|
|
|
|
|
|
|
// Lazy init paletteVector:
|
|
|
|
if (this.paletteVector is null) |
|
|
|
{ |
|
|
|
this.paletteVector = new Vector4[this.palette.Length]; |
|
|
|
PixelOperations<TPixel>.Instance.ToVector4( |
|
|
|
this.Configuration, |
|
|
|
(ReadOnlySpan<TPixel>)this.palette, |
|
|
|
(Span<Vector4>)this.paletteVector, |
|
|
|
this.palette.Memory.Span, |
|
|
|
this.paletteVector.Memory.Span, |
|
|
|
PixelConversionModifiers.Scale); |
|
|
|
} |
|
|
|
|
|
|
|
this.palleteVectorMapped = true; |
|
|
|
|
|
|
|
base.BeforeFrameApply(source); |
|
|
|
} |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
protected override void Dispose(bool disposing) |
|
|
|
{ |
|
|
|
if (this.isDisposed) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (disposing) |
|
|
|
{ |
|
|
|
this.palette?.Dispose(); |
|
|
|
this.paletteVector?.Dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
this.palette = null; |
|
|
|
this.paletteVector = null; |
|
|
|
|
|
|
|
this.isDisposed = true; |
|
|
|
base.Dispose(disposing); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Returns the two closest colors from the palette calculated via Euclidean distance in the Rgba space.
|
|
|
|
/// </summary>
|
|
|
|
@ -93,21 +111,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering |
|
|
|
|
|
|
|
TPixel closest = default; |
|
|
|
TPixel secondClosest = default; |
|
|
|
for (int index = 0; index < this.paletteVector.Length; index++) |
|
|
|
Span<TPixel> paletteSpan = this.palette.Memory.Span; |
|
|
|
ref TPixel paletteSpanBase = ref MemoryMarshal.GetReference(paletteSpan); |
|
|
|
Span<Vector4> paletteVectorSpan = this.paletteVector.Memory.Span; |
|
|
|
ref Vector4 paletteVectorSpanBase = ref MemoryMarshal.GetReference(paletteVectorSpan); |
|
|
|
|
|
|
|
for (int index = 0; index < paletteVectorSpan.Length; index++) |
|
|
|
{ |
|
|
|
ref Vector4 candidate = ref this.paletteVector[index]; |
|
|
|
ref Vector4 candidate = ref Unsafe.Add(ref paletteVectorSpanBase, index); |
|
|
|
float distance = Vector4.DistanceSquared(vector, candidate); |
|
|
|
|
|
|
|
if (distance < leastDistance) |
|
|
|
{ |
|
|
|
leastDistance = distance; |
|
|
|
secondClosest = closest; |
|
|
|
closest = this.palette[index]; |
|
|
|
closest = Unsafe.Add(ref paletteSpanBase, index); |
|
|
|
} |
|
|
|
else if (distance < secondLeastDistance) |
|
|
|
{ |
|
|
|
secondLeastDistance = distance; |
|
|
|
secondClosest = this.palette[index]; |
|
|
|
secondClosest = Unsafe.Add(ref paletteSpanBase, index); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|