|
|
|
@ -168,20 +168,19 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
this.palette = new TPixel[this.colors]; |
|
|
|
for (int k = 0; k < this.colors; k++) |
|
|
|
{ |
|
|
|
this.Mark(this.colorCube[k], (byte)k); |
|
|
|
this.Mark(ref this.colorCube[k], (byte)k); |
|
|
|
|
|
|
|
float weight = Volume(this.colorCube[k], this.vwt); |
|
|
|
float weight = Volume(ref this.colorCube[k], this.vwt); |
|
|
|
|
|
|
|
if (MathF.Abs(weight) > Constants.Epsilon) |
|
|
|
{ |
|
|
|
float r = Volume(this.colorCube[k], this.vmr) / weight; |
|
|
|
float g = Volume(this.colorCube[k], this.vmg) / weight; |
|
|
|
float b = Volume(this.colorCube[k], this.vmb) / weight; |
|
|
|
float a = Volume(this.colorCube[k], this.vma) / weight; |
|
|
|
|
|
|
|
var color = default(TPixel); |
|
|
|
color.PackFromVector4(new Vector4(r, g, b, a) / 255F); |
|
|
|
this.palette[k] = color; |
|
|
|
float r = Volume(ref this.colorCube[k], this.vmr); |
|
|
|
float g = Volume(ref this.colorCube[k], this.vmg); |
|
|
|
float b = Volume(ref this.colorCube[k], this.vmb); |
|
|
|
float a = Volume(ref this.colorCube[k], this.vma); |
|
|
|
|
|
|
|
ref TPixel color = ref this.palette[k]; |
|
|
|
color.PackFromVector4(new Vector4(r, g, b, a) / weight / 255F); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -197,19 +196,21 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
var rgba = default(Rgba32); |
|
|
|
pixel.ToRgba32(ref rgba); |
|
|
|
|
|
|
|
int r = rgba.R >> 2; // 8 - IndexBits
|
|
|
|
int g = rgba.G >> 2; |
|
|
|
int b = rgba.B >> 2; |
|
|
|
int a = rgba.A >> 5; // 8 - IndexAlphaBits
|
|
|
|
int r = rgba.R >> (8 - IndexBits); |
|
|
|
int g = rgba.G >> (8 - IndexBits); |
|
|
|
int b = rgba.B >> (8 - IndexBits); |
|
|
|
int a = rgba.A >> (8 - IndexAlphaBits); |
|
|
|
|
|
|
|
int index = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1); |
|
|
|
|
|
|
|
int ind = GetPaletteIndex(r + 1, g + 1, b + 1, a + 1); |
|
|
|
this.vwt[index]++; |
|
|
|
this.vmr[index] += rgba.R; |
|
|
|
this.vmg[index] += rgba.G; |
|
|
|
this.vmb[index] += rgba.B; |
|
|
|
this.vma[index] += rgba.A; |
|
|
|
|
|
|
|
this.vwt[ind]++; |
|
|
|
this.vmr[ind] += r; |
|
|
|
this.vmg[ind] += g; |
|
|
|
this.vmb[ind] += b; |
|
|
|
this.vma[ind] += a; |
|
|
|
this.m2[ind] += (r * r) + (g * g) + (b * b) + (a * a); |
|
|
|
var vector = new Vector4(rgba.R, rgba.G, rgba.B, rgba.A); |
|
|
|
this.m2[index] += Vector4.Dot(vector, vector); |
|
|
|
} |
|
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
|
@ -301,7 +302,7 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
/// <param name="cube">The cube.</param>
|
|
|
|
/// <param name="moment">The moment.</param>
|
|
|
|
/// <returns>The result.</returns>
|
|
|
|
private static float Volume(Box cube, long[] moment) |
|
|
|
private static float Volume(ref Box cube, long[] moment) |
|
|
|
{ |
|
|
|
return moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] |
|
|
|
- moment[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A0)] |
|
|
|
@ -328,12 +329,12 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
/// <param name="direction">The direction.</param>
|
|
|
|
/// <param name="moment">The moment.</param>
|
|
|
|
/// <returns>The result.</returns>
|
|
|
|
private static long Bottom(Box cube, int direction, long[] moment) |
|
|
|
private static long Bottom(ref Box cube, int direction, long[] moment) |
|
|
|
{ |
|
|
|
switch (direction) |
|
|
|
{ |
|
|
|
// Red
|
|
|
|
case 0: |
|
|
|
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)] |
|
|
|
@ -344,7 +345,7 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
+ moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; |
|
|
|
|
|
|
|
// Green
|
|
|
|
case 1: |
|
|
|
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)] |
|
|
|
@ -355,7 +356,7 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
+ moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; |
|
|
|
|
|
|
|
// Blue
|
|
|
|
case 2: |
|
|
|
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)] |
|
|
|
@ -366,7 +367,7 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
+ moment[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; |
|
|
|
|
|
|
|
// Alpha
|
|
|
|
case 3: |
|
|
|
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)] |
|
|
|
@ -389,12 +390,12 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
/// <param name="position">The position.</param>
|
|
|
|
/// <param name="moment">The moment.</param>
|
|
|
|
/// <returns>The result.</returns>
|
|
|
|
private static long Top(Box cube, int direction, int position, long[] moment) |
|
|
|
private static long Top(ref Box cube, int direction, int position, long[] moment) |
|
|
|
{ |
|
|
|
switch (direction) |
|
|
|
{ |
|
|
|
// Red
|
|
|
|
case 0: |
|
|
|
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)] |
|
|
|
@ -405,7 +406,7 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
- moment[GetPaletteIndex(position, cube.G0, cube.B0, cube.A0)]; |
|
|
|
|
|
|
|
// Green
|
|
|
|
case 1: |
|
|
|
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)] |
|
|
|
@ -416,7 +417,7 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
- moment[GetPaletteIndex(cube.R0, position, cube.B0, cube.A0)]; |
|
|
|
|
|
|
|
// Blue
|
|
|
|
case 2: |
|
|
|
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)] |
|
|
|
@ -427,7 +428,7 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
- moment[GetPaletteIndex(cube.R0, cube.G0, position, cube.A0)]; |
|
|
|
|
|
|
|
// Alpha
|
|
|
|
case 3: |
|
|
|
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)] |
|
|
|
@ -553,12 +554,12 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
/// </summary>
|
|
|
|
/// <param name="cube">The cube.</param>
|
|
|
|
/// <returns>The <see cref="float"/>.</returns>
|
|
|
|
private float Variance(Box cube) |
|
|
|
private float Variance(ref Box cube) |
|
|
|
{ |
|
|
|
float dr = Volume(cube, this.vmr); |
|
|
|
float dg = Volume(cube, this.vmg); |
|
|
|
float db = Volume(cube, this.vmb); |
|
|
|
float da = Volume(cube, this.vma); |
|
|
|
float dr = Volume(ref cube, this.vmr); |
|
|
|
float dg = Volume(ref cube, this.vmg); |
|
|
|
float db = Volume(ref cube, this.vmb); |
|
|
|
float da = Volume(ref cube, this.vma); |
|
|
|
|
|
|
|
float xx = |
|
|
|
this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] |
|
|
|
@ -578,7 +579,8 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
- this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A1)] |
|
|
|
+ this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; |
|
|
|
|
|
|
|
return xx - (((dr * dr) + (dg * dg) + (db * db) + (da * da)) / Volume(cube, this.vwt)); |
|
|
|
var vector = new Vector4(dr, dg, db, da); |
|
|
|
return xx - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt)); |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
@ -599,38 +601,33 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
/// <param name="wholeA">The whole alpha.</param>
|
|
|
|
/// <param name="wholeW">The whole weight.</param>
|
|
|
|
/// <returns>The <see cref="float"/>.</returns>
|
|
|
|
private float Maximize(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, float wholeR, float wholeG, float wholeB, float wholeA, float wholeW) |
|
|
|
{ |
|
|
|
long baseR = Bottom(cube, direction, this.vmr); |
|
|
|
long baseG = Bottom(cube, direction, this.vmg); |
|
|
|
long baseB = Bottom(cube, direction, this.vmb); |
|
|
|
long baseA = Bottom(cube, direction, this.vma); |
|
|
|
long baseW = Bottom(cube, direction, this.vwt); |
|
|
|
long baseR = Bottom(ref cube, direction, this.vmr); |
|
|
|
long baseG = Bottom(ref cube, direction, this.vmg); |
|
|
|
long baseB = Bottom(ref cube, direction, this.vmb); |
|
|
|
long baseA = Bottom(ref cube, direction, this.vma); |
|
|
|
long baseW = Bottom(ref cube, direction, this.vwt); |
|
|
|
|
|
|
|
float max = 0F; |
|
|
|
cut = -1; |
|
|
|
|
|
|
|
for (int i = first; i < last; i++) |
|
|
|
{ |
|
|
|
float halfR = baseR + Top(cube, direction, i, this.vmr); |
|
|
|
float halfG = baseG + Top(cube, direction, i, this.vmg); |
|
|
|
float halfB = baseB + Top(cube, direction, i, this.vmb); |
|
|
|
float halfA = baseA + Top(cube, direction, i, this.vma); |
|
|
|
float halfW = baseW + Top(cube, direction, i, this.vwt); |
|
|
|
|
|
|
|
float temp; |
|
|
|
float halfR = baseR + Top(ref cube, direction, i, this.vmr); |
|
|
|
float halfG = baseG + Top(ref cube, direction, i, this.vmg); |
|
|
|
float halfB = baseB + Top(ref cube, direction, i, this.vmb); |
|
|
|
float halfA = baseA + Top(ref cube, direction, i, this.vma); |
|
|
|
float halfW = baseW + Top(ref cube, direction, i, this.vwt); |
|
|
|
|
|
|
|
if (MathF.Abs(halfW) < Constants.Epsilon) |
|
|
|
{ |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
temp = ((halfR * halfR) + (halfG * halfG) + (halfB * halfB) + (halfA * halfA)) / halfW; |
|
|
|
var vector = new Vector4(halfR, halfG, halfB, halfA); |
|
|
|
float temp = Vector4.Dot(vector, vector) / halfW; |
|
|
|
|
|
|
|
halfR = wholeR - halfR; |
|
|
|
halfG = wholeG - halfG; |
|
|
|
halfB = wholeB - halfB; |
|
|
|
halfA = wholeA - halfA; |
|
|
|
halfW = wholeW - halfW; |
|
|
|
|
|
|
|
if (MathF.Abs(halfW) < Constants.Epsilon) |
|
|
|
@ -638,7 +635,14 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
temp += ((halfR * halfR) + (halfG * halfG) + (halfB * halfB) + (halfA * halfA)) / halfW; |
|
|
|
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; |
|
|
|
|
|
|
|
if (temp > max) |
|
|
|
{ |
|
|
|
@ -656,24 +660,24 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
/// <param name="set1">The first set.</param>
|
|
|
|
/// <param name="set2">The second set.</param>
|
|
|
|
/// <returns>Returns a value indicating whether the box has been split.</returns>
|
|
|
|
private bool Cut(Box set1, Box set2) |
|
|
|
private bool Cut(ref Box set1, ref Box set2) |
|
|
|
{ |
|
|
|
float wholeR = Volume(set1, this.vmr); |
|
|
|
float wholeG = Volume(set1, this.vmg); |
|
|
|
float wholeB = Volume(set1, this.vmb); |
|
|
|
float wholeA = Volume(set1, this.vma); |
|
|
|
float wholeW = Volume(set1, this.vwt); |
|
|
|
float wholeR = Volume(ref set1, this.vmr); |
|
|
|
float wholeG = Volume(ref set1, this.vmg); |
|
|
|
float wholeB = Volume(ref set1, this.vmb); |
|
|
|
float wholeA = Volume(ref set1, this.vma); |
|
|
|
float wholeW = Volume(ref set1, this.vwt); |
|
|
|
|
|
|
|
float maxr = this.Maximize(set1, 0, set1.R0 + 1, set1.R1, out int cutr, wholeR, wholeG, wholeB, wholeA, wholeW); |
|
|
|
float maxg = this.Maximize(set1, 1, set1.G0 + 1, set1.G1, out int cutg, wholeR, wholeG, wholeB, wholeA, wholeW); |
|
|
|
float maxb = this.Maximize(set1, 2, set1.B0 + 1, set1.B1, out int cutb, wholeR, wholeG, wholeB, wholeA, wholeW); |
|
|
|
float maxa = this.Maximize(set1, 3, set1.A0 + 1, set1.A1, out int cuta, wholeR, wholeG, wholeB, wholeA, wholeW); |
|
|
|
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); |
|
|
|
|
|
|
|
int dir; |
|
|
|
|
|
|
|
if ((maxr >= maxg) && (maxr >= maxb) && (maxr >= maxa)) |
|
|
|
{ |
|
|
|
dir = 0; |
|
|
|
dir = 3; |
|
|
|
|
|
|
|
if (cutr < 0) |
|
|
|
{ |
|
|
|
@ -682,15 +686,15 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
} |
|
|
|
else if ((maxg >= maxr) && (maxg >= maxb) && (maxg >= maxa)) |
|
|
|
{ |
|
|
|
dir = 1; |
|
|
|
dir = 2; |
|
|
|
} |
|
|
|
else if ((maxb >= maxr) && (maxb >= maxg) && (maxb >= maxa)) |
|
|
|
{ |
|
|
|
dir = 2; |
|
|
|
dir = 1; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
dir = 3; |
|
|
|
dir = 0; |
|
|
|
} |
|
|
|
|
|
|
|
set2.R1 = set1.R1; |
|
|
|
@ -701,7 +705,7 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
switch (dir) |
|
|
|
{ |
|
|
|
// Red
|
|
|
|
case 0: |
|
|
|
case 3: |
|
|
|
set2.R0 = set1.R1 = cutr; |
|
|
|
set2.G0 = set1.G0; |
|
|
|
set2.B0 = set1.B0; |
|
|
|
@ -709,7 +713,7 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
break; |
|
|
|
|
|
|
|
// Green
|
|
|
|
case 1: |
|
|
|
case 2: |
|
|
|
set2.G0 = set1.G1 = cutg; |
|
|
|
set2.R0 = set1.R0; |
|
|
|
set2.B0 = set1.B0; |
|
|
|
@ -717,7 +721,7 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
break; |
|
|
|
|
|
|
|
// Blue
|
|
|
|
case 2: |
|
|
|
case 1: |
|
|
|
set2.B0 = set1.B1 = cutb; |
|
|
|
set2.R0 = set1.R0; |
|
|
|
set2.G0 = set1.G0; |
|
|
|
@ -725,7 +729,7 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
break; |
|
|
|
|
|
|
|
// Alpha
|
|
|
|
case 3: |
|
|
|
case 0: |
|
|
|
set2.A0 = set1.A1 = cuta; |
|
|
|
set2.R0 = set1.R0; |
|
|
|
set2.G0 = set1.G0; |
|
|
|
@ -744,7 +748,7 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
/// </summary>
|
|
|
|
/// <param name="cube">The cube.</param>
|
|
|
|
/// <param name="label">A label.</param>
|
|
|
|
private void Mark(Box cube, byte label) |
|
|
|
private void Mark(ref Box cube, byte label) |
|
|
|
{ |
|
|
|
for (int r = cube.R0 + 1; r <= cube.R1; r++) |
|
|
|
{ |
|
|
|
@ -769,23 +773,21 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
this.colorCube = new Box[this.colors]; |
|
|
|
float[] vv = new float[this.colors]; |
|
|
|
|
|
|
|
for (int i = 0; i < this.colors; i++) |
|
|
|
{ |
|
|
|
this.colorCube[i] = new Box(); |
|
|
|
} |
|
|
|
|
|
|
|
this.colorCube[0].R0 = this.colorCube[0].G0 = this.colorCube[0].B0 = this.colorCube[0].A0 = 0; |
|
|
|
this.colorCube[0].R1 = this.colorCube[0].G1 = this.colorCube[0].B1 = IndexCount - 1; |
|
|
|
this.colorCube[0].A1 = IndexAlphaCount - 1; |
|
|
|
ref var 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; |
|
|
|
|
|
|
|
int next = 0; |
|
|
|
|
|
|
|
for (int i = 1; i < this.colors; i++) |
|
|
|
{ |
|
|
|
if (this.Cut(this.colorCube[next], this.colorCube[i])) |
|
|
|
ref var nextCube = ref this.colorCube[next]; |
|
|
|
ref var currentCube = ref this.colorCube[i]; |
|
|
|
if (this.Cut(ref nextCube, ref currentCube)) |
|
|
|
{ |
|
|
|
vv[next] = this.colorCube[next].Volume > 1 ? this.Variance(this.colorCube[next]) : 0F; |
|
|
|
vv[i] = this.colorCube[i].Volume > 1 ? this.Variance(this.colorCube[i]) : 0F; |
|
|
|
vv[next] = nextCube.Volume > 1 ? this.Variance(ref nextCube) : 0F; |
|
|
|
vv[i] = currentCube.Volume > 1 ? this.Variance(ref currentCube) : 0F; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
@ -805,7 +807,7 @@ namespace SixLabors.ImageSharp.Quantizers |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (temp <= 0.0) |
|
|
|
if (temp <= 0F) |
|
|
|
{ |
|
|
|
this.colors = i + 1; |
|
|
|
break; |
|
|
|
|