Browse Source

Split Vp8LHistogram and clean up

pull/2546/head
James Jackson-South 2 years ago
parent
commit
83ced124c4
  1. 4
      src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs
  2. 2
      src/ImageSharp/Formats/Webp/Lossless/CostModel.cs
  3. 10
      src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs
  4. 2
      src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
  5. 99
      src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogram.cs
  6. 62
      src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogramSet.cs
  7. 4
      tests/ImageSharp.Tests/Formats/WebP/DominantCostRangeTests.cs
  8. 6
      tests/ImageSharp.Tests/Formats/WebP/Vp8LHistogramTests.cs

4
src/ImageSharp/Formats/Webp/Lossless/BackwardReferenceEncoder.cs

@ -85,7 +85,7 @@ internal static class BackwardReferenceEncoder
}
// Keep the best backward references.
using Vp8LHistogram histo = Vp8LHistogram.Create(memoryAllocator, worst, cacheBitsTmp);
using OwnedVp8LHistogram histo = OwnedVp8LHistogram.Create(memoryAllocator, worst, cacheBitsTmp);
double bitCost = histo.EstimateBits(stats, bitsEntropy);
if (lz77TypeBest == 0 || bitCost < bitCostBest)
@ -102,7 +102,7 @@ internal static class BackwardReferenceEncoder
{
Vp8LHashChain hashChainTmp = lz77TypeBest == (int)Vp8LLz77Type.Lz77Standard ? hashChain : hashChainBox!;
BackwardReferencesTraceBackwards(width, height, memoryAllocator, bgra, cacheBits, hashChainTmp, best, worst);
using Vp8LHistogram histo = Vp8LHistogram.Create(memoryAllocator, worst, cacheBits);
using OwnedVp8LHistogram histo = OwnedVp8LHistogram.Create(memoryAllocator, worst, cacheBits);
double bitCostTrace = histo.EstimateBits(stats, bitsEntropy);
if (bitCostTrace < bitCostBest)
{

2
src/ImageSharp/Formats/Webp/Lossless/CostModel.cs

@ -37,7 +37,7 @@ internal class CostModel
public void Build(int xSize, int cacheBits, Vp8LBackwardRefs backwardRefs)
{
using Vp8LHistogram histogram = Vp8LHistogram.Create(this.memoryAllocator, cacheBits);
using OwnedVp8LHistogram histogram = OwnedVp8LHistogram.Create(this.memoryAllocator, cacheBits);
// The following code is similar to HistogramCreate but converts the distance to plane code.
for (int i = 0; i < backwardRefs.Refs.Count; i++)

10
src/ImageSharp/Formats/Webp/Lossless/HistogramEncoder.cs

@ -172,8 +172,8 @@ internal static class HistogramEncoder
// Skip the histogram if it is completely empty, which can happen for tiles with no information (when they are skipped because of LZ77).
if (!origHistogram.IsUsed(0) && !origHistogram.IsUsed(1) && !origHistogram.IsUsed(2) && !origHistogram.IsUsed(3) && !origHistogram.IsUsed(4))
{
origHistograms.DisposeAt(i);
histograms.DisposeAt(i);
origHistograms[i] = null;
histograms[i] = null;
histogramSymbols[i] = InvalidHistogramSymbol;
}
else
@ -254,7 +254,7 @@ internal static class HistogramEncoder
// Move the (better) merged histogram to its final slot.
(histograms[first], curCombo) = (curCombo, histograms[first]);
histograms.DisposeAt(idx);
histograms[idx] = null;
indicesToRemove.Add(idx);
clusterMappings[clusters[idx]] = clusters[first];
}
@ -415,7 +415,7 @@ internal static class HistogramEncoder
// Merge the histograms and remove bestIdx2 from the list.
HistogramAdd(histograms[bestIdx2], histograms[bestIdx1], histograms[bestIdx1]);
histograms[bestIdx1].BitCost = histoPriorityList[0].CostCombo;
histograms.DisposeAt(bestIdx2);
histograms[bestIdx2] = null;
numUsed--;
for (int j = 0; j < histoPriorityList.Count;)
@ -512,7 +512,7 @@ internal static class HistogramEncoder
histograms[idx1].BitCost = histoPriorityList[0].CostCombo;
// Remove merged histogram.
histograms.DisposeAt(idx2);
histograms[idx2] = null;
// Remove pairs intersecting the just combined best pair.
for (int i = 0; i < histoPriorityList.Count;)

2
src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs

@ -589,7 +589,7 @@ internal class Vp8LEncoder : IDisposable
Vp8LBackwardRefs refsTmp = this.Refs[refsBest.Equals(this.Refs[0]) ? 1 : 0];
this.bitWriter.Reset(bwInit);
using Vp8LHistogram tmpHisto = Vp8LHistogram.Create(this.memoryAllocator, cacheBits);
using OwnedVp8LHistogram tmpHisto = OwnedVp8LHistogram.Create(this.memoryAllocator, cacheBits);
using Vp8LHistogramSet histogramImage = new(this.memoryAllocator, histogramImageXySize, cacheBits);
// Build histogram image and symbols from backward references.

99
src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogram.cs

@ -10,13 +10,9 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Webp.Lossless;
internal sealed unsafe class Vp8LHistogram : IDisposable
internal abstract unsafe class Vp8LHistogram
{
private const uint NonTrivialSym = 0xffffffff;
private readonly IMemoryOwner<uint>? bufferOwner;
private readonly Memory<uint> buffer;
private readonly MemoryHandle bufferHandle;
private readonly uint* red;
private readonly uint* blue;
private readonly uint* alpha;
@ -35,32 +31,21 @@ internal sealed unsafe class Vp8LHistogram : IDisposable
/// <summary>
/// Initializes a new instance of the <see cref="Vp8LHistogram"/> class.
/// </summary>
/// <remarks>
/// This constructor should be used when the histogram is a member of a <see cref="Vp8LHistogramSet"/>.
/// </remarks>
/// <param name="buffer">The backing buffer.</param>
/// <param name="basePointer">The base pointer to the backing memory.</param>
/// <param name="refs">The backward references to initialize the histogram with.</param>
/// <param name="paletteCodeBits">The palette code bits.</param>
public Vp8LHistogram(Memory<uint> buffer, Vp8LBackwardRefs refs, int paletteCodeBits)
: this(buffer, paletteCodeBits) => this.StoreRefs(refs);
protected Vp8LHistogram(uint* basePointer, Vp8LBackwardRefs refs, int paletteCodeBits)
: this(basePointer, paletteCodeBits) => this.StoreRefs(refs);
/// <summary>
/// Initializes a new instance of the <see cref="Vp8LHistogram"/> class.
/// </summary>
/// <remarks>
/// This constructor should be used when the histogram is a member of a <see cref="Vp8LHistogramSet"/>.
/// </remarks>
/// <param name="buffer">The backing buffer.</param>
/// <param name="basePointer">The base pointer to the backing memory.</param>
/// <param name="paletteCodeBits">The palette code bits.</param>
/// <param name="bufferOwner">Optional buffer owner to dispose.</param>
public Vp8LHistogram(Memory<uint> buffer, int paletteCodeBits, IMemoryOwner<uint>? bufferOwner = null)
protected Vp8LHistogram(uint* basePointer, int paletteCodeBits)
{
this.bufferOwner = bufferOwner;
this.buffer = buffer;
this.bufferHandle = this.buffer.Pin();
this.PaletteCodeBits = paletteCodeBits;
this.red = (uint*)this.bufferHandle.Pointer;
this.red = basePointer;
this.blue = this.red + RedSize;
this.alpha = this.blue + BlueSize;
this.distance = this.alpha + AlphaSize;
@ -109,27 +94,6 @@ internal sealed unsafe class Vp8LHistogram : IDisposable
private Span<uint> TotalSpan => new(this.red, BufferSize);
public bool IsDisposed { get; set; }
/// <summary>
/// Creates an <see cref="Vp8LHistogram"/> that is not a member of a <see cref="Vp8LHistogramSet"/>.
/// </summary>
public static Vp8LHistogram Create(MemoryAllocator memoryAllocator, int paletteCodeBits)
{
IMemoryOwner<uint> bufferOwner = memoryAllocator.Allocate<uint>(BufferSize, AllocationOptions.Clean);
return new Vp8LHistogram(bufferOwner.Memory, paletteCodeBits, bufferOwner);
}
/// <summary>
/// Creates an <see cref="Vp8LHistogram"/> that is not a member of a <see cref="Vp8LHistogramSet"/>.
/// </summary>
public static Vp8LHistogram Create(MemoryAllocator memoryAllocator, Vp8LBackwardRefs refs, int paletteCodeBits)
{
Vp8LHistogram histogram = Create(memoryAllocator, paletteCodeBits);
histogram.StoreRefs(refs);
return histogram;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsUsed(int index) => this.IsUsedSpan[index] == 1u;
@ -621,14 +585,57 @@ internal sealed unsafe class Vp8LHistogram : IDisposable
}
}
}
}
internal sealed unsafe class OwnedVp8LHistogram : Vp8LHistogram, IDisposable
{
private readonly IMemoryOwner<uint> bufferOwner;
private MemoryHandle bufferHandle;
private bool isDisposed;
private OwnedVp8LHistogram(
IMemoryOwner<uint> bufferOwner,
ref MemoryHandle bufferHandle,
uint* basePointer,
int paletteCodeBits)
: base(basePointer, paletteCodeBits)
{
this.bufferOwner = bufferOwner;
this.bufferHandle = bufferHandle;
}
/// <summary>
/// Creates an <see cref="OwnedVp8LHistogram"/> that is not a member of a <see cref="Vp8LHistogramSet"/>.
/// </summary>
/// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="paletteCodeBits">The palette code bits.</param>
public static OwnedVp8LHistogram Create(MemoryAllocator memoryAllocator, int paletteCodeBits)
{
IMemoryOwner<uint> bufferOwner = memoryAllocator.Allocate<uint>(BufferSize, AllocationOptions.Clean);
MemoryHandle bufferHandle = bufferOwner.Memory.Pin();
return new OwnedVp8LHistogram(bufferOwner, ref bufferHandle, (uint*)bufferHandle.Pointer, paletteCodeBits);
}
/// <summary>
/// Creates an <see cref="OwnedVp8LHistogram"/> that is not a member of a <see cref="Vp8LHistogramSet"/>.
/// </summary>
/// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="refs">The backward references to initialize the histogram with.</param>
/// <param name="paletteCodeBits">The palette code bits.</param>
public static OwnedVp8LHistogram Create(MemoryAllocator memoryAllocator, Vp8LBackwardRefs refs, int paletteCodeBits)
{
OwnedVp8LHistogram histogram = Create(memoryAllocator, paletteCodeBits);
histogram.StoreRefs(refs);
return histogram;
}
public void Dispose()
{
if (!this.IsDisposed)
if (!this.isDisposed)
{
this.bufferHandle.Dispose();
this.bufferOwner?.Dispose();
this.IsDisposed = true;
this.bufferOwner.Dispose();
this.isDisposed = true;
}
}
}

62
src/ImageSharp/Formats/Webp/Lossless/Vp8LHistogramSet.cs

@ -13,30 +13,39 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless;
internal sealed class Vp8LHistogramSet : IEnumerable<Vp8LHistogram>, IDisposable
{
private readonly IMemoryOwner<uint> buffer;
private MemoryHandle bufferHandle;
private readonly List<Vp8LHistogram> items;
private bool isDisposed;
public Vp8LHistogramSet(MemoryAllocator memoryAllocator, int capacity, int cacheBits)
{
this.buffer = memoryAllocator.Allocate<uint>(Vp8LHistogram.BufferSize * capacity, AllocationOptions.Clean);
this.bufferHandle = this.buffer.Memory.Pin();
this.items = new List<Vp8LHistogram>(capacity);
for (int i = 0; i < capacity; i++)
unsafe
{
Memory<uint> subBuffer = this.buffer.Memory.Slice(Vp8LHistogram.BufferSize * i, Vp8LHistogram.BufferSize);
this.items.Add(new Vp8LHistogram(subBuffer, cacheBits));
uint* basePointer = (uint*)this.bufferHandle.Pointer;
this.items = new List<Vp8LHistogram>(capacity);
for (int i = 0; i < capacity; i++)
{
this.items.Add(new MemberVp8LHistogram(basePointer + (Vp8LHistogram.BufferSize * i), cacheBits));
}
}
}
public Vp8LHistogramSet(MemoryAllocator memoryAllocator, Vp8LBackwardRefs refs, int capacity, int cacheBits)
{
this.buffer = memoryAllocator.Allocate<uint>(Vp8LHistogram.BufferSize * capacity, AllocationOptions.Clean);
this.bufferHandle = this.buffer.Memory.Pin();
this.items = new List<Vp8LHistogram>(capacity);
for (int i = 0; i < capacity; i++)
unsafe
{
Memory<uint> subBuffer = this.buffer.Memory.Slice(Vp8LHistogram.BufferSize * i, Vp8LHistogram.BufferSize);
this.items.Add(new Vp8LHistogram(subBuffer, refs, cacheBits));
uint* basePointer = (uint*)this.bufferHandle.Pointer;
this.items = new List<Vp8LHistogram>(capacity);
for (int i = 0; i < capacity; i++)
{
this.items.Add(new MemberVp8LHistogram(basePointer + (Vp8LHistogram.BufferSize * i), refs, cacheBits));
}
}
}
@ -49,30 +58,13 @@ internal sealed class Vp8LHistogramSet : IEnumerable<Vp8LHistogram>, IDisposable
public Vp8LHistogram this[int index]
{
get => this.items[index];
// TODO: Should we check and throw for null?
set => this.items[index] = value;
}
public void DisposeAt(int index)
{
this.CheckDisposed();
Vp8LHistogram item = this.items[index];
item?.Dispose();
this.items[index] = null;
}
public void RemoveAt(int index)
{
this.CheckDisposed();
Vp8LHistogram item = this.items[index];
item?.Dispose();
this.items.RemoveAt(index);
#pragma warning disable IDE0059 // Unnecessary assignment of a value
item = null;
#pragma warning restore IDE0059 // Unnecessary assignment of a value
}
public void Dispose()
@ -82,13 +74,8 @@ internal sealed class Vp8LHistogramSet : IEnumerable<Vp8LHistogram>, IDisposable
return;
}
foreach (Vp8LHistogram item in this.items)
{
// First, make sure to unpin individual sub buffers.
item?.Dispose();
}
this.buffer.Dispose();
this.bufferHandle.Dispose();
this.items.Clear();
this.isDisposed = true;
}
@ -107,4 +94,17 @@ internal sealed class Vp8LHistogramSet : IEnumerable<Vp8LHistogram>, IDisposable
}
private static void ThrowDisposed() => throw new ObjectDisposedException(nameof(Vp8LHistogramSet));
private sealed unsafe class MemberVp8LHistogram : Vp8LHistogram
{
public MemberVp8LHistogram(uint* basePointer, int paletteCodeBits)
: base(basePointer, paletteCodeBits)
{
}
public MemberVp8LHistogram(uint* basePointer, Vp8LBackwardRefs refs, int paletteCodeBits)
: base(basePointer, refs, paletteCodeBits)
{
}
}
}

4
tests/ImageSharp.Tests/Formats/WebP/DominantCostRangeTests.cs

@ -25,7 +25,7 @@ public class DominantCostRangeTests
{
// arrange
DominantCostRange dominantCostRange = new();
using Vp8LHistogram histogram = Vp8LHistogram.Create(Configuration.Default.MemoryAllocator, 10);
using OwnedVp8LHistogram histogram = OwnedVp8LHistogram.Create(Configuration.Default.MemoryAllocator, 10);
histogram.LiteralCost = 1.0d;
histogram.RedCost = 2.0d;
histogram.BlueCost = 3.0d;
@ -57,7 +57,7 @@ public class DominantCostRangeTests
RedMax = 191.0,
RedMin = 109.0
};
using Vp8LHistogram histogram = Vp8LHistogram.Create(Configuration.Default.MemoryAllocator, 6);
using OwnedVp8LHistogram histogram = OwnedVp8LHistogram.Create(Configuration.Default.MemoryAllocator, 6);
histogram.LiteralCost = 247.0d;
histogram.RedCost = 112.0d;
histogram.BlueCost = 202.0d;

6
tests/ImageSharp.Tests/Formats/WebP/Vp8LHistogramTests.cs

@ -78,15 +78,15 @@ public class Vp8LHistogramTests
}
MemoryAllocator memoryAllocator = Configuration.Default.MemoryAllocator;
using Vp8LHistogram histogram0 = Vp8LHistogram.Create(memoryAllocator, backwardRefs, 3);
using Vp8LHistogram histogram1 = Vp8LHistogram.Create(memoryAllocator, backwardRefs, 3);
using OwnedVp8LHistogram histogram0 = OwnedVp8LHistogram.Create(memoryAllocator, backwardRefs, 3);
using OwnedVp8LHistogram histogram1 = OwnedVp8LHistogram.Create(memoryAllocator, backwardRefs, 3);
for (int i = 0; i < 5; i++)
{
histogram0.IsUsed(i, true);
histogram1.IsUsed(i, true);
}
using Vp8LHistogram output = Vp8LHistogram.Create(memoryAllocator, 3);
using OwnedVp8LHistogram output = OwnedVp8LHistogram.Create(memoryAllocator, 3);
// act
histogram0.Add(histogram1, output);

Loading…
Cancel
Save