|
|
|
@ -10,8 +10,6 @@ using SixLabors.ImageSharp.Advanced; |
|
|
|
using SixLabors.ImageSharp.Memory; |
|
|
|
using SixLabors.ImageSharp.PixelFormats; |
|
|
|
|
|
|
|
// 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>
|
|
|
|
@ -69,34 +67,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
private const int TableLength = IndexCount * IndexCount * IndexCount * IndexAlphaCount; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Moment of <c>P(c)</c>.
|
|
|
|
/// Color moments.
|
|
|
|
/// </summary>
|
|
|
|
private IMemoryOwner<long> vwt; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Moment of <c>r*P(c)</c>.
|
|
|
|
/// </summary>
|
|
|
|
private IMemoryOwner<long> vmr; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Moment of <c>g*P(c)</c>.
|
|
|
|
/// </summary>
|
|
|
|
private IMemoryOwner<long> vmg; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Moment of <c>b*P(c)</c>.
|
|
|
|
/// </summary>
|
|
|
|
private IMemoryOwner<long> vmb; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Moment of <c>a*P(c)</c>.
|
|
|
|
/// </summary>
|
|
|
|
private IMemoryOwner<long> vma; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Moment of <c>c^2*P(c)</c>.
|
|
|
|
/// </summary>
|
|
|
|
private IMemoryOwner<double> m2; |
|
|
|
private IMemoryOwner<Moment> moments; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Color space tag.
|
|
|
|
@ -148,15 +121,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
: base(configuration, quantizer, false) |
|
|
|
{ |
|
|
|
this.memoryAllocator = this.Configuration.MemoryAllocator; |
|
|
|
|
|
|
|
this.vwt = this.memoryAllocator.Allocate<long>(TableLength, AllocationOptions.Clean); |
|
|
|
this.vmr = this.memoryAllocator.Allocate<long>(TableLength, AllocationOptions.Clean); |
|
|
|
this.vmg = this.memoryAllocator.Allocate<long>(TableLength, AllocationOptions.Clean); |
|
|
|
this.vmb = this.memoryAllocator.Allocate<long>(TableLength, AllocationOptions.Clean); |
|
|
|
this.vma = this.memoryAllocator.Allocate<long>(TableLength, AllocationOptions.Clean); |
|
|
|
this.m2 = this.memoryAllocator.Allocate<double>(TableLength, AllocationOptions.Clean); |
|
|
|
this.moments = this.memoryAllocator.Allocate<Moment>(TableLength, AllocationOptions.Clean); |
|
|
|
this.tag = this.memoryAllocator.Allocate<byte>(TableLength, AllocationOptions.Clean); |
|
|
|
|
|
|
|
this.colors = maxColors; |
|
|
|
} |
|
|
|
|
|
|
|
@ -170,21 +136,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
|
|
|
|
if (disposing) |
|
|
|
{ |
|
|
|
this.vwt?.Dispose(); |
|
|
|
this.vmr?.Dispose(); |
|
|
|
this.vmg?.Dispose(); |
|
|
|
this.vmb?.Dispose(); |
|
|
|
this.vma?.Dispose(); |
|
|
|
this.m2?.Dispose(); |
|
|
|
this.moments?.Dispose(); |
|
|
|
this.tag?.Dispose(); |
|
|
|
} |
|
|
|
|
|
|
|
this.vwt = null; |
|
|
|
this.vmr = null; |
|
|
|
this.vmg = null; |
|
|
|
this.vmb = null; |
|
|
|
this.vma = null; |
|
|
|
this.m2 = null; |
|
|
|
this.moments = null; |
|
|
|
this.tag = null; |
|
|
|
|
|
|
|
this.isDisposed = true; |
|
|
|
@ -199,27 +155,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
if (this.palette is null) |
|
|
|
{ |
|
|
|
this.palette = new TPixel[this.colors]; |
|
|
|
Span<long> vwtSpan = this.vwt.GetSpan(); |
|
|
|
Span<long> vmrSpan = this.vmr.GetSpan(); |
|
|
|
Span<long> vmgSpan = this.vmg.GetSpan(); |
|
|
|
Span<long> vmbSpan = this.vmb.GetSpan(); |
|
|
|
Span<long> vmaSpan = this.vma.GetSpan(); |
|
|
|
ReadOnlySpan<Moment> momentsSpan = this.moments.GetSpan(); |
|
|
|
|
|
|
|
for (int k = 0; k < this.colors; k++) |
|
|
|
{ |
|
|
|
this.Mark(ref this.colorCube[k], (byte)k); |
|
|
|
|
|
|
|
float weight = Volume(ref this.colorCube[k], vwtSpan); |
|
|
|
Moment moment = Volume(ref this.colorCube[k], momentsSpan); |
|
|
|
|
|
|
|
if (MathF.Abs(weight) > Constants.Epsilon) |
|
|
|
if (moment.Weight > 0) |
|
|
|
{ |
|
|
|
float r = Volume(ref this.colorCube[k], vmrSpan); |
|
|
|
float g = Volume(ref this.colorCube[k], vmgSpan); |
|
|
|
float b = Volume(ref this.colorCube[k], vmbSpan); |
|
|
|
float a = Volume(ref this.colorCube[k], vmaSpan); |
|
|
|
|
|
|
|
ref TPixel color = ref this.palette[k]; |
|
|
|
color.FromScaledVector4(new Vector4(r, g, b, a) / weight / 255F); |
|
|
|
color.FromScaledVector4(moment.Normalize()); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -307,26 +254,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
/// Computes sum over a box of any given statistic.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="cube">The cube.</param>
|
|
|
|
/// <param name="moment">The moment.</param>
|
|
|
|
/// <param name="moments">The moment.</param>
|
|
|
|
/// <returns>The result.</returns>
|
|
|
|
private static float Volume(ref Box cube, Span<long> moment) |
|
|
|
private static Moment Volume(ref Box cube, ReadOnlySpan<Moment> moments) |
|
|
|
{ |
|
|
|
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)]; |
|
|
|
return moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)]; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -334,55 +281,55 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
/// </summary>
|
|
|
|
/// <param name="cube">The cube.</param>
|
|
|
|
/// <param name="direction">The direction.</param>
|
|
|
|
/// <param name="moment">The moment.</param>
|
|
|
|
/// <param name="moments">The moment.</param>
|
|
|
|
/// <returns>The result.</returns>
|
|
|
|
private static long Bottom(ref Box cube, int direction, Span<long> moment) |
|
|
|
private static Moment Bottom(ref Box cube, int direction, ReadOnlySpan<Moment> moments) |
|
|
|
{ |
|
|
|
switch (direction) |
|
|
|
{ |
|
|
|
// Red
|
|
|
|
case 3: |
|
|
|
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)]; |
|
|
|
return -moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)]; |
|
|
|
|
|
|
|
// Green
|
|
|
|
case 2: |
|
|
|
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)]; |
|
|
|
return -moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)]; |
|
|
|
|
|
|
|
// Blue
|
|
|
|
case 1: |
|
|
|
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)]; |
|
|
|
return -moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)]; |
|
|
|
|
|
|
|
// Alpha
|
|
|
|
case 0: |
|
|
|
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)]; |
|
|
|
return -moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)]; |
|
|
|
|
|
|
|
default: |
|
|
|
throw new ArgumentOutOfRangeException(nameof(direction)); |
|
|
|
@ -395,55 +342,55 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
/// <param name="cube">The cube.</param>
|
|
|
|
/// <param name="direction">The direction.</param>
|
|
|
|
/// <param name="position">The position.</param>
|
|
|
|
/// <param name="moment">The moment.</param>
|
|
|
|
/// <param name="moments">The moment.</param>
|
|
|
|
/// <returns>The result.</returns>
|
|
|
|
private static long Top(ref Box cube, int direction, int position, Span<long> moment) |
|
|
|
private static Moment Top(ref Box cube, int direction, int position, ReadOnlySpan<Moment> moments) |
|
|
|
{ |
|
|
|
switch (direction) |
|
|
|
{ |
|
|
|
// Red
|
|
|
|
case 3: |
|
|
|
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)]; |
|
|
|
return moments[GetPaletteIndex(position, cube.GMax, cube.BMax, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(position, cube.GMax, cube.BMax, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(position, cube.GMax, cube.BMin, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(position, cube.GMax, cube.BMin, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(position, cube.GMin, cube.BMax, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(position, cube.GMin, cube.BMax, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(position, cube.GMin, cube.BMin, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(position, cube.GMin, cube.BMin, cube.AMin)]; |
|
|
|
|
|
|
|
// Green
|
|
|
|
case 2: |
|
|
|
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)]; |
|
|
|
return moments[GetPaletteIndex(cube.RMax, position, cube.BMax, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(cube.RMax, position, cube.BMax, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(cube.RMax, position, cube.BMin, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMax, position, cube.BMin, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, position, cube.BMax, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, position, cube.BMax, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, position, cube.BMin, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, position, cube.BMin, cube.AMin)]; |
|
|
|
|
|
|
|
// Blue
|
|
|
|
case 1: |
|
|
|
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)]; |
|
|
|
return moments[GetPaletteIndex(cube.RMax, cube.GMax, position, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(cube.RMax, cube.GMax, position, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(cube.RMax, cube.GMin, position, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMax, cube.GMin, position, cube.AMin)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMax, position, cube.AMax)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMax, position, cube.AMin)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMin, position, cube.AMax)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMin, position, cube.AMin)]; |
|
|
|
|
|
|
|
// Alpha
|
|
|
|
case 0: |
|
|
|
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)]; |
|
|
|
return moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, position)] |
|
|
|
- moments[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, position)] |
|
|
|
- moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, position)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, position)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, position)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, position)] |
|
|
|
+ moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, position)] |
|
|
|
- moments[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, position)]; |
|
|
|
|
|
|
|
default: |
|
|
|
throw new ArgumentOutOfRangeException(nameof(direction)); |
|
|
|
@ -458,12 +405,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
/// <param name="height">The height in pixels of the image.</param>
|
|
|
|
private void Build3DHistogram(ImageFrame<TPixel> source, int width, int height) |
|
|
|
{ |
|
|
|
Span<long> vwtSpan = this.vwt.GetSpan(); |
|
|
|
Span<long> vmrSpan = this.vmr.GetSpan(); |
|
|
|
Span<long> vmgSpan = this.vmg.GetSpan(); |
|
|
|
Span<long> vmbSpan = this.vmb.GetSpan(); |
|
|
|
Span<long> vmaSpan = this.vma.GetSpan(); |
|
|
|
Span<double> m2Span = this.m2.GetSpan(); |
|
|
|
Span<Moment> momentSpan = this.moments.GetSpan(); |
|
|
|
|
|
|
|
// Build up the 3-D color histogram
|
|
|
|
// Loop through each row
|
|
|
|
@ -487,15 +429,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
int a = rgba.A >> (8 - IndexAlphaBits); |
|
|
|
|
|
|
|
int index = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1); |
|
|
|
|
|
|
|
vwtSpan[index]++; |
|
|
|
vmrSpan[index] += rgba.R; |
|
|
|
vmgSpan[index] += rgba.G; |
|
|
|
vmbSpan[index] += rgba.B; |
|
|
|
vmaSpan[index] += rgba.A; |
|
|
|
|
|
|
|
var vector = new Vector4(rgba.R, rgba.G, rgba.B, rgba.A); |
|
|
|
m2Span[index] += Vector4.Dot(vector, vector); |
|
|
|
momentSpan[index] += rgba; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -507,102 +441,37 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
/// <param name="memoryAllocator">The memory allocator used for allocating buffers.</param>
|
|
|
|
private void Get3DMoments(MemoryAllocator memoryAllocator) |
|
|
|
{ |
|
|
|
Span<long> vwtSpan = this.vwt.GetSpan(); |
|
|
|
Span<long> vmrSpan = this.vmr.GetSpan(); |
|
|
|
Span<long> vmgSpan = this.vmg.GetSpan(); |
|
|
|
Span<long> vmbSpan = this.vmb.GetSpan(); |
|
|
|
Span<long> vmaSpan = this.vma.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<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<double> area2 = memoryAllocator.Allocate<double>(IndexAlphaCount)) |
|
|
|
Span<Moment> momentSpan = this.moments.GetSpan(); |
|
|
|
using (IMemoryOwner<Moment> volume = memoryAllocator.Allocate<Moment>(IndexCount * IndexAlphaCount)) |
|
|
|
using (IMemoryOwner<Moment> area = memoryAllocator.Allocate<Moment>(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<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<double> area2Span = area2.GetSpan(); |
|
|
|
Span<Moment> volumeSpan = volume.GetSpan(); |
|
|
|
Span<Moment> areaSpan = area.GetSpan(); |
|
|
|
|
|
|
|
for (int r = 1; r < IndexCount; r++) |
|
|
|
{ |
|
|
|
volume.Clear(); |
|
|
|
volumeR.Clear(); |
|
|
|
volumeG.Clear(); |
|
|
|
volumeB.Clear(); |
|
|
|
volumeA.Clear(); |
|
|
|
volume2.Clear(); |
|
|
|
|
|
|
|
for (int g = 1; g < IndexCount; g++) |
|
|
|
{ |
|
|
|
area.Clear(); |
|
|
|
areaR.Clear(); |
|
|
|
areaG.Clear(); |
|
|
|
areaB.Clear(); |
|
|
|
areaA.Clear(); |
|
|
|
area2.Clear(); |
|
|
|
|
|
|
|
for (int b = 1; b < IndexCount; b++) |
|
|
|
{ |
|
|
|
long line = 0; |
|
|
|
long lineR = 0; |
|
|
|
long lineG = 0; |
|
|
|
long lineB = 0; |
|
|
|
long lineA = 0; |
|
|
|
double line2 = 0; |
|
|
|
Moment line = default; |
|
|
|
|
|
|
|
for (int a = 1; a < IndexAlphaCount; a++) |
|
|
|
{ |
|
|
|
int ind1 = GetPaletteIndex(r, g, b, a); |
|
|
|
|
|
|
|
line += vwtSpan[ind1]; |
|
|
|
lineR += vmrSpan[ind1]; |
|
|
|
lineG += vmgSpan[ind1]; |
|
|
|
lineB += vmbSpan[ind1]; |
|
|
|
lineA += vmaSpan[ind1]; |
|
|
|
line2 += m2Span[ind1]; |
|
|
|
line += momentSpan[ind1]; |
|
|
|
|
|
|
|
areaSpan[a] += line; |
|
|
|
areaRSpan[a] += lineR; |
|
|
|
areaGSpan[a] += lineG; |
|
|
|
areaBSpan[a] += lineB; |
|
|
|
areaASpan[a] += lineA; |
|
|
|
area2Span[a] += line2; |
|
|
|
|
|
|
|
int inv = (b * IndexAlphaCount) + a; |
|
|
|
|
|
|
|
volumeSpan[inv] += areaSpan[a]; |
|
|
|
volumeRSpan[inv] += areaRSpan[a]; |
|
|
|
volumeGSpan[inv] += areaGSpan[a]; |
|
|
|
volumeBSpan[inv] += areaBSpan[a]; |
|
|
|
volumeASpan[inv] += areaASpan[a]; |
|
|
|
volume2Span[inv] += area2Span[a]; |
|
|
|
|
|
|
|
int ind2 = ind1 - GetPaletteIndex(1, 0, 0, 0); |
|
|
|
|
|
|
|
vwtSpan[ind1] = vwtSpan[ind2] + volumeSpan[inv]; |
|
|
|
vmrSpan[ind1] = vmrSpan[ind2] + volumeRSpan[inv]; |
|
|
|
vmgSpan[ind1] = vmgSpan[ind2] + volumeGSpan[inv]; |
|
|
|
vmbSpan[ind1] = vmbSpan[ind2] + volumeBSpan[inv]; |
|
|
|
vmaSpan[ind1] = vmaSpan[ind2] + volumeASpan[inv]; |
|
|
|
m2Span[ind1] = m2Span[ind2] + volume2Span[inv]; |
|
|
|
momentSpan[ind1] = momentSpan[ind2] + volumeSpan[inv]; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -617,33 +486,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
/// <returns>The <see cref="float"/>.</returns>
|
|
|
|
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<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 moment - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt.GetSpan())); |
|
|
|
ReadOnlySpan<Moment> momentSpan = this.moments.GetSpan(); |
|
|
|
|
|
|
|
Moment volume = Volume(ref cube, momentSpan); |
|
|
|
Moment variance = |
|
|
|
momentSpan[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMax)] |
|
|
|
- momentSpan[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMax, cube.AMin)] |
|
|
|
- momentSpan[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMax)] |
|
|
|
+ momentSpan[GetPaletteIndex(cube.RMax, cube.GMax, cube.BMin, cube.AMin)] |
|
|
|
- momentSpan[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMax)] |
|
|
|
+ momentSpan[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMax, cube.AMin)] |
|
|
|
+ momentSpan[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMax)] |
|
|
|
- momentSpan[GetPaletteIndex(cube.RMax, cube.GMin, cube.BMin, cube.AMin)] |
|
|
|
- momentSpan[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMax)] |
|
|
|
+ momentSpan[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMax, cube.AMin)] |
|
|
|
+ momentSpan[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMax)] |
|
|
|
- momentSpan[GetPaletteIndex(cube.RMin, cube.GMax, cube.BMin, cube.AMin)] |
|
|
|
+ momentSpan[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMax)] |
|
|
|
- momentSpan[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMax, cube.AMin)] |
|
|
|
- momentSpan[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMax)] |
|
|
|
+ momentSpan[GetPaletteIndex(cube.RMin, cube.GMin, cube.BMin, cube.AMin)]; |
|
|
|
|
|
|
|
var vector = new Vector4(volume.R, volume.G, volume.B, volume.A); |
|
|
|
return variance.Moment2 - (Vector4.Dot(vector, vector) / volume.Weight); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -658,60 +523,37 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
/// <param name="first">The first position.</param>
|
|
|
|
/// <param name="last">The last position.</param>
|
|
|
|
/// <param name="cut">The cutting point.</param>
|
|
|
|
/// <param name="wholeR">The whole red.</param>
|
|
|
|
/// <param name="wholeG">The whole green.</param>
|
|
|
|
/// <param name="wholeB">The whole blue.</param>
|
|
|
|
/// <param name="wholeA">The whole alpha.</param>
|
|
|
|
/// <param name="wholeW">The whole weight.</param>
|
|
|
|
/// <param name="whole">The whole moment.</param>
|
|
|
|
/// <returns>The <see cref="float"/>.</returns>
|
|
|
|
private float Maximize(ref Box cube, int direction, int first, int last, out int cut, float wholeR, float wholeG, float wholeB, float wholeA, float wholeW) |
|
|
|
private float Maximize(ref Box cube, int direction, int first, int last, out int cut, Moment whole) |
|
|
|
{ |
|
|
|
Span<long> vwtSpan = this.vwt.GetSpan(); |
|
|
|
Span<long> vmrSpan = this.vmr.GetSpan(); |
|
|
|
Span<long> vmgSpan = this.vmg.GetSpan(); |
|
|
|
Span<long> vmbSpan = this.vmb.GetSpan(); |
|
|
|
Span<long> vmaSpan = this.vma.GetSpan(); |
|
|
|
|
|
|
|
long baseR = Bottom(ref cube, direction, vmrSpan); |
|
|
|
long baseG = Bottom(ref cube, direction, vmgSpan); |
|
|
|
long baseB = Bottom(ref cube, direction, vmbSpan); |
|
|
|
long baseA = Bottom(ref cube, direction, vmaSpan); |
|
|
|
long baseW = Bottom(ref cube, direction, vwtSpan); |
|
|
|
ReadOnlySpan<Moment> momentSpan = this.moments.GetSpan(); |
|
|
|
Moment bottom = Bottom(ref cube, direction, momentSpan); |
|
|
|
|
|
|
|
float max = 0F; |
|
|
|
cut = -1; |
|
|
|
|
|
|
|
for (int i = first; i < last; i++) |
|
|
|
{ |
|
|
|
float halfR = baseR + Top(ref cube, direction, i, vmrSpan); |
|
|
|
float halfG = baseG + Top(ref cube, direction, i, vmgSpan); |
|
|
|
float halfB = baseB + Top(ref cube, direction, i, vmbSpan); |
|
|
|
float halfA = baseA + Top(ref cube, direction, i, vmaSpan); |
|
|
|
float halfW = baseW + Top(ref cube, direction, i, vwtSpan); |
|
|
|
Moment half = bottom + Top(ref cube, direction, i, momentSpan); |
|
|
|
|
|
|
|
if (MathF.Abs(halfW) < Constants.Epsilon) |
|
|
|
if (half.Weight == 0) |
|
|
|
{ |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
var vector = new Vector4(halfR, halfG, halfB, halfA); |
|
|
|
float temp = Vector4.Dot(vector, vector) / halfW; |
|
|
|
var vector = new Vector4(half.R, half.G, half.B, half.A); |
|
|
|
float temp = Vector4.Dot(vector, vector) / half.Weight; |
|
|
|
|
|
|
|
halfW = wholeW - halfW; |
|
|
|
half = whole - half; |
|
|
|
|
|
|
|
if (MathF.Abs(halfW) < Constants.Epsilon) |
|
|
|
if (half.Weight == 0) |
|
|
|
{ |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
halfR = wholeR - halfR; |
|
|
|
halfG = wholeG - halfG; |
|
|
|
halfB = wholeB - halfB; |
|
|
|
halfA = wholeA - halfA; |
|
|
|
|
|
|
|
vector = new Vector4(halfR, halfG, halfB, halfA); |
|
|
|
|
|
|
|
temp += Vector4.Dot(vector, vector) / halfW; |
|
|
|
vector = new Vector4(half.R, half.G, half.B, half.A); |
|
|
|
temp += Vector4.Dot(vector, vector) / half.Weight; |
|
|
|
|
|
|
|
if (temp > max) |
|
|
|
{ |
|
|
|
@ -731,33 +573,29 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
/// <returns>Returns a value indicating whether the box has been split.</returns>
|
|
|
|
private bool Cut(ref Box set1, ref Box set2) |
|
|
|
{ |
|
|
|
float wholeR = Volume(ref set1, this.vmr.GetSpan()); |
|
|
|
float wholeG = Volume(ref set1, this.vmg.GetSpan()); |
|
|
|
float wholeB = Volume(ref set1, this.vmb.GetSpan()); |
|
|
|
float wholeA = Volume(ref set1, this.vma.GetSpan()); |
|
|
|
float wholeW = Volume(ref set1, this.vwt.GetSpan()); |
|
|
|
Moment whole = Volume(ref set1, this.moments.GetSpan()); |
|
|
|
|
|
|
|
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); |
|
|
|
float maxR = this.Maximize(ref set1, 3, set1.RMin + 1, set1.RMax, out int cutR, whole); |
|
|
|
float maxG = this.Maximize(ref set1, 2, set1.GMin + 1, set1.GMax, out int cutG, whole); |
|
|
|
float maxB = this.Maximize(ref set1, 1, set1.BMin + 1, set1.BMax, out int cutB, whole); |
|
|
|
float maxA = this.Maximize(ref set1, 0, set1.AMin + 1, set1.AMax, out int cutA, whole); |
|
|
|
|
|
|
|
int dir; |
|
|
|
|
|
|
|
if ((maxr >= maxg) && (maxr >= maxb) && (maxr >= maxa)) |
|
|
|
if ((maxR >= maxG) && (maxR >= maxB) && (maxR >= maxA)) |
|
|
|
{ |
|
|
|
dir = 3; |
|
|
|
|
|
|
|
if (cutr < 0) |
|
|
|
if (cutR < 0) |
|
|
|
{ |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
else if ((maxg >= maxr) && (maxg >= maxb) && (maxg >= maxa)) |
|
|
|
else if ((maxG >= maxR) && (maxG >= maxB) && (maxG >= maxA)) |
|
|
|
{ |
|
|
|
dir = 2; |
|
|
|
} |
|
|
|
else if ((maxb >= maxr) && (maxb >= maxg) && (maxb >= maxa)) |
|
|
|
else if ((maxB >= maxR) && (maxB >= maxG) && (maxB >= maxA)) |
|
|
|
{ |
|
|
|
dir = 1; |
|
|
|
} |
|
|
|
@ -775,7 +613,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
{ |
|
|
|
// Red
|
|
|
|
case 3: |
|
|
|
set2.RMin = set1.RMax = cutr; |
|
|
|
set2.RMin = set1.RMax = cutR; |
|
|
|
set2.GMin = set1.GMin; |
|
|
|
set2.BMin = set1.BMin; |
|
|
|
set2.AMin = set1.AMin; |
|
|
|
@ -783,7 +621,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
|
|
|
|
// Green
|
|
|
|
case 2: |
|
|
|
set2.GMin = set1.GMax = cutg; |
|
|
|
set2.GMin = set1.GMax = cutG; |
|
|
|
set2.RMin = set1.RMin; |
|
|
|
set2.BMin = set1.BMin; |
|
|
|
set2.AMin = set1.AMin; |
|
|
|
@ -791,7 +629,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
|
|
|
|
// Blue
|
|
|
|
case 1: |
|
|
|
set2.BMin = set1.BMax = cutb; |
|
|
|
set2.BMin = set1.BMax = cutB; |
|
|
|
set2.RMin = set1.RMin; |
|
|
|
set2.GMin = set1.GMin; |
|
|
|
set2.AMin = set1.AMin; |
|
|
|
@ -799,7 +637,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
|
|
|
|
// Alpha
|
|
|
|
case 0: |
|
|
|
set2.AMin = set1.AMax = cuta; |
|
|
|
set2.AMin = set1.AMax = cutA; |
|
|
|
set2.RMin = set1.RMin; |
|
|
|
set2.GMin = set1.GMin; |
|
|
|
set2.BMin = set1.BMin; |
|
|
|
@ -857,8 +695,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
ref Box currentCube = ref this.colorCube[i]; |
|
|
|
if (this.Cut(ref nextCube, ref currentCube)) |
|
|
|
{ |
|
|
|
vv[next] = nextCube.Volume > 1 ? this.Variance(ref nextCube) : 0F; |
|
|
|
vv[i] = currentCube.Volume > 1 ? this.Variance(ref currentCube) : 0F; |
|
|
|
vv[next] = nextCube.Volume > 1 ? this.Variance(ref nextCube) : 0D; |
|
|
|
vv[i] = currentCube.Volume > 1 ? this.Variance(ref currentCube) : 0D; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
@ -917,6 +755,94 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization |
|
|
|
return tagSpan[GetPaletteIndex(r + 1, g + 1, b + 1, a + 1)]; |
|
|
|
} |
|
|
|
|
|
|
|
private struct Moment |
|
|
|
{ |
|
|
|
/// <summary>
|
|
|
|
/// Moment of <c>r*P(c)</c>.
|
|
|
|
/// </summary>
|
|
|
|
public long R; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Moment of <c>g*P(c)</c>.
|
|
|
|
/// </summary>
|
|
|
|
public long G; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Moment of <c>b*P(c)</c>.
|
|
|
|
/// </summary>
|
|
|
|
public long B; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Moment of <c>a*P(c)</c>.
|
|
|
|
/// </summary>
|
|
|
|
public long A; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Moment of <c>P(c)</c>.
|
|
|
|
/// </summary>
|
|
|
|
public long Weight; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Moment of <c>c^2*P(c)</c>.
|
|
|
|
/// </summary>
|
|
|
|
public double Moment2; |
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public static Moment operator +(Moment x, Moment y) |
|
|
|
{ |
|
|
|
x.R += y.R; |
|
|
|
x.G += y.G; |
|
|
|
x.B += y.B; |
|
|
|
x.A += y.A; |
|
|
|
x.Weight += y.Weight; |
|
|
|
x.Moment2 += y.Moment2; |
|
|
|
return x; |
|
|
|
} |
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public static Moment operator -(Moment x, Moment y) |
|
|
|
{ |
|
|
|
x.R -= y.R; |
|
|
|
x.G -= y.G; |
|
|
|
x.B -= y.B; |
|
|
|
x.A -= y.A; |
|
|
|
x.Weight -= y.Weight; |
|
|
|
x.Moment2 -= y.Moment2; |
|
|
|
return x; |
|
|
|
} |
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public static Moment operator -(Moment x) |
|
|
|
{ |
|
|
|
x.R = -x.R; |
|
|
|
x.G = -x.G; |
|
|
|
x.B = -x.B; |
|
|
|
x.A = -x.A; |
|
|
|
x.Weight = -x.Weight; |
|
|
|
x.Moment2 = -x.Moment2; |
|
|
|
return x; |
|
|
|
} |
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public static Moment operator +(Moment x, Rgba32 y) |
|
|
|
{ |
|
|
|
x.R += y.R; |
|
|
|
x.G += y.G; |
|
|
|
x.B += y.B; |
|
|
|
x.A += y.A; |
|
|
|
x.Weight++; |
|
|
|
|
|
|
|
var vector = new Vector4(y.R, y.G, y.B, y.A); |
|
|
|
x.Moment2 += Vector4.Dot(vector, vector); |
|
|
|
|
|
|
|
return x; |
|
|
|
} |
|
|
|
|
|
|
|
[MethodImpl(InliningOptions.ShortMethod)] |
|
|
|
public readonly Vector4 Normalize() |
|
|
|
=> new Vector4(this.R, this.G, this.B, this.A) / this.Weight / 255F; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Represents a box color cube.
|
|
|
|
/// </summary>
|
|
|
|
|