@ -11,6 +11,8 @@ using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats ;
using SixLabors.Memory ;
// TODO: Isn't an AOS ("array of structures") layout more efficient & more readable than SOA ("structure of arrays") for this particular use case?
// (T, R, G, B, A, M2) could be grouped together! Investigate a ColorMoment struct.
namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
/// <summary>
@ -36,20 +38,17 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
internal sealed class WuFrameQuantizer < TPixel > : FrameQuantizerBase < TPixel >
where TPixel : struct , IPixel < TPixel >
{
// TODO: The WuFrameQuantizer<TPixel> code is rising several questions:
// - Do we really need to ALWAYS allocate the whole table of size TableLength? (~ 2471625 * sizeof(long) * 5 bytes ) JS. I'm afraid so.
// - Isn't an AOS ("array of structures") layout more efficient & more readable than SOA ("structure of arrays") for this particular use case?
// (T, R, G, B, A, M2) could be grouped together!
// - It's a frequently used class, we need tests! (So we can optimize safely.) There are tests in the original!!! We should just adopt them!
// https://github.com/JeremyAnsel/JeremyAnsel.ColorQuant/blob/master/JeremyAnsel.ColorQuant/JeremyAnsel.ColorQuant.Tests/WuColorQuantizerTests.cs
// The following two variables determine the amount of bits to preserve when calculating the histogram.
// Reducing the value of these numbers the granularity of the color maps produced, making it much faster
// and using much less memory but potentially less accurate. Current results are very good though!
/// <summary>
/// The index bits.
/// The index bits. 6 in original code.
/// </summary>
private const int IndexBits = 5 ;
/// <summary>
/// The index alpha bits. Keep separate for now to allow easy adjustment .
/// The index alpha bits. 3 in original code .
/// </summary>
private const int IndexAlphaBits = 5 ;
@ -64,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
private const int IndexAlphaCount = ( 1 < < IndexAlphaBits ) + 1 ;
/// <summary>
/// The table length. Now 1185921.
/// The table length. Now 1185921. originally 2471625.
/// </summary>
private const int TableLength = IndexCount * IndexCount * IndexCount * IndexAlphaCount ;
@ -96,7 +95,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// <summary>
/// Moment of <c>c^2*P(c)</c>.
/// </summary>
private IMemoryOwner < float > m2 ;
private IMemoryOwner < double > m2 ;
/// <summary>
/// Color space tag.
@ -149,28 +148,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
Guard . NotNull ( image , nameof ( image ) ) ;
MemoryAllocator memoryAllocator = image . MemoryAllocator ;
try
{
this . vwt = memoryAllocator . Allocate < long > ( TableLength , AllocationOptions . Clean ) ;
this . vmr = memoryAllocator . Allocate < long > ( TableLength , AllocationOptions . Clean ) ;
this . vmg = memoryAllocator . Allocate < long > ( TableLength , AllocationOptions . Clean ) ;
this . vmb = memoryAllocator . Allocate < long > ( TableLength , AllocationOptions . Clean ) ;
this . vma = memoryAllocator . Allocate < long > ( TableLength , AllocationOptions . Clean ) ;
this . m2 = memoryAllocator . Allocate < float > ( TableLength , AllocationOptions . Clean ) ;
this . tag = memoryAllocator . Allocate < byte > ( TableLength , AllocationOptions . Clean ) ;
return base . QuantizeFrame ( image ) ;
}
finally
{
this . vwt ? . Dispose ( ) ;
this . vmr ? . Dispose ( ) ;
this . vmg ? . Dispose ( ) ;
this . vmb ? . Dispose ( ) ;
this . vma ? . Dispose ( ) ;
this . m2 ? . Dispose ( ) ;
this . tag ? . Dispose ( ) ;
}
this . vwt = memoryAllocator . Allocate < long > ( TableLength , AllocationOptions . Clean ) ;
this . vmr = memoryAllocator . Allocate < long > ( TableLength , AllocationOptions . Clean ) ;
this . vmg = memoryAllocator . Allocate < long > ( TableLength , AllocationOptions . Clean ) ;
this . vmb = memoryAllocator . Allocate < long > ( TableLength , AllocationOptions . Clean ) ;
this . vma = memoryAllocator . Allocate < long > ( TableLength , AllocationOptions . Clean ) ;
this . m2 = memoryAllocator . Allocate < double > ( TableLength , AllocationOptions . Clean ) ;
this . tag = memoryAllocator . Allocate < byte > ( TableLength , AllocationOptions . Clean ) ;
return base . QuantizeFrame ( image ) ;
}
/// <inheritdoc/>
public override void Dispose ( )
{
this . vwt ? . Dispose ( ) ;
this . vmr ? . Dispose ( ) ;
this . vmg ? . Dispose ( ) ;
this . vmb ? . Dispose ( ) ;
this . vma ? . Dispose ( ) ;
this . m2 ? . Dispose ( ) ;
this . tag ? . Dispose ( ) ;
}
internal TPixel [ ] AotGetPalette ( ) = > this . GetPalette ( ) ;
@ -275,9 +273,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetPaletteIndex ( int r , int g , int b , int a )
{
return ( r < < ( ( IndexBits * 2 ) + IndexAlphaBits ) ) + ( r < < ( IndexBits + IndexAlphaBits + 1 ) )
+ ( g < < ( IndexBits + IndexAlphaBits ) ) + ( r < < ( IndexBits * 2 ) ) + ( r < < ( IndexBits + 1 ) )
+ ( g < < IndexBits ) + ( ( r + g + b ) < < IndexAlphaBits ) + r + g + b + a ;
return ( r < < ( ( IndexBits * 2 ) + IndexAlphaBits ) )
+ ( r < < ( IndexBits + IndexAlphaBits + 1 ) )
+ ( g < < ( IndexBits + IndexAlphaBits ) )
+ ( r < < ( IndexBits * 2 ) )
+ ( r < < ( IndexBits + 1 ) )
+ ( g < < IndexBits )
+ ( ( r + g + b ) < < IndexAlphaBits )
+ r + g + b + a ;
}
/// <summary>
@ -288,26 +291,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// <returns>The result.</returns>
private static float Volume ( ref Box cube , Span < long > moment )
{
return moment [ GetPaletteIndex ( cube . R1 , cube . G1 , cube . B1 , cube . A1 ) ]
- moment [ GetPaletteIndex ( cube . R1 , cube . G1 , cube . B1 , cube . A0 ) ]
- moment [ GetPaletteIndex ( cube . R1 , cube . G1 , cube . B0 , cube . A1 ) ]
+ moment [ GetPaletteIndex ( cube . R1 , cube . G1 , cube . B0 , cube . A0 ) ]
- moment [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B1 , cube . A1 ) ]
+ moment [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B1 , cube . A0 ) ]
+ moment [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B0 , cube . A1 ) ]
- moment [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B0 , cube . A0 ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B1 , cube . A1 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B1 , cube . A0 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B0 , cube . A1 ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B0 , cube . A0 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B1 , cube . A1 ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B1 , cube . A0 ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B0 , cube . A1 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B0 , cube . A0 ) ] ;
return moment [ GetPaletteIndex ( cube . RMax , cube . GMax , cube . BMax , cube . AMax ) ]
- moment [ GetPaletteIndex ( cube . RMax , cube . GMax , cube . BMax , cube . AMin ) ]
- moment [ GetPaletteIndex ( cube . RMax , cube . GMax , cube . BMin , cube . AMax ) ]
+ moment [ GetPaletteIndex ( cube . RMax , cube . GMax , cube . BMin , cube . AMin ) ]
- moment [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMax , cube . AMax ) ]
+ moment [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMax , cube . AMin ) ]
+ moment [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMin , cube . AMax ) ]
- moment [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMin , cube . AMin ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMax , cube . AMax ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMax , cube . AMin ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMin , cube . AMax ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMin , cube . AMin ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMax , cube . AMax ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMax , cube . AMin ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMin , cube . AMax ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMin , cube . AMin ) ] ;
}
/// <summary>
/// Computes part of Volume(cube, moment) that doesn't depend on r1, g1, or b1 (depending on direction).
/// Computes part of Volume(cube, moment) that doesn't depend on RMax, GMax, BMax, or AMax (depending on direction).
/// </summary>
/// <param name="cube">The cube.</param>
/// <param name="direction">The direction.</param>
@ -319,47 +322,47 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
// Red
case 3 :
return - moment [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B1 , cube . A1 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B1 , cube . A0 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B0 , cube . A1 ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B0 , cube . A0 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B1 , cube . A1 ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B1 , cube . A0 ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B0 , cube . A1 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B0 , cube . A0 ) ] ;
return - moment [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMax , cube . AMax ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMax , cube . AMin ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMin , cube . AMax ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMin , cube . AMin ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMax , cube . AMax ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMax , cube . AMin ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMin , cube . AMax ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMin , cube . AMin ) ] ;
// Green
case 2 :
return - moment [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B1 , cube . A1 ) ]
+ moment [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B1 , cube . A0 ) ]
+ moment [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B0 , cube . A1 ) ]
- moment [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B0 , cube . A0 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B1 , cube . A1 ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B1 , cube . A0 ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B0 , cube . A1 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B0 , cube . A0 ) ] ;
return - moment [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMax , cube . AMax ) ]
+ moment [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMax , cube . AMin ) ]
+ moment [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMin , cube . AMax ) ]
- moment [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMin , cube . AMin ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMax , cube . AMax ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMax , cube . AMin ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMin , cube . AMax ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMin , cube . AMin ) ] ;
// Blue
case 1 :
return - moment [ GetPaletteIndex ( cube . R1 , cube . G1 , cube . B0 , cube . A1 ) ]
+ moment [ GetPaletteIndex ( cube . R1 , cube . G1 , cube . B0 , cube . A0 ) ]
+ moment [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B0 , cube . A1 ) ]
- moment [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B0 , cube . A0 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B0 , cube . A1 ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B0 , cube . A0 ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B0 , cube . A1 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B0 , cube . A0 ) ] ;
return - moment [ GetPaletteIndex ( cube . RMax , cube . GMax , cube . BMin , cube . AMax ) ]
+ moment [ GetPaletteIndex ( cube . RMax , cube . GMax , cube . BMin , cube . AMin ) ]
+ moment [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMin , cube . AMax ) ]
- moment [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMin , cube . AMin ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMin , cube . AMax ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMin , cube . AMin ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMin , cube . AMax ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMin , cube . AMin ) ] ;
// Alpha
case 0 :
return - moment [ GetPaletteIndex ( cube . R1 , cube . G1 , cube . B1 , cube . A0 ) ]
+ moment [ GetPaletteIndex ( cube . R1 , cube . G1 , cube . B0 , cube . A0 ) ]
+ moment [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B1 , cube . A0 ) ]
- moment [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B0 , cube . A0 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B1 , cube . A0 ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B0 , cube . A0 ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B1 , cube . A0 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B0 , cube . A0 ) ] ;
return - moment [ GetPaletteIndex ( cube . RMax , cube . GMax , cube . BMax , cube . AMin ) ]
+ moment [ GetPaletteIndex ( cube . RMax , cube . GMax , cube . BMin , cube . AMin ) ]
+ moment [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMax , cube . AMin ) ]
- moment [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMin , cube . AMin ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMax , cube . AMin ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMin , cube . AMin ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMax , cube . AMin ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMin , cube . AMin ) ] ;
default :
throw new ArgumentOutOfRangeException ( nameof ( direction ) ) ;
@ -367,7 +370,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
}
/// <summary>
/// Computes remainder of Volume(cube, moment), substituting position for r1, g1, or b1 (depending on direction).
/// Computes remainder of Volume(cube, moment), substituting position for RMax, GMax, BMax, or AMax (depending on direction).
/// </summary>
/// <param name="cube">The cube.</param>
/// <param name="direction">The direction.</param>
@ -380,47 +383,47 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
// Red
case 3 :
return moment [ GetPaletteIndex ( position , cube . G1 , cube . B1 , cube . A1 ) ]
- moment [ GetPaletteIndex ( position , cube . G1 , cube . B1 , cube . A0 ) ]
- moment [ GetPaletteIndex ( position , cube . G1 , cube . B0 , cube . A1 ) ]
+ moment [ GetPaletteIndex ( position , cube . G1 , cube . B0 , cube . A0 ) ]
- moment [ GetPaletteIndex ( position , cube . G0 , cube . B1 , cube . A1 ) ]
+ moment [ GetPaletteIndex ( position , cube . G0 , cube . B1 , cube . A0 ) ]
+ moment [ GetPaletteIndex ( position , cube . G0 , cube . B0 , cube . A1 ) ]
- moment [ GetPaletteIndex ( position , cube . G0 , cube . B0 , cube . A0 ) ] ;
return moment [ GetPaletteIndex ( position , cube . GMax , cube . BMax , cube . AMax ) ]
- moment [ GetPaletteIndex ( position , cube . GMax , cube . BMax , cube . AMin ) ]
- moment [ GetPaletteIndex ( position , cube . GMax , cube . BMin , cube . AMax ) ]
+ moment [ GetPaletteIndex ( position , cube . GMax , cube . BMin , cube . AMin ) ]
- moment [ GetPaletteIndex ( position , cube . GMin , cube . BMax , cube . AMax ) ]
+ moment [ GetPaletteIndex ( position , cube . GMin , cube . BMax , cube . AMin ) ]
+ moment [ GetPaletteIndex ( position , cube . GMin , cube . BMin , cube . AMax ) ]
- moment [ GetPaletteIndex ( position , cube . GMin , cube . BMin , cube . AMin ) ] ;
// Green
case 2 :
return moment [ GetPaletteIndex ( cube . R1 , position , cube . B1 , cube . A1 ) ]
- moment [ GetPaletteIndex ( cube . R1 , position , cube . B1 , cube . A0 ) ]
- moment [ GetPaletteIndex ( cube . R1 , position , cube . B0 , cube . A1 ) ]
+ moment [ GetPaletteIndex ( cube . R1 , position , cube . B0 , cube . A0 ) ]
- moment [ GetPaletteIndex ( cube . R0 , position , cube . B1 , cube . A1 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , position , cube . B1 , cube . A0 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , position , cube . B0 , cube . A1 ) ]
- moment [ GetPaletteIndex ( cube . R0 , position , cube . B0 , cube . A0 ) ] ;
return moment [ GetPaletteIndex ( cube . RMax , position , cube . BMax , cube . AMax ) ]
- moment [ GetPaletteIndex ( cube . RMax , position , cube . BMax , cube . AMin ) ]
- moment [ GetPaletteIndex ( cube . RMax , position , cube . BMin , cube . AMax ) ]
+ moment [ GetPaletteIndex ( cube . RMax , position , cube . BMin , cube . AMin ) ]
- moment [ GetPaletteIndex ( cube . RMin , position , cube . BMax , cube . AMax ) ]
+ moment [ GetPaletteIndex ( cube . RMin , position , cube . BMax , cube . AMin ) ]
+ moment [ GetPaletteIndex ( cube . RMin , position , cube . BMin , cube . AMax ) ]
- moment [ GetPaletteIndex ( cube . RMin , position , cube . BMin , cube . AMin ) ] ;
// Blue
case 1 :
return moment [ GetPaletteIndex ( cube . R1 , cube . G1 , position , cube . A1 ) ]
- moment [ GetPaletteIndex ( cube . R1 , cube . G1 , position , cube . A0 ) ]
- moment [ GetPaletteIndex ( cube . R1 , cube . G0 , position , cube . A1 ) ]
+ moment [ GetPaletteIndex ( cube . R1 , cube . G0 , position , cube . A0 ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G1 , position , cube . A1 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G1 , position , cube . A0 ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G0 , position , cube . A1 ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G0 , position , cube . A0 ) ] ;
return moment [ GetPaletteIndex ( cube . RMax , cube . GMax , position , cube . AMax ) ]
- moment [ GetPaletteIndex ( cube . RMax , cube . GMax , position , cube . AMin ) ]
- moment [ GetPaletteIndex ( cube . RMax , cube . GMin , position , cube . AMax ) ]
+ moment [ GetPaletteIndex ( cube . RMax , cube . GMin , position , cube . AMin ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMax , position , cube . AMax ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMax , position , cube . AMin ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMin , position , cube . AMax ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMin , position , cube . AMin ) ] ;
// Alpha
case 0 :
return moment [ GetPaletteIndex ( cube . R1 , cube . G1 , cube . B1 , position ) ]
- moment [ GetPaletteIndex ( cube . R1 , cube . G1 , cube . B0 , position ) ]
- moment [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B1 , position ) ]
+ moment [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B0 , position ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B1 , position ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B0 , position ) ]
+ moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B1 , position ) ]
- moment [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B0 , position ) ] ;
return moment [ GetPaletteIndex ( cube . RMax , cube . GMax , cube . BMax , position ) ]
- moment [ GetPaletteIndex ( cube . RMax , cube . GMax , cube . BMin , position ) ]
- moment [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMax , position ) ]
+ moment [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMin , position ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMax , position ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMin , position ) ]
+ moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMax , position ) ]
- moment [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMin , position ) ] ;
default :
throw new ArgumentOutOfRangeException ( nameof ( direction ) ) ;
@ -440,7 +443,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
Span < long > vmgSpan = this . vmg . GetSpan ( ) ;
Span < long > vmbSpan = this . vmb . GetSpan ( ) ;
Span < long > vmaSpan = this . vma . GetSpan ( ) ;
Span < float > m2Span = this . m2 . GetSpan ( ) ;
Span < double > m2Span = this . m2 . GetSpan ( ) ;
// Build up the 3-D color histogram
// Loop through each row
@ -489,34 +492,34 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
Span < long > vmgSpan = this . vmg . GetSpan ( ) ;
Span < long > vmbSpan = this . vmb . GetSpan ( ) ;
Span < long > vmaSpan = this . vma . GetSpan ( ) ;
Span < float > m2Span = this . m2 . GetSpan ( ) ;
Span < double > m2Span = this . m2 . GetSpan ( ) ;
using ( IMemoryOwner < long > volume = memoryAllocator . Allocate < long > ( IndexCount * IndexAlphaCount ) )
using ( IMemoryOwner < long > volumeR = memoryAllocator . Allocate < long > ( IndexCount * IndexAlphaCount ) )
using ( IMemoryOwner < long > volumeG = memoryAllocator . Allocate < long > ( IndexCount * IndexAlphaCount ) )
using ( IMemoryOwner < long > volumeB = memoryAllocator . Allocate < long > ( IndexCount * IndexAlphaCount ) )
using ( IMemoryOwner < long > volumeA = memoryAllocator . Allocate < long > ( IndexCount * IndexAlphaCount ) )
using ( IMemoryOwner < float > volume2 = memoryAllocator . Allocate < float > ( IndexCount * IndexAlphaCount ) )
using ( IMemoryOwner < double > volume2 = memoryAllocator . Allocate < double > ( IndexCount * IndexAlphaCount ) )
using ( IMemoryOwner < long > area = memoryAllocator . Allocate < long > ( IndexAlphaCount ) )
using ( IMemoryOwner < long > areaR = memoryAllocator . Allocate < long > ( IndexAlphaCount ) )
using ( IMemoryOwner < long > areaG = memoryAllocator . Allocate < long > ( IndexAlphaCount ) )
using ( IMemoryOwner < long > areaB = memoryAllocator . Allocate < long > ( IndexAlphaCount ) )
using ( IMemoryOwner < long > areaA = memoryAllocator . Allocate < long > ( IndexAlphaCount ) )
using ( IMemoryOwner < float > area2 = memoryAllocator . Allocate < float > ( IndexAlphaCount ) )
using ( IMemoryOwner < double > area2 = memoryAllocator . Allocate < double > ( IndexAlphaCount ) )
{
Span < long > volumeSpan = volume . GetSpan ( ) ;
Span < long > volumeRSpan = volumeR . GetSpan ( ) ;
Span < long > volumeGSpan = volumeG . GetSpan ( ) ;
Span < long > volumeBSpan = volumeB . GetSpan ( ) ;
Span < long > volumeASpan = volumeA . GetSpan ( ) ;
Span < float > volume2Span = volume2 . GetSpan ( ) ;
Span < double > volume2Span = volume2 . GetSpan ( ) ;
Span < long > areaSpan = area . GetSpan ( ) ;
Span < long > areaRSpan = areaR . GetSpan ( ) ;
Span < long > areaGSpan = areaG . GetSpan ( ) ;
Span < long > areaBSpan = areaB . GetSpan ( ) ;
Span < long > areaASpan = areaA . GetSpan ( ) ;
Span < float > area2Span = area2 . GetSpan ( ) ;
Span < double > area2Span = area2 . GetSpan ( ) ;
for ( int r = 1 ; r < IndexCount ; r + + )
{
@ -543,7 +546,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
long lineG = 0 ;
long lineB = 0 ;
long lineA = 0 ;
float line2 = 0 ;
double line2 = 0 ;
for ( int a = 1 ; a < IndexAlphaCount ; a + + )
{
@ -592,35 +595,35 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// </summary>
/// <param name="cube">The cube.</param>
/// <returns>The <see cref="float"/>.</returns>
private float Variance ( ref Box cube )
private double Variance ( ref Box cube )
{
float dr = Volume ( ref cube , this . vmr . GetSpan ( ) ) ;
float dg = Volume ( ref cube , this . vmg . GetSpan ( ) ) ;
float db = Volume ( ref cube , this . vmb . GetSpan ( ) ) ;
float da = Volume ( ref cube , this . vma . GetSpan ( ) ) ;
Span < float > m2Span = this . m2 . GetSpan ( ) ;
float xx =
m2Span [ GetPaletteIndex ( cube . R1 , cube . G1 , cube . B1 , cube . A1 ) ]
- m2Span [ GetPaletteIndex ( cube . R1 , cube . G1 , cube . B1 , cube . A0 ) ]
- m2Span [ GetPaletteIndex ( cube . R1 , cube . G1 , cube . B0 , cube . A1 ) ]
+ m2Span [ GetPaletteIndex ( cube . R1 , cube . G1 , cube . B0 , cube . A0 ) ]
- m2Span [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B1 , cube . A1 ) ]
+ m2Span [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B1 , cube . A0 ) ]
+ m2Span [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B0 , cube . A1 ) ]
- m2Span [ GetPaletteIndex ( cube . R1 , cube . G0 , cube . B0 , cube . A0 ) ]
- m2Span [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B1 , cube . A1 ) ]
+ m2Span [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B1 , cube . A0 ) ]
+ m2Span [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B0 , cube . A1 ) ]
- m2Span [ GetPaletteIndex ( cube . R0 , cube . G1 , cube . B0 , cube . A0 ) ]
+ m2Span [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B1 , cube . A1 ) ]
- m2Span [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B1 , cube . A0 ) ]
- m2Span [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B0 , cube . A1 ) ]
+ m2Span [ GetPaletteIndex ( cube . R0 , cube . G0 , cube . B0 , cube . A0 ) ] ;
Span < double > m2Span = this . m2 . GetSpan ( ) ;
double moment =
m2Span [ GetPaletteIndex ( cube . RMax , cube . GMax , cube . BMax , cube . AMax ) ]
- m2Span [ GetPaletteIndex ( cube . RMax , cube . GMax , cube . BMax , cube . AMin ) ]
- m2Span [ GetPaletteIndex ( cube . RMax , cube . GMax , cube . BMin , cube . AMax ) ]
+ m2Span [ GetPaletteIndex ( cube . RMax , cube . GMax , cube . BMin , cube . AMin ) ]
- m2Span [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMax , cube . AMax ) ]
+ m2Span [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMax , cube . AMin ) ]
+ m2Span [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMin , cube . AMax ) ]
- m2Span [ GetPaletteIndex ( cube . RMax , cube . GMin , cube . BMin , cube . AMin ) ]
- m2Span [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMax , cube . AMax ) ]
+ m2Span [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMax , cube . AMin ) ]
+ m2Span [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMin , cube . AMax ) ]
- m2Span [ GetPaletteIndex ( cube . RMin , cube . GMax , cube . BMin , cube . AMin ) ]
+ m2Span [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMax , cube . AMax ) ]
- m2Span [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMax , cube . AMin ) ]
- m2Span [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMin , cube . AMax ) ]
+ m2Span [ GetPaletteIndex ( cube . RMin , cube . GMin , cube . BMin , cube . AMin ) ] ;
var vector = new Vector4 ( dr , dg , db , da ) ;
return xx - ( Vector4 . Dot ( vector , vector ) / Volume ( ref cube , this . vwt . GetSpan ( ) ) ) ;
return moment - ( Vector4 . Dot ( vector , vector ) / Volume ( ref cube , this . vwt . GetSpan ( ) ) ) ;
}
/// <summary>
@ -714,10 +717,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
float wholeA = Volume ( ref set1 , this . vma . GetSpan ( ) ) ;
float wholeW = Volume ( ref set1 , this . vwt . GetSpan ( ) ) ;
float maxr = this . Maximize ( ref set1 , 3 , set1 . R0 + 1 , set1 . R1 , out int cutr , wholeR , wholeG , wholeB , wholeA , wholeW ) ;
float maxg = this . Maximize ( ref set1 , 2 , set1 . G0 + 1 , set1 . G1 , out int cutg , wholeR , wholeG , wholeB , wholeA , wholeW ) ;
float maxb = this . Maximize ( ref set1 , 1 , set1 . B0 + 1 , set1 . B1 , out int cutb , wholeR , wholeG , wholeB , wholeA , wholeW ) ;
float maxa = this . Maximize ( ref set1 , 0 , set1 . A0 + 1 , set1 . A1 , out int cuta , wholeR , wholeG , wholeB , wholeA , wholeW ) ;
float maxr = this . Maximize ( ref set1 , 3 , set1 . RMin + 1 , set1 . RMax , out int cutr , wholeR , wholeG , wholeB , wholeA , wholeW ) ;
float maxg = this . Maximize ( ref set1 , 2 , set1 . GMin + 1 , set1 . GMax , out int cutg , wholeR , wholeG , wholeB , wholeA , wholeW ) ;
float maxb = this . Maximize ( ref set1 , 1 , set1 . BMin + 1 , set1 . BMax , out int cutb , wholeR , wholeG , wholeB , wholeA , wholeW ) ;
float maxa = this . Maximize ( ref set1 , 0 , set1 . AMin + 1 , set1 . AMax , out int cuta , wholeR , wholeG , wholeB , wholeA , wholeW ) ;
int dir ;
@ -743,48 +746,48 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
dir = 0 ;
}
set2 . R1 = set1 . R1 ;
set2 . G1 = set1 . G1 ;
set2 . B1 = set1 . B1 ;
set2 . A1 = set1 . A1 ;
set2 . RMax = set1 . RMax ;
set2 . GMax = set1 . GMax ;
set2 . BMax = set1 . BMax ;
set2 . AMax = set1 . AMax ;
switch ( dir )
{
// Red
case 3 :
set2 . R0 = set1 . R1 = cutr ;
set2 . G0 = set1 . G0 ;
set2 . B0 = set1 . B0 ;
set2 . A0 = set1 . A0 ;
set2 . RMin = set1 . RMax = cutr ;
set2 . GMin = set1 . GMin ;
set2 . BMin = set1 . BMin ;
set2 . AMin = set1 . AMin ;
break ;
// Green
case 2 :
set2 . G0 = set1 . G1 = cutg ;
set2 . R0 = set1 . R0 ;
set2 . B0 = set1 . B0 ;
set2 . A0 = set1 . A0 ;
set2 . GMin = set1 . GMax = cutg ;
set2 . RMin = set1 . RMin ;
set2 . BMin = set1 . BMin ;
set2 . AMin = set1 . AMin ;
break ;
// Blue
case 1 :
set2 . B0 = set1 . B1 = cutb ;
set2 . R0 = set1 . R0 ;
set2 . G0 = set1 . G0 ;
set2 . A0 = set1 . A0 ;
set2 . BMin = set1 . BMax = cutb ;
set2 . RMin = set1 . RMin ;
set2 . GMin = set1 . GMin ;
set2 . AMin = set1 . AMin ;
break ;
// Alpha
case 0 :
set2 . A0 = set1 . A1 = cuta ;
set2 . R0 = set1 . R0 ;
set2 . G0 = set1 . G0 ;
set2 . B0 = set1 . B0 ;
set2 . AMin = set1 . AMax = cuta ;
set2 . RMin = set1 . RMin ;
set2 . GMin = set1 . GMin ;
set2 . BMin = set1 . BMin ;
break ;
}
set1 . Volume = ( set1 . R1 - set1 . R0 ) * ( set1 . G1 - set1 . G0 ) * ( set1 . B1 - set1 . B0 ) * ( set1 . A1 - set1 . A0 ) ;
set2 . Volume = ( set2 . R1 - set2 . R0 ) * ( set2 . G1 - set2 . G0 ) * ( set2 . B1 - set2 . B0 ) * ( set2 . A1 - set2 . A0 ) ;
set1 . Volume = ( set1 . RMax - set1 . RMin ) * ( set1 . GMax - set1 . GMin ) * ( set1 . BMax - set1 . BMin ) * ( set1 . AMax - set1 . AMin ) ;
set2 . Volume = ( set2 . RMax - set2 . RMin ) * ( set2 . GMax - set2 . GMin ) * ( set2 . BMax - set2 . BMin ) * ( set2 . AMax - set2 . AMin ) ;
return true ;
}
@ -798,13 +801,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
{
Span < byte > tagSpan = this . tag . GetSpan ( ) ;
for ( int r = cube . R0 + 1 ; r < = cube . R1 ; r + + )
for ( int r = cube . RMin + 1 ; r < = cube . RMax ; r + + )
{
for ( int g = cube . G0 + 1 ; g < = cube . G1 ; g + + )
for ( int g = cube . GMin + 1 ; g < = cube . GMax ; g + + )
{
for ( int b = cube . B0 + 1 ; b < = cube . B1 ; b + + )
for ( int b = cube . BMin + 1 ; b < = cube . BMax ; b + + )
{
for ( int a = cube . A0 + 1 ; a < = cube . A1 ; a + + )
for ( int a = cube . AMin + 1 ; a < = cube . AMax ; a + + )
{
tagSpan [ GetPaletteIndex ( r , g , b , a ) ] = label ;
}
@ -819,12 +822,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
private void BuildCube ( )
{
this . colorCube = new Box [ this . colors ] ;
float [ ] vv = new float [ this . colors ] ;
double [ ] vv = new double [ this . colors ] ;
ref Box cube = ref this . colorCube [ 0 ] ;
cube . R0 = cube . G0 = cube . B0 = cube . A0 = 0 ;
cube . R1 = cube . G1 = cube . B1 = IndexCount - 1 ;
cube . A1 = IndexAlphaCount - 1 ;
cube . RMin = cube . GMin = cube . BMin = cube . AMin = 0 ;
cube . RMax = cube . GMax = cube . BMax = IndexCount - 1 ;
cube . AMax = IndexAlphaCount - 1 ;
int next = 0 ;
@ -839,13 +842,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
}
else
{
vv [ next ] = 0F ;
vv [ next ] = 0D ;
i - - ;
}
next = 0 ;
float temp = vv [ 0 ] ;
double temp = vv [ 0 ] ;
for ( int k = 1 ; k < = i ; k + + )
{
if ( vv [ k ] > temp )
@ -855,7 +858,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
}
}
if ( temp < = 0F )
if ( temp < = 0D )
{
this . colors = i + 1 ;
break ;
@ -897,52 +900,83 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// <summary>
/// Represents a box color cube.
/// </summary>
private struct Box
private struct Box : IEquatable < Box >
{
/// <summary>
/// Gets or sets the min red value, exclusive.
/// </summary>
public int R0 ;
public int RMin ;
/// <summary>
/// Gets or sets the max red value, inclusive.
/// </summary>
public int R1 ;
public int RMax ;
/// <summary>
/// Gets or sets the min green value, exclusive.
/// </summary>
public int G0 ;
public int GMin ;
/// <summary>
/// Gets or sets the max green value, inclusive.
/// </summary>
public int G1 ;
public int GMax ;
/// <summary>
/// Gets or sets the min blue value, exclusive.
/// </summary>
public int B0 ;
public int BMin ;
/// <summary>
/// Gets or sets the max blue value, inclusive.
/// </summary>
public int B1 ;
public int BMax ;
/// <summary>
/// Gets or sets the min alpha value, exclusive.
/// </summary>
public int A0 ;
public int AMin ;
/// <summary>
/// Gets or sets the max alpha value, inclusive.
/// </summary>
public int A1 ;
public int AMax ;
/// <summary>
/// Gets or sets the volume.
/// </summary>
public int Volume ;
/// <inheritdoc/>
public override bool Equals ( object obj ) = > obj is Box box & & this . Equals ( box ) ;
/// <inheritdoc/>
public bool Equals ( Box other ) = >
this . RMin = = other . RMin
& & this . RMax = = other . RMax
& & this . GMin = = other . GMin
& & this . GMax = = other . GMax
& & this . BMin = = other . BMin
& & this . BMax = = other . BMax
& & this . AMin = = other . AMin
& & this . AMax = = other . AMax
& & this . Volume = = other . Volume ;
/// <inheritdoc/>
public override int GetHashCode ( )
{
HashCode hash = default ;
hash . Add ( this . RMin ) ;
hash . Add ( this . RMax ) ;
hash . Add ( this . GMin ) ;
hash . Add ( this . GMax ) ;
hash . Add ( this . BMin ) ;
hash . Add ( this . BMax ) ;
hash . Add ( this . AMin ) ;
hash . Add ( this . AMax ) ;
hash . Add ( this . Volume ) ;
return hash . ToHashCode ( ) ;
}
}
}
}