From 00291ae57ed18d1215926a3466f0baef3d5dfa2c Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 21 Dec 2016 17:15:05 +1100 Subject: [PATCH] Drop 127MB and 50ms off the Wu quantizer. --- src/ImageSharp/Quantizers/Wu/WuQuantizer.cs | 375 +++++++++++--------- 1 file changed, 208 insertions(+), 167 deletions(-) diff --git a/src/ImageSharp/Quantizers/Wu/WuQuantizer.cs b/src/ImageSharp/Quantizers/Wu/WuQuantizer.cs index 6eef9633c4..47e41e84aa 100644 --- a/src/ImageSharp/Quantizers/Wu/WuQuantizer.cs +++ b/src/ImageSharp/Quantizers/Wu/WuQuantizer.cs @@ -65,6 +65,21 @@ namespace ImageSharp.Quantizers /// private const int TableLength = IndexCount * IndexCount * IndexCount * IndexAlphaCount; + /// + /// The long array pool. + /// + private static readonly ArrayPool LongPool = ArrayPool.Create(TableLength, 25); + + /// + /// The double array pool. + /// + private static readonly ArrayPool DoublePool = ArrayPool.Create(TableLength, 5); + + /// + /// The byte array pool. + /// + private static readonly ArrayPool BytePool = ArrayPool.Create(TableLength, 5); + /// /// Moment of P(c). /// @@ -100,18 +115,24 @@ namespace ImageSharp.Quantizers /// private readonly byte[] tag; + /// + /// A buffer for storing pixels + /// + private readonly byte[] rgbaBuffer = new byte[4]; + /// /// Initializes a new instance of the class. /// public WuQuantizer() { - this.vwt = new long[TableLength]; - this.vmr = new long[TableLength]; - this.vmg = new long[TableLength]; - this.vmb = new long[TableLength]; - this.vma = new long[TableLength]; - this.m2 = new double[TableLength]; - this.tag = new byte[TableLength]; + // TODO: We might have to use a custom pool here. The default doesn't appear to save memory + this.vwt = LongPool.Rent(TableLength); + this.vmr = LongPool.Rent(TableLength); + this.vmg = LongPool.Rent(TableLength); + this.vmb = LongPool.Rent(TableLength); + this.vma = LongPool.Rent(TableLength); + this.m2 = DoublePool.Rent(TableLength); + this.tag = BytePool.Rent(TableLength); } /// @@ -145,14 +166,9 @@ namespace ImageSharp.Quantizers /// The index. 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; } /// @@ -164,21 +180,21 @@ namespace ImageSharp.Quantizers private static double Volume(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)] - - 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)]; + - 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)]; } /// @@ -195,46 +211,46 @@ namespace ImageSharp.Quantizers // Red case 0: 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)]; + + 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)]; // Green case 1: 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)]; + + 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)]; // Blue case 2: 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)]; + + 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)]; // Alpha case 3: 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)]; + + 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)]; default: throw new ArgumentOutOfRangeException(nameof(direction)); @@ -256,46 +272,46 @@ namespace ImageSharp.Quantizers // Red case 0: 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)]; + - 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)]; // Green case 1: 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)]; + - 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)]; // Blue case 2: 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)]; + - 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)]; // Alpha case 3: 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)]; + - 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)]; default: throw new ArgumentOutOfRangeException(nameof(direction)); @@ -313,7 +329,6 @@ namespace ImageSharp.Quantizers Array.Clear(this.vmb, 0, TableLength); Array.Clear(this.vma, 0, TableLength); Array.Clear(this.m2, 0, TableLength); - Array.Clear(this.tag, 0, TableLength); } @@ -323,18 +338,17 @@ namespace ImageSharp.Quantizers /// The pixel accessor. private void Build3DHistogram(PixelAccessor pixels) { - byte[] rgba = new byte[4]; for (int y = 0; y < pixels.Height; y++) { for (int x = 0; x < pixels.Width; x++) { // Colors are expected in r->g->b->a format - pixels[x, y].ToBytes(rgba, 0, ComponentOrder.XYZW); + pixels[x, y].ToBytes(this.rgbaBuffer, 0, ComponentOrder.XYZW); - byte r = rgba[0]; - byte g = rgba[1]; - byte b = rgba[2]; - byte a = rgba[3]; + byte r = this.rgbaBuffer[0]; + byte g = this.rgbaBuffer[1]; + byte b = this.rgbaBuffer[2]; + byte a = this.rgbaBuffer[3]; int inr = r >> (8 - IndexBits); int ing = g >> (8 - IndexBits); @@ -354,91 +368,109 @@ namespace ImageSharp.Quantizers } /// - /// Converts the histogram into moments so that we can rapidly calculate - /// the sums of the above quantities over any desired box. + /// Converts the histogram into moments so that we can rapidly calculate the sums of the above quantities over any desired box. /// private void Get3DMoments() { - long[] volume = new long[IndexCount * IndexAlphaCount]; - long[] volumeR = new long[IndexCount * IndexAlphaCount]; - long[] volumeG = new long[IndexCount * IndexAlphaCount]; - long[] volumeB = new long[IndexCount * IndexAlphaCount]; - long[] volumeA = new long[IndexCount * IndexAlphaCount]; - double[] volume2 = new double[IndexCount * IndexAlphaCount]; - - long[] area = new long[IndexAlphaCount]; - long[] areaR = new long[IndexAlphaCount]; - long[] areaG = new long[IndexAlphaCount]; - long[] areaB = new long[IndexAlphaCount]; - long[] areaA = new long[IndexAlphaCount]; - double[] area2 = new double[IndexAlphaCount]; - - for (int r = 1; r < IndexCount; r++) + long[] volume = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); + long[] volumeR = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); + long[] volumeG = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); + long[] volumeB = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); + long[] volumeA = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); + double[] volume2 = ArrayPool.Shared.Rent(IndexCount * IndexAlphaCount); + + long[] area = ArrayPool.Shared.Rent(IndexAlphaCount); + long[] areaR = ArrayPool.Shared.Rent(IndexAlphaCount); + long[] areaG = ArrayPool.Shared.Rent(IndexAlphaCount); + long[] areaB = ArrayPool.Shared.Rent(IndexAlphaCount); + long[] areaA = ArrayPool.Shared.Rent(IndexAlphaCount); + double[] area2 = ArrayPool.Shared.Rent(IndexAlphaCount); + + try { - Array.Clear(volume, 0, IndexCount * IndexAlphaCount); - Array.Clear(volumeR, 0, IndexCount * IndexAlphaCount); - Array.Clear(volumeG, 0, IndexCount * IndexAlphaCount); - Array.Clear(volumeB, 0, IndexCount * IndexAlphaCount); - Array.Clear(volumeA, 0, IndexCount * IndexAlphaCount); - Array.Clear(volume2, 0, IndexCount * IndexAlphaCount); - - for (int g = 1; g < IndexCount; g++) + for (int r = 1; r < IndexCount; r++) { - Array.Clear(area, 0, IndexAlphaCount); - Array.Clear(areaR, 0, IndexAlphaCount); - Array.Clear(areaG, 0, IndexAlphaCount); - Array.Clear(areaB, 0, IndexAlphaCount); - Array.Clear(areaA, 0, IndexAlphaCount); - Array.Clear(area2, 0, IndexAlphaCount); - - for (int b = 1; b < IndexCount; b++) + Array.Clear(volume, 0, IndexCount * IndexAlphaCount); + Array.Clear(volumeR, 0, IndexCount * IndexAlphaCount); + Array.Clear(volumeG, 0, IndexCount * IndexAlphaCount); + Array.Clear(volumeB, 0, IndexCount * IndexAlphaCount); + Array.Clear(volumeA, 0, IndexCount * IndexAlphaCount); + Array.Clear(volume2, 0, IndexCount * IndexAlphaCount); + + for (int g = 1; g < IndexCount; g++) { - long line = 0; - long lineR = 0; - long lineG = 0; - long lineB = 0; - long lineA = 0; - double line2 = 0; - - for (int a = 1; a < IndexAlphaCount; a++) + Array.Clear(area, 0, IndexAlphaCount); + Array.Clear(areaR, 0, IndexAlphaCount); + Array.Clear(areaG, 0, IndexAlphaCount); + Array.Clear(areaB, 0, IndexAlphaCount); + Array.Clear(areaA, 0, IndexAlphaCount); + Array.Clear(area2, 0, IndexAlphaCount); + + for (int b = 1; b < IndexCount; b++) { - int ind1 = GetPaletteIndex(r, g, b, a); - - line += this.vwt[ind1]; - lineR += this.vmr[ind1]; - lineG += this.vmg[ind1]; - lineB += this.vmb[ind1]; - lineA += this.vma[ind1]; - line2 += this.m2[ind1]; - - area[a] += line; - areaR[a] += lineR; - areaG[a] += lineG; - areaB[a] += lineB; - areaA[a] += lineA; - area2[a] += line2; - - int inv = (b * IndexAlphaCount) + a; - - volume[inv] += area[a]; - volumeR[inv] += areaR[a]; - volumeG[inv] += areaG[a]; - volumeB[inv] += areaB[a]; - volumeA[inv] += areaA[a]; - volume2[inv] += area2[a]; - - int ind2 = ind1 - GetPaletteIndex(1, 0, 0, 0); - - this.vwt[ind1] = this.vwt[ind2] + volume[inv]; - this.vmr[ind1] = this.vmr[ind2] + volumeR[inv]; - this.vmg[ind1] = this.vmg[ind2] + volumeG[inv]; - this.vmb[ind1] = this.vmb[ind2] + volumeB[inv]; - this.vma[ind1] = this.vma[ind2] + volumeA[inv]; - this.m2[ind1] = this.m2[ind2] + volume2[inv]; + long line = 0; + long lineR = 0; + long lineG = 0; + long lineB = 0; + long lineA = 0; + double line2 = 0; + + for (int a = 1; a < IndexAlphaCount; a++) + { + int ind1 = GetPaletteIndex(r, g, b, a); + + line += this.vwt[ind1]; + lineR += this.vmr[ind1]; + lineG += this.vmg[ind1]; + lineB += this.vmb[ind1]; + lineA += this.vma[ind1]; + line2 += this.m2[ind1]; + + area[a] += line; + areaR[a] += lineR; + areaG[a] += lineG; + areaB[a] += lineB; + areaA[a] += lineA; + area2[a] += line2; + + int inv = (b * IndexAlphaCount) + a; + + volume[inv] += area[a]; + volumeR[inv] += areaR[a]; + volumeG[inv] += areaG[a]; + volumeB[inv] += areaB[a]; + volumeA[inv] += areaA[a]; + volume2[inv] += area2[a]; + + int ind2 = ind1 - GetPaletteIndex(1, 0, 0, 0); + + this.vwt[ind1] = this.vwt[ind2] + volume[inv]; + this.vmr[ind1] = this.vmr[ind2] + volumeR[inv]; + this.vmg[ind1] = this.vmg[ind2] + volumeG[inv]; + this.vmb[ind1] = this.vmb[ind2] + volumeB[inv]; + this.vma[ind1] = this.vma[ind2] + volumeA[inv]; + this.m2[ind1] = this.m2[ind2] + volume2[inv]; + } } } } } + finally + { + ArrayPool.Shared.Return(volume); + ArrayPool.Shared.Return(volumeR); + ArrayPool.Shared.Return(volumeG); + ArrayPool.Shared.Return(volumeB); + ArrayPool.Shared.Return(volumeA); + ArrayPool.Shared.Return(volume2); + + ArrayPool.Shared.Return(area); + ArrayPool.Shared.Return(areaR); + ArrayPool.Shared.Return(areaG); + ArrayPool.Shared.Return(areaB); + ArrayPool.Shared.Return(areaA); + ArrayPool.Shared.Return(area2); + } } /// @@ -770,6 +802,15 @@ namespace ImageSharp.Quantizers ArrayPool.Shared.Return(rgba); }); + // Cleanup + LongPool.Return(this.vwt); + LongPool.Return(this.vmr); + LongPool.Return(this.vmg); + LongPool.Return(this.vmb); + LongPool.Return(this.vma); + DoublePool.Return(this.m2); + BytePool.Return(this.tag); + return new QuantizedImage(width, height, pallette, pixels); } }