Browse Source

Fix Wu palette, reduce memory usage

pull/1654/head
James Jackson-South 5 years ago
parent
commit
08c36f23b2
  1. 16
      src/ImageSharp/Processing/Extensions/Dithering/DitherExtensions.cs
  2. 6
      src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs
  3. 21
      src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs
  4. 5
      src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs

16
src/ImageSharp/Processing/Extensions/Dithering/DitherExtensions.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing
Dither(source, KnownDitherings.Bayer8x8);
/// <summary>
/// Dithers the image reducing it to a web-safe palette using ordered dithering.
/// Dithers the image reducing it to a web-safe palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="dither">The ordered ditherer.</param>
@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Processing
source.ApplyProcessor(new PaletteDitherProcessor(dither));
/// <summary>
/// Dithers the image reducing it to a web-safe palette using ordered dithering.
/// Dithers the image reducing it to a web-safe palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="dither">The ordered ditherer.</param>
@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing
source.ApplyProcessor(new PaletteDitherProcessor(dither, ditherScale));
/// <summary>
/// Dithers the image reducing it to the given palette using ordered dithering.
/// Dithers the image reducing it to the given palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="dither">The ordered ditherer.</param>
@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Processing
source.ApplyProcessor(new PaletteDitherProcessor(dither, palette));
/// <summary>
/// Dithers the image reducing it to the given palette using ordered dithering.
/// Dithers the image reducing it to the given palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="dither">The ordered ditherer.</param>
@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Processing
Dither(source, KnownDitherings.Bayer8x8, rectangle);
/// <summary>
/// Dithers the image reducing it to a web-safe palette using ordered dithering.
/// Dithers the image reducing it to a web-safe palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="dither">The ordered ditherer.</param>
@ -99,7 +99,7 @@ namespace SixLabors.ImageSharp.Processing
source.ApplyProcessor(new PaletteDitherProcessor(dither), rectangle);
/// <summary>
/// Dithers the image reducing it to a web-safe palette using ordered dithering.
/// Dithers the image reducing it to a web-safe palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="dither">The ordered ditherer.</param>
@ -116,7 +116,7 @@ namespace SixLabors.ImageSharp.Processing
source.ApplyProcessor(new PaletteDitherProcessor(dither, ditherScale), rectangle);
/// <summary>
/// Dithers the image reducing it to the given palette using ordered dithering.
/// Dithers the image reducing it to the given palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="dither">The ordered ditherer.</param>
@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Processing
source.ApplyProcessor(new PaletteDitherProcessor(dither, palette), rectangle);
/// <summary>
/// Dithers the image reducing it to the given palette using ordered dithering.
/// Dithers the image reducing it to the given palette.
/// </summary>
/// <param name="source">The image this method extends.</param>
/// <param name="dither">The ordered ditherer.</param>

6
src/ImageSharp/Processing/Processors/Quantization/EuclideanPixelMap{TPixel}.cs

@ -121,13 +121,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
/// significantly negatively affect performance.
/// </summary>
/// <remarks>
/// The cache is limited to 2471625 entries at 4MB.
/// This could be halfed by reducing the alpha accuracy but this treats
/// gradients less well in gifs than our previous cache implementation.
/// The cache is limited to 646866 entries at 0.62MB.
/// </remarks>
private struct ColorDistanceCache
{
private const int IndexBits = 6;
private const int IndexBits = 5;
private const int IndexAlphaBits = 3;
private const int IndexCount = (1 << IndexBits) + 1;
private const int IndexAlphaCount = (1 << IndexAlphaBits) + 1;

21
src/ImageSharp/Processing/Processors/Quantization/OctreeQuantizer{TPixel}.cs

@ -20,6 +20,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
where TPixel : unmanaged, IPixel<TPixel>
{
private readonly int maxColors;
private readonly int bitDepth;
private readonly Octree octree;
private IMemoryOwner<TPixel> paletteOwner;
private ReadOnlyMemory<TPixel> palette;
@ -41,9 +42,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
this.Configuration = configuration;
this.Options = options;
this.maxColors = Math.Min(byte.MaxValue, this.Options.MaxColors);
this.octree = new Octree(Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(this.maxColors), 1, 8));
this.paletteOwner = configuration.MemoryAllocator.Allocate<TPixel>(this.maxColors + 1, AllocationOptions.Clean);
this.maxColors = this.Options.MaxColors;
this.bitDepth = Numerics.Clamp(ColorNumerics.GetBitsNeededForColorDepth(this.maxColors), 1, 8);
this.octree = new Octree(this.bitDepth);
this.paletteOwner = configuration.MemoryAllocator.Allocate<TPixel>(this.maxColors, AllocationOptions.Clean);
this.palette = default;
this.pixelMap = default;
this.isDithering = !(this.Options.Dither is null);
@ -92,8 +94,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
int paletteIndex = 0;
Span<TPixel> paletteSpan = this.paletteOwner.GetSpan();
this.octree.Palletize(paletteSpan, this.maxColors, ref paletteIndex);
// On very rare occasions, (blur.png), the quantizer does not preserve a
// transparent entry when palletizing the captured colors.
// To workaround this we ensure the palette ends with the default color
// for higher bit depths. Lower bit depths will correctly reduce the palette.
// TODO: Investigate more evenly reduced palette reduction.
int max = this.maxColors;
if (this.bitDepth == 8)
{
max--;
}
this.octree.Palletize(paletteSpan, max, ref paletteIndex);
ReadOnlyMemory<TPixel> result = this.paletteOwner.Memory.Slice(0, paletteSpan.Length);
this.pixelMap = new EuclideanPixelMap<TPixel>(this.Configuration, result);
this.palette = result;

5
src/ImageSharp/Processing/Processors/Quantization/WuQuantizer{TPixel}.cs

@ -126,9 +126,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Quantization
this.Get3DMoments(this.memoryAllocator);
this.BuildCube();
// Slice again since maxColors has been updated since the buffer was created.
Span<TPixel> paletteSpan = this.paletteOwner.GetSpan().Slice(0, this.maxColors);
ReadOnlySpan<Moment> momentsSpan = this.momentsOwner.GetSpan();
Span<TPixel> paletteSpan = this.paletteOwner.GetSpan();
for (int k = 0; k < this.maxColors; k++)
for (int k = 0; k < paletteSpan.Length; k++)
{
this.Mark(ref this.colorCube[k], (byte)k);

Loading…
Cancel
Save