Browse Source

Store AV1 cumulative probabilities in 16 bits

pull/2633/head
James Jackson-South 4 weeks ago
parent
commit
a658a2cb7c
  1. 23
      src/ImageSharp/Formats/Heif/Av1/Entropy/Av1Distribution.cs

23
src/ImageSharp/Formats/Heif/Av1/Entropy/Av1Distribution.cs

@ -34,9 +34,9 @@ internal sealed class Av1Distribution
private const int ProbabilityBitCount = 15;
/// <summary>
/// The inverse cumulative thresholds followed by the required zero sentinel.
/// The Q15 inverse cumulative thresholds followed by the required zero sentinel.
/// </summary>
private InlineArray16<uint> probabilities;
private InlineArray16<ushort> probabilities;
/// <summary>
/// The symbol-count contribution to the adaptive update rate.
@ -273,13 +273,13 @@ internal sealed class Av1Distribution
/// <param name="speed">The symbol-count contribution to the update rate.</param>
private Av1Distribution(ReadOnlySpan<uint> props, int speed)
{
Span<uint> probabilities = this.probabilities;
Span<ushort> probabilities = this.probabilities;
// AV1 range coding consumes inverse cumulative thresholds. The defaults are written in the more readable
// forward form, so convert every real threshold while leaving the final zero sentinel untouched.
// Q15 thresholds range from zero through 32768, including zero-mass intervals, and fit in unsigned 16-bit storage.
// Convert the forward defaults before narrowing; the final zero sentinel is already initialized.
for (int i = 0; i < props.Length - 1; i++)
{
probabilities[i] = ProbabilityTop - props[i];
probabilities[i] = (ushort)(ProbabilityTop - props[i]);
}
this.NumberOfSymbols = props.Length;
@ -292,8 +292,8 @@ internal sealed class Av1Distribution
/// <param name="source">The distribution state to copy.</param>
private Av1Distribution(Av1Distribution source)
{
ReadOnlySpan<uint> sourceProbabilities = source.probabilities;
Span<uint> probabilities = this.probabilities;
ReadOnlySpan<ushort> sourceProbabilities = source.probabilities;
Span<ushort> probabilities = this.probabilities;
sourceProbabilities[..source.NumberOfSymbols].CopyTo(probabilities);
// The adaptation rate depends on both the alphabet size and prior update count, so copying only the
@ -329,8 +329,8 @@ internal sealed class Av1Distribution
{
// Entropy contexts are created from the same fixed default table shape. Copy only mutable state so resetting a
// working tile never allocates or replaces the distribution objects referenced by the symbol decoder.
ReadOnlySpan<uint> sourceProbabilities = source.probabilities;
Span<uint> probabilities = this.probabilities;
ReadOnlySpan<ushort> sourceProbabilities = source.probabilities;
Span<ushort> probabilities = this.probabilities;
sourceProbabilities[..source.NumberOfSymbols].CopyTo(probabilities);
this.updateCount = source.updateCount;
}
@ -402,7 +402,8 @@ internal sealed class Av1Distribution
int tmp = ProbabilityTop;
// Switching tmp to zero at the observed symbol moves the thresholds on either side toward the sample while
// preserving their inverse-cumulative ordering in one pass.
// preserving their inverse-cumulative ordering in one pass. Arithmetic stays wide until the stored update;
// each step moves toward zero or 32768, so narrowing cannot discard a significant probability bit.
for (int i = 0; i < this.NumberOfSymbols - 1; i++)
{
tmp = i == value ? 0 : tmp;

Loading…
Cancel
Save