Browse Source

Av1 bitstream tests

pull/2633/head
Ynse Hoornenborg 2 years ago
parent
commit
e45a41b54e
  1. 23
      src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs
  2. 56
      src/ImageSharp/Formats/Heif/Av1/Av1BitStreamWriter.cs
  3. 86
      tests/ImageSharp.Tests/Formats/Heif/Av1/Av1BitStreamTests.cs

23
src/ImageSharp/Formats/Heif/Av1/Av1BitStreamReader.cs

@ -1,11 +1,13 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers.Binary;
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
internal ref struct Av1BitStreamReader(Span<byte> data)
{
private const int WordSize = sizeof(byte);
private const int WordSize = sizeof(byte) * 8;
private const int DoubleWordSize = 2 * WordSize;
private readonly Span<byte> data = data;
private int wordPosition = 0;
@ -33,25 +35,14 @@ internal ref struct Av1BitStreamReader(Span<byte> data)
public uint ReadLiteral(int bitCount)
{
uint bits = (uint)(this.data[this.wordPosition] << this.bitOffset) >> (WordSize - bitCount);
this.bitOffset += bitCount;
while (this.bitOffset > WordSize)
{
uint nextWord = this.data[this.wordPosition + 1];
bits |= nextWord << (DoubleWordSize - bitCount);
}
if (this.bitOffset >= WordSize)
{
this.bitOffset -= WordSize;
}
return bits;
uint bits = BinaryPrimitives.ReadUInt32BigEndian(this.data[this.wordPosition..]);
this.Skip(bitCount);
return bits >> (32 - bitCount);
}
internal bool ReadBoolean()
{
bool bit = (this.data[this.wordPosition] & (1 << (WordSize - this.bitOffset))) > 0;
bool bit = (this.data[this.wordPosition] & (1 << (WordSize - this.bitOffset - 1))) > 0;
this.Skip(1);
return bit;
}

56
src/ImageSharp/Formats/Heif/Av1/Av1BitStreamWriter.cs

@ -0,0 +1,56 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Heif.Av1;
internal ref struct Av1BitStreamWriter(Stream stream)
{
private const int WordSize = sizeof(byte) * 8;
private readonly Stream stream = stream;
private byte buffer = 0;
private int bitOffset = 0;
public readonly int BitPosition => (int)(this.stream.Position * WordSize) + this.bitOffset;
public readonly int Length => (int)this.stream.Length;
public void Skip(int bitCount)
{
this.bitOffset += bitCount;
while (this.bitOffset >= WordSize)
{
this.bitOffset -= WordSize;
this.stream.WriteByte(this.buffer);
this.buffer = 0;
}
}
public void Flush()
{
this.stream.WriteByte(this.buffer);
this.bitOffset = 0;
}
public void WriteLiteral(uint value, int bitCount)
{
int shift = 24;
uint padded = value << ((32 - bitCount) - this.bitOffset);
while (bitCount >= 8)
{
byte current = (byte)(((padded >> shift) & 0xff) | this.buffer);
this.stream.WriteByte(current);
shift -= 8;
bitCount -= 8;
this.buffer = 0;
this.bitOffset = 0;
}
if (bitCount > 0)
{
this.buffer = (byte)(((padded >> shift) & 0xff) | this.buffer);
this.bitOffset += bitCount;
}
}
internal void WriteBoolean(bool value) => this.WriteLiteral(value ? 1U : 0U, 1);
}

86
tests/ImageSharp.Tests/Formats/Heif/Av1/Av1BitStreamTests.cs

@ -0,0 +1,86 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Buffers.Binary;
using SixLabors.ImageSharp.Formats.Heif.Av1;
namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1;
[Trait("Format", "Avif")]
public class Av1BitsStreamTests
{
[Theory]
[InlineData(42, new bool[] { false, false, true, false, true, false, true, false })]
[InlineData(52, new bool[] { false, false, true, true, false, true, false, false })]
public void ReadAsBoolean(int value, bool[] bits)
{
int bitCount = bits.Length;
byte[] buffer = new byte[4];
BinaryPrimitives.WriteInt32BigEndian(buffer, value << (32 - bitCount));
Av1BitStreamReader reader = new(buffer);
bool[] actual = new bool[bitCount];
for (int i = 0; i < bitCount; i++)
{
actual[i] = reader.ReadBoolean();
}
Assert.Equal(bits, actual);
}
[Theory]
[InlineData(6, 4)]
[InlineData(42, 8)]
[InlineData(52, 8)]
[InlineData(4050, 16)]
public void ReadAsLiteral(uint expected, int bitCount)
{
byte[] buffer = new byte[4];
BinaryPrimitives.WriteUInt32BigEndian(buffer, expected << (32 - bitCount));
Av1BitStreamReader reader = new(buffer);
uint actual = reader.ReadLiteral(bitCount);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(new bool[] { false, false, true, false, true, false, true, false })]
[InlineData(new bool[] { false, true, false, true })]
public void WriteAsBoolean(bool[] booleans)
{
using MemoryStream stream = new(4);
Av1BitStreamWriter writer = new(stream);
for (int i = 0; i < booleans.Length; i++)
{
writer.WriteBoolean(booleans[i]);
}
writer.Flush();
// Read the written value back.
Av1BitStreamReader reader = new(stream.GetBuffer());
bool[] actual = new bool[booleans.Length];
for (int i = 0; i < booleans.Length; i++)
{
actual[i] = reader.ReadBoolean();
}
Assert.Equal(booleans, actual);
}
[Theory]
[InlineData(6, 4)]
[InlineData(42, 8)]
[InlineData(52, 8)]
[InlineData(4050, 16)]
public void WriteAsLiteral(uint value, int bitCount)
{
using MemoryStream stream = new(4);
Av1BitStreamWriter writer = new(stream);
writer.WriteLiteral(value, bitCount);
writer.Flush();
// Read the written value back.
Av1BitStreamReader reader = new(stream.GetBuffer());
uint actual = reader.ReadLiteral(bitCount);
Assert.Equal(value, actual);
}
}
Loading…
Cancel
Save