ryujinx/Ryujinx.Graphics.Nvdec/VDec/VpxRangeEncoder.cs
2020-01-09 02:13:00 +01:00

134 lines
3.6 KiB
C#

using System.IO;
namespace Ryujinx.Graphics.VDec
{
class VpxRangeEncoder
{
private const int HalfProbability = 128;
private static readonly int[] NormLut = new int[]
{
0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
private Stream _baseStream;
private uint _lowValue;
private uint _range;
private int _count;
public VpxRangeEncoder(Stream baseStream)
{
_baseStream = baseStream;
_range = 0xff;
_count = -24;
Write(false);
}
public void WriteByte(byte value)
{
Write(value, 8);
}
public void Write(int value, int valueSize)
{
for (int bit = valueSize - 1; bit >= 0; bit--)
{
Write(((value >> bit) & 1) != 0);
}
}
public void Write(bool bit)
{
Write(bit, HalfProbability);
}
public void Write(bool bit, int probability)
{
uint range = _range;
uint split = 1 + (((range - 1) * (uint)probability) >> 8);
range = split;
if (bit)
{
_lowValue += split;
range = _range - split;
}
int shift = NormLut[range];
range <<= shift;
_count += shift;
if (_count >= 0)
{
int offset = shift - _count;
if (((_lowValue << (offset - 1)) >> 31) != 0)
{
long currentPos = _baseStream.Position;
_baseStream.Seek(-1, SeekOrigin.Current);
while (_baseStream.Position >= 0 && PeekByte() == 0xff)
{
_baseStream.WriteByte(0);
_baseStream.Seek(-2, SeekOrigin.Current);
}
_baseStream.WriteByte((byte)(PeekByte() + 1));
_baseStream.Seek(currentPos, SeekOrigin.Begin);
}
_baseStream.WriteByte((byte)(_lowValue >> (24 - offset)));
_lowValue <<= offset;
shift = _count;
_lowValue &= 0xffffff;
_count -= 8;
}
_lowValue <<= shift;
_range = range;
}
private byte PeekByte()
{
byte value = (byte)_baseStream.ReadByte();
_baseStream.Seek(-1, SeekOrigin.Current);
return value;
}
public void End()
{
for (int index = 0; index < 32; index++)
{
Write(false);
}
}
}
}