VIDEO: Avoid 64-bit math in Smacker bitstreams

This commit is contained in:
Cameron Cawley 2022-06-04 18:32:59 +01:00 committed by Eugene Sandulenko
parent d3069f84e8
commit 9a71eb1a6d
3 changed files with 57 additions and 47 deletions

View File

@ -51,13 +51,13 @@ namespace Common {
* for valueBits, isLE and isMSB2LSB, reads 32-bit little-endian values
* from the data stream and hands out the bits in the order of LSB to MSB.
*/
template<class STREAM, int valueBits, bool isLE, bool MSB2LSB>
template<class STREAM, typename CONTAINER, int valueBits, bool isLE, bool MSB2LSB>
class BitStreamImpl {
private:
STREAM *_stream; //!< The input stream.
DisposeAfterUse::Flag _disposeAfterUse; //!< Whether to delete the stream on destruction.
uint64 _bitContainer; //!< The currently available bits.
CONTAINER _bitContainer; //!< The currently available bits.
uint8 _bitsLeft; //!< Number of bits currently left in the bit container.
uint32 _size; //!< Total bit stream size (in bits).
uint32 _pos; //!< Current bit stream position (in bits).
@ -88,7 +88,7 @@ private:
FORCEINLINE void fillContainer(size_t min) {
while (_bitsLeft < min) {
uint64 data;
CONTAINER data;
if (_pos + _bitsLeft + valueBits <= _size) {
data = readData();
} else {
@ -102,7 +102,7 @@ private:
// Move the data value to the right position in the bit container
if (MSB2LSB)
_bitContainer |= data << (64 - valueBits - _bitsLeft);
_bitContainer |= data << ((sizeof(_bitContainer) * 8) - valueBits - _bitsLeft);
else
_bitContainer |= data << _bitsLeft;
@ -111,11 +111,11 @@ private:
}
/** Get @p n bits from the bit container. */
FORCEINLINE static uint32 getNBits(uint64 value, size_t n) {
FORCEINLINE static uint32 getNBits(CONTAINER value, size_t n) {
if (n == 0)
return 0;
const size_t toShift = 64 - n;
const size_t toShift = (sizeof(value) * 8) - n;
if (MSB2LSB)
return value >> toShift;
@ -281,6 +281,11 @@ public:
/** Skip the specified number of bits. */
void skip(uint32 n) {
if (n >= _bitsLeft) {
n -= _bitsLeft;
skipBits(_bitsLeft);
}
while (n > 32) {
fillContainer(32);
skipBits(32);
@ -300,12 +305,12 @@ public:
}
/** Return the stream position in bits. */
uint64 pos() const {
uint32 pos() const {
return _pos;
}
/** Return the stream size in bits. */
uint64 size() const {
uint32 size() const {
return _size;
}
@ -360,11 +365,11 @@ public:
return false;
}
int64 pos() const {
uint32 pos() const {
return _pos;
}
int64 size() const {
uint32 size() const {
return _size;
}
@ -469,52 +474,52 @@ public:
*/
/** 8-bit data, MSB to LSB. */
typedef BitStreamImpl<SeekableReadStream, 8, false, true > BitStream8MSB;
typedef BitStreamImpl<SeekableReadStream, uint64, 8, false, true > BitStream8MSB;
/** 8-bit data, LSB to MSB. */
typedef BitStreamImpl<SeekableReadStream, 8, false, false> BitStream8LSB;
typedef BitStreamImpl<SeekableReadStream, uint64, 8, false, false> BitStream8LSB;
/** 16-bit little-endian data, MSB to LSB. */
typedef BitStreamImpl<SeekableReadStream, 16, true , true > BitStream16LEMSB;
typedef BitStreamImpl<SeekableReadStream, uint64, 16, true , true > BitStream16LEMSB;
/** 16-bit little-endian data, LSB to MSB. */
typedef BitStreamImpl<SeekableReadStream, 16, true , false> BitStream16LELSB;
typedef BitStreamImpl<SeekableReadStream, uint64, 16, true , false> BitStream16LELSB;
/** 16-bit big-endian data, MSB to LSB. */
typedef BitStreamImpl<SeekableReadStream, 16, false, true > BitStream16BEMSB;
typedef BitStreamImpl<SeekableReadStream, uint64, 16, false, true > BitStream16BEMSB;
/** 16-bit big-endian data, LSB to MSB. */
typedef BitStreamImpl<SeekableReadStream, 16, false, false> BitStream16BELSB;
typedef BitStreamImpl<SeekableReadStream, uint64, 16, false, false> BitStream16BELSB;
/** 32-bit little-endian data, MSB to LSB. */
typedef BitStreamImpl<SeekableReadStream, 32, true , true > BitStream32LEMSB;
typedef BitStreamImpl<SeekableReadStream, uint64, 32, true , true > BitStream32LEMSB;
/** 32-bit little-endian data, LSB to MSB. */
typedef BitStreamImpl<SeekableReadStream, 32, true , false> BitStream32LELSB;
typedef BitStreamImpl<SeekableReadStream, uint64, 32, true , false> BitStream32LELSB;
/** 32-bit big-endian data, MSB to LSB. */
typedef BitStreamImpl<SeekableReadStream, 32, false, true > BitStream32BEMSB;
typedef BitStreamImpl<SeekableReadStream, uint64, 32, false, true > BitStream32BEMSB;
/** 32-bit big-endian data, LSB to MSB. */
typedef BitStreamImpl<SeekableReadStream, 32, false, false> BitStream32BELSB;
typedef BitStreamImpl<SeekableReadStream, uint64, 32, false, false> BitStream32BELSB;
/** 8-bit data, MSB to LSB. */
typedef BitStreamImpl<BitStreamMemoryStream, 8, false, true > BitStreamMemory8MSB;
typedef BitStreamImpl<BitStreamMemoryStream, uint64, 8, false, true > BitStreamMemory8MSB;
/** 8-bit data, LSB to MSB. */
typedef BitStreamImpl<BitStreamMemoryStream, 8, false, false> BitStreamMemory8LSB;
typedef BitStreamImpl<BitStreamMemoryStream, uint64, 8, false, false> BitStreamMemory8LSB;
/** 16-bit little-endian data, MSB to LSB. */
typedef BitStreamImpl<BitStreamMemoryStream, 16, true , true > BitStreamMemory16LEMSB;
typedef BitStreamImpl<BitStreamMemoryStream, uint64, 16, true , true > BitStreamMemory16LEMSB;
/** 16-bit little-endian data, LSB to MSB. */
typedef BitStreamImpl<BitStreamMemoryStream, 16, true , false> BitStreamMemory16LELSB;
typedef BitStreamImpl<BitStreamMemoryStream, uint64, 16, true , false> BitStreamMemory16LELSB;
/** 16-bit big-endian data, MSB to LSB. */
typedef BitStreamImpl<BitStreamMemoryStream, 16, false, true > BitStreamMemory16BEMSB;
typedef BitStreamImpl<BitStreamMemoryStream, uint64, 16, false, true > BitStreamMemory16BEMSB;
/** 16-bit big-endian data, LSB to MSB. */
typedef BitStreamImpl<BitStreamMemoryStream, 16, false, false> BitStreamMemory16BELSB;
typedef BitStreamImpl<BitStreamMemoryStream, uint64, 16, false, false> BitStreamMemory16BELSB;
/** 32-bit little-endian data, MSB to LSB. */
typedef BitStreamImpl<BitStreamMemoryStream, 32, true , true > BitStreamMemory32LEMSB;
typedef BitStreamImpl<BitStreamMemoryStream, uint64, 32, true , true > BitStreamMemory32LEMSB;
/** 32-bit little-endian data, LSB to MSB. */
typedef BitStreamImpl<BitStreamMemoryStream, 32, true , false> BitStreamMemory32LELSB;
typedef BitStreamImpl<BitStreamMemoryStream, uint64, 32, true , false> BitStreamMemory32LELSB;
/** 32-bit big-endian data, MSB to LSB. */
typedef BitStreamImpl<BitStreamMemoryStream, 32, false, true > BitStreamMemory32BEMSB;
typedef BitStreamImpl<BitStreamMemoryStream, uint64, 32, false, true > BitStreamMemory32BEMSB;
/** 32-bit big-endian data, LSB to MSB. */
typedef BitStreamImpl<BitStreamMemoryStream, 32, false, false> BitStreamMemory32BELSB;
typedef BitStreamImpl<BitStreamMemoryStream, uint64, 32, false, false> BitStreamMemory32BELSB;
/** @} */

View File

@ -53,9 +53,9 @@ enum SmkBlockTypes {
class SmallHuffmanTree {
public:
SmallHuffmanTree(Common::BitStreamMemory8LSB &bs);
SmallHuffmanTree(SmackerBitStream &bs);
uint16 getCode(Common::BitStreamMemory8LSB &bs);
uint16 getCode(SmackerBitStream &bs);
private:
enum {
SMK_NODE = 0x8000
@ -69,11 +69,11 @@ private:
uint16 _prefixtree[256];
byte _prefixlength[256];
Common::BitStreamMemory8LSB &_bs;
SmackerBitStream &_bs;
bool _empty;
};
SmallHuffmanTree::SmallHuffmanTree(Common::BitStreamMemory8LSB &bs)
SmallHuffmanTree::SmallHuffmanTree(SmackerBitStream &bs)
: _treeSize(0), _bs(bs), _empty(false) {
if (!_bs.getBit()) {
_empty = true;
@ -122,7 +122,7 @@ uint16 SmallHuffmanTree::decodeTree(uint32 prefix, int length) {
return r1+r2+1;
}
uint16 SmallHuffmanTree::getCode(Common::BitStreamMemory8LSB &bs) {
uint16 SmallHuffmanTree::getCode(SmackerBitStream &bs) {
if (_empty)
return 0;
@ -149,11 +149,11 @@ uint16 SmallHuffmanTree::getCode(Common::BitStreamMemory8LSB &bs) {
class BigHuffmanTree {
public:
BigHuffmanTree(Common::BitStreamMemory8LSB &bs, int allocSize);
BigHuffmanTree(SmackerBitStream &bs, int allocSize);
~BigHuffmanTree();
void reset();
uint32 getCode(Common::BitStreamMemory8LSB &bs);
uint32 getCode(SmackerBitStream &bs);
private:
enum {
SMK_NODE = 0x80000000
@ -169,13 +169,13 @@ private:
byte _prefixlength[256];
/* Used during construction */
Common::BitStreamMemory8LSB &_bs;
SmackerBitStream &_bs;
uint32 _markers[3];
SmallHuffmanTree *_loBytes;
SmallHuffmanTree *_hiBytes;
};
BigHuffmanTree::BigHuffmanTree(Common::BitStreamMemory8LSB &bs, int allocSize)
BigHuffmanTree::BigHuffmanTree(SmackerBitStream &bs, int allocSize)
: _bs(bs) {
uint32 bit = _bs.getBit();
if (!bit) {
@ -265,7 +265,7 @@ uint32 BigHuffmanTree::decodeTree(uint32 prefix, int length) {
return r1+r2+1;
}
uint32 BigHuffmanTree::getCode(Common::BitStreamMemory8LSB &bs) {
uint32 BigHuffmanTree::getCode(SmackerBitStream &bs) {
// Peeking data out of bounds is well-defined and returns 0 bits.
// This is for convenience when using speed-up techniques reading
// more bits than actually available.
@ -413,7 +413,7 @@ bool SmackerDecoder::loadStream(Common::SeekableReadStream *stream) {
byte *huffmanTrees = (byte *) malloc(_header.treesSize);
_fileStream->read(huffmanTrees, _header.treesSize);
Common::BitStreamMemory8LSB bs(new Common::BitStreamMemoryStream(huffmanTrees, _header.treesSize, DisposeAfterUse::YES), DisposeAfterUse::YES);
SmackerBitStream bs(new Common::BitStreamMemoryStream(huffmanTrees, _header.treesSize, DisposeAfterUse::YES), DisposeAfterUse::YES);
videoTrack->readTrees(bs, _header.mMapSize, _header.mClrSize, _header.fullSize, _header.typeSize);
_firstFrameStart = _fileStream->pos();
@ -539,7 +539,7 @@ void SmackerDecoder::readNextPacket() {
_fileStream->read(frameData, frameDataSize);
Common::BitStreamMemory8LSB bs(new Common::BitStreamMemoryStream(frameData, frameDataSize + 1, DisposeAfterUse::YES), DisposeAfterUse::YES);
SmackerBitStream bs(new Common::BitStreamMemoryStream(frameData, frameDataSize + 1, DisposeAfterUse::YES), DisposeAfterUse::YES);
videoTrack->decodeFrame(bs);
_fileStream->seek(startPos + frameSize);
@ -624,14 +624,14 @@ Graphics::PixelFormat SmackerDecoder::SmackerVideoTrack::getPixelFormat() const
return _surface->format;
}
void SmackerDecoder::SmackerVideoTrack::readTrees(Common::BitStreamMemory8LSB &bs, uint32 mMapSize, uint32 mClrSize, uint32 fullSize, uint32 typeSize) {
void SmackerDecoder::SmackerVideoTrack::readTrees(SmackerBitStream &bs, uint32 mMapSize, uint32 mClrSize, uint32 fullSize, uint32 typeSize) {
_MMapTree = new BigHuffmanTree(bs, mMapSize);
_MClrTree = new BigHuffmanTree(bs, mClrSize);
_FullTree = new BigHuffmanTree(bs, fullSize);
_TypeTree = new BigHuffmanTree(bs, typeSize);
}
void SmackerDecoder::SmackerVideoTrack::decodeFrame(Common::BitStreamMemory8LSB &bs) {
void SmackerDecoder::SmackerVideoTrack::decodeFrame(SmackerBitStream &bs) {
_MMapTree->reset();
_MClrTree->reset();
_FullTree->reset();
@ -853,7 +853,7 @@ Audio::AudioStream *SmackerDecoder::SmackerAudioTrack::getAudioStream() const {
}
void SmackerDecoder::SmackerAudioTrack::queueCompressedBuffer(byte *buffer, uint32 bufferSize, uint32 unpackedSize) {
Common::BitStreamMemory8LSB audioBS(new Common::BitStreamMemoryStream(buffer, bufferSize), DisposeAfterUse::YES);
SmackerBitStream audioBS(new Common::BitStreamMemoryStream(buffer, bufferSize), DisposeAfterUse::YES);
bool dataPresent = audioBS.getBit();
if (!dataPresent)

View File

@ -43,6 +43,11 @@ namespace Video {
class BigHuffmanTree;
// Because the maximum number of bits read from a bitstream is 16, and the data is 8-bit, the container only
// needs to hold up to 23 bits at any given time. As such, we use a bitstream with a 32-bit container to
// avoid the overhead of 64-bit maths on systems that don't support it natively.
typedef Common::BitStreamImpl<Common::BitStreamMemoryStream, uint32, 8, false, false> SmackerBitStream;
/**
* Decoder for Smacker v2/v4 videos.
*
@ -100,9 +105,9 @@ protected:
const byte *getPalette() const { _dirtyPalette = false; return _palette; }
bool hasDirtyPalette() const { return _dirtyPalette; }
void readTrees(Common::BitStreamMemory8LSB &bs, uint32 mMapSize, uint32 mClrSize, uint32 fullSize, uint32 typeSize);
void readTrees(SmackerBitStream &bs, uint32 mMapSize, uint32 mClrSize, uint32 fullSize, uint32 typeSize);
void increaseCurFrame() { _curFrame++; }
void decodeFrame(Common::BitStreamMemory8LSB &bs);
void decodeFrame(SmackerBitStream &bs);
void unpackPalette(Common::SeekableReadStream *stream);
Common::Rational getFrameRate() const { return _frameRate; }