From 4e4cdd57ed914e6a934a0288b9e4d945ccbf93ea Mon Sep 17 00:00:00 2001 From: Lauri Kotilainen Date: Sat, 13 Jan 2018 14:26:20 +0200 Subject: [PATCH] - Removed ArrayPool from WuQuantizer --- src/ImageSharp/Formats/Png/PngEncoderCore.cs | 2 +- src/ImageSharp/Quantizers/Quantize.cs | 2 +- src/ImageSharp/Quantizers/WuArrayPool.cs | 34 ---- .../Quantizers/WuQuantizer{TPixel}.cs | 177 +++++++++--------- .../Quantization/QuantizedImageTests.cs | 2 +- 5 files changed, 88 insertions(+), 129 deletions(-) delete mode 100644 src/ImageSharp/Quantizers/WuArrayPool.cs diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index d6adaae42..325027040 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -509,7 +509,7 @@ namespace SixLabors.ImageSharp.Formats.Png if (this.quantizer == null) { - this.quantizer = new WuQuantizer(); + this.quantizer = new WuQuantizer(this.memoryManager); } // Quantize the image returning a palette. This boxing is icky. diff --git a/src/ImageSharp/Quantizers/Quantize.cs b/src/ImageSharp/Quantizers/Quantize.cs index d99d4af34..296f4192d 100644 --- a/src/ImageSharp/Quantizers/Quantize.cs +++ b/src/ImageSharp/Quantizers/Quantize.cs @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp switch (mode) { case Quantization.Wu: - quantizer = new WuQuantizer(); + quantizer = new WuQuantizer(source.GetMemoryManager()); break; case Quantization.Palette: diff --git a/src/ImageSharp/Quantizers/WuArrayPool.cs b/src/ImageSharp/Quantizers/WuArrayPool.cs deleted file mode 100644 index 04a70637b..000000000 --- a/src/ImageSharp/Quantizers/WuArrayPool.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Buffers; - -namespace SixLabors.ImageSharp.Quantizers -{ - /// - /// Provides array pooling for the . - /// This is a separate class so that the pools can be shared accross multiple generic quantizer instaces. - /// - internal static class WuArrayPool - { - /// - /// The long array pool. - /// - public static readonly ArrayPool LongPool = ArrayPool.Create(TableLength, 25); - - /// - /// The float array pool. - /// - public static readonly ArrayPool FloatPool = ArrayPool.Create(TableLength, 5); - - /// - /// The byte array pool. - /// - public static readonly ArrayPool BytePool = ArrayPool.Create(TableLength, 5); - - /// - /// The table length. Matches the calculated value in - /// - private const int TableLength = 2471625; - } -} \ No newline at end of file diff --git a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs index cb9eb9b0e..bd3efe3f3 100644 --- a/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs +++ b/src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Numerics; using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Quantizers.Base; @@ -35,6 +36,8 @@ namespace SixLabors.ImageSharp.Quantizers public class WuQuantizer : QuantizerBase where TPixel : struct, IPixel { + private readonly MemoryManager memoryManager; + /// /// The index bits. /// @@ -68,37 +71,37 @@ namespace SixLabors.ImageSharp.Quantizers /// /// Moment of P(c). /// - private long[] vwt; + private Buffer vwt; /// /// Moment of r*P(c). /// - private long[] vmr; + private Buffer vmr; /// /// Moment of g*P(c). /// - private long[] vmg; + private Buffer vmg; /// /// Moment of b*P(c). /// - private long[] vmb; + private Buffer vmb; /// /// Moment of a*P(c). /// - private long[] vma; + private Buffer vma; /// /// Moment of c^2*P(c). /// - private float[] m2; + private Buffer m2; /// /// Color space tag. /// - private byte[] tag; + private Buffer tag; /// /// Maximum allowed color depth @@ -118,13 +121,14 @@ namespace SixLabors.ImageSharp.Quantizers /// /// Initializes a new instance of the class. /// + /// The to use for buffer allocations. /// /// The Wu quantizer is a two pass algorithm. The initial pass sets up the 3-D color histogram, /// the second pass quantizes a color based on the position in the histogram. /// - public WuQuantizer() - : base(false) - { + public WuQuantizer(MemoryManager memoryManager) + : base(false) { + this.memoryManager = memoryManager; } /// @@ -138,28 +142,35 @@ namespace SixLabors.ImageSharp.Quantizers try { - this.vwt = WuArrayPool.LongPool.Rent(TableLength); - this.vmr = WuArrayPool.LongPool.Rent(TableLength); - this.vmg = WuArrayPool.LongPool.Rent(TableLength); - this.vmb = WuArrayPool.LongPool.Rent(TableLength); - this.vma = WuArrayPool.LongPool.Rent(TableLength); - this.m2 = WuArrayPool.FloatPool.Rent(TableLength); - this.tag = WuArrayPool.BytePool.Rent(TableLength); + this.vwt = this.memoryManager.Allocate(TableLength, true); + this.vmr = this.memoryManager.Allocate(TableLength, true); + this.vmg = this.memoryManager.Allocate(TableLength, true); + this.vmb = this.memoryManager.Allocate(TableLength, true); + this.vma = this.memoryManager.Allocate(TableLength, true); + this.m2 = this.memoryManager.Allocate(TableLength, true); + this.tag = this.memoryManager.Allocate(TableLength, true); return base.Quantize(image, this.colors); } finally { - WuArrayPool.LongPool.Return(this.vwt, true); - WuArrayPool.LongPool.Return(this.vmr, true); - WuArrayPool.LongPool.Return(this.vmg, true); - WuArrayPool.LongPool.Return(this.vmb, true); - WuArrayPool.LongPool.Return(this.vma, true); - WuArrayPool.FloatPool.Return(this.m2, true); - WuArrayPool.BytePool.Return(this.tag, true); + this.DisposeBuffer(ref this.vwt); + this.DisposeBuffer(ref this.vmr); + this.DisposeBuffer(ref this.vmg); + this.DisposeBuffer(ref this.vmb); + this.DisposeBuffer(ref this.vma); + this.DisposeBuffer(ref this.m2); + this.DisposeBuffer(ref this.tag); } } + private void DisposeBuffer(ref Buffer buffer) + where T : struct + { + buffer?.Dispose(); + buffer = null; + } + /// protected override TPixel[] GetPalette() { @@ -170,14 +181,14 @@ namespace SixLabors.ImageSharp.Quantizers { this.Mark(ref this.colorCube[k], (byte)k); - float weight = Volume(ref this.colorCube[k], this.vwt); + float weight = Volume(ref this.colorCube[k], this.vwt.Array); if (MathF.Abs(weight) > Constants.Epsilon) { - 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); + float r = Volume(ref this.colorCube[k], this.vmr.Array); + float g = Volume(ref this.colorCube[k], this.vmg.Array); + float b = Volume(ref this.colorCube[k], this.vmb.Array); + float a = Volume(ref this.colorCube[k], this.vma.Array); ref TPixel color = ref this.palette[k]; color.PackFromVector4(new Vector4(r, g, b, a) / weight / 255F); @@ -448,39 +459,37 @@ namespace SixLabors.ImageSharp.Quantizers /// private void Get3DMoments() { - 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); - float[] 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); - float[] area2 = ArrayPool.Shared.Rent(IndexAlphaCount); - - try + using (Buffer volume = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (Buffer volumeR = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (Buffer volumeG = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (Buffer volumeB = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (Buffer volumeA = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) + using (Buffer volume2 = this.memoryManager.Allocate(IndexCount * IndexAlphaCount)) + + using (Buffer area = this.memoryManager.Allocate(IndexAlphaCount)) + using (Buffer areaR = this.memoryManager.Allocate(IndexAlphaCount)) + using (Buffer areaG = this.memoryManager.Allocate(IndexAlphaCount)) + using (Buffer areaB = this.memoryManager.Allocate(IndexAlphaCount)) + using (Buffer areaA = this.memoryManager.Allocate(IndexAlphaCount)) + using (Buffer area2 = this.memoryManager.Allocate(IndexAlphaCount)) { for (int r = 1; r < IndexCount; r++) { - 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); + volume.Clear(); + volumeR.Clear(); + volumeG.Clear(); + volumeB.Clear(); + volumeA.Clear(); + volume2.Clear(); for (int g = 1; g < IndexCount; g++) { - 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); + area.Clear(); + areaR.Clear(); + areaG.Clear(); + areaB.Clear(); + areaA.Clear(); + area2.Clear(); for (int b = 1; b < IndexCount; b++) { @@ -531,22 +540,6 @@ namespace SixLabors.ImageSharp.Quantizers } } } - 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); - } } /// @@ -556,10 +549,10 @@ namespace SixLabors.ImageSharp.Quantizers /// The . private float Variance(ref Box cube) { - 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 dr = Volume(ref cube, this.vmr.Array); + float dg = Volume(ref cube, this.vmg.Array); + float db = Volume(ref cube, this.vmb.Array); + float da = Volume(ref cube, this.vma.Array); float xx = this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] @@ -580,7 +573,7 @@ namespace SixLabors.ImageSharp.Quantizers + this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)]; var vector = new Vector4(dr, dg, db, da); - return xx - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt)); + return xx - (Vector4.Dot(vector, vector) / Volume(ref cube, this.vwt.Array)); } /// @@ -603,22 +596,22 @@ namespace SixLabors.ImageSharp.Quantizers /// The . 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(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); + long baseR = Bottom(ref cube, direction, this.vmr.Array); + long baseG = Bottom(ref cube, direction, this.vmg.Array); + long baseB = Bottom(ref cube, direction, this.vmb.Array); + long baseA = Bottom(ref cube, direction, this.vma.Array); + long baseW = Bottom(ref cube, direction, this.vwt.Array); float max = 0F; cut = -1; for (int i = first; i < last; i++) { - 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); + float halfR = baseR + Top(ref cube, direction, i, this.vmr.Array); + float halfG = baseG + Top(ref cube, direction, i, this.vmg.Array); + float halfB = baseB + Top(ref cube, direction, i, this.vmb.Array); + float halfA = baseA + Top(ref cube, direction, i, this.vma.Array); + float halfW = baseW + Top(ref cube, direction, i, this.vwt.Array); if (MathF.Abs(halfW) < Constants.Epsilon) { @@ -662,11 +655,11 @@ namespace SixLabors.ImageSharp.Quantizers /// Returns a value indicating whether the box has been split. private bool Cut(ref Box set1, ref Box set2) { - 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 wholeR = Volume(ref set1, this.vmr.Array); + float wholeG = Volume(ref set1, this.vmg.Array); + float wholeB = Volume(ref set1, this.vmb.Array); + float wholeA = Volume(ref set1, this.vma.Array); + float wholeW = Volume(ref set1, this.vwt.Array); 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); diff --git a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs index a0b14b09b..3afaa2606 100644 --- a/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs +++ b/tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs @@ -61,7 +61,7 @@ { Assert.True(image[0, 0].Equals(default(TPixel))); - IQuantizer quantizer = new WuQuantizer { Dither = dither }; + IQuantizer quantizer = new WuQuantizer(Configuration.Default.MemoryManager) { Dither = dither }; foreach (ImageFrame frame in image.Frames) {