diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1Distribution.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1Distribution.cs
index 5e00c837c4..c645520d02 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1Distribution.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1Distribution.cs
@@ -4,90 +4,260 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Entropy;
///
-/// Class representing the probability distribution used for symbol coding.
+/// Stores and adapts an AV1 inverse cumulative distribution used by the range coder.
///
internal class Av1Distribution
{
+ ///
+ /// The exclusive upper bound of the Q15 probability domain.
+ ///
internal const int ProbabilityTop = 1 << ProbabilityBitCount;
+
+ ///
+ /// The minimum sub-range reserved for each symbol during range coding.
+ ///
internal const int ProbabilityMinimum = 4;
+
+ ///
+ /// The shift that converts stored Q15 cumulative values to the range-coder precision.
+ ///
internal const int CdfShift = 15 - ProbabilityBitCount;
+
+ ///
+ /// The precision reduction applied before multiplying a cumulative value by the coding range.
+ ///
internal const int ProbabilityShift = 6;
+ ///
+ /// The number of fractional bits in a stored cumulative probability.
+ ///
private const int ProbabilityBitCount = 15;
+ ///
+ /// The inverse cumulative thresholds followed by the required zero sentinel.
+ ///
private readonly uint[] probabilities;
+
+ ///
+ /// The symbol-count contribution to the adaptive update rate.
+ ///
private readonly int speed;
+
+ ///
+ /// The capped number of observations already incorporated into this distribution.
+ ///
private int updateCount;
+ ///
+ /// Initializes a new instance of the class for a binary alphabet.
+ ///
+ /// The cumulative threshold following symbol zero.
public Av1Distribution(uint p0)
: this([p0, 0], 1)
{
}
+ ///
+ /// Initializes a new instance of the class for a three-symbol alphabet.
+ ///
+ /// The cumulative threshold following symbol zero.
+ /// The cumulative threshold following symbol one.
public Av1Distribution(uint p0, uint p1)
: this([p0, p1, 0], 1)
{
}
+ ///
+ /// Initializes a new instance of the class for a four-symbol alphabet.
+ ///
+ /// The cumulative threshold following symbol zero.
+ /// The cumulative threshold following symbol one.
+ /// The cumulative threshold following symbol two.
public Av1Distribution(uint p0, uint p1, uint p2)
: this([p0, p1, p2, 0], 2)
{
}
+ ///
+ /// Initializes a new instance of the class for a five-symbol alphabet.
+ ///
+ /// The cumulative threshold following symbol zero.
+ /// The cumulative threshold following symbol one.
+ /// The cumulative threshold following symbol two.
+ /// The cumulative threshold following symbol three.
public Av1Distribution(uint p0, uint p1, uint p2, uint p3)
: this([p0, p1, p2, p3, 0], 2)
{
}
+ ///
+ /// Initializes a new instance of the class for a six-symbol alphabet.
+ ///
+ /// The cumulative threshold following symbol zero.
+ /// The cumulative threshold following symbol one.
+ /// The cumulative threshold following symbol two.
+ /// The cumulative threshold following symbol three.
+ /// The cumulative threshold following symbol four.
public Av1Distribution(uint p0, uint p1, uint p2, uint p3, uint p4)
: this([p0, p1, p2, p3, p4, 0], 2)
{
}
+ ///
+ /// Initializes a new instance of the class for a seven-symbol alphabet.
+ ///
+ /// The cumulative threshold following symbol zero.
+ /// The cumulative threshold following symbol one.
+ /// The cumulative threshold following symbol two.
+ /// The cumulative threshold following symbol three.
+ /// The cumulative threshold following symbol four.
+ /// The cumulative threshold following symbol five.
public Av1Distribution(uint p0, uint p1, uint p2, uint p3, uint p4, uint p5)
: this([p0, p1, p2, p3, p4, p5, 0], 2)
{
}
+ ///
+ /// Initializes a new instance of the class for an eight-symbol alphabet.
+ ///
+ /// The cumulative threshold following symbol zero.
+ /// The cumulative threshold following symbol one.
+ /// The cumulative threshold following symbol two.
+ /// The cumulative threshold following symbol three.
+ /// The cumulative threshold following symbol four.
+ /// The cumulative threshold following symbol five.
+ /// The cumulative threshold following symbol six.
public Av1Distribution(uint p0, uint p1, uint p2, uint p3, uint p4, uint p5, uint p6)
: this([p0, p1, p2, p3, p4, p5, p6, 0], 2)
{
}
+ ///
+ /// Initializes a new instance of the class for a nine-symbol alphabet.
+ ///
+ /// The cumulative threshold following symbol zero.
+ /// The cumulative threshold following symbol one.
+ /// The cumulative threshold following symbol two.
+ /// The cumulative threshold following symbol three.
+ /// The cumulative threshold following symbol four.
+ /// The cumulative threshold following symbol five.
+ /// The cumulative threshold following symbol six.
+ /// The cumulative threshold following symbol seven.
public Av1Distribution(uint p0, uint p1, uint p2, uint p3, uint p4, uint p5, uint p6, uint p7)
: this([p0, p1, p2, p3, p4, p5, p6, p7, 0], 2)
{
}
+ ///
+ /// Initializes a new instance of the class for a ten-symbol alphabet.
+ ///
+ /// The cumulative threshold following symbol zero.
+ /// The cumulative threshold following symbol one.
+ /// The cumulative threshold following symbol two.
+ /// The cumulative threshold following symbol three.
+ /// The cumulative threshold following symbol four.
+ /// The cumulative threshold following symbol five.
+ /// The cumulative threshold following symbol six.
+ /// The cumulative threshold following symbol seven.
+ /// The cumulative threshold following symbol eight.
public Av1Distribution(uint p0, uint p1, uint p2, uint p3, uint p4, uint p5, uint p6, uint p7, uint p8)
: this([p0, p1, p2, p3, p4, p5, p6, p7, p8, 0], 2)
{
}
+ ///
+ /// Initializes a new instance of the class for an eleven-symbol alphabet.
+ ///
+ /// The cumulative threshold following symbol zero.
+ /// The cumulative threshold following symbol one.
+ /// The cumulative threshold following symbol two.
+ /// The cumulative threshold following symbol three.
+ /// The cumulative threshold following symbol four.
+ /// The cumulative threshold following symbol five.
+ /// The cumulative threshold following symbol six.
+ /// The cumulative threshold following symbol seven.
+ /// The cumulative threshold following symbol eight.
+ /// The cumulative threshold following symbol nine.
public Av1Distribution(uint p0, uint p1, uint p2, uint p3, uint p4, uint p5, uint p6, uint p7, uint p8, uint p9)
: this([p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, 0], 2)
{
}
+ ///
+ /// Initializes a new instance of the class for a thirteen-symbol alphabet.
+ ///
+ /// The cumulative threshold following symbol zero.
+ /// The cumulative threshold following symbol one.
+ /// The cumulative threshold following symbol two.
+ /// The cumulative threshold following symbol three.
+ /// The cumulative threshold following symbol four.
+ /// The cumulative threshold following symbol five.
+ /// The cumulative threshold following symbol six.
+ /// The cumulative threshold following symbol seven.
+ /// The cumulative threshold following symbol eight.
+ /// The cumulative threshold following symbol nine.
+ /// The cumulative threshold following symbol ten.
+ /// The cumulative threshold following symbol eleven.
public Av1Distribution(uint p0, uint p1, uint p2, uint p3, uint p4, uint p5, uint p6, uint p7, uint p8, uint p9, uint p10, uint p11)
: this([p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, 0], 2)
{
}
+ ///
+ /// Initializes a new instance of the class for a fourteen-symbol alphabet.
+ ///
+ /// The cumulative threshold following symbol zero.
+ /// The cumulative threshold following symbol one.
+ /// The cumulative threshold following symbol two.
+ /// The cumulative threshold following symbol three.
+ /// The cumulative threshold following symbol four.
+ /// The cumulative threshold following symbol five.
+ /// The cumulative threshold following symbol six.
+ /// The cumulative threshold following symbol seven.
+ /// The cumulative threshold following symbol eight.
+ /// The cumulative threshold following symbol nine.
+ /// The cumulative threshold following symbol ten.
+ /// The cumulative threshold following symbol eleven.
+ /// The cumulative threshold following symbol twelve.
public Av1Distribution(uint p0, uint p1, uint p2, uint p3, uint p4, uint p5, uint p6, uint p7, uint p8, uint p9, uint p10, uint p11, uint p12)
: this([p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, 0], 2)
{
}
+ ///
+ /// Initializes a new instance of the class for a sixteen-symbol alphabet.
+ ///
+ /// The cumulative threshold following symbol zero.
+ /// The cumulative threshold following symbol one.
+ /// The cumulative threshold following symbol two.
+ /// The cumulative threshold following symbol three.
+ /// The cumulative threshold following symbol four.
+ /// The cumulative threshold following symbol five.
+ /// The cumulative threshold following symbol six.
+ /// The cumulative threshold following symbol seven.
+ /// The cumulative threshold following symbol eight.
+ /// The cumulative threshold following symbol nine.
+ /// The cumulative threshold following symbol ten.
+ /// The cumulative threshold following symbol eleven.
+ /// The cumulative threshold following symbol twelve.
+ /// The cumulative threshold following symbol thirteen.
+ /// The cumulative threshold following symbol fourteen.
public Av1Distribution(uint p0, uint p1, uint p2, uint p3, uint p4, uint p5, uint p6, uint p7, uint p8, uint p9, uint p10, uint p11, uint p12, uint p13, uint p14)
: this([p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, 0], 2)
{
}
+ ///
+ /// Initializes a new instance of the class from forward cumulative thresholds.
+ ///
+ /// The forward Q15 thresholds followed by a zero sentinel slot.
+ /// The symbol-count contribution to the update rate.
private Av1Distribution(uint[] props, int speed)
{
- // this.probabilities = props;
this.probabilities = new uint[props.Length];
+
+ // 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.
for (int i = 0; i < props.Length - 1; i++)
{
this.probabilities[i] = ProbabilityTop - props[i];
@@ -97,18 +267,33 @@ internal class Av1Distribution
this.speed = speed;
}
+ ///
+ /// Gets the number of symbols represented by the distribution.
+ ///
public int NumberOfSymbols { get; }
+ ///
+ /// Gets an inverse cumulative threshold by symbol index.
+ ///
+ /// The zero-based threshold index.
+ /// The Q15 inverse cumulative threshold.
public uint this[int index] => this.probabilities[index];
+ ///
+ /// Adapts the cumulative thresholds after coding one symbol.
+ ///
+ /// The zero-based symbol that was coded.
public void Update(int value)
{
+ // AV1 slows adaptation after 16 and 32 observations. The symbol-count term is precomputed by each overload
+ // because every distribution has a fixed alphabet size.
int rate15 = this.updateCount > 15 ? 1 : 0;
int rate31 = this.updateCount > 31 ? 1 : 0;
- int rate = 3 + rate15 + rate31 + this.speed; // + get_msb(nsymbs);
+ int rate = 3 + rate15 + rate31 + this.speed;
int tmp = ProbabilityTop;
- // Single loop (faster)
+ // 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.
for (int i = 0; i < this.NumberOfSymbols - 1; i++)
{
tmp = i == value ? 0 : tmp;
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolReader.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolReader.cs
index 9f61999abd..19e4284575 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolReader.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolReader.cs
@@ -3,31 +3,54 @@
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Entropy;
+///
+/// Reads AV1 literals and adaptively coded symbols from one bounded entropy-coded byte span.
+///
internal ref struct Av1SymbolReader
{
+ ///
+ /// The number of bits in the range-decoder code-value window.
+ ///
private const int DecoderWindowsSize = 32;
+
+ ///
+ /// The synthetic count used after the bounded input has been exhausted and zero padding begins.
+ ///
private const int LotsOfBits = 0x4000;
+ ///
+ /// The bounded entropy-coded bytes available to this reader.
+ ///
private readonly Span buffer;
+
+ ///
+ /// The next byte position to load into the code-value window.
+ ///
private int position;
- /*
- * The difference between the high end of the current range, (low + rng), and
- * the coded value, minus 1.
- * This stores up to OD_EC_WINDOW_SIZE bits of that difference, but the
- * decoder only uses the top 16 bits of the window to decode the next symbol.
- * As we shift up during renormalization, if we don't have enough bits left in
- * the window to fill the top 16, we'll read in more bits of the coded
- * value.
- */
+ ///
+ /// The difference between the upper end of the current range and the coded value, minus one.
+ ///
+ ///
+ /// The decoder compares the upper 16 bits. Renormalization shifts consumed bits out and refills the lower portion
+ /// from so the comparison remains aligned with .
+ ///
private uint difference;
- // The number of values in the current range.
+ ///
+ /// The number of code values in the current normalized interval.
+ ///
private uint range;
- // The number of bits in the current value.
+ ///
+ /// The number of buffered bits below the 16-bit comparison window.
+ ///
private int count;
+ ///
+ /// Initializes a new instance of the struct over one entropy-coded span.
+ ///
+ /// The bounded entropy-coded bytes.
public Av1SymbolReader(Span span)
{
this.buffer = span;
@@ -38,15 +61,25 @@ internal ref struct Av1SymbolReader
this.Refill();
}
+ ///
+ /// Reads one symbol and adapts its distribution.
+ ///
+ /// The inverse cumulative distribution for the symbol alphabet.
+ /// The decoded zero-based symbol.
public int ReadSymbol(Av1Distribution distribution)
{
int value = this.DecodeIntegerQ15(distribution);
- // UpdateCdf(probabilities, value, numberOfSymbols);
+ // Decoder and encoder must adapt after the same symbol so their subsequent intervals remain identical.
distribution.Update(value);
return value;
}
+ ///
+ /// Reads an unsigned literal in most-significant-bit-first order.
+ ///
+ /// The number of literal bits to read.
+ /// The decoded literal.
public int ReadLiteral(int bitCount)
{
const uint prob = (0x7FFFFFU - (128 << 15) + 128) >> 8;
@@ -66,6 +99,7 @@ internal ref struct Av1SymbolReader
/// Decode a single binary value.
///
/// The probability that the bit is one, scaled by 32768.
+ /// The decoded binary value.
private bool DecodeBoolQ15(uint frequency)
{
uint dif;
@@ -75,13 +109,11 @@ internal ref struct Av1SymbolReader
uint v;
bool ret;
- // assert(0 < f);
- // assert(f < 32768U);
dif = this.difference;
range = this.range;
- // assert(dif >> (DecoderWindowsSize - 16) < r);
- // assert(32768U <= r);
+ // Reserve a minimum interval for both outcomes after reducing the Q15 frequency to the range-coder
+ // multiplication precision. This is the same rounding model used by Av1SymbolWriter.
v = ((range >> 8) * (frequency >> Av1Distribution.ProbabilityShift)) >> (7 - Av1Distribution.ProbabilityShift);
v += Av1Distribution.ProbabilityMinimum;
vw = v << (DecoderWindowsSize - 16);
@@ -146,16 +178,14 @@ internal ref struct Av1SymbolReader
/// has value between 32768 and 65536 (reading more bytes from the stream into dif if
/// necessary), and stores them back in the decoder context.
///
+ /// The updated code-value difference.
+ /// The updated coding interval width.
private void Normalize(uint dif, uint rng)
{
- int d;
-
- // assert(rng <= 65535U);
- /*The number of leading zeros in the 16-bit binary representation of rng.*/
- d = 15 - Av1Math.MostSignificantBit(rng);
- /*d bits in dec->dif are consumed.*/
+ // Shifting by the leading-zero count restores the interval to [32768, 65536) and consumes the same number of
+ // code-value bits. Adding one before the shift preserves the decoder's difference-minus-one representation.
+ int d = 15 - Av1Math.MostSignificantBit(rng);
this.count -= d;
- /*This is equivalent to shifting in 1's instead of 0's.*/
this.difference = ((dif + 1) << d) - 1;
this.range = rng << d;
if (this.count < 0)
@@ -164,19 +194,20 @@ internal ref struct Av1SymbolReader
}
}
+ ///
+ /// Loads whole bytes into the lower portion of the code-value window after renormalization.
+ ///
private void Refill()
{
- int s;
uint dif = this.difference;
int cnt = this.count;
int position = this.position;
int end = this.buffer.Length;
- s = DecoderWindowsSize - 9 - (cnt + 15);
+ int s = DecoderWindowsSize - 9 - (cnt + 15);
for (; s >= 0 && position < end; s -= 8, position++)
{
- /*Each time a byte is inserted into the window (dif), bptr advances and cnt
- is incremented by 8, so the total number of consumed bits (the return
- value of od_ec_dec_tell) does not change.*/
+ // XOR inserts a source byte into the difference-minus-one representation. Advancing both the byte
+ // position and buffered-bit count leaves the logical number of consumed bits unchanged.
DebugGuard.MustBeLessThan(s, DecoderWindowsSize - 8, nameof(s));
dif ^= (uint)this.buffer[position] << s;
cnt += 8;
@@ -184,19 +215,8 @@ internal ref struct Av1SymbolReader
if (position >= end)
{
- /*
- * We've reached the end of the buffer. It is perfectly valid for us to need
- * to fill the window with additional bits past the end of the buffer (and
- * this happens in normal operation). These bits should all just be taken
- * as zero. But we cannot increment bptr past 'end' (this is undefined
- * behavior), so we start to increment dec->tell_offs. We also don't want
- * to keep testing bptr against 'end', so we set cnt to OD_EC_LOTS_OF_BITS
- * and adjust dec->tell_offs so that the total number of unconsumed bits in
- * the window (dec->cnt - dec->tell_offs) does not change. This effectively
- * puts lots of zero bits into the window, and means we won't try to refill
- * it from the buffer for a very long time (at which point we'll put lots
- * of zero bits into the window again).
- */
+ // AV1 range decoding permits the final interval to consume implicit zero padding. A large count models
+ // that padding without advancing beyond the bounded source span or repeatedly attempting to refill it.
cnt = LotsOfBits;
}
diff --git a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolWriter.cs b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolWriter.cs
index 5c2702ad3c..913228cf1e 100644
--- a/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolWriter.cs
+++ b/src/ImageSharp/Formats/Heif/Av1/Entropy/Av1SymbolWriter.cs
@@ -6,28 +6,73 @@ using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Entropy;
+///
+/// Writes AV1 literals and adaptively coded symbols to a range-coded byte sequence.
+///
internal class Av1SymbolWriter : IDisposable
{
+ ///
+ /// The lower endpoint of the current coding interval.
+ ///
private uint low;
+
+ ///
+ /// The width of the current normalized coding interval.
+ ///
private uint rng = 0x8000U;
- // Count is initialized to -9 so that it crosses zero after we've accumulated one byte + one carry bit.
+ ///
+ /// The number of accumulated bits relative to the next byte-and-carry flush boundary.
+ ///
+ ///
+ /// The initial value of -9 crosses zero after one output byte and its carry bit have accumulated.
+ ///
private int cnt = -9;
+
+ ///
+ /// The configuration that supplies output allocation.
+ ///
private readonly Configuration configuration;
+
+ ///
+ /// The pre-carry output values accumulated during renormalization.
+ ///
private readonly AutoExpandingMemory memory;
+
+ ///
+ /// The next pre-carry output position.
+ ///
private int position;
+ ///
+ /// Initializes a new instance of the class with an estimated output size.
+ ///
+ /// The configuration that supplies output allocation.
+ /// The estimated encoded size in bytes.
public Av1SymbolWriter(Configuration configuration, int initialSize)
{
this.configuration = configuration;
this.memory = new AutoExpandingMemory(configuration, (initialSize + 1) >> 1);
}
+ ///
+ /// Releases the expandable pre-carry buffer.
+ ///
public void Dispose() => this.memory.Dispose();
+ ///
+ /// Writes one binary symbol and adapts its distribution.
+ ///
+ /// The binary symbol.
+ /// The inverse cumulative distribution for the binary alphabet.
public void WriteSymbol(bool symbol, Av1Distribution distribution)
=> this.WriteSymbol(symbol ? 1 : 0, distribution);
+ ///
+ /// Writes one symbol and adapts its distribution.
+ ///
+ /// The zero-based symbol.
+ /// The inverse cumulative distribution for the symbol alphabet.
public void WriteSymbol(int symbol, Av1Distribution distribution)
{
DebugGuard.MustBeGreaterThanOrEqualTo(symbol, 0, nameof(symbol));
@@ -38,8 +83,17 @@ internal class Av1SymbolWriter : IDisposable
distribution.Update(symbol);
}
+ ///
+ /// Writes one equiprobable literal bit.
+ ///
+ /// The literal bit.
public void WriteLiteral(bool value) => this.WriteLiteral(value ? 1u : 0u, 1);
+ ///
+ /// Writes the requested low-order bits in most-significant-bit-first order.
+ ///
+ /// The unsigned literal value.
+ /// The number of low-order bits to write.
public void WriteLiteral(uint value, int bitCount)
{
const uint p = 0x4000U; // (0x7FFFFFU - (128 << 15) + 128) >> 8;
@@ -50,10 +104,14 @@ internal class Av1SymbolWriter : IDisposable
}
}
+ ///
+ /// Terminates the range-coded sequence and propagates pending carries into an owned byte buffer.
+ ///
+ /// An owner containing the shortest byte sequence that preserves every encoded symbol.
public IMemoryOwner Exit()
{
- // We output the minimum number of bits that ensures that the symbols encoded
- // thus far will be decoded correctly regardless of the bits that follow.
+ // Round the low endpoint into the current interval so the emitted prefix selects every symbol encoded so far
+ // regardless of the bits that follow it.
uint l = this.low;
int c = this.cnt;
int pos = this.position;
@@ -80,7 +138,8 @@ internal class Av1SymbolWriter : IDisposable
c = Math.Max((s + 7) >> 3, 0);
IMemoryOwner output = this.configuration.MemoryAllocator.Allocate(pos + c);
- // Perform carry propagation.
+ // Pre-carry values use 16-bit elements so a byte plus a propagated carry can coexist. Walking backwards folds
+ // each carry into the preceding byte without shifting the buffered sequence.
Span outputSlice = output.GetSpan()[(output.Length() - pos)..];
c = 0;
while (pos > 0)
@@ -109,6 +168,9 @@ internal class Av1SymbolWriter : IDisposable
l = this.low;
r = this.rng;
DebugGuard.MustBeGreaterThanOrEqualTo(r, 32768U, nameof(r));
+
+ // Reduce the Q15 frequency to the range-coder multiplication precision and retain a nonzero interval for
+ // both outcomes. Av1SymbolReader applies the identical rounding model.
v = ((r >> 8) * (frequency >> Av1Distribution.ProbabilityShift)) >> (7 - Av1Distribution.ProbabilityShift);
v += Av1Distribution.ProbabilityMinimum;
if (val)
@@ -136,6 +198,13 @@ internal class Av1SymbolWriter : IDisposable
private void EncodeIntegerQ15(int symbol, Av1Distribution distribution)
=> this.EncodeIntegerQ15(symbol > 0 ? distribution[symbol - 1] : Av1Distribution.ProbabilityTop, distribution[symbol], symbol, distribution.NumberOfSymbols);
+ ///
+ /// Narrows the coding interval to one symbol's inverse-cumulative bounds.
+ ///
+ /// The inverse cumulative threshold preceding the symbol.
+ /// The inverse cumulative threshold following the symbol.
+ /// The zero-based symbol.
+ /// The size of the symbol alphabet.
private void EncodeIntegerQ15(uint lowFrequency, uint highFrequency, int symbol, int numberOfSymbols)
{
const int totalShift = 7 - Av1Distribution.ProbabilityShift - Av1Distribution.CdfShift;
@@ -182,11 +251,9 @@ internal class Av1SymbolWriter : IDisposable
DebugGuard.MustBeLessThanOrEqualTo(rng, 65535U, nameof(rng));
d = 15 - Av1Math.MostSignificantBit(rng);
s = c + d;
- /*TODO: Right now we flush every time we have at least one byte available.
- Instead we should use an OdEcWindow and flush right before we're about to
- shift bits off the end of the window.
- For a 32-bit window this is about the same amount of work, but for a 64-bit
- window it should be a fair win.*/
+
+ // The 32-bit low endpoint is flushed whenever a byte becomes available. Retaining pre-carry values as
+ // ushort elements defers carry propagation until Exit without requiring a separate wider coding window.
if (s >= 0)
{
uint m;