Browse Source

Initial symbol parsing

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
95d9f0916a
  1. 27
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1DefaultDistributions.cs
  2. 12
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SymbolDecoder.cs
  3. 12
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SymbolEncoder.cs
  4. 243
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SymbolReader.cs
  5. 190
      src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SymbolWriter.cs
  6. 234
      tests/ImageSharp.Tests/Formats/Heif/Av1/SymbolTest.cs

27
src/ImageSharp/Formats/Heif/Av1/Symbol/Av1DefaultDistributions.cs

@ -0,0 +1,27 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
internal static class Av1DefaultDistributions
{
public static uint[] YMode => [Av1SymbolReader.CdfProbabilityTop, 0];
public static uint[] UvModeCflNotAllowed => [Av1SymbolReader.CdfProbabilityTop, 0];
public static uint[] UvModeCflAllowed => [Av1SymbolReader.CdfProbabilityTop, 0];
public static uint[] AngleDelta => [Av1SymbolReader.CdfProbabilityTop, 0];
public static uint[] IntraBlockCopy => [30531, 0, 0];
public static uint[] PartitionWidth8 => [Av1SymbolReader.CdfProbabilityTop, 0];
public static uint[] PartitionWidth16 => [Av1SymbolReader.CdfProbabilityTop, 0];
public static uint[] PartitionWidth32 => [Av1SymbolReader.CdfProbabilityTop, 0];
public static uint[] PartitionWidth64 => [Av1SymbolReader.CdfProbabilityTop, 0];
public static uint[] SegmentId => [Av1SymbolReader.CdfProbabilityTop, 0];
}

12
src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SymbolDecoder.cs

@ -0,0 +1,12 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
internal class Av1SymbolDecoder
{
private readonly uint[] tileIntraBlockCopy = Av1DefaultDistributions.IntraBlockCopy;
public bool ReadUseIntraBlockCopySymbol(ref Av1SymbolReader reader)
=> reader.ReadSymbol(this.tileIntraBlockCopy, 2) > 0;
}

12
src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SymbolEncoder.cs

@ -0,0 +1,12 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
internal class Av1SymbolEncoder
{
private readonly uint[] tileIntraBlockCopy = Av1DefaultDistributions.IntraBlockCopy;
public void WriteUseIntraBlockCopySymbol(Av1SymbolWriter writer, bool value)
=> writer.WriteSymbol(value ? 1 : 0, this.tileIntraBlockCopy, 2);
}

243
src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SymbolReader.cs

@ -0,0 +1,243 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
internal ref struct Av1SymbolReader
{
internal const int CdfProbabilityTop = 1 << CdfProbabilityBitCount;
internal const int ProbabilityMinimum = 4;
internal const int CdfShift = 15 - CdfProbabilityBitCount;
internal const int ProbabilityShift = 6;
internal static readonly int[] NumberOfSymbols2Speed = [0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2];
private const int CdfProbabilityBitCount = 15;
private const int DecoderWindowsSize = 32;
private const int LotsOfBits = 0x4000;
private readonly Span<byte> buffer;
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.
*/
private uint difference;
// The number of values in the current range.
private uint range;
// The number of bits in the current value.
private int count;
public Av1SymbolReader(Span<byte> span)
{
this.buffer = span;
this.position = 0;
this.difference = (1U << (DecoderWindowsSize - 1)) - 1;
this.range = 0x8000;
this.count = -15;
this.Refill();
}
public int ReadSymbol(uint[] probabilities, int numberOfSymbols)
{
int value = this.DecodeIntegerQ15(probabilities, numberOfSymbols);
UpdateCdf(probabilities, value, numberOfSymbols);
return value;
}
public int ReadLiteral(int bitCount)
{
const uint prob = (0x7FFFFFU - (128 << 15) + 128) >> 8;
int literal = 0;
for (int bit = bitCount - 1; bit >= 0; bit--)
{
if (this.DecodeBoolQ15(prob))
{
literal |= 1 << bit;
}
}
return literal;
}
/// <summary>
/// Decode a single binary value.
/// </summary>
/// <param name="frequency">The probability that the bit is one, scaled by 32768.</param>
private bool DecodeBoolQ15(uint frequency)
{
uint dif;
uint vw;
uint range;
uint newRange;
uint v;
bool ret;
// assert(0 < f);
// assert(f < 32768U);
dif = this.difference;
range = this.range;
// assert(dif >> (DecoderWindowsSize - 16) < r);
// assert(32768U <= r);
v = ((range >> 8) * (frequency >> ProbabilityShift)) >> (7 - ProbabilityShift);
v += ProbabilityMinimum;
vw = v << (DecoderWindowsSize - 16);
ret = true;
newRange = v;
if (dif >= vw)
{
newRange = range - v;
dif -= vw;
ret = false;
}
this.Normalize(dif, newRange);
return ret;
}
/// <summary>
/// Decodes a symbol given an inverse cumulative distribution function(CDF) table in Q15.
/// </summary>
/// <param name="probabilities">
/// CDF_PROB_TOP minus the CDF, such that symbol s falls in the range
/// [s > 0 ? (CDF_PROB_TOP - icdf[s - 1]) : 0, CDF_PROB_TOP - icdf[s]).
/// The values must be monotonically non - increasing, and icdf[nsyms - 1] must be 0.
/// </param>
/// <param name="numberOfSymbols">
/// The number of symbols in the alphabet.
/// This should be at most 16.
/// </param>
/// <returns>The decoded symbol.</returns>
private int DecodeIntegerQ15(uint[] probabilities, int numberOfSymbols)
{
uint c;
uint u;
uint v;
int ret;
uint dif = this.difference;
uint r = this.range;
int n = numberOfSymbols - 1;
DebugGuard.MustBeLessThan(dif >> (DecoderWindowsSize - 16), r, nameof(r));
DebugGuard.IsTrue(probabilities[numberOfSymbols - 1] == 0, "Last value in probability array needs to be zero.");
DebugGuard.MustBeGreaterThanOrEqualTo(r, 32768U, nameof(r));
DebugGuard.MustBeGreaterThanOrEqualTo(7 - ProbabilityShift - CdfShift, 0, nameof(CdfShift));
c = dif >> (DecoderWindowsSize - 16);
v = r;
ret = -1;
do
{
u = v;
v = ((r >> 8) * (probabilities[++ret] >> ProbabilityShift)) >> (7 - ProbabilityShift - CdfShift);
v += (uint)(ProbabilityMinimum * (n - ret));
}
while (c < v);
DebugGuard.MustBeLessThan(v, u, nameof(v));
DebugGuard.MustBeLessThan(u, r, nameof(u));
r = u - v;
dif -= v << (DecoderWindowsSize - 16);
this.Normalize(dif, r);
return ret;
}
/// <summary>
/// Takes updated dif and range values, renormalizes them so that
/// <paramref name="rng"/> has value between 32768 and 65536 (reading more bytes from the stream into dif if
/// necessary), and stores them back in the decoder context.
/// </summary>
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.*/
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)
{
this.Refill();
}
}
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);
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.*/
DebugGuard.MustBeLessThan(s, DecoderWindowsSize - 8, nameof(s));
dif ^= (uint)this.buffer[0] << s;
cnt += 8;
}
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).
*/
cnt = LotsOfBits;
}
this.difference = dif;
this.count = cnt;
this.position = position;
}
internal static void UpdateCdf(uint[] probabilities, int value, int numberOfSymbols)
{
DebugGuard.MustBeLessThan(numberOfSymbols, 17, nameof(numberOfSymbols));
int rate15 = probabilities[numberOfSymbols] > 15 ? 1 : 0;
int rate31 = probabilities[numberOfSymbols] > 31 ? 1 : 0;
int rate = 3 + rate15 + rate31 + NumberOfSymbols2Speed[numberOfSymbols]; // + get_msb(nsymbs);
int tmp = CdfProbabilityTop;
// Single loop (faster)
for (int i = 0; i < numberOfSymbols - 1; i++)
{
tmp = (i == value) ? 0 : tmp;
if (tmp < probabilities[i])
{
probabilities[i] -= (ushort)((probabilities[i] - tmp) >> rate);
}
else
{
probabilities[i] += (ushort)((tmp - probabilities[i]) >> rate);
}
}
probabilities[numberOfSymbols] = Math.Min(probabilities[numberOfSymbols]++, 32);
}
}

190
src/ImageSharp/Formats/Heif/Av1/Symbol/Av1SymbolWriter.cs

@ -0,0 +1,190 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
namespace SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
internal class Av1SymbolWriter
{
private uint low;
private uint rng = 0x8000U;
// Count is initialized to -9 so that it crosses zero after we've accumulated one byte + one carry bit.
private int cnt = -9;
private readonly Stream stream;
public Av1SymbolWriter(Stream stream) => this.stream = stream;
public void WriteSymbol(int symbol, uint[] probabilities, int numberOfSymbols)
{
DebugGuard.MustBeGreaterThanOrEqualTo(symbol, 0, nameof(symbol));
DebugGuard.MustBeLessThan(symbol, numberOfSymbols, nameof(symbol));
DebugGuard.IsTrue(probabilities[numberOfSymbols - 1] == 0, "Last entry in Probabilities table needs to be zero.");
this.EncodeIntegerQ15(symbol, probabilities, numberOfSymbols);
Av1SymbolReader.UpdateCdf(probabilities, symbol, numberOfSymbols);
}
public void WriteLiteral(uint value, int bitCount)
{
const uint p = 0x4000U; // (0x7FFFFFU - (128 << 15) + 128) >> 8;
for (int bit = bitCount - 1; bit >= 0; bit--)
{
bool bitValue = ((value >> bit) & 0x1) > 0;
this.EncodeBoolQ15(bitValue, p);
}
}
public void Exit()
{
uint m;
uint e;
uint l;
int c;
int s;
// 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.
l = this.low;
c = this.cnt;
s = 10;
m = 0x3FFFU;
e = ((l + m) & ~m) | (m + 1);
s += c;
if (s > 0)
{
uint n = (1U << (c + 16)) - 1;
do
{
this.stream.WriteByte((byte)(e >> (c + 16)));
e &= n;
s -= 8;
c -= 8;
n >>= 8;
}
while (s > 0);
}
}
/// <summary>
/// Encode a single binary value.
/// </summary>
/// <param name="val">The value to encode.</param>
/// <param name="frequency">The probability that the value is true, scaled by 32768.</param>
private void EncodeBoolQ15(bool val, uint frequency)
{
uint l;
uint r;
uint v;
DebugGuard.MustBeGreaterThan(frequency, 0U, nameof(frequency));
DebugGuard.MustBeLessThanOrEqualTo(frequency, 32768U, nameof(frequency));
l = this.low;
r = this.rng;
DebugGuard.MustBeGreaterThanOrEqualTo(r, 32768U, nameof(r));
v = ((r >> 8) * (frequency >> Av1SymbolReader.ProbabilityShift)) >> (7 - Av1SymbolReader.ProbabilityShift);
v += Av1SymbolReader.ProbabilityMinimum;
if (val)
{
l += r - v;
r = v;
}
else
{
r -= v;
}
this.Normalize(l, r);
}
/// <summary>
/// Encodes a symbol given an inverse cumulative distribution function(CDF) table in Q15.
/// </summary>
/// <param name="symbol">The value to encode.</param>
/// <param name="probabilities">
/// CDF_PROB_TOP minus the CDF, such that symbol s falls in the range
/// [s > 0 ? (CDF_PROB_TOP - icdf[s - 1]) : 0, CDF_PROB_TOP - icdf[s]).
/// The values must be monotonically non - increasing, and icdf[nsyms - 1] must be 0.
/// </param>
/// <param name="numberOfSymbols">
/// The number of symbols in the alphabet.
/// This should be at most 16.
/// </param>
private void EncodeIntegerQ15(int symbol, uint[] probabilities, int numberOfSymbols)
=> this.EncodeIntegerQ15(symbol > 0 ? probabilities[symbol - 1] : Av1SymbolReader.CdfProbabilityTop, probabilities[symbol], symbol, numberOfSymbols);
private void EncodeIntegerQ15(uint lowFrequency, uint highFrequency, int symbol, int numberOfSymbols)
{
uint l = this.low;
uint r = this.rng;
int totalShift = 7 - Av1SymbolReader.ProbabilityShift - Av1SymbolReader.CdfShift;
DebugGuard.MustBeLessThanOrEqualTo(32768U, r, nameof(r));
DebugGuard.MustBeLessThanOrEqualTo(highFrequency, lowFrequency, nameof(highFrequency));
DebugGuard.MustBeLessThanOrEqualTo(lowFrequency, 32768U, nameof(lowFrequency));
DebugGuard.MustBeGreaterThanOrEqualTo(totalShift, 0, string.Empty);
int n = numberOfSymbols - 1;
if (lowFrequency < Av1SymbolReader.CdfProbabilityTop)
{
uint u;
uint v;
u = (uint)((((r >> 8) * (lowFrequency >> Av1SymbolReader.ProbabilityShift)) >> totalShift) +
(Av1SymbolReader.ProbabilityMinimum * (n - (symbol - 1))));
v = (uint)((((r >> 8) * (highFrequency >> Av1SymbolReader.ProbabilityShift)) >> totalShift) +
(Av1SymbolReader.ProbabilityMinimum * (n - (symbol + 0))));
l += r - u;
r = u - v;
}
else
{
r -= (uint)((((r >> 8) * (highFrequency >> Av1SymbolReader.ProbabilityShift)) >> totalShift) +
(Av1SymbolReader.ProbabilityMinimum * (n - (symbol + 0))));
}
this.Normalize(l, r);
}
/// <summary>
/// Takes updated low and range values, renormalizes them so that <paramref name="rng"/>
/// lies between 32768 and 65536 (flushing bytes from low to the pre-carry buffer if necessary),
/// and stores them back in the encoder context.
/// </summary>
/// <param name="low">The new value of <see cref="low"/>.</param>
/// <param name="rng">The new value of <see cref="rng"/>.</param>
private void Normalize(uint low, uint rng)
{
int d;
int c;
int s;
c = this.cnt;
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.*/
if (s >= 0)
{
uint m;
c += 16;
m = (1U << c) - 1;
if (s >= 8)
{
this.stream.WriteByte((byte)(low >> c));
low &= m;
c -= 8;
m >>= 8;
}
this.stream.WriteByte((byte)(low >> c));
s = c + d - 24;
low &= m;
}
this.low = low << d;
this.rng = rng << d;
this.cnt = s;
}
}

234
tests/ImageSharp.Tests/Formats/Heif/Av1/SymbolTest.cs

@ -0,0 +1,234 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using Newtonsoft.Json.Linq;
using SixLabors.ImageSharp.Formats.Heif.Av1.Symbol;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
[Trait("Format", "Avif")]
public class SymbolTest
{
[Fact]
public void ReadRandomLiteral()
{
// Assign
const int bitCount = 4;
Random rand = new(bitCount);
byte[] values = Enumerable.Range(0, 100).Select(x => (byte)rand.Next(1 << bitCount)).ToArray();
Av1SymbolReader reader = new(values);
List<int> actuals = [];
// Act
for (int i = 0; i < values.Length; i++)
{
actuals.Add(reader.ReadLiteral(bitCount));
}
// Assert
Assert.True(values.Length > bitCount);
}
[Fact]
public void WriteRandomLiteral()
{
// Assign
const int bitCount = 4;
Random rand = new(bitCount);
uint[] values = Enumerable.Range(0, 100).Select(x => (uint)rand.Next(1 << bitCount)).ToArray();
MemoryStream output = new();
Av1SymbolWriter writer = new(output);
// Act
for (int i = 0; i < values.Length; i++)
{
writer.WriteLiteral(values[i], bitCount);
}
// Assert
Assert.True(output.Position > 0);
}
[Theory]
[InlineData(0, 0, 128)]
[InlineData(1, 255, 128)]
public void RawBytesFromWriteLiteral1Bit(uint value, byte exp0, byte exp1)
{
byte[] expected = [exp0, exp1];
AssertRawBytesWritten(1, value, expected);
}
[Theory]
[InlineData(0, 0, 0, 128)]
[InlineData(1, 85, 118, 192)]
[InlineData(2, 170, 165, 128)]
[InlineData(3, 255, 255, 128)]
public void RawBytesFromWriteLiteral2Bits(uint value, byte exp0, byte exp1, byte exp2)
{
byte[] expected = [exp0, exp1, exp2];
AssertRawBytesWritten(2, value, expected);
}
[Theory]
[InlineData(0, 0, 0, 0, 128)]
[InlineData(1, 36, 198, 146, 128)]
[InlineData(2, 73, 81, 182, 192)]
[InlineData(3, 109, 192, 146, 64)]
[InlineData(4, 146, 66, 73, 128)]
[InlineData(5, 182, 214, 219, 128)]
[InlineData(6, 219, 107, 109, 128)]
[InlineData(7, 255, 255, 255, 128)]
public void RawBytesFromWriteLiteral3Bits(uint value, byte exp0, byte exp1, byte exp2, byte exp3)
{
byte[] expected = [exp0, exp1, exp2, exp3];
AssertRawBytesWritten(3, value, expected);
}
[Theory]
[InlineData(0, 0, 0, 0, 0, 128)]
[InlineData(1, 17, 68, 34, 34, 128)]
[InlineData(2, 34, 86, 68, 68, 128)]
[InlineData(3, 51, 104, 102, 102, 128)]
[InlineData(4, 68, 118, 34, 34, 64)]
[InlineData(5, 85, 118, 170, 170, 64)]
[InlineData(6, 102, 119, 51, 51, 64)]
[InlineData(7, 119, 119, 187, 187, 192)]
[InlineData(8, 136, 129, 17, 17, 128)]
[InlineData(9, 153, 147, 51, 51, 128)]
[InlineData(10, 170, 165, 85, 85, 128)]
[InlineData(11, 187, 183, 119, 119, 128)]
[InlineData(12, 204, 201, 153, 153, 128)]
[InlineData(13, 221, 219, 187, 187, 128)]
[InlineData(14, 238, 237, 221, 221, 128)]
[InlineData(15, 255, 255, 255, 255, 128)]
public void RawBytesFromWriteLiteral4Bits(uint value, byte exp0, byte exp1, byte exp2, byte exp3, byte exp4)
{
byte[] expected = [exp0, exp1, exp2, exp3, exp4];
AssertRawBytesWritten(4, value, expected);
}
private static void AssertRawBytesWritten(int bitCount, uint value, byte[] expected)
{
// Assign
uint[] values = new uint[8];
Array.Fill(values, value);
MemoryStream output = new();
Av1SymbolWriter writer = new(output);
// Act
for (int i = 0; i < 8; i++)
{
writer.WriteLiteral(value, bitCount);
}
writer.Exit();
// Assert
Assert.Equal(expected, output.ToArray());
}
[Theory]
[InlineData(0, 0, 128)]
[InlineData(1, 255, 128)]
public void RawBytesReadLiteral1Bit(int value, byte exp0, byte exp1)
{
byte[] buffer = [exp0, exp1];
AssertRawBytesRead(1, buffer, value);
}
[Theory]
[InlineData(0, 0, 0, 128)]
[InlineData(1, 85, 118, 192)]
[InlineData(2, 170, 165, 128)]
[InlineData(3, 255, 255, 128)]
public void RawBytesReadLiteral2Bits(int value, byte exp0, byte exp1, byte exp2)
{
byte[] buffer = [exp0, exp1, exp2];
AssertRawBytesRead(2, buffer, value);
}
[Theory]
[InlineData(0, 0, 0, 0, 128)]
[InlineData(1, 36, 198, 146, 128)]
[InlineData(2, 73, 81, 182, 192)]
[InlineData(3, 109, 192, 146, 64)]
[InlineData(4, 146, 66, 73, 128)]
[InlineData(5, 182, 214, 219, 128)]
[InlineData(6, 219, 107, 109, 128)]
[InlineData(7, 255, 255, 255, 128)]
public void RawBytesReadLiteral3Bits(int value, byte exp0, byte exp1, byte exp2, byte exp3)
{
byte[] buffer = [exp0, exp1, exp2, exp3];
AssertRawBytesRead(3, buffer, value);
}
[Theory]
[InlineData(0, 0, 0, 0, 0, 128)]
[InlineData(1, 17, 68, 34, 34, 128)]
[InlineData(2, 34, 86, 68, 68, 128)]
[InlineData(3, 51, 104, 102, 102, 128)]
[InlineData(4, 68, 118, 34, 34, 64)]
[InlineData(5, 85, 118, 170, 170, 64)]
[InlineData(6, 102, 119, 51, 51, 64)]
[InlineData(7, 119, 119, 187, 187, 192)]
[InlineData(8, 136, 129, 17, 17, 128)]
[InlineData(9, 153, 147, 51, 51, 128)]
[InlineData(10, 170, 165, 85, 85, 128)]
[InlineData(11, 187, 183, 119, 119, 128)]
[InlineData(12, 204, 201, 153, 153, 128)]
[InlineData(13, 221, 219, 187, 187, 128)]
[InlineData(14, 238, 237, 221, 221, 128)]
[InlineData(15, 255, 255, 255, 255, 128)]
public void RawBytesReadLiteral4Bits(int value, byte exp0, byte exp1, byte exp2, byte exp3, byte exp4)
{
byte[] buffer = [exp0, exp1, exp2, exp3, exp4];
AssertRawBytesRead(4, buffer, value);
}
private static void AssertRawBytesRead(int bitCount, byte[] buffer, int expected)
{
// Assign
int[] values = new int[8];
int[] expectedValues = new int[8];
Array.Fill(expectedValues, expected);
Av1SymbolReader reader = new(buffer);
// Act
for (int i = 0; i < 8; i++)
{
values[i] = reader.ReadLiteral(bitCount);
}
// Assert
Assert.Equal(expectedValues, values);
}
[Fact]
public void RoundTripUseIntraBlockCopy()
{
// Assign
bool[] values = [true, true, false, true, false, false, false];
MemoryStream output = new(100);
Av1SymbolWriter writer = new(output);
Av1SymbolEncoder encoder = new();
Av1SymbolDecoder decoder = new();
bool[] actuals = new bool[values.Length];
// Act
foreach (bool value in values)
{
encoder.WriteUseIntraBlockCopySymbol(writer, value);
}
Av1SymbolReader reader = new(output.ToArray());
for (int i = 0; i < values.Length; i++)
{
actuals[i] = decoder.ReadUseIntraBlockCopySymbol(ref reader);
}
// Assert
Assert.Equal(values, actuals);
}
}
Loading…
Cancel
Save