Browse Source

- Removed ArrayPool from WuQuantizer

af/merge-core
Lauri Kotilainen 8 years ago
parent
commit
d6a2082749
  1. 2
      src/ImageSharp/Formats/Png/PngEncoderCore.cs
  2. 2
      src/ImageSharp/Quantizers/Quantize.cs
  3. 34
      src/ImageSharp/Quantizers/WuArrayPool.cs
  4. 177
      src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs
  5. 2
      tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs

2
src/ImageSharp/Formats/Png/PngEncoderCore.cs

@ -509,7 +509,7 @@ namespace SixLabors.ImageSharp.Formats.Png
if (this.quantizer == null) if (this.quantizer == null)
{ {
this.quantizer = new WuQuantizer<TPixel>(); this.quantizer = new WuQuantizer<TPixel>(this.memoryManager);
} }
// Quantize the image returning a palette. This boxing is icky. // Quantize the image returning a palette. This boxing is icky.

2
src/ImageSharp/Quantizers/Quantize.cs

@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp
switch (mode) switch (mode)
{ {
case Quantization.Wu: case Quantization.Wu:
quantizer = new WuQuantizer<TPixel>(); quantizer = new WuQuantizer<TPixel>(source.GetMemoryManager());
break; break;
case Quantization.Palette: case Quantization.Palette:

34
src/ImageSharp/Quantizers/WuArrayPool.cs

@ -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
{
/// <summary>
/// Provides array pooling for the <see cref="WuQuantizer{TPixel}"/>.
/// This is a separate class so that the pools can be shared accross multiple generic quantizer instaces.
/// </summary>
internal static class WuArrayPool
{
/// <summary>
/// The long array pool.
/// </summary>
public static readonly ArrayPool<long> LongPool = ArrayPool<long>.Create(TableLength, 25);
/// <summary>
/// The float array pool.
/// </summary>
public static readonly ArrayPool<float> FloatPool = ArrayPool<float>.Create(TableLength, 5);
/// <summary>
/// The byte array pool.
/// </summary>
public static readonly ArrayPool<byte> BytePool = ArrayPool<byte>.Create(TableLength, 5);
/// <summary>
/// The table length. Matches the calculated value in <see cref="WuQuantizer{TPixel}"/>
/// </summary>
private const int TableLength = 2471625;
}
}

177
src/ImageSharp/Quantizers/WuQuantizer{TPixel}.cs

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Quantizers.Base; using SixLabors.ImageSharp.Quantizers.Base;
@ -35,6 +36,8 @@ namespace SixLabors.ImageSharp.Quantizers
public class WuQuantizer<TPixel> : QuantizerBase<TPixel> public class WuQuantizer<TPixel> : QuantizerBase<TPixel>
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
{ {
private readonly MemoryManager memoryManager;
/// <summary> /// <summary>
/// The index bits. /// The index bits.
/// </summary> /// </summary>
@ -68,37 +71,37 @@ namespace SixLabors.ImageSharp.Quantizers
/// <summary> /// <summary>
/// Moment of <c>P(c)</c>. /// Moment of <c>P(c)</c>.
/// </summary> /// </summary>
private long[] vwt; private Buffer<long> vwt;
/// <summary> /// <summary>
/// Moment of <c>r*P(c)</c>. /// Moment of <c>r*P(c)</c>.
/// </summary> /// </summary>
private long[] vmr; private Buffer<long> vmr;
/// <summary> /// <summary>
/// Moment of <c>g*P(c)</c>. /// Moment of <c>g*P(c)</c>.
/// </summary> /// </summary>
private long[] vmg; private Buffer<long> vmg;
/// <summary> /// <summary>
/// Moment of <c>b*P(c)</c>. /// Moment of <c>b*P(c)</c>.
/// </summary> /// </summary>
private long[] vmb; private Buffer<long> vmb;
/// <summary> /// <summary>
/// Moment of <c>a*P(c)</c>. /// Moment of <c>a*P(c)</c>.
/// </summary> /// </summary>
private long[] vma; private Buffer<long> vma;
/// <summary> /// <summary>
/// Moment of <c>c^2*P(c)</c>. /// Moment of <c>c^2*P(c)</c>.
/// </summary> /// </summary>
private float[] m2; private Buffer<float> m2;
/// <summary> /// <summary>
/// Color space tag. /// Color space tag.
/// </summary> /// </summary>
private byte[] tag; private Buffer<byte> tag;
/// <summary> /// <summary>
/// Maximum allowed color depth /// Maximum allowed color depth
@ -118,13 +121,14 @@ namespace SixLabors.ImageSharp.Quantizers
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="WuQuantizer{TPixel}"/> class. /// Initializes a new instance of the <see cref="WuQuantizer{TPixel}"/> class.
/// </summary> /// </summary>
/// <param name="memoryManager">The <see cref="MemoryManager"/> to use for buffer allocations.</param>
/// <remarks> /// <remarks>
/// The Wu quantizer is a two pass algorithm. The initial pass sets up the 3-D color histogram, /// 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. /// the second pass quantizes a color based on the position in the histogram.
/// </remarks> /// </remarks>
public WuQuantizer() public WuQuantizer(MemoryManager memoryManager)
: base(false) : base(false) {
{ this.memoryManager = memoryManager;
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -138,28 +142,35 @@ namespace SixLabors.ImageSharp.Quantizers
try try
{ {
this.vwt = WuArrayPool.LongPool.Rent(TableLength); this.vwt = this.memoryManager.Allocate<long>(TableLength, true);
this.vmr = WuArrayPool.LongPool.Rent(TableLength); this.vmr = this.memoryManager.Allocate<long>(TableLength, true);
this.vmg = WuArrayPool.LongPool.Rent(TableLength); this.vmg = this.memoryManager.Allocate<long>(TableLength, true);
this.vmb = WuArrayPool.LongPool.Rent(TableLength); this.vmb = this.memoryManager.Allocate<long>(TableLength, true);
this.vma = WuArrayPool.LongPool.Rent(TableLength); this.vma = this.memoryManager.Allocate<long>(TableLength, true);
this.m2 = WuArrayPool.FloatPool.Rent(TableLength); this.m2 = this.memoryManager.Allocate<float>(TableLength, true);
this.tag = WuArrayPool.BytePool.Rent(TableLength); this.tag = this.memoryManager.Allocate<byte>(TableLength, true);
return base.Quantize(image, this.colors); return base.Quantize(image, this.colors);
} }
finally finally
{ {
WuArrayPool.LongPool.Return(this.vwt, true); this.DisposeBuffer(ref this.vwt);
WuArrayPool.LongPool.Return(this.vmr, true); this.DisposeBuffer(ref this.vmr);
WuArrayPool.LongPool.Return(this.vmg, true); this.DisposeBuffer(ref this.vmg);
WuArrayPool.LongPool.Return(this.vmb, true); this.DisposeBuffer(ref this.vmb);
WuArrayPool.LongPool.Return(this.vma, true); this.DisposeBuffer(ref this.vma);
WuArrayPool.FloatPool.Return(this.m2, true); this.DisposeBuffer(ref this.m2);
WuArrayPool.BytePool.Return(this.tag, true); this.DisposeBuffer(ref this.tag);
} }
} }
private void DisposeBuffer<T>(ref Buffer<T> buffer)
where T : struct
{
buffer?.Dispose();
buffer = null;
}
/// <inheritdoc/> /// <inheritdoc/>
protected override TPixel[] GetPalette() protected override TPixel[] GetPalette()
{ {
@ -170,14 +181,14 @@ namespace SixLabors.ImageSharp.Quantizers
{ {
this.Mark(ref this.colorCube[k], (byte)k); 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) if (MathF.Abs(weight) > Constants.Epsilon)
{ {
float r = Volume(ref this.colorCube[k], this.vmr); float r = Volume(ref this.colorCube[k], this.vmr.Array);
float g = Volume(ref this.colorCube[k], this.vmg); float g = Volume(ref this.colorCube[k], this.vmg.Array);
float b = Volume(ref this.colorCube[k], this.vmb); float b = Volume(ref this.colorCube[k], this.vmb.Array);
float a = Volume(ref this.colorCube[k], this.vma); float a = Volume(ref this.colorCube[k], this.vma.Array);
ref TPixel color = ref this.palette[k]; ref TPixel color = ref this.palette[k];
color.PackFromVector4(new Vector4(r, g, b, a) / weight / 255F); color.PackFromVector4(new Vector4(r, g, b, a) / weight / 255F);
@ -448,39 +459,37 @@ namespace SixLabors.ImageSharp.Quantizers
/// </summary> /// </summary>
private void Get3DMoments() private void Get3DMoments()
{ {
long[] volume = ArrayPool<long>.Shared.Rent(IndexCount * IndexAlphaCount); using (Buffer<long> volume = this.memoryManager.Allocate<long>(IndexCount * IndexAlphaCount))
long[] volumeR = ArrayPool<long>.Shared.Rent(IndexCount * IndexAlphaCount); using (Buffer<long> volumeR = this.memoryManager.Allocate<long>(IndexCount * IndexAlphaCount))
long[] volumeG = ArrayPool<long>.Shared.Rent(IndexCount * IndexAlphaCount); using (Buffer<long> volumeG = this.memoryManager.Allocate<long>(IndexCount * IndexAlphaCount))
long[] volumeB = ArrayPool<long>.Shared.Rent(IndexCount * IndexAlphaCount); using (Buffer<long> volumeB = this.memoryManager.Allocate<long>(IndexCount * IndexAlphaCount))
long[] volumeA = ArrayPool<long>.Shared.Rent(IndexCount * IndexAlphaCount); using (Buffer<long> volumeA = this.memoryManager.Allocate<long>(IndexCount * IndexAlphaCount))
float[] volume2 = ArrayPool<float>.Shared.Rent(IndexCount * IndexAlphaCount); using (Buffer<float> volume2 = this.memoryManager.Allocate<float>(IndexCount * IndexAlphaCount))
long[] area = ArrayPool<long>.Shared.Rent(IndexAlphaCount); using (Buffer<long> area = this.memoryManager.Allocate<long>(IndexAlphaCount))
long[] areaR = ArrayPool<long>.Shared.Rent(IndexAlphaCount); using (Buffer<long> areaR = this.memoryManager.Allocate<long>(IndexAlphaCount))
long[] areaG = ArrayPool<long>.Shared.Rent(IndexAlphaCount); using (Buffer<long> areaG = this.memoryManager.Allocate<long>(IndexAlphaCount))
long[] areaB = ArrayPool<long>.Shared.Rent(IndexAlphaCount); using (Buffer<long> areaB = this.memoryManager.Allocate<long>(IndexAlphaCount))
long[] areaA = ArrayPool<long>.Shared.Rent(IndexAlphaCount); using (Buffer<long> areaA = this.memoryManager.Allocate<long>(IndexAlphaCount))
float[] area2 = ArrayPool<float>.Shared.Rent(IndexAlphaCount); using (Buffer<float> area2 = this.memoryManager.Allocate<float>(IndexAlphaCount))
try
{ {
for (int r = 1; r < IndexCount; r++) for (int r = 1; r < IndexCount; r++)
{ {
Array.Clear(volume, 0, IndexCount * IndexAlphaCount); volume.Clear();
Array.Clear(volumeR, 0, IndexCount * IndexAlphaCount); volumeR.Clear();
Array.Clear(volumeG, 0, IndexCount * IndexAlphaCount); volumeG.Clear();
Array.Clear(volumeB, 0, IndexCount * IndexAlphaCount); volumeB.Clear();
Array.Clear(volumeA, 0, IndexCount * IndexAlphaCount); volumeA.Clear();
Array.Clear(volume2, 0, IndexCount * IndexAlphaCount); volume2.Clear();
for (int g = 1; g < IndexCount; g++) for (int g = 1; g < IndexCount; g++)
{ {
Array.Clear(area, 0, IndexAlphaCount); area.Clear();
Array.Clear(areaR, 0, IndexAlphaCount); areaR.Clear();
Array.Clear(areaG, 0, IndexAlphaCount); areaG.Clear();
Array.Clear(areaB, 0, IndexAlphaCount); areaB.Clear();
Array.Clear(areaA, 0, IndexAlphaCount); areaA.Clear();
Array.Clear(area2, 0, IndexAlphaCount); area2.Clear();
for (int b = 1; b < IndexCount; b++) for (int b = 1; b < IndexCount; b++)
{ {
@ -531,22 +540,6 @@ namespace SixLabors.ImageSharp.Quantizers
} }
} }
} }
finally
{
ArrayPool<long>.Shared.Return(volume);
ArrayPool<long>.Shared.Return(volumeR);
ArrayPool<long>.Shared.Return(volumeG);
ArrayPool<long>.Shared.Return(volumeB);
ArrayPool<long>.Shared.Return(volumeA);
ArrayPool<float>.Shared.Return(volume2);
ArrayPool<long>.Shared.Return(area);
ArrayPool<long>.Shared.Return(areaR);
ArrayPool<long>.Shared.Return(areaG);
ArrayPool<long>.Shared.Return(areaB);
ArrayPool<long>.Shared.Return(areaA);
ArrayPool<float>.Shared.Return(area2);
}
} }
/// <summary> /// <summary>
@ -556,10 +549,10 @@ namespace SixLabors.ImageSharp.Quantizers
/// <returns>The <see cref="float"/>.</returns> /// <returns>The <see cref="float"/>.</returns>
private float Variance(ref Box cube) private float Variance(ref Box cube)
{ {
float dr = Volume(ref cube, this.vmr); float dr = Volume(ref cube, this.vmr.Array);
float dg = Volume(ref cube, this.vmg); float dg = Volume(ref cube, this.vmg.Array);
float db = Volume(ref cube, this.vmb); float db = Volume(ref cube, this.vmb.Array);
float da = Volume(ref cube, this.vma); float da = Volume(ref cube, this.vma.Array);
float xx = float xx =
this.m2[GetPaletteIndex(cube.R1, cube.G1, cube.B1, cube.A1)] 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)]; + this.m2[GetPaletteIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
var vector = new Vector4(dr, dg, db, da); 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));
} }
/// <summary> /// <summary>
@ -603,22 +596,22 @@ namespace SixLabors.ImageSharp.Quantizers
/// <returns>The <see cref="float"/>.</returns> /// <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, float wholeR, float wholeG, float wholeB, float wholeA, float wholeW)
{ {
long baseR = Bottom(ref cube, direction, this.vmr); long baseR = Bottom(ref cube, direction, this.vmr.Array);
long baseG = Bottom(ref cube, direction, this.vmg); long baseG = Bottom(ref cube, direction, this.vmg.Array);
long baseB = Bottom(ref cube, direction, this.vmb); long baseB = Bottom(ref cube, direction, this.vmb.Array);
long baseA = Bottom(ref cube, direction, this.vma); long baseA = Bottom(ref cube, direction, this.vma.Array);
long baseW = Bottom(ref cube, direction, this.vwt); long baseW = Bottom(ref cube, direction, this.vwt.Array);
float max = 0F; float max = 0F;
cut = -1; cut = -1;
for (int i = first; i < last; i++) for (int i = first; i < last; i++)
{ {
float halfR = baseR + Top(ref cube, direction, i, this.vmr); float halfR = baseR + Top(ref cube, direction, i, this.vmr.Array);
float halfG = baseG + Top(ref cube, direction, i, this.vmg); float halfG = baseG + Top(ref cube, direction, i, this.vmg.Array);
float halfB = baseB + Top(ref cube, direction, i, this.vmb); float halfB = baseB + Top(ref cube, direction, i, this.vmb.Array);
float halfA = baseA + Top(ref cube, direction, i, this.vma); float halfA = baseA + Top(ref cube, direction, i, this.vma.Array);
float halfW = baseW + Top(ref cube, direction, i, this.vwt); float halfW = baseW + Top(ref cube, direction, i, this.vwt.Array);
if (MathF.Abs(halfW) < Constants.Epsilon) if (MathF.Abs(halfW) < Constants.Epsilon)
{ {
@ -662,11 +655,11 @@ namespace SixLabors.ImageSharp.Quantizers
/// <returns>Returns a value indicating whether the box has been split.</returns> /// <returns>Returns a value indicating whether the box has been split.</returns>
private bool Cut(ref Box set1, ref Box set2) private bool Cut(ref Box set1, ref Box set2)
{ {
float wholeR = Volume(ref set1, this.vmr); float wholeR = Volume(ref set1, this.vmr.Array);
float wholeG = Volume(ref set1, this.vmg); float wholeG = Volume(ref set1, this.vmg.Array);
float wholeB = Volume(ref set1, this.vmb); float wholeB = Volume(ref set1, this.vmb.Array);
float wholeA = Volume(ref set1, this.vma); float wholeA = Volume(ref set1, this.vma.Array);
float wholeW = Volume(ref set1, this.vwt); 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 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 maxg = this.Maximize(ref set1, 2, set1.G0 + 1, set1.G1, out int cutg, wholeR, wholeG, wholeB, wholeA, wholeW);

2
tests/ImageSharp.Tests/Quantization/QuantizedImageTests.cs

@ -61,7 +61,7 @@
{ {
Assert.True(image[0, 0].Equals(default(TPixel))); Assert.True(image[0, 0].Equals(default(TPixel)));
IQuantizer<TPixel> quantizer = new WuQuantizer<TPixel> { Dither = dither }; IQuantizer<TPixel> quantizer = new WuQuantizer<TPixel>(Configuration.Default.MemoryManager) { Dither = dither };
foreach (ImageFrame<TPixel> frame in image.Frames) foreach (ImageFrame<TPixel> frame in image.Frames)
{ {

Loading…
Cancel
Save