aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/huffman.cpp71
-rw-r--r--common/huffman.h118
-rw-r--r--common/module.mk1
-rw-r--r--image/codecs/svq1.cpp12
-rw-r--r--image/codecs/svq1.h15
-rw-r--r--test/common/huffman.h51
-rw-r--r--video/bink_decoder.cpp2
-rw-r--r--video/bink_decoder.h3
-rw-r--r--video/psx_decoder.cpp8
-rw-r--r--video/psx_decoder.h7
10 files changed, 123 insertions, 165 deletions
diff --git a/common/huffman.cpp b/common/huffman.cpp
deleted file mode 100644
index 3268ddf251..0000000000
--- a/common/huffman.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-// Based on eos' Huffman code
-
-#include "common/huffman.h"
-#include "common/util.h"
-#include "common/textconsole.h"
-#include "common/bitstream.h"
-
-namespace Common {
-
-Huffman::Symbol::Symbol(uint32 c, uint32 s) : code(c), symbol(s) {
-}
-
-
-Huffman::Huffman(uint8 maxLength, uint32 codeCount, const uint32 *codes, const uint8 *lengths, const uint32 *symbols) {
- assert(codeCount > 0);
-
- assert(codes);
- assert(lengths);
-
- if (maxLength == 0)
- for (uint32 i = 0; i < codeCount; i++)
- maxLength = MAX(maxLength, lengths[i]);
-
- assert(maxLength <= 32);
-
- _codes.resize(maxLength);
- _symbols.resize(codeCount);
-
- for (uint32 i = 0; i < codeCount; i++) {
- // The symbol. If none were specified, just assume it's identical to the code index
- uint32 symbol = symbols ? symbols[i] : i;
-
- // Put the code and symbol into the correct list
- _codes[lengths[i] - 1].push_back(Symbol(codes[i], symbol));
-
- // And put the pointer to the symbol/code struct into the symbol list.
- _symbols[i] = &_codes[lengths[i] - 1].back();
- }
-}
-
-Huffman::~Huffman() {
-}
-
-void Huffman::setSymbols(const uint32 *symbols) {
- for (uint32 i = 0; i < _symbols.size(); i++)
- _symbols[i]->symbol = symbols ? *symbols++ : i;
-}
-
-} // End of namespace Common
diff --git a/common/huffman.h b/common/huffman.h
index b4de5ab483..052c7bf825 100644
--- a/common/huffman.h
+++ b/common/huffman.h
@@ -20,7 +20,7 @@
*
*/
-// Based on eos' Huffman code
+// Based on xoreos' Huffman code
#ifndef COMMON_HUFFMAN_H
#define COMMON_HUFFMAN_H
@@ -31,12 +31,22 @@
namespace Common {
+inline uint32 REVERSEBITS(uint32 x) {
+ x = (((x & ~0x55555555) >> 1) | ((x & 0x55555555) << 1));
+ x = (((x & ~0x33333333) >> 2) | ((x & 0x33333333) << 2));
+ x = (((x & ~0x0F0F0F0F) >> 4) | ((x & 0x0F0F0F0F) << 4));
+ x = (((x & ~0x00FF00FF) >> 8) | ((x & 0x00FF00FF) << 8));
+
+ return((x >> 16) | (x << 16));
+}
+
/**
* Huffman bitstream decoding
*
* Used in engines:
* - scumm
*/
+template<class BITSTREAM>
class Huffman {
public:
/** Construct a Huffman decoder.
@@ -48,47 +58,107 @@ public:
* @param symbols The symbols. If 0, assume they are identical to the code indices.
*/
Huffman(uint8 maxLength, uint32 codeCount, const uint32 *codes, const uint8 *lengths, const uint32 *symbols = nullptr);
- ~Huffman();
-
- /** Modify the codes' symbols. */
- void setSymbols(const uint32 *symbols = nullptr);
/** Return the next symbol in the bitstream. */
- template<class BITSTREAM>
- uint32 getSymbol(BITSTREAM &bits) const {
- uint32 code = 0;
-
- for (uint32 i = 0; i < _codes.size(); i++) {
- bits.addBit(code, i);
-
- for (CodeList::const_iterator cCode = _codes[i].begin(); cCode != _codes[i].end(); ++cCode)
- if (code == cCode->code)
- return cCode->symbol;
- }
-
- error("Unknown Huffman code");
- return 0;
- }
+ uint32 getSymbol(BITSTREAM &bits) const;
private:
struct Symbol {
uint32 code;
uint32 symbol;
- Symbol(uint32 c, uint32 s);
+ Symbol(uint32 c, uint32 s) : code(c), symbol(s) {}
};
typedef List<Symbol> CodeList;
typedef Array<CodeList> CodeLists;
- typedef Array<Symbol *> SymbolList;
/** Lists of codes and their symbols, sorted by code length. */
CodeLists _codes;
- /** Sorted list of pointers to the symbols. */
- SymbolList _symbols;
+ /** Prefix lookup table used to speed up the decoding of short codes. */
+ struct PrefixEntry {
+ uint32 symbol;
+ uint8 length;
+
+ PrefixEntry() : length(0xFF) {}
+ };
+
+ static const uint8 _prefixTableBits = 8;
+ PrefixEntry _prefixTable[1 << _prefixTableBits];
};
+template <class BITSTREAM>
+Huffman<BITSTREAM>::Huffman(uint8 maxLength, uint32 codeCount, const uint32 *codes, const uint8 *lengths, const uint32 *symbols) {
+ assert(codeCount > 0);
+
+ assert(codes);
+ assert(lengths);
+
+ if (maxLength == 0)
+ for (uint32 i = 0; i < codeCount; i++)
+ maxLength = MAX(maxLength, lengths[i]);
+
+ assert(maxLength <= 32);
+
+ // Codes that don't fit in the prefix table are stored in the _codes array
+ _codes.resize(MAX(maxLength - _prefixTableBits, 0));
+
+ for (uint i = 0; i < codeCount; i++) {
+ uint8 length = lengths[i];
+
+ // The symbol. If none were specified, just assume it's identical to the code index
+ uint32 symbol = symbols ? symbols[i] : i;
+
+ if (length <= _prefixTableBits) {
+ // Short codes go in the prefix lookup table. Set all the entries in the table
+ // with an index starting with the code to the symbol value.
+ uint32 startIndex;
+ if (BITSTREAM::isMSB2LSB()) {
+ startIndex = codes[i] << (_prefixTableBits - length);
+ } else {
+ startIndex = REVERSEBITS(codes[i]) >> (32 - _prefixTableBits);
+ }
+
+ uint32 endIndex = startIndex | ((1 << (_prefixTableBits - length)) - 1);
+
+ for (uint32 j = startIndex; j <= endIndex; j++) {
+ uint32 index = BITSTREAM::isMSB2LSB() ? j : REVERSEBITS(j) >> (32 - _prefixTableBits);
+ _prefixTable[index].symbol = symbol;
+ _prefixTable[index].length = length;
+ }
+ } else {
+ // Put the code and symbol into the correct list for the length
+ _codes[lengths[i] - 1 - _prefixTableBits].push_back(Symbol(codes[i], symbol));
+ }
+ }
+}
+
+template <class BITSTREAM>
+uint32 Huffman<BITSTREAM>::getSymbol(BITSTREAM &bits) const {
+ uint32 code = bits.peekBits(_prefixTableBits);
+
+ uint8 length = _prefixTable[code].length;
+
+ if (length != 0xFF) {
+ bits.skip(length);
+ return _prefixTable[code].symbol;
+ } else {
+ bits.skip(_prefixTableBits);
+
+ for (uint32 i = 0; i < _codes.size(); i++) {
+ bits.addBit(code, i + _prefixTableBits);
+
+ for (typename CodeList::const_iterator cCode = _codes[i].begin(); cCode != _codes[i].end(); ++cCode)
+ if (code == cCode->code)
+ return cCode->symbol;
+ }
+ }
+
+ error("Unknown Huffman code");
+ return 0;
+}
+
} // End of namespace Common
#endif // COMMON_HUFFMAN_H
diff --git a/common/module.mk b/common/module.mk
index 6742fef3e8..f456604ebb 100644
--- a/common/module.mk
+++ b/common/module.mk
@@ -49,7 +49,6 @@ MODULE_OBJS += \
cosinetables.o \
dct.o \
fft.o \
- huffman.o \
rdft.o \
sinetables.o
diff --git a/image/codecs/svq1.cpp b/image/codecs/svq1.cpp
index 2d86070801..eba5fb872a 100644
--- a/image/codecs/svq1.cpp
+++ b/image/codecs/svq1.cpp
@@ -56,16 +56,16 @@ SVQ1Decoder::SVQ1Decoder(uint16 width, uint16 height) {
_last[2] = 0;
// Setup Variable Length Code Tables
- _blockType = new Common::Huffman(0, 4, s_svq1BlockTypeCodes, s_svq1BlockTypeLengths);
+ _blockType = new HuffmanDecoder(0, 4, s_svq1BlockTypeCodes, s_svq1BlockTypeLengths);
for (int i = 0; i < 6; i++) {
- _intraMultistage[i] = new Common::Huffman(0, 8, s_svq1IntraMultistageCodes[i], s_svq1IntraMultistageLengths[i]);
- _interMultistage[i] = new Common::Huffman(0, 8, s_svq1InterMultistageCodes[i], s_svq1InterMultistageLengths[i]);
+ _intraMultistage[i] = new HuffmanDecoder(0, 8, s_svq1IntraMultistageCodes[i], s_svq1IntraMultistageLengths[i]);
+ _interMultistage[i] = new HuffmanDecoder(0, 8, s_svq1InterMultistageCodes[i], s_svq1InterMultistageLengths[i]);
}
- _intraMean = new Common::Huffman(0, 256, s_svq1IntraMeanCodes, s_svq1IntraMeanLengths);
- _interMean = new Common::Huffman(0, 512, s_svq1InterMeanCodes, s_svq1InterMeanLengths);
- _motionComponent = new Common::Huffman(0, 33, s_svq1MotionComponentCodes, s_svq1MotionComponentLengths);
+ _intraMean = new HuffmanDecoder(0, 256, s_svq1IntraMeanCodes, s_svq1IntraMeanLengths);
+ _interMean = new HuffmanDecoder(0, 512, s_svq1InterMeanCodes, s_svq1InterMeanLengths);
+ _motionComponent = new HuffmanDecoder(0, 33, s_svq1MotionComponentCodes, s_svq1MotionComponentLengths);
}
SVQ1Decoder::~SVQ1Decoder() {
diff --git a/image/codecs/svq1.h b/image/codecs/svq1.h
index 148501d17f..4a005761a5 100644
--- a/image/codecs/svq1.h
+++ b/image/codecs/svq1.h
@@ -27,6 +27,7 @@
#include "image/codecs/codec.h"
namespace Common {
+template <class BITSTREAM>
class Huffman;
struct Point;
}
@@ -53,12 +54,14 @@ private:
byte *_last[3];
- Common::Huffman *_blockType;
- Common::Huffman *_intraMultistage[6];
- Common::Huffman *_interMultistage[6];
- Common::Huffman *_intraMean;
- Common::Huffman *_interMean;
- Common::Huffman *_motionComponent;
+ typedef Common::Huffman<Common::BitStream32BEMSB> HuffmanDecoder;
+
+ HuffmanDecoder *_blockType;
+ HuffmanDecoder *_intraMultistage[6];
+ HuffmanDecoder *_interMultistage[6];
+ HuffmanDecoder *_intraMean;
+ HuffmanDecoder *_interMean;
+ HuffmanDecoder *_motionComponent;
bool svq1DecodeBlockIntra(Common::BitStream32BEMSB *s, byte *pixels, int pitch);
bool svq1DecodeBlockNonIntra(Common::BitStream32BEMSB *s, byte *pixels, int pitch);
diff --git a/test/common/huffman.h b/test/common/huffman.h
index 53353aaa60..3e3956a321 100644
--- a/test/common/huffman.h
+++ b/test/common/huffman.h
@@ -32,7 +32,7 @@ class HuffmanTestSuite : public CxxTest::TestSuite {
const uint32 codes[] = {0x2, 0x3, 0x3, 0x0, 0x2};
const uint32 symbols[] = {0xA, 0xB, 0xC, 0xD, 0xE};
- Common::Huffman h(maxLength, codeCount, codes, lengths, symbols);
+ Common::Huffman<Common::BitStream8MSB> h(maxLength, codeCount, codes, lengths, symbols);
byte input[] = {0x4F, 0x20};
// Provided input...
@@ -78,7 +78,7 @@ class HuffmanTestSuite : public CxxTest::TestSuite {
const uint8 lengths[] = {3,3,2,2,2};
const uint32 codes[] = {0x2, 0x3, 0x3, 0x0, 0x2};
- Common::Huffman h(0, codeCount, codes, lengths, 0);
+ Common::Huffman<Common::BitStream8MSB> h(0, codeCount, codes, lengths, 0);
byte input[] = {0x4F, 0x20};
uint32 expected[] = {0, 1, 2, 3, 4, 3 ,3};
@@ -94,51 +94,4 @@ class HuffmanTestSuite : public CxxTest::TestSuite {
TS_ASSERT_EQUALS(h.getSymbol(bs), expected[5]);
TS_ASSERT_EQUALS(h.getSymbol(bs), expected[6]);
}
-
- void test_get_after_set_symbols() {
-
- /*
- * Another variation of test_get_with_full_symbols.
- * I use the setSymbols method to define, a posteriori,
- * an alphabet to be used in place of array indices.
- * The encoding is, at first,
- * 0=010
- * 1=011
- * 2=11
- * 3=00
- * 4=10
- * (=array indices).
- */
-
- uint32 codeCount = 5;
- const uint8 lengths[] = {3,3,2,2,2};
- const uint32 codes[] = {0x2, 0x3, 0x3, 0x0, 0x2};
-
- Common::Huffman h(0, codeCount, codes, lengths, 0);
-
- const uint32 symbols[] = {0xA, 0xB, 0xC, 0xD, 0xE};
- h.setSymbols(symbols);
-
- byte input[] = {0x4F, 0x20};
- uint32 expected[] = {0xA, 0xB, 0xC, 0xD, 0xE, 0xD, 0xD};
-
- Common::MemoryReadStream ms(input, sizeof(input));
- Common::BitStream8MSB bs(ms);
-
- /* New symbols:
- * A=010
- * B=011
- * C=11
- * D=00
- * E=10
- */
-
- TS_ASSERT_EQUALS(h.getSymbol(bs), expected[0]);
- TS_ASSERT_EQUALS(h.getSymbol(bs), expected[1]);
- TS_ASSERT_EQUALS(h.getSymbol(bs), expected[2]);
- TS_ASSERT_EQUALS(h.getSymbol(bs), expected[3]);
- TS_ASSERT_EQUALS(h.getSymbol(bs), expected[4]);
- TS_ASSERT_EQUALS(h.getSymbol(bs), expected[5]);
- TS_ASSERT_EQUALS(h.getSymbol(bs), expected[6]);
- }
};
diff --git a/video/bink_decoder.cpp b/video/bink_decoder.cpp
index 406c6e4d34..4074043768 100644
--- a/video/bink_decoder.cpp
+++ b/video/bink_decoder.cpp
@@ -594,7 +594,7 @@ void BinkDecoder::BinkVideoTrack::deinitBundles() {
void BinkDecoder::BinkVideoTrack::initHuffman() {
for (int i = 0; i < 16; i++)
- _huffman[i] = new Common::Huffman(binkHuffmanLengths[i][15], 16, binkHuffmanCodes[i], binkHuffmanLengths[i]);
+ _huffman[i] = new Common::Huffman<Common::BitStream32LELSB>(binkHuffmanLengths[i][15], 16, binkHuffmanCodes[i], binkHuffmanLengths[i]);
}
byte BinkDecoder::BinkVideoTrack::getHuffmanSymbol(VideoFrame &video, Huffman &huffman) {
diff --git a/video/bink_decoder.h b/video/bink_decoder.h
index 29d16020b1..11694314b7 100644
--- a/video/bink_decoder.h
+++ b/video/bink_decoder.h
@@ -46,6 +46,7 @@ class QueuingAudioStream;
namespace Common {
class SeekableReadStream;
+template <class BITSTREAM>
class Huffman;
class RDFT;
@@ -247,7 +248,7 @@ private:
Bundle _bundles[kSourceMAX]; ///< Bundles for decoding all data types.
- Common::Huffman *_huffman[16]; ///< The 16 Huffman codebooks used in Bink decoding.
+ Common::Huffman<Common::BitStream32LELSB> *_huffman[16]; ///< The 16 Huffman codebooks used in Bink decoding.
/** Huffman codebooks to use for decoding high nibbles in color data types. */
Huffman _colHighHuffman[16];
diff --git a/video/psx_decoder.cpp b/video/psx_decoder.cpp
index 562da885b1..ef42a72873 100644
--- a/video/psx_decoder.cpp
+++ b/video/psx_decoder.cpp
@@ -438,9 +438,9 @@ PSXStreamDecoder::PSXVideoTrack::PSXVideoTrack(Common::SeekableReadStream *first
_endOfTrack = false;
_curFrame = -1;
- _acHuffman = new Common::Huffman(0, AC_CODE_COUNT, s_huffmanACCodes, s_huffmanACLengths, s_huffmanACSymbols);
- _dcHuffmanChroma = new Common::Huffman(0, DC_CODE_COUNT, s_huffmanDCChromaCodes, s_huffmanDCChromaLengths, s_huffmanDCSymbols);
- _dcHuffmanLuma = new Common::Huffman(0, DC_CODE_COUNT, s_huffmanDCLumaCodes, s_huffmanDCLumaLengths, s_huffmanDCSymbols);
+ _acHuffman = new HuffmanDecoder(0, AC_CODE_COUNT, s_huffmanACCodes, s_huffmanACLengths, s_huffmanACSymbols);
+ _dcHuffmanChroma = new HuffmanDecoder(0, DC_CODE_COUNT, s_huffmanDCChromaCodes, s_huffmanDCChromaLengths, s_huffmanDCSymbols);
+ _dcHuffmanLuma = new HuffmanDecoder(0, DC_CODE_COUNT, s_huffmanDCLumaCodes, s_huffmanDCLumaLengths, s_huffmanDCSymbols);
}
PSXStreamDecoder::PSXVideoTrack::~PSXVideoTrack() {
@@ -552,7 +552,7 @@ int PSXStreamDecoder::PSXVideoTrack::readDC(Common::BitStreamMemory16LEMSB *bits
// Version 3 has it stored as huffman codes as a difference from the previous DC value
- Common::Huffman *huffman = (plane == kPlaneY) ? _dcHuffmanLuma : _dcHuffmanChroma;
+ HuffmanDecoder *huffman = (plane == kPlaneY) ? _dcHuffmanLuma : _dcHuffmanChroma;
uint32 symbol = huffman->getSymbol(*bits);
int dc = 0;
diff --git a/video/psx_decoder.h b/video/psx_decoder.h
index c96642276a..183b6da317 100644
--- a/video/psx_decoder.h
+++ b/video/psx_decoder.h
@@ -36,6 +36,7 @@ class QueuingAudioStream;
}
namespace Common {
+template <class BITSTREAM>
class Huffman;
}
@@ -105,16 +106,18 @@ private:
kPlaneV = 2
};
+ typedef Common::Huffman<Common::BitStreamMemory16LEMSB> HuffmanDecoder;
+
uint16 _macroBlocksW, _macroBlocksH;
byte *_yBuffer, *_cbBuffer, *_crBuffer;
void decodeMacroBlock(Common::BitStreamMemory16LEMSB *bits, int mbX, int mbY, uint16 scale, uint16 version);
void decodeBlock(Common::BitStreamMemory16LEMSB *bits, byte *block, int pitch, uint16 scale, uint16 version, PlaneType plane);
void readAC(Common::BitStreamMemory16LEMSB *bits, int *block);
- Common::Huffman *_acHuffman;
+ HuffmanDecoder *_acHuffman;
int readDC(Common::BitStreamMemory16LEMSB *bits, uint16 version, PlaneType plane);
- Common::Huffman *_dcHuffmanLuma, *_dcHuffmanChroma;
+ HuffmanDecoder *_dcHuffmanLuma, *_dcHuffmanChroma;
int _lastDC[3];
void dequantizeBlock(int *coefficients, float *block, uint16 scale);