diff options
Diffstat (limited to 'image')
-rw-r--r-- | image/codecs/codec.cpp | 7 | ||||
-rw-r--r-- | image/codecs/indeo/get_bits.cpp | 60 | ||||
-rw-r--r-- | image/codecs/indeo/get_bits.h | 63 | ||||
-rw-r--r-- | image/codecs/indeo/indeo.cpp | 1729 | ||||
-rw-r--r-- | image/codecs/indeo/indeo.h | 584 | ||||
-rw-r--r-- | image/codecs/indeo/indeo_dsp.cpp | 644 | ||||
-rw-r--r-- | image/codecs/indeo/indeo_dsp.h | 336 | ||||
-rw-r--r-- | image/codecs/indeo/mem.cpp | 172 | ||||
-rw-r--r-- | image/codecs/indeo/mem.h | 148 | ||||
-rw-r--r-- | image/codecs/indeo/vlc.cpp | 336 | ||||
-rw-r--r-- | image/codecs/indeo/vlc.h | 135 | ||||
-rw-r--r-- | image/codecs/indeo4.cpp | 951 | ||||
-rw-r--r-- | image/codecs/indeo4.h | 140 | ||||
-rw-r--r-- | image/codecs/indeo5.cpp | 744 | ||||
-rw-r--r-- | image/codecs/indeo5.h | 136 | ||||
-rw-r--r-- | image/module.mk | 9 |
16 files changed, 6193 insertions, 1 deletions
diff --git a/image/codecs/codec.cpp b/image/codecs/codec.cpp index 9e5c1bfe08..913b7afab3 100644 --- a/image/codecs/codec.cpp +++ b/image/codecs/codec.cpp @@ -30,6 +30,8 @@ #include "image/codecs/cdtoons.h" #include "image/codecs/cinepak.h" #include "image/codecs/indeo3.h" +#include "image/codecs/indeo4.h" +#include "image/codecs/indeo5.h" #include "image/codecs/mjpeg.h" #include "image/codecs/mpeg.h" #include "image/codecs/msvideo1.h" @@ -209,6 +211,11 @@ Codec *createBitmapCodec(uint32 tag, int width, int height, int bitsPerPixel) { return new CinepakDecoder(bitsPerPixel); case MKTAG('I','V','3','2'): return new Indeo3Decoder(width, height); + case MKTAG('I', 'V', '4', '1'): + case MKTAG('I', 'V', '4', '2'): + return new Indeo4Decoder(width, height); + case MKTAG('I', 'V', '5', '0'): + return new Indeo5Decoder(width, height); #ifdef IMAGE_CODECS_TRUEMOTION1_H case MKTAG('D','U','C','K'): case MKTAG('d','u','c','k'): diff --git a/image/codecs/indeo/get_bits.cpp b/image/codecs/indeo/get_bits.cpp new file mode 100644 index 0000000000..f808a96e76 --- /dev/null +++ b/image/codecs/indeo/get_bits.cpp @@ -0,0 +1,60 @@ +/* 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. + * + */ + +#include "image/codecs/indeo/get_bits.h" + +namespace Image { +namespace Indeo { + +int GetBits::getVLC2(int16 (*table)[2], int bits, int maxDepth) { + int code; + int n, nbBits; + unsigned int index; + + index = peekBits(bits); + code = table[index][0]; + n = table[index][1]; + + if (maxDepth > 1 && n < 0) { + skip(bits); + nbBits = -n; + + index = peekBits(nbBits) + code; + code = table[index][0]; + n = table[index][1]; + + if (maxDepth > 2 && n < 0) { + skip(nbBits); + nbBits = -n; + + index = peekBits(nbBits) + code; + code = table[index][0]; + n = table[index][1]; + } + } + + skip(n); + return code; +} + +} // End of namespace Indeo +} // End of namespace Image diff --git a/image/codecs/indeo/get_bits.h b/image/codecs/indeo/get_bits.h new file mode 100644 index 0000000000..359d8fcab0 --- /dev/null +++ b/image/codecs/indeo/get_bits.h @@ -0,0 +1,63 @@ +/* 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. + * + */ + +#ifndef IMAGE_CODECS_INDEO_GET_BITS_H +#define IMAGE_CODECS_INDEO_GET_BITS_H + +#include "common/bitstream.h" + +namespace Image { +namespace Indeo { + +/** + * Intel Indeo Bitstream reader + */ +class GetBits : public Common::BitStream8LSB { +public: + /** + * Constructor + * @param stream Source stream to reader from + * @param disposeAfterUse Whether to destroy stream in destructor + */ + GetBits(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse + = DisposeAfterUse::YES) : Common::BitStream8LSB(stream, disposeAfterUse) {} + + /** + * The number of bits left + */ + int getBitsLeft() const { return size() - pos(); } + + /** + * Parse a VLC code. + * @param bits is the number of bits which will be read at once, must be + * identical to nbBits in init_vlc() + * @param maxDepth is the number of times bits bits must be read to completely + * read the longest vlc code + * = (max_vlc_length + bits - 1) / bits + */ + int getVLC2(int16 (*table)[2], int bits, int maxDepth); +}; + +} // End of namespace Indeo +} // End of namespace Image + +#endif diff --git a/image/codecs/indeo/indeo.cpp b/image/codecs/indeo/indeo.cpp new file mode 100644 index 0000000000..2bc699ecf5 --- /dev/null +++ b/image/codecs/indeo/indeo.cpp @@ -0,0 +1,1729 @@ +/* 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. + * + */ + +/* Common structures, macros, and base class shared by both Indeo4 and + * Indeo5 decoders, derived from ffmpeg. We don't currently support Indeo5 + * decoding, but just in case we eventually need it, this is kept as a separate + * file like it is in ffmpeg. + * + * Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg + * written, produced, and directed by Alan Smithee + */ + +#include "image/codecs/indeo/indeo.h" +#include "image/codecs/indeo/indeo_dsp.h" +#include "image/codecs/indeo/mem.h" +#include "graphics/yuv_to_rgb.h" +#include "common/system.h" +#include "common/algorithm.h" +#include "common/textconsole.h" +#include "common/util.h" + +namespace Image { +namespace Indeo { + +/** + * These are 2x8 predefined Huffman codebooks for coding macroblock/block + * signals. They are specified using "huffman descriptors" in order to + * avoid huge static tables. The decoding tables will be generated at + * startup from these descriptors. + */ + +/** + * Static macroblock huffman tables + */ +static const IVIHuffDesc ivi_mb_huff_desc[8] = { + {8, {0, 4, 5, 4, 4, 4, 6, 6}}, + {12, {0, 2, 2, 3, 3, 3, 3, 5, 3, 2, 2, 2}}, + {12, {0, 2, 3, 4, 3, 3, 3, 3, 4, 3, 2, 2}}, + {12, {0, 3, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2}}, + {13, {0, 4, 4, 3, 3, 3, 3, 2, 3, 3, 2, 1, 1}}, + {9, {0, 4, 4, 4, 4, 3, 3, 3, 2}}, + {10, {0, 4, 4, 4, 4, 3, 3, 2, 2, 2}}, + {12, {0, 4, 4, 4, 3, 3, 2, 3, 2, 2, 2, 2}} +}; + +/** + * static block huffman tables + */ +static const IVIHuffDesc ivi_blk_huff_desc[8] = { + {10, {1, 2, 3, 4, 4, 7, 5, 5, 4, 1} }, + {11, {2, 3, 4, 4, 4, 7, 5, 4, 3, 3, 2} }, + {12, {2, 4, 5, 5, 5, 5, 6, 4, 4, 3, 1, 1} }, + {13, {3, 3, 4, 4, 5, 6, 6, 4, 4, 3, 2, 1, 1} }, + {11, {3, 4, 4, 5, 5, 5, 6, 5, 4, 2, 2} }, + {13, {3, 4, 5, 5, 5, 5, 6, 4, 3, 3, 2, 1, 1} }, + {13, {3, 4, 5, 5, 5, 6, 5, 4, 3, 3, 2, 1, 1} }, + {9, {3, 4, 4, 5, 5, 5, 6, 5, 5} } +}; + +/*------------------------------------------------------------------------*/ + +/** + * calculate number of _tiles in a stride + */ +#define IVI_NUM_TILES(stride, tileSize) (((stride) + (tileSize) - 1) / (tileSize)) + +/*------------------------------------------------------------------------*/ + +int IVIHuffDesc::createHuffFromDesc(VLC *vlc, bool flag) const { + int pos, i, j, codesPerRow, prefix, notLastRow; + uint16 codewords[256]; + uint8 bits[256]; + + pos = 0; // current position = 0 + + for (i = 0; i < _numRows; i++) { + codesPerRow = 1 << _xBits[i]; + notLastRow = (i != _numRows - 1); + prefix = ((1 << i) - 1) << (_xBits[i] + notLastRow); + + for (j = 0; j < codesPerRow; j++) { + if (pos >= 256) // Some Indeo5 codebooks can have more than 256 + break; // elements, but only 256 codes are allowed! + + bits[pos] = i + _xBits[i] + notLastRow; + if (bits[pos] > IVI_VLC_BITS) + return -1; // invalid descriptor + + codewords[pos] = invertBits((prefix | j), bits[pos]); + if (!bits[pos]) + bits[pos] = 1; + + pos++; + }//for j + }//for i + + // number of codewords = pos + return vlc->init_vlc(IVI_VLC_BITS, pos, bits, 1, 1, codewords, 2, 2, + (flag ? INIT_VLC_USE_NEW_STATIC : 0) | INIT_VLC_LE); +} + +/*------------------------------------------------------------------------*/ + +bool IVIHuffDesc::huffDescCompare(const IVIHuffDesc *desc2) const { + return _numRows != desc2->_numRows || memcmp(_xBits, desc2->_xBits, _numRows); +} + +void IVIHuffDesc::huffDescCopy(const IVIHuffDesc *src) { + _numRows = src->_numRows; + memcpy(_xBits, src->_xBits, src->_numRows); +} + +/*------------------------------------------------------------------------*/ + +IVIHuffTab::IVIHuffTab() : _tab(nullptr) { +} + +int IVIHuffTab::decodeHuffDesc(IVI45DecContext *ctx, int descCoded, int whichTab) { + int i, result; + IVIHuffDesc newHuff; + + if (!descCoded) { + // select default table + _tab = (whichTab) ? &ctx->_iviBlkVlcTabs[7] + : &ctx->_iviMbVlcTabs[7]; + return 0; + } + + _tabSel = ctx->_gb->getBits(3); + if (_tabSel == 7) { + // custom huffman table (explicitly encoded) + newHuff._numRows = ctx->_gb->getBits(4); + if (!newHuff._numRows) { + warning("Empty custom Huffman table!"); + return -1; + } + + for (i = 0; i < newHuff._numRows; i++) + newHuff._xBits[i] = ctx->_gb->getBits(4); + + // Have we got the same custom table? Rebuild if not. + if (newHuff.huffDescCompare(&_custDesc) || !_custTab._table) { + _custDesc.huffDescCopy(&newHuff); + + if (_custTab._table) + _custTab.freeVlc(); + result = _custDesc.createHuffFromDesc(&_custTab, false); + if (result) { + // reset faulty description + _custDesc._numRows = 0; + warning("Error while initializing custom vlc table!"); + return result; + } + } + _tab = &_custTab; + } else { + // select one of predefined tables + _tab = (whichTab) ? &ctx->_iviBlkVlcTabs[_tabSel] + : &ctx->_iviMbVlcTabs[_tabSel]; + } + + return 0; +} + +/*------------------------------------------------------------------------*/ + +IVIMbInfo::IVIMbInfo() : _xPos(0), _yPos(0), _bufOffs(0), _type(0), _cbp(0), + _qDelta(0), _mvX(0), _mvY(0), _bMvX(0), _bMvY(0) { +} + +/*------------------------------------------------------------------------*/ + +IVITile::IVITile() : _xPos(0), _yPos(0), _width(0), _height(0), _mbSize(0), + _isEmpty(false), _dataSize(0), _numMBs(0), _mbs(nullptr), _refMbs(nullptr) { +} + +/*------------------------------------------------------------------------*/ + +IVIBandDesc::IVIBandDesc() : _plane(0), _bandNum(0), _width(0), _height(0), + _aHeight(0), _dataPtr(nullptr), _dataSize(0), _buf(nullptr), + _refBuf(nullptr), _bRefBuf(nullptr), _pitch(0), _isEmpty(false), + _mbSize(0), _blkSize(0), _isHalfpel(false), _inheritMv(false), _bufSize(0), + _inheritQDelta(false), _qdeltaPresent(false), _quantMat(0), _globQuant(0), + _scan(nullptr), _scanSize(0), _numCorr(0), _rvmapSel(0), _rvMap(nullptr), + _numTiles(0), _tiles(nullptr), _invTransform(nullptr), _transformSize(0), + _dcTransform(nullptr), _is2dTrans(0), _checksum(0), _checksumPresent(false), + _intraBase(nullptr), _interBase(nullptr), _intraScale(nullptr), + _interScale(nullptr) { + Common::fill(&_bufs[0], &_bufs[4], (int16 *)nullptr); + Common::fill(&_corr[0], &_corr[61 * 2], 0); +} + +int IVIBandDesc::initTiles(IVITile *refTile, int p, int b, int tHeight, int tWidth) { + int x, y; + IVITile *tile = _tiles; + + for (y = 0; y < _height; y += tHeight) { + for (x = 0; x < _width; x += tWidth) { + tile->_xPos = x; + tile->_yPos = y; + tile->_mbSize = _mbSize; + tile->_width = MIN(_width - x, tWidth); + tile->_height = MIN(_height - y, tHeight); + tile->_dataSize = 0; + tile->_isEmpty = false; + + // calculate number of macroblocks + tile->_numMBs = IVI_MBs_PER_TILE(tile->_width, tile->_height, + _mbSize); + + avFreeP(&tile->_mbs); + tile->_mbs = (IVIMbInfo *)avMallocZArray(tile->_numMBs, sizeof(IVIMbInfo)); + if (!tile->_mbs) + return -2; + + tile->_refMbs = 0; + if (p || b) { + if (tile->_numMBs != refTile->_numMBs) { + warning("refTile mismatch"); + return -1; + } + tile->_refMbs = refTile->_mbs; + refTile++; + } + tile++; + } + } + + return 0; +} + +/*------------------------------------------------------------------------*/ + +IVIPicConfig::IVIPicConfig() : _picWidth(0), _picHeight(0), _chromaWidth(0), + _chromaHeight(0), _tileWidth(0), _tileHeight(0), _lumaBands(0), _chromaBands(0) { +} + +bool IVIPicConfig::ivi_pic_config_cmp(const IVIPicConfig &cfg2) { + return _picWidth != cfg2._picWidth || _picHeight != cfg2._picHeight || + _chromaWidth != cfg2._chromaWidth || _chromaHeight != cfg2._chromaHeight || + _tileWidth != cfg2._tileWidth || _tileHeight != cfg2._tileHeight || + _lumaBands != cfg2._lumaBands || _chromaBands != cfg2._chromaBands; +} + +/*------------------------------------------------------------------------*/ + +IVIPlaneDesc::IVIPlaneDesc() : _width(0), _height(0), _numBands(0), _bands(nullptr) { +} + +int IVIPlaneDesc::initPlanes(IVIPlaneDesc *planes, const IVIPicConfig *cfg, bool isIndeo4) { + int p, b; + uint32 b_width, b_height, align_fac, width_aligned, height_aligned, bufSize; + IVIBandDesc *band; + + freeBuffers(planes); + + if (checkImageSize(cfg->_picWidth, cfg->_picHeight, 0) < 0 || + cfg->_lumaBands < 1 || cfg->_chromaBands < 1) + return -1; + + // fill in the descriptor of the luminance _plane + planes[0]._width = cfg->_picWidth; + planes[0]._height = cfg->_picHeight; + planes[0]._numBands = cfg->_lumaBands; + + // fill in the descriptors of the chrominance planes + planes[1]._width = planes[2]._width = (cfg->_picWidth + 3) >> 2; + planes[1]._height = planes[2]._height = (cfg->_picHeight + 3) >> 2; + planes[1]._numBands = planes[2]._numBands = cfg->_chromaBands; + + for (p = 0; p < 3; p++) { + planes[p]._bands = (IVIBandDesc *)avMallocZArray(planes[p]._numBands, sizeof(IVIBandDesc)); + if (!planes[p]._bands) + return -2; + + // select band dimensions: if there is only one band then it + // has the full size, if there are several bands each of them + // has only half size + b_width = planes[p]._numBands == 1 ? planes[p]._width + : (planes[p]._width + 1) >> 1; + b_height = planes[p]._numBands == 1 ? planes[p]._height + : (planes[p]._height + 1) >> 1; + + // luma band buffers will be aligned on 16x16 (max macroblock size) + // chroma band buffers will be aligned on 8x8 (max macroblock size) + align_fac = p ? 8 : 16; + width_aligned = FFALIGN(b_width, align_fac); + height_aligned = FFALIGN(b_height, align_fac); + bufSize = width_aligned * height_aligned * sizeof(int16); + + for (b = 0; b < planes[p]._numBands; b++) { + band = &planes[p]._bands[b]; // select appropriate _plane/band + band->_plane = p; + band->_bandNum = b; + band->_width = b_width; + band->_height = b_height; + band->_pitch = width_aligned; + band->_aHeight = height_aligned; + band->_bufs[0] = (int16 *)avMallocZ(bufSize); + band->_bufs[1] = (int16 *)avMallocZ(bufSize); + band->_bufSize = bufSize / 2; + if (!band->_bufs[0] || !band->_bufs[1]) + return -2; + + // allocate the 3rd band buffer for scalability mode + if (cfg->_lumaBands > 1) { + band->_bufs[2] = (int16 *)avMallocZ(bufSize); + if (!band->_bufs[2]) + return -2; + } + if (isIndeo4) { + band->_bufs[3] = (int16 *)avMallocZ(bufSize); + if (!band->_bufs[3]) + return -2; + } + // reset custom vlc + planes[p]._bands[0]._blkVlc._custDesc._numRows = 0; + } + } + + return 0; +} + +int IVIPlaneDesc::initTiles(IVIPlaneDesc *planes, int tileWidth, int tileHeight) { + int p, b, xTiles, yTiles, tWidth, tHeight, ret; + IVIBandDesc *band; + + for (p = 0; p < 3; p++) { + tWidth = !p ? tileWidth : (tileWidth + 3) >> 2; + tHeight = !p ? tileHeight : (tileHeight + 3) >> 2; + + if (!p && planes[0]._numBands == 4) { + tWidth >>= 1; + tHeight >>= 1; + } + if (tWidth <= 0 || tHeight <= 0) + return -3; + + for (b = 0; b < planes[p]._numBands; b++) { + band = &planes[p]._bands[b]; + xTiles = IVI_NUM_TILES(band->_width, tWidth); + yTiles = IVI_NUM_TILES(band->_height, tHeight); + band->_numTiles = xTiles * yTiles; + + avFreeP(&band->_tiles); + band->_tiles = (IVITile *)avMallocZArray(band->_numTiles, sizeof(IVITile)); + if (!band->_tiles) + return -2; + + // use the first luma band as reference for motion vectors + // and quant + ret = band->initTiles(planes[0]._bands[0]._tiles, + p, b, tHeight, tWidth); + if (ret < 0) + return ret; + } + } + + return 0; +} + +void IVIPlaneDesc::freeBuffers(IVIPlaneDesc *planes) { + int p, b, t; + + for (p = 0; p < 3; p++) { + if (planes[p]._bands) + for (b = 0; b < planes[p]._numBands; b++) { + avFreeP(&planes[p]._bands[b]._bufs[0]); + avFreeP(&planes[p]._bands[b]._bufs[1]); + avFreeP(&planes[p]._bands[b]._bufs[2]); + avFreeP(&planes[p]._bands[b]._bufs[3]); + + if (planes[p]._bands[b]._blkVlc._custTab._table) + planes[p]._bands[b]._blkVlc._custTab.freeVlc(); + for (t = 0; t < planes[p]._bands[b]._numTiles; t++) + avFreeP(&planes[p]._bands[b]._tiles[t]._mbs); + avFreeP(&planes[p]._bands[b]._tiles); + } + avFreeP(&planes[p]._bands); + planes[p]._numBands = 0; + } +} + +int IVIPlaneDesc::checkImageSize(unsigned int w, unsigned int h, int log_offset) { + if (((w + 128) * (uint64)(h + 128)) < (MAX_INTEGER / 8)) + return 0; + + error("Picture size %ux%u is invalid", w, h); +} + +/*------------------------------------------------------------------------*/ + +AVFrame::AVFrame() { + Common::fill(&_data[0], &_data[AV_NUM_DATA_POINTERS], (uint8 *)nullptr); + Common::fill(&_linesize[0], &_linesize[AV_NUM_DATA_POINTERS], 0); +} + +int AVFrame::setDimensions(uint16 width, uint16 height) { + _width = width; + _height = height; + _linesize[0] = _linesize[1] = _linesize[2] = width; + + return 0; +} + +int AVFrame::getBuffer(int flags) { + freeFrame(); + + // Luminance channel + _data[0] = (uint8 *)avMallocZ(_width * _height); + + // UV Chroma Channels + _data[1] = (uint8 *)malloc(_width * _height); + _data[2] = (uint8 *)malloc(_width * _height); + Common::fill(_data[1], _data[1] + _width * _height, 0x80); + Common::fill(_data[2], _data[2] + _width * _height, 0x80); + + return 0; +} + +void AVFrame::freeFrame() { + avFreeP(&_data[0]); + avFreeP(&_data[1]); + avFreeP(&_data[2]); +} + +/*------------------------------------------------------------------------*/ + +IVI45DecContext::IVI45DecContext() : _gb(nullptr), _frameNum(0), _frameType(0), + _prevFrameType(0), _dataSize(0), _isScalable(0), _frameData(0), + _interScal(0), _frameSize(0), _picHdrSize(0), _frameFlags(0), + _checksum(0), _bufSwitch(0), _dstBuf(0), _refBuf(0), _ref2Buf(0), + _bRefBuf(0), _rvmapSel(0), _inImf(false), _inQ(false), _picGlobQuant(0), + _unknown1(0), _gopHdrSize(0), _gopFlags(0), _lockWord(0), _hasBFrames(false), + _hasTransp(false), _usesTiling(false), _usesHaar(false), _usesFullpel(false), + _gopInvalid(false), _isIndeo4(false), _pFrame(nullptr), _gotPFrame(false) { + Common::fill(&_bufInvalid[0], &_bufInvalid[4], 0); + Common::copy(&_ff_ivi_rvmap_tabs[0], &_ff_ivi_rvmap_tabs[9], &_rvmapTabs[0]); + + for (int idx = 0; idx < (8192 * 16); ++idx) + _tableData[idx][0] = _tableData[idx][1] = 0; + + for (int i = 0; i < 8; i++) { + _iviMbVlcTabs[i]._table = _tableData + i * 2 * 8192; + _iviMbVlcTabs[i]._tableAllocated = 8192; + ivi_mb_huff_desc[i].createHuffFromDesc(&_iviMbVlcTabs[i], true); + _iviBlkVlcTabs[i]._table = _tableData + (i * 2 + 1) * 8192; + _iviBlkVlcTabs[i]._tableAllocated = 8192; + ivi_blk_huff_desc[i].createHuffFromDesc(&_iviBlkVlcTabs[i], true); + } +} + +/*------------------------------------------------------------------------*/ + +IndeoDecoderBase::IndeoDecoderBase(uint16 width, uint16 height) : Codec() { + _pixelFormat = g_system->getScreenFormat(); + assert(_pixelFormat.bytesPerPixel > 1); + _surface = new Graphics::ManagedSurface(); + _surface->create(width, height, _pixelFormat); + _surface->fillRect(Common::Rect(0, 0, width, height), 0); + _ctx._bRefBuf = 3; // buffer 2 is used for scalability mode +} + +IndeoDecoderBase::~IndeoDecoderBase() { + delete _surface; + IVIPlaneDesc::freeBuffers(_ctx._planes); + if (_ctx._mbVlc._custTab._table) + _ctx._mbVlc._custTab.freeVlc(); + + delete _ctx._pFrame; +} + +int IndeoDecoderBase::decodeIndeoFrame() { + int result, p, b; + AVFrame frameData; + AVFrame *frame = &frameData; + + // Decode the header + if (decodePictureHeader() < 0) + return -1; + + if (_ctx._gopInvalid) + return -1; + + if (_ctx._frameType == IVI4_FRAMETYPE_NULL_LAST) { + // Returning the previous frame, so exit wth success + return 0; + } + + if (_ctx._gopFlags & IVI5_IS_PROTECTED) { + warning("Password-protected clip"); + return -1; + } + + if (!_ctx._planes[0]._bands) { + warning("Color planes not initialized yet"); + return -1; + } + + switchBuffers(); + + //{ START_TIMER; + + if (isNonNullFrame()) { + _ctx._bufInvalid[_ctx._dstBuf] = 1; + for (p = 0; p < 3; p++) { + for (b = 0; b < _ctx._planes[p]._numBands; b++) { + result = decode_band(&_ctx._planes[p]._bands[b]); + if (result < 0) { + warning("Error while decoding band: %d, _plane: %d", b, p); + return result; + } + } + } + _ctx._bufInvalid[_ctx._dstBuf] = 0; + } else { + if (_ctx._isScalable) + return -1; + + for (p = 0; p < 3; p++) { + if (!_ctx._planes[p]._bands[0]._buf) + return -1; + } + } + if (_ctx._bufInvalid[_ctx._dstBuf]) + return -1; + + //STOP_TIMER("decode_planes"); } + + if (!isNonNullFrame()) + return 0; + + assert(_ctx._planes[0]._width <= _surface->w && _ctx._planes[0]._height <= _surface->h); + result = frame->setDimensions(_ctx._planes[0]._width, _ctx._planes[0]._height); + if (result < 0) + return result; + + if ((result = frame->getBuffer(0)) < 0) + return result; + + if (_ctx._isScalable) { + if (_ctx._isIndeo4) + recomposeHaar(&_ctx._planes[0], frame->_data[0], frame->_linesize[0]); + else + recompose53(&_ctx._planes[0], frame->_data[0], frame->_linesize[0]); + } else { + outputPlane(&_ctx._planes[0], frame->_data[0], frame->_linesize[0]); + } + + outputPlane(&_ctx._planes[2], frame->_data[1], frame->_linesize[1]); + outputPlane(&_ctx._planes[1], frame->_data[2], frame->_linesize[2]); + + // If the bidirectional mode is enabled, next I and the following P + // frame will be sent together. Unfortunately the approach below seems + // to be the only way to handle the B-frames mode. + // That's exactly the same Intel decoders do. + if (_ctx._isIndeo4 && _ctx._frameType == IVI4_FRAMETYPE_INTRA) { + int left; + + // skip version string + while (_ctx._gb->getBits(8)) { + if (_ctx._gb->getBitsLeft() < 8) + return -1; + } + left = _ctx._gb->pos() & 0x18; + _ctx._gb->skip(64 - left); + if (_ctx._gb->getBitsLeft() > 18 && + _ctx._gb->peekBits(21) == 0xBFFF8) { // syncheader + inter _type + error("Indeo decoder: Mode not currently implemented in ScummVM"); + } + } + + // Merge the planes into the final surface + Graphics::Surface s = _surface->getSubArea(Common::Rect(0, 0, _surface->w, _surface->h)); + YUVToRGBMan.convert410(&s, Graphics::YUVToRGBManager::kScaleITU, + frame->_data[0], frame->_data[1], frame->_data[2], frame->_width, frame->_height, + frame->_width, frame->_width); + + // Free the now un-needed frame data + frame->freeFrame(); + + return 0; +} + +int IndeoDecoderBase::decode_band(IVIBandDesc *band) { + int result, i, t, idx1, idx2, pos; + IVITile *tile; + + band->_buf = band->_bufs[_ctx._dstBuf]; + if (!band->_buf) { + warning("Band buffer points to no data!"); + return -1; + } + if (_ctx._isIndeo4 && _ctx._frameType == IVI4_FRAMETYPE_BIDIR) { + band->_refBuf = band->_bufs[_ctx._bRefBuf]; + band->_bRefBuf = band->_bufs[_ctx._refBuf]; + } else { + band->_refBuf = band->_bufs[_ctx._refBuf]; + band->_bRefBuf = 0; + } + band->_dataPtr = _ctx._frameData + (_ctx._gb->pos() >> 3); + + result = decodeBandHeader(band); + if (result) { + warning("Error while decoding band header: %d", + result); + return result; + } + + if (band->_isEmpty) { + warning("Empty band encountered!"); + return -1; + } + + band->_rvMap = &_ctx._rvmapTabs[band->_rvmapSel]; + + // apply corrections to the selected rvmap table if present + for (i = 0; i < band->_numCorr; i++) { + idx1 = band->_corr[i * 2]; + idx2 = band->_corr[i * 2 + 1]; + SWAP(band->_rvMap->_runtab[idx1], band->_rvMap->_runtab[idx2]); + SWAP(band->_rvMap->_valtab[idx1], band->_rvMap->_valtab[idx2]); + if (idx1 == band->_rvMap->_eobSym || idx2 == band->_rvMap->_eobSym) + band->_rvMap->_eobSym ^= idx1 ^ idx2; + if (idx1 == band->_rvMap->_escSym || idx2 == band->_rvMap->_escSym) + band->_rvMap->_escSym ^= idx1 ^ idx2; + } + + pos = _ctx._gb->pos(); + + for (t = 0; t < band->_numTiles; t++) { + tile = &band->_tiles[t]; + + if (tile->_mbSize != band->_mbSize) { + warning("MB sizes mismatch: %d vs. %d", + band->_mbSize, tile->_mbSize); + return -1; + } + tile->_isEmpty = _ctx._gb->getBit(); + if (tile->_isEmpty) { + result = processEmptyTile(band, tile, + (_ctx._planes[0]._bands[0]._mbSize >> 3) - (band->_mbSize >> 3)); + if (result < 0) + break; + warning("Empty tile encountered!"); + } else { + tile->_dataSize = decodeTileDataSize(_ctx._gb); + if (!tile->_dataSize) { + warning("Tile data size is zero!"); + result = -1; + break; + } + + result = decodeMbInfo(band, tile); + if (result < 0) + break; + + result = decodeBlocks(_ctx._gb, band, tile); + if (result < 0) { + warning("Corrupted tile data encountered!"); + break; + } + + if ((((int)_ctx._gb->pos() - pos) >> 3) != tile->_dataSize) { + warning("Tile _dataSize mismatch!"); + result = -1; + break; + } + + pos += tile->_dataSize << 3; // skip to next tile + } + } + + // restore the selected rvmap table by applying its corrections in + // reverse order + for (i = band->_numCorr - 1; i >= 0; i--) { + idx1 = band->_corr[i * 2]; + idx2 = band->_corr[i * 2 + 1]; + SWAP(band->_rvMap->_runtab[idx1], band->_rvMap->_runtab[idx2]); + SWAP(band->_rvMap->_valtab[idx1], band->_rvMap->_valtab[idx2]); + if (idx1 == band->_rvMap->_eobSym || idx2 == band->_rvMap->_eobSym) + band->_rvMap->_eobSym ^= idx1 ^ idx2; + if (idx1 == band->_rvMap->_escSym || idx2 == band->_rvMap->_escSym) + band->_rvMap->_escSym ^= idx1 ^ idx2; + } + + _ctx._gb->align(); + + return result; +} + +void IndeoDecoderBase::recomposeHaar(const IVIPlaneDesc *_plane, + uint8 *dst, const int dstPitch) { + int x, y, indx, b0, b1, b2, b3, p0, p1, p2, p3; + const short *b0Ptr, *b1Ptr, *b2Ptr, *b3Ptr; + int32 pitch; + + // all bands should have the same _pitch + pitch = _plane->_bands[0]._pitch; + + // get pointers to the wavelet bands + b0Ptr = _plane->_bands[0]._buf; + b1Ptr = _plane->_bands[1]._buf; + b2Ptr = _plane->_bands[2]._buf; + b3Ptr = _plane->_bands[3]._buf; + + for (y = 0; y < _plane->_height; y += 2) { + for (x = 0, indx = 0; x < _plane->_width; x += 2, indx++) { + // load coefficients + b0 = b0Ptr[indx]; //should be: b0 = (_numBands > 0) ? b0Ptr[indx] : 0; + b1 = b1Ptr[indx]; //should be: b1 = (_numBands > 1) ? b1Ptr[indx] : 0; + b2 = b2Ptr[indx]; //should be: b2 = (_numBands > 2) ? b2Ptr[indx] : 0; + b3 = b3Ptr[indx]; //should be: b3 = (_numBands > 3) ? b3Ptr[indx] : 0; + + // haar wavelet recomposition + p0 = (b0 + b1 + b2 + b3 + 2) >> 2; + p1 = (b0 + b1 - b2 - b3 + 2) >> 2; + p2 = (b0 - b1 + b2 - b3 + 2) >> 2; + p3 = (b0 - b1 - b2 + b3 + 2) >> 2; + + // bias, convert and output four pixels + dst[x] = avClipUint8(p0 + 128); + dst[x + 1] = avClipUint8(p1 + 128); + dst[dstPitch + x] = avClipUint8(p2 + 128); + dst[dstPitch + x + 1] = avClipUint8(p3 + 128); + }// for x + + dst += dstPitch << 1; + + b0Ptr += pitch; + b1Ptr += pitch; + b2Ptr += pitch; + b3Ptr += pitch; + }// for y +} + +void IndeoDecoderBase::recompose53(const IVIPlaneDesc *_plane, + uint8 *dst, const int dstPitch) { + int x, y, indx; + int32 p0, p1, p2, p3, tmp0, tmp1, tmp2; + int32 b0_1, b0_2, b1_1, b1_2, b1_3, b2_1, b2_2, b2_3, b2_4, b2_5, b2_6; + int32 b3_1, b3_2, b3_3, b3_4, b3_5, b3_6, b3_7, b3_8, b3_9; + int32 _pitch, back_pitch; + const short *b0Ptr, *b1Ptr, *b2Ptr, *b3Ptr; + const int numBands = 4; + + // all bands should have the same _pitch + _pitch = _plane->_bands[0]._pitch; + + // pixels at the position "y-1" will be set to pixels at the "y" for the 1st iteration + back_pitch = 0; + + // get pointers to the wavelet bands + b0Ptr = _plane->_bands[0]._buf; + b1Ptr = _plane->_bands[1]._buf; + b2Ptr = _plane->_bands[2]._buf; + b3Ptr = _plane->_bands[3]._buf; + + for (y = 0; y < _plane->_height; y += 2) { + + if (y + 2 >= _plane->_height) + _pitch = 0; + // load storage variables with values + if (numBands > 0) { + b0_1 = b0Ptr[0]; + b0_2 = b0Ptr[_pitch]; + } + + if (numBands > 1) { + b1_1 = b1Ptr[back_pitch]; + b1_2 = b1Ptr[0]; + b1_3 = b1_1 - b1_2 * 6 + b1Ptr[_pitch]; + } + + if (numBands > 2) { + b2_2 = b2Ptr[0]; // b2[x, y ] + b2_3 = b2_2; // b2[x+1,y ] = b2[x,y] + b2_5 = b2Ptr[_pitch]; // b2[x ,y+1] + b2_6 = b2_5; // b2[x+1,y+1] = b2[x,y+1] + } + + if (numBands > 3) { + b3_2 = b3Ptr[back_pitch]; // b3[x ,y-1] + b3_3 = b3_2; // b3[x+1,y-1] = b3[x ,y-1] + b3_5 = b3Ptr[0]; // b3[x ,y ] + b3_6 = b3_5; // b3[x+1,y ] = b3[x ,y ] + b3_8 = b3_2 - b3_5 * 6 + b3Ptr[_pitch]; + b3_9 = b3_8; + } + + for (x = 0, indx = 0; x < _plane->_width; x += 2, indx++) { + if (x + 2 >= _plane->_width) { + b0Ptr--; + b1Ptr--; + b2Ptr--; + b3Ptr--; + } + + // some values calculated in the previous iterations can + // be reused in the next ones, so do appropriate copying + b2_1 = b2_2; // b2[x-1,y ] = b2[x, y ] + b2_2 = b2_3; // b2[x ,y ] = b2[x+1,y ] + b2_4 = b2_5; // b2[x-1,y+1] = b2[x ,y+1] + b2_5 = b2_6; // b2[x ,y+1] = b2[x+1,y+1] + b3_1 = b3_2; // b3[x-1,y-1] = b3[x ,y-1] + b3_2 = b3_3; // b3[x ,y-1] = b3[x+1,y-1] + b3_4 = b3_5; // b3[x-1,y ] = b3[x ,y ] + b3_5 = b3_6; // b3[x ,y ] = b3[x+1,y ] + b3_7 = b3_8; // vert_HPF(x-1) + b3_8 = b3_9; // vert_HPF(x ) + + p0 = p1 = p2 = p3 = 0; + + // process the LL-band by applying LPF both vertically and horizontally + if (numBands > 0) { + tmp0 = b0_1; + tmp2 = b0_2; + b0_1 = b0Ptr[indx + 1]; + b0_2 = b0Ptr[_pitch + indx + 1]; + tmp1 = tmp0 + b0_1; + + p0 = tmp0 << 4; + p1 = tmp1 << 3; + p2 = (tmp0 + tmp2) << 3; + p3 = (tmp1 + tmp2 + b0_2) << 2; + } + + // process the HL-band by applying HPF vertically and LPF horizontally + if (numBands > 1) { + tmp0 = b1_2; + tmp1 = b1_1; + b1_2 = b1Ptr[indx + 1]; + b1_1 = b1Ptr[back_pitch + indx + 1]; + + tmp2 = tmp1 - tmp0 * 6 + b1_3; + b1_3 = b1_1 - b1_2 * 6 + b1Ptr[_pitch + indx + 1]; + + p0 += (tmp0 + tmp1) << 3; + p1 += (tmp0 + tmp1 + b1_1 + b1_2) << 2; + p2 += tmp2 << 2; + p3 += (tmp2 + b1_3) << 1; + } + + // process the LH-band by applying LPF vertically and HPF horizontally + if (numBands > 2) { + b2_3 = b2Ptr[indx + 1]; + b2_6 = b2Ptr[_pitch + indx + 1]; + + tmp0 = b2_1 + b2_2; + tmp1 = b2_1 - b2_2 * 6 + b2_3; + + p0 += tmp0 << 3; + p1 += tmp1 << 2; + p2 += (tmp0 + b2_4 + b2_5) << 2; + p3 += (tmp1 + b2_4 - b2_5 * 6 + b2_6) << 1; + } + + // process the HH-band by applying HPF both vertically and horizontally + if (numBands > 3) { + b3_6 = b3Ptr[indx + 1]; // b3[x+1,y ] + b3_3 = b3Ptr[back_pitch + indx + 1]; // b3[x+1,y-1] + + tmp0 = b3_1 + b3_4; + tmp1 = b3_2 + b3_5; + tmp2 = b3_3 + b3_6; + + b3_9 = b3_3 - b3_6 * 6 + b3Ptr[_pitch + indx + 1]; + + p0 += (tmp0 + tmp1) << 2; + p1 += (tmp0 - tmp1 * 6 + tmp2) << 1; + p2 += (b3_7 + b3_8) << 1; + p3 += b3_7 - b3_8 * 6 + b3_9; + } + + // output four pixels + dst[x] = avClipUint8((p0 >> 6) + 128); + dst[x + 1] = avClipUint8((p1 >> 6) + 128); + dst[dstPitch + x] = avClipUint8((p2 >> 6) + 128); + dst[dstPitch + x + 1] = avClipUint8((p3 >> 6) + 128); + }// for x + + dst += dstPitch << 1; + + back_pitch = -_pitch; + + b0Ptr += _pitch + 1; + b1Ptr += _pitch + 1; + b2Ptr += _pitch + 1; + b3Ptr += _pitch + 1; + } +} + +void IndeoDecoderBase::outputPlane(IVIPlaneDesc *_plane, uint8 *dst, int dstPitch) { + int x, y; + const int16 *src = _plane->_bands[0]._buf; + uint32 pitch = _plane->_bands[0]._pitch; + + if (!src) + return; + + for (y = 0; y < _plane->_height; y++) { + for (x = 0; x < _plane->_width; x++) + dst[x] = avClipUint8(src[x] + 128); + src += pitch; + dst += dstPitch; + } +} + +int IndeoDecoderBase::processEmptyTile(IVIBandDesc *band, + IVITile *tile, int32 mvScale) { + int x, y, needMc, mbn, blk, numBlocks, mvX, mvY, mcType; + int offs, mbOffset, rowOffset, ret; + IVIMbInfo *mb, *refMb; + const int16 *src; + int16 *dst; + IviMCFunc mcNoDeltaFunc; + + if (tile->_numMBs != IVI_MBs_PER_TILE(tile->_width, tile->_height, band->_mbSize)) { + warning("Allocated tile size %d mismatches " + "parameters %d in processEmptyTile()", + tile->_numMBs, IVI_MBs_PER_TILE(tile->_width, tile->_height, band->_mbSize)); + return -1; + } + + offs = tile->_yPos * band->_pitch + tile->_xPos; + mb = tile->_mbs; + refMb = tile->_refMbs; + rowOffset = band->_mbSize * band->_pitch; + needMc = 0; // reset the mc tracking flag + + for (y = tile->_yPos; y < (tile->_yPos + tile->_height); y += band->_mbSize) { + mbOffset = offs; + + for (x = tile->_xPos; x < (tile->_xPos + tile->_width); x += band->_mbSize) { + mb->_xPos = x; + mb->_yPos = y; + mb->_bufOffs = mbOffset; + + mb->_type = 1; // set the macroblocks _type = INTER + mb->_cbp = 0; // all blocks are empty + + if (!band->_qdeltaPresent && !band->_plane && !band->_bandNum) { + mb->_qDelta = band->_globQuant; + mb->_mvX = 0; + mb->_mvY = 0; + } + + if (band->_inheritQDelta && refMb) + mb->_qDelta = refMb->_qDelta; + + if (band->_inheritMv && refMb) { + // motion vector inheritance + if (mvScale) { + mb->_mvX = scaleMV(refMb->_mvX, mvScale); + mb->_mvY = scaleMV(refMb->_mvY, mvScale); + } else { + mb->_mvX = refMb->_mvX; + mb->_mvY = refMb->_mvY; + } + needMc |= mb->_mvX || mb->_mvY; // tracking non-zero motion vectors + { + int dmv_x, dmv_y, cx, cy; + + dmv_x = mb->_mvX >> band->_isHalfpel; + dmv_y = mb->_mvY >> band->_isHalfpel; + cx = mb->_mvX & band->_isHalfpel; + cy = mb->_mvY & band->_isHalfpel; + + if (mb->_xPos + dmv_x < 0 + || mb->_xPos + dmv_x + band->_mbSize + cx > band->_pitch + || mb->_yPos + dmv_y < 0 + || mb->_yPos + dmv_y + band->_mbSize + cy > band->_aHeight) { + warning("MV out of bounds"); + return -1; + } + } + } + + mb++; + if (refMb) + refMb++; + mbOffset += band->_mbSize; + } // for x + offs += rowOffset; + } // for y + + if (band->_inheritMv && needMc) { // apply motion compensation if there is at least one non-zero motion vector + numBlocks = (band->_mbSize != band->_blkSize) ? 4 : 1; // number of blocks per mb + mcNoDeltaFunc = (band->_blkSize == 8) ? IndeoDSP::ffIviMc8x8NoDelta + : IndeoDSP::ffIviMc4x4NoDelta; + + for (mbn = 0, mb = tile->_mbs; mbn < tile->_numMBs; mb++, mbn++) { + mvX = mb->_mvX; + mvY = mb->_mvY; + if (!band->_isHalfpel) { + mcType = 0; // we have only fullpel vectors + } else { + mcType = ((mvY & 1) << 1) | (mvX & 1); + mvX >>= 1; + mvY >>= 1; // convert halfpel vectors into fullpel ones + } + + for (blk = 0; blk < numBlocks; blk++) { + // adjust block position in the buffer according with its number + offs = mb->_bufOffs + band->_blkSize * ((blk & 1) + !!(blk & 2) * band->_pitch); + ret = iviMc(band, mcNoDeltaFunc, nullptr, offs, + mvX, mvY, 0, 0, mcType, -1); + if (ret < 0) + return ret; + } + } + } else { + // copy data from the reference tile into the current one + src = band->_refBuf + tile->_yPos * band->_pitch + tile->_xPos; + dst = band->_buf + tile->_yPos * band->_pitch + tile->_xPos; + for (y = 0; y < tile->_height; y++) { + memcpy(dst, src, tile->_width*sizeof(band->_buf[0])); + src += band->_pitch; + dst += band->_pitch; + } + } + + return 0; +} + +int IndeoDecoderBase::decodeTileDataSize(GetBits *gb) { + int len = 0; + + if (gb->getBit()) { + len = gb->getBits(8); + if (len == 255) + len = gb->getBits(24); + } + + // align the bitstream reader on the byte boundary + gb->align(); + + return len; +} + +int IndeoDecoderBase::decodeBlocks(GetBits *_gb, IVIBandDesc *band, IVITile *tile) { + int mbn, blk, numBlocks, blkSize, ret, isIntra; + int mcType = 0, mcType2 = -1; + int mvX = 0, mvY = 0, mvX2 = 0, mvY2 = 0; + int32 prevDc; + uint32 cbp, quant, bufOffs; + IVIMbInfo *mb; + IviMCFunc mcWithDeltaFunc, mcNoDeltaFunc; + IviMCAvgFunc mcAvgWithDeltaFunc, mcAvgNoDeltaFunc; + const uint8 *scaleTab; + + // init intra prediction for the DC coefficient + prevDc = 0; + blkSize = band->_blkSize; + // number of blocks per mb + numBlocks = (band->_mbSize != blkSize) ? 4 : 1; + if (blkSize == 8) { + mcWithDeltaFunc = IndeoDSP::ffIviMc8x8Delta; + mcNoDeltaFunc = IndeoDSP::ffIviMc8x8NoDelta; + mcAvgWithDeltaFunc = IndeoDSP::ffIviMcAvg8x8Delta; + mcAvgNoDeltaFunc = IndeoDSP::ffIviMcAvg8x8NoDelta; + } else { + mcWithDeltaFunc = IndeoDSP::ffIviMc4x4Delta; + mcNoDeltaFunc = IndeoDSP::ffIviMc4x4NoDelta; + mcAvgWithDeltaFunc = IndeoDSP::ffIviMcAvg4x4Delta; + mcAvgNoDeltaFunc = IndeoDSP::ffIviMcAvg4x4NoDelta; + } + + for (mbn = 0, mb = tile->_mbs; mbn < tile->_numMBs; mb++, mbn++) { + isIntra = !mb->_type; + cbp = mb->_cbp; + bufOffs = mb->_bufOffs; + + quant = band->_globQuant + mb->_qDelta; + if (_ctx._isIndeo4) + quant = avClipUintp2(quant, 5); + else + quant = av_clip((int)quant, 0, 23); + + scaleTab = isIntra ? band->_intraScale : band->_interScale; + if (scaleTab) + quant = scaleTab[quant]; + + if (!isIntra) { + mvX = mb->_mvX; + mvY = mb->_mvY; + mvX2 = mb->_bMvX; + mvY2 = mb->_bMvY; + if (band->_isHalfpel) { + mcType = ((mvY & 1) << 1) | (mvX & 1); + mcType2 = ((mvY2 & 1) << 1) | (mvX2 & 1); + mvX >>= 1; + mvY >>= 1; + mvX2 >>= 1; + mvY2 >>= 1; // convert halfpel vectors into fullpel ones + } + if (mb->_type == 2) + mcType = -1; + if (mb->_type != 2 && mb->_type != 3) + mcType2 = -1; + if (mb->_type) { + int dmv_x, dmv_y, cx, cy; + + dmv_x = mb->_mvX >> band->_isHalfpel; + dmv_y = mb->_mvY >> band->_isHalfpel; + cx = mb->_mvX & band->_isHalfpel; + cy = mb->_mvY & band->_isHalfpel; + + if (mb->_xPos + dmv_x < 0 || + mb->_xPos + dmv_x + band->_mbSize + cx > band->_pitch || + mb->_yPos + dmv_y < 0 || + mb->_yPos + dmv_y + band->_mbSize + cy > band->_aHeight) { + return -1; + } + } + if (mb->_type == 2 || mb->_type == 3) { + int dmv_x, dmv_y, cx, cy; + + dmv_x = mb->_bMvX >> band->_isHalfpel; + dmv_y = mb->_bMvY >> band->_isHalfpel; + cx = mb->_bMvX & band->_isHalfpel; + cy = mb->_bMvY & band->_isHalfpel; + + if (mb->_xPos + dmv_x < 0 || + mb->_xPos + dmv_x + band->_mbSize + cx > band->_pitch || + mb->_yPos + dmv_y < 0 || + mb->_yPos + dmv_y + band->_mbSize + cy > band->_aHeight) { + return -1; + } + } + } + + for (blk = 0; blk < numBlocks; blk++) { + // adjust block position in the buffer according to its number + if (blk & 1) { + bufOffs += blkSize; + } else if (blk == 2) { + bufOffs -= blkSize; + bufOffs += blkSize * band->_pitch; + } + + if (cbp & 1) { // block coded ? + ret = ivi_decode_coded_blocks(_gb, band, mcWithDeltaFunc, + mcAvgWithDeltaFunc, + mvX, mvY, mvX2, mvY2, + &prevDc, isIntra, + mcType, mcType2, quant, + bufOffs); + if (ret < 0) + return ret; + } else { + // block not coded + // for intra blocks apply the dc slant transform + // for inter - perform the motion compensation without delta + if (isIntra) { + ret = ivi_dc_transform(band, &prevDc, bufOffs, blkSize); + if (ret < 0) + return ret; + } else { + ret = iviMc(band, mcNoDeltaFunc, mcAvgNoDeltaFunc, + bufOffs, mvX, mvY, mvX2, mvY2, + mcType, mcType2); + if (ret < 0) + return ret; + } + } + + cbp >>= 1; + }// for blk + }// for mbn + + _gb->align(); + return 0; +} + +int IndeoDecoderBase::scaleMV(int mv, int mvScale) { + return (mv + (mv > 0) + (mvScale - 1)) >> mvScale; +} + +int IndeoDecoderBase::iviMc(IVIBandDesc *band, IviMCFunc mc, IviMCAvgFunc mcAvg, + int offs, int mvX, int mvY, int mvX2, int mvY2, + int mcType, int mcType2) { + int refOffs = offs + mvY * band->_pitch + mvX; + int bufSize = band->_pitch * band->_aHeight; + int minSize = band->_pitch * (band->_blkSize - 1) + band->_blkSize; + int refSize = (mcType > 1) * band->_pitch + (mcType & 1); + + if (mcType != -1) { + assert(offs >= 0 && refOffs >= 0 && band->_refBuf); + assert(bufSize - minSize >= offs); + assert(bufSize - minSize - refSize >= refOffs); + } + + if (mcType2 == -1) { + mc(band->_buf + offs, band->_refBuf + refOffs, band->_pitch, mcType); + } else { + int ref_offs2 = offs + mvY2 * band->_pitch + mvX2; + int ref_size2 = (mcType2 > 1) * band->_pitch + (mcType2 & 1); + if (offs < 0 || ref_offs2 < 0 || !band->_bRefBuf) + return -1; + if (bufSize - minSize - ref_size2 < ref_offs2) + return -1; + + if (mcType == -1) + mc(band->_buf + offs, band->_bRefBuf + ref_offs2, + band->_pitch, mcType2); + else + mcAvg(band->_buf + offs, band->_refBuf + refOffs, + band->_bRefBuf + ref_offs2, band->_pitch, + mcType, mcType2); + } + + return 0; +} + +int IndeoDecoderBase::ivi_decode_coded_blocks(GetBits *gb, IVIBandDesc *band, + IviMCFunc mc, IviMCAvgFunc mcAvg, int mvX, int mvY, + int mvX2, int mvY2, int *prevDc, int isIntra, + int mcType, int mcType2, uint32 quant, int offs) { + const uint16 *baseTab = isIntra ? band->_intraBase : band->_interBase; + RVMapDesc *rvmap = band->_rvMap; + uint8 colFlags[8]; + int32 trvec[64]; + uint32 sym = 0, q; + int lo, hi; + int pos, run, val; + int blkSize = band->_blkSize; + int numCoeffs = blkSize * blkSize; + int colMask = blkSize - 1; + int scanPos = -1; + int minSize = band->_pitch * (band->_transformSize - 1) + + band->_transformSize; + int bufSize = band->_pitch * band->_aHeight - offs; + + if (minSize > bufSize) + return -1; + + if (!band->_scan) { + warning("Scan pattern is not set."); + return -1; + } + + // zero transform vector + memset(trvec, 0, numCoeffs * sizeof(trvec[0])); + // zero column flags + memset(colFlags, 0, sizeof(colFlags)); + while (scanPos <= numCoeffs) { + sym = gb->getVLC2(band->_blkVlc._tab->_table, + IVI_VLC_BITS, 1); + if (sym == rvmap->_eobSym) + break; // End of block + + // Escape - run/val explicitly coded using 3 vlc codes + if (sym == rvmap->_escSym) { + run = gb->getVLC2(band->_blkVlc._tab->_table, IVI_VLC_BITS, 1) + 1; + lo = gb->getVLC2(band->_blkVlc._tab->_table, IVI_VLC_BITS, 1); + hi = gb->getVLC2(band->_blkVlc._tab->_table, IVI_VLC_BITS, 1); + // merge them and convert into signed val + val = IVI_TOSIGNED((hi << 6) | lo); + } else { + if (sym >= 256U) { + warning("Invalid sym encountered"); + return -1; + } + run = rvmap->_runtab[sym]; + val = rvmap->_valtab[sym]; + } + + // de-zigzag and dequantize + scanPos += run; + if (scanPos >= numCoeffs || scanPos < 0) + break; + pos = band->_scan[scanPos]; + + if (!val) + warning("Val = 0 encountered!"); + + q = (baseTab[pos] * quant) >> 9; + if (q > 1) + val = val * q + FFSIGN(val) * (((q ^ 1) - 1) >> 1); + trvec[pos] = val; + // track columns containing non-zero coeffs + colFlags[pos & colMask] |= !!val; + } + + if (scanPos < 0 || (scanPos >= numCoeffs && sym != rvmap->_eobSym)) + return -1; // corrupt block data + + // undoing DC coeff prediction for intra-blocks + if (isIntra && band->_is2dTrans) { + *prevDc += trvec[0]; + trvec[0] = *prevDc; + colFlags[0] |= !!*prevDc; + } + + if (band->_transformSize > band->_blkSize) { + warning("Too large transform"); + return -1; + } + + // apply inverse transform + band->_invTransform(trvec, band->_buf + offs, + band->_pitch, colFlags); + + // apply motion compensation + if (!isIntra) + return iviMc(band, mc, mcAvg, offs, mvX, mvY, mvX2, mvY2, + mcType, mcType2); + + return 0; +} + +int IndeoDecoderBase::ivi_dc_transform(IVIBandDesc *band, int *prevDc, + int bufOffs, int blkSize) { + int bufSize = band->_pitch * band->_aHeight - bufOffs; + int minSize = (blkSize - 1) * band->_pitch + blkSize; + + if (minSize > bufSize) + return -1; + + band->_dcTransform(prevDc, band->_buf + bufOffs, + band->_pitch, blkSize); + + return 0; +} + +/*------------------------------------------------------------------------*/ + +const uint8 IndeoDecoderBase::_ffIviVerticalScan8x8[64] = { + 0, 8, 16, 24, 32, 40, 48, 56, + 1, 9, 17, 25, 33, 41, 49, 57, + 2, 10, 18, 26, 34, 42, 50, 58, + 3, 11, 19, 27, 35, 43, 51, 59, + 4, 12, 20, 28, 36, 44, 52, 60, + 5, 13, 21, 29, 37, 45, 53, 61, + 6, 14, 22, 30, 38, 46, 54, 62, + 7, 15, 23, 31, 39, 47, 55, 63 +}; + +const uint8 IndeoDecoderBase::_ffIviHorizontalScan8x8[64] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63 +}; + +const uint8 IndeoDecoderBase::_ffIviDirectScan4x4[16] = { + 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 +}; + +const RVMapDesc IVI45DecContext::_ff_ivi_rvmap_tabs[9] = { +{ // MapTab0 + 5, // _eobSym + 2, // _escSym + // run table + {1, 1, 0, 1, 1, 0, 1, 1, 2, 2, 1, 1, 1, 1, 3, 3, + 1, 1, 2, 2, 1, 1, 4, 4, 1, 1, 1, 1, 2, 2, 5, 5, + 1, 1, 3, 3, 1, 1, 6, 6, 1, 2, 1, 2, 7, 7, 1, 1, + 8, 8, 1, 1, 4, 2, 1, 4, 2, 1, 3, 3, 1, 1, 1, 9, + 9, 1, 2, 1, 2, 1, 5, 5, 1, 1, 10, 10, 1, 1, 3, 3, + 2, 2, 1, 1, 11, 11, 6, 4, 4, 1, 6, 1, 2, 1, 2, 12, + 8, 1, 12, 7, 8, 7, 1, 16, 1, 16, 1, 3, 3, 13, 1, 13, + 2, 2, 1, 15, 1, 5, 14, 15, 1, 5, 14, 1, 17, 8, 17, 8, + 1, 4, 4, 2, 2, 1, 25, 25, 24, 24, 1, 3, 1, 3, 1, 8, + 6, 7, 6, 1, 18, 8, 18, 1, 7, 23, 2, 2, 23, 1, 1, 21, + 22, 9, 9, 22, 19, 1, 21, 5, 19, 5, 1, 33, 20, 33, 20, 8, + 4, 4, 1, 32, 2, 2, 8, 3, 32, 26, 3, 1, 7, 7, 26, 6, + 1, 6, 1, 1, 16, 1, 10, 1, 10, 2, 16, 29, 28, 2, 29, 28, + 1, 27, 5, 8, 5, 27, 1, 8, 3, 7, 3, 31, 41, 31, 1, 41, + 6, 1, 6, 7, 4, 4, 1, 1, 2, 1, 2, 11, 34, 30, 11, 1, + 30, 15, 15, 34, 36, 40, 36, 40, 35, 35, 37, 37, 39, 39, 38, 38}, + + // value table + { 1, -1, 0, 2, -2, 0, 3, -3, 1, -1, 4, -4, 5, -5, 1, -1, + 6, -6, 2, -2, 7, -7, 1, -1, 8, -8, 9, -9, 3, -3, 1, -1, + 10, -10, 2, -2, 11, -11, 1, -1, 12, 4, -12, -4, 1, -1, 13, -13, + 1, -1, 14, -14, 2, 5, 15, -2, -5, -15, -3, 3, 16, -16, 17, 1, + -1, -17, 6, 18, -6, -18, 2, -2, 19, -19, 1, -1, 20, -20, 4, -4, + 7, -7, 21, -21, 1, -1, 2, 3, -3, 22, -2, -22, 8, 23, -8, 1, + 2, -23, -1, 2, -2, -2, 24, 1, -24, -1, 25, 5, -5, 1, -25, -1, + 9, -9, 26, 1, -26, 3, 1, -1, 27, -3, -1, -27, 1, 3, -1, -3, + 28, -4, 4, 10, -10, -28, 1, -1, 1, -1, 29, 6, -29, -6, 30, -4, + 3, 3, -3, -30, 1, 4, -1, 31, -3, 1, 11, -11, -1, -31, 32, -1, + -1, 2, -2, 1, 1, -32, 1, 4, -1, -4, 33, -1, 1, 1, -1, 5, + 5, -5, -33, -1, -12, 12, -5, -7, 1, 1, 7, 34, 4, -4, -1, 4, + -34, -4, 35, 36, -2, -35, -2, -36, 2, 13, 2, -1, 1, -13, 1, -1, + 37, 1, -5, 6, 5, -1, 38, -6, -8, 5, 8, -1, 1, 1, -37, -1, + 5, 39, -5, -5, 6, -6, -38, -39, -14, 40, 14, 2, 1, 1, -2, -40, + -1, -2, 2, -1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1} +},{ + // MapTab1 + 0, // _eobSym + 38, // _escSym + // run table + {0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 8, 6, 8, 7, + 7, 9, 9, 10, 10, 11, 11, 1, 12, 1, 12, 13, 13, 16, 14, 16, + 14, 15, 15, 17, 17, 18, 0, 18, 19, 20, 21, 19, 22, 21, 20, 22, + 25, 24, 2, 25, 24, 23, 23, 2, 26, 28, 26, 28, 29, 27, 29, 27, + 33, 33, 1, 32, 1, 3, 32, 30, 36, 3, 36, 30, 31, 31, 35, 34, + 37, 41, 34, 35, 37, 4, 41, 4, 49, 8, 8, 49, 40, 38, 5, 38, + 40, 39, 5, 39, 42, 43, 42, 7, 57, 6, 43, 44, 6, 50, 7, 44, + 57, 48, 50, 48, 45, 45, 46, 47, 51, 46, 47, 58, 1, 51, 58, 1, + 52, 59, 53, 9, 52, 55, 55, 59, 53, 56, 54, 56, 54, 9, 64, 64, + 60, 63, 60, 63, 61, 62, 61, 62, 2, 10, 2, 10, 11, 1, 11, 13, + 12, 1, 12, 13, 16, 16, 8, 8, 14, 3, 3, 15, 14, 15, 4, 4, + 1, 17, 17, 5, 1, 7, 7, 5, 6, 1, 2, 2, 6, 22, 1, 25, + 21, 22, 8, 24, 1, 21, 25, 24, 8, 18, 18, 23, 9, 20, 23, 33, + 29, 33, 20, 1, 19, 1, 29, 36, 9, 36, 19, 41, 28, 57, 32, 3, + 28, 3, 1, 27, 49, 49, 1, 32, 26, 26, 2, 4, 4, 7, 57, 41, + 2, 7, 10, 5, 37, 16, 10, 27, 8, 8, 13, 16, 37, 13, 1, 5}, + + // value table + {0, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, + -1, 1, -1, 1, -1, 1, -1, 2, 1, -2, -1, 1, -1, 1, 1, -1, + -1, 1, -1, 1, -1, 1, 0, -1, 1, 1, 1, -1, 1, -1, -1, -1, + 1, 1, 2, -1, -1, 1, -1, -2, 1, 1, -1, -1, 1, 1, -1, -1, + 1, -1, 3, 1, -3, 2, -1, 1, 1, -2, -1, -1, -1, 1, 1, 1, + 1, 1, -1, -1, -1, 2, -1, -2, 1, 2, -2, -1, 1, 1, 2, -1, + -1, 1, -2, -1, 1, 1, -1, 2, 1, 2, -1, 1, -2, -1, -2, -1, + -1, 1, 1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 4, -1, -1, -4, + 1, 1, 1, 2, -1, -1, 1, -1, -1, 1, -1, -1, 1, -2, 1, -1, + 1, 1, -1, -1, 1, 1, -1, -1, 3, 2, -3, -2, 2, 5, -2, 2, + 2, -5, -2, -2, -2, 2, -3, 3, 2, 3, -3, 2, -2, -2, 3, -3, + 6, 2, -2, 3, -6, 3, -3, -3, 3, 7, -4, 4, -3, 2, -7, 2, + 2, -2, -4, 2, 8, -2, -2, -2, 4, 2, -2, 2, 3, 2, -2, -2, + 2, 2, -2, -8, -2, 9, -2, 2, -3, -2, 2, -2, 2, 2, 2, 4, + -2, -4, 10, 2, 2, -2, -9, -2, 2, -2, 5, 4, -4, 4, -2, 2, + -5, -4, -3, 4, 2, -3, 3, -2, -5, 5, 3, 3, -2, -3, -10, -4} +},{ + // MapTab2 + 2, // _eobSym + 11, // _escSym + // run table + {1, 1, 0, 2, 2, 1, 1, 3, 3, 4, 4, 0, 1, 1, 5, 5, + 2, 2, 6, 6, 7, 7, 1, 8, 1, 8, 3, 3, 9, 9, 1, 2, + 2, 1, 4, 10, 4, 10, 11, 11, 1, 5, 12, 12, 1, 5, 13, 13, + 3, 3, 6, 6, 2, 2, 14, 14, 16, 16, 15, 7, 15, 8, 8, 7, + 1, 1, 17, 17, 4, 4, 1, 1, 18, 18, 2, 2, 5, 5, 25, 3, + 9, 3, 25, 9, 19, 24, 19, 24, 1, 21, 20, 1, 21, 22, 20, 22, + 23, 23, 8, 6, 33, 6, 8, 33, 7, 7, 26, 26, 1, 32, 1, 32, + 28, 4, 28, 10, 29, 27, 27, 10, 41, 4, 29, 2, 2, 41, 36, 31, + 49, 31, 34, 30, 34, 36, 30, 35, 1, 49, 11, 5, 35, 11, 1, 3, + 3, 5, 37, 37, 8, 40, 8, 40, 12, 12, 42, 42, 1, 38, 16, 57, + 1, 6, 16, 39, 38, 6, 7, 7, 13, 13, 39, 43, 2, 43, 57, 2, + 50, 9, 44, 9, 50, 4, 15, 48, 44, 4, 1, 15, 48, 14, 14, 1, + 45, 45, 8, 3, 5, 8, 51, 47, 3, 46, 46, 47, 5, 51, 1, 17, + 17, 58, 1, 58, 2, 52, 52, 2, 53, 7, 59, 6, 6, 56, 53, 55, + 7, 55, 1, 54, 59, 56, 54, 10, 1, 10, 4, 60, 1, 60, 8, 4, + 8, 64, 64, 61, 1, 63, 3, 63, 62, 61, 5, 11, 5, 3, 11, 62}, + + // value table + { 1, -1, 0, 1, -1, 2, -2, 1, -1, 1, -1, 0, 3, -3, 1, -1, + 2, -2, 1, -1, 1, -1, 4, 1, -4, -1, 2, -2, 1, -1, 5, 3, + -3, -5, 2, 1, -2, -1, 1, -1, 6, 2, 1, -1, -6, -2, 1, -1, + 3, -3, 2, -2, 4, -4, 1, -1, 1, -1, 1, 2, -1, 2, -2, -2, + 7, -7, 1, -1, 3, -3, 8, -8, 1, -1, 5, -5, 3, -3, 1, 4, + 2, -4, -1, -2, 1, 1, -1, -1, 9, 1, 1, -9, -1, 1, -1, -1, + 1, -1, 3, -3, 1, 3, -3, -1, 3, -3, 1, -1, 10, 1, -10, -1, + 1, 4, -1, 2, 1, -1, 1, -2, 1, -4, -1, 6, -6, -1, 1, 1, + 1, -1, 1, 1, -1, -1, -1, 1, 11, -1, -2, 4, -1, 2, -11, 5, + -5, -4, -1, 1, 4, 1, -4, -1, -2, 2, 1, -1, 12, 1, -2, 1, + -12, 4, 2, 1, -1, -4, 4, -4, 2, -2, -1, 1, 7, -1, -1, -7, + -1, -3, 1, 3, 1, 5, 2, 1, -1, -5, 13, -2, -1, 2, -2, -13, + 1, -1, 5, 6, 5, -5, 1, 1, -6, 1, -1, -1, -5, -1, 14, 2, + -2, 1, -14, -1, 8, 1, -1, -8, 1, 5, 1, 5, -5, 1, -1, 1, + -5, -1, 15, 1, -1, -1, -1, 3, -15, -3, 6, 1, 16, -1, 6, -6, + -6, 1, -1, 1, -16, 1, 7, -1, 1, -1, -6, -3, 6, -7, 3, -1} +},{ + // MapTab3 + 0, // _eobSym + 35, // _escSym + // run table + {0, 1, 1, 2, 2, 3, 3, 4, 4, 1, 1, 5, 5, 6, 6, 7, + 7, 8, 8, 9, 9, 2, 2, 10, 10, 1, 1, 11, 11, 12, 12, 3, + 3, 13, 13, 0, 14, 14, 16, 15, 16, 15, 4, 4, 17, 1, 17, 1, + 5, 5, 18, 18, 2, 2, 6, 6, 8, 19, 7, 8, 7, 19, 20, 20, + 21, 21, 22, 24, 22, 24, 23, 23, 1, 1, 25, 25, 3, 3, 26, 26, + 9, 9, 27, 27, 28, 28, 33, 29, 4, 33, 29, 1, 4, 1, 32, 32, + 2, 2, 31, 10, 30, 10, 30, 31, 34, 34, 5, 5, 36, 36, 35, 41, + 35, 11, 41, 11, 37, 1, 8, 8, 37, 6, 1, 6, 40, 7, 7, 40, + 12, 38, 12, 39, 39, 38, 49, 13, 49, 13, 3, 42, 3, 42, 16, 16, + 43, 43, 14, 14, 1, 1, 44, 15, 44, 15, 2, 2, 57, 48, 50, 48, + 57, 50, 4, 45, 45, 4, 46, 47, 47, 46, 1, 51, 1, 17, 17, 51, + 8, 9, 9, 5, 58, 8, 58, 5, 52, 52, 55, 56, 53, 56, 55, 59, + 59, 53, 54, 1, 6, 54, 7, 7, 6, 1, 2, 3, 2, 3, 64, 60, + 60, 10, 10, 64, 61, 62, 61, 63, 1, 63, 62, 1, 18, 24, 18, 4, + 25, 4, 8, 21, 21, 1, 24, 22, 25, 22, 8, 11, 19, 11, 23, 1, + 20, 23, 19, 20, 5, 12, 5, 1, 16, 2, 12, 13, 2, 13, 1, 16}, + + // value table + { 0, 1, -1, 1, -1, 1, -1, 1, -1, 2, -2, 1, -1, 1, -1, 1, + -1, 1, -1, 1, -1, 2, -2, 1, -1, 3, -3, 1, -1, 1, -1, 2, + -2, 1, -1, 0, 1, -1, 1, 1, -1, -1, 2, -2, 1, 4, -1, -4, + 2, -2, 1, -1, -3, 3, 2, -2, 2, 1, 2, -2, -2, -1, 1, -1, + 1, -1, 1, 1, -1, -1, 1, -1, 5, -5, 1, -1, 3, -3, 1, -1, + 2, -2, 1, -1, 1, -1, 1, 1, 3, -1, -1, 6, -3, -6, -1, 1, + 4, -4, 1, 2, 1, -2, -1, -1, 1, -1, 3, -3, 1, -1, 1, 1, + -1, 2, -1, -2, 1, 7, -3, 3, -1, 3, -7, -3, 1, -3, 3, -1, + 2, 1, -2, 1, -1, -1, 1, 2, -1, -2, -4, -1, 4, 1, 2, -2, + 1, -1, -2, 2, 8, -8, -1, 2, 1, -2, -5, 5, 1, -1, -1, 1, + -1, 1, 4, -1, 1, -4, -1, -1, 1, 1, 9, 1, -9, 2, -2, -1, + -4, 3, -3, -4, -1, 4, 1, 4, 1, -1, 1, -1, 1, 1, -1, 1, + -1, -1, -1, 10, 4, 1, 4, -4, -4, -10, 6, 5, -6, -5, 1, -1, + 1, 3, -3, -1, 1, -1, -1, -1, 11, 1, 1, -11, -2, -2, 2, 5, + -2, -5, -5, 2, -2, 12, 2, -2, 2, 2, 5, -3, -2, 3, -2, -12, + -2, 2, 2, 2, -5, 3, 5, 13, -3, 7, -3, -3, -7, 3, -13, 3} +},{ + // MapTab4 + 0, // _eobSym + 34, // _escSym + // run table + {0, 1, 1, 1, 2, 2, 1, 3, 3, 1, 1, 1, 4, 4, 1, 5, + 2, 1, 5, 2, 1, 1, 6, 6, 1, 1, 1, 1, 1, 7, 3, 1, + 2, 3, 0, 1, 2, 7, 1, 1, 1, 8, 1, 1, 8, 1, 1, 1, + 9, 1, 9, 1, 2, 1, 1, 2, 1, 1, 10, 4, 1, 10, 1, 4, + 1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 2, 1, 5, 1, 1, 1, + 2, 5, 1, 11, 1, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 1, 6, 1, 6, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 12, + 3, 1, 12, 1, 1, 1, 2, 1, 1, 3, 1, 1, 1, 1, 1, 1, + 4, 1, 1, 1, 2, 1, 1, 4, 1, 1, 1, 1, 1, 1, 2, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 2, 1, 1, 5, + 1, 1, 1, 1, 1, 7, 1, 7, 1, 1, 2, 3, 1, 1, 1, 1, + 5, 1, 1, 1, 1, 1, 1, 2, 13, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 13, 2, 1, 1, 4, 1, 1, 1, + 3, 1, 6, 1, 1, 1, 14, 1, 1, 1, 1, 1, 14, 6, 1, 1, + 1, 1, 15, 2, 4, 1, 2, 3, 15, 1, 1, 1, 8, 1, 1, 8, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1}, + + // value table + { 0, 1, -1, 2, 1, -1, -2, 1, -1, 3, -3, 4, 1, -1, -4, 1, + 2, 5, -1, -2, -5, 6, 1, -1, -6, 7, -7, 8, -8, 1, 2, 9, + 3, -2, 0, -9, -3, -1, 10, -10, 11, 1, -11, 12, -1, -12, 13, -13, + 1, 14, -1, -14, 4, 15, -15, -4, 16, -16, 1, 2, 17, -1, -17, -2, + 18, -18, 19, -19, 20, 3, -20, 21, -21, -3, 5, 22, 2, -22, -23, 23, + -5, -2, 24, 1, -24, -1, 25, -25, 26, -26, -27, 27, 28, 29, -28, -29, + 6, 30, 2, -31, -2, -30, 31, -6, -32, 32, 33, -33, 34, -35, -34, 1, + 4, -36, -1, 35, 37, 36, 7, -37, 38, -4, -38, 39, 41, 40, -40, -39, + 3, 42, -43, -41, -7, -42, 43, -3, 44, -44, 45, -45, 46, 47, 8, -47, + -48, -46, 50, -50, 48, 49, 51, -49, 52, -52, 5, -51, -8, -53, 53, 3, + -56, 56, 55, 54, -54, 2, 60, -2, -55, 58, 9, -5, 59, 57, -57, -63, + -3, -58, -60, -61, 61, -59, -62, -9, 1, 64, 62, 69, -64, 63, 65, -67, + -68, 66, -65, 68, -66, -69, 67, -70, -1, 10, 71, -71, 4, 73, 72, 70, + 6, -76, -3, 74, -78, -74, 1, 78, 80, -72, -75, 76, -1, 3, -73, 79, + 75, 77, 1, 11, -4, -79, -10, -6, -1, -77, -83, -80, 2, 81, -84, -2, + 83, -81, 82, -82, 84, -87, -86, 85, -11, -85, 86, -89, 87, -88, 88, 89} +},{ + // MapTab5 + 2, // _eobSym + 33, // _escSym + // run table + {1, 1, 0, 2, 1, 2, 1, 3, 3, 1, 1, 4, 4, 2, 2, 1, + 1, 5, 5, 6, 1, 6, 1, 7, 7, 3, 3, 2, 8, 2, 8, 1, + 1, 0, 9, 9, 1, 1, 10, 4, 10, 4, 11, 11, 2, 1, 2, 1, + 12, 12, 3, 3, 1, 1, 13, 5, 5, 13, 14, 1, 1, 14, 2, 2, + 6, 6, 15, 1, 1, 15, 16, 4, 7, 16, 4, 7, 1, 1, 3, 3, + 8, 8, 2, 2, 1, 1, 17, 17, 1, 1, 18, 18, 5, 5, 2, 2, + 1, 1, 9, 19, 9, 19, 20, 3, 3, 20, 1, 10, 21, 1, 10, 4, + 4, 21, 22, 6, 6, 22, 1, 1, 23, 24, 2, 2, 23, 24, 11, 1, + 1, 11, 7, 25, 7, 1, 1, 25, 8, 8, 3, 26, 3, 1, 12, 2, + 2, 26, 1, 12, 5, 5, 27, 4, 1, 4, 1, 27, 28, 1, 28, 13, + 1, 13, 2, 29, 2, 1, 32, 6, 1, 30, 14, 29, 14, 6, 3, 31, + 3, 1, 30, 1, 32, 31, 33, 9, 33, 1, 1, 7, 9, 7, 2, 2, + 1, 1, 4, 36, 34, 4, 5, 10, 10, 5, 34, 1, 1, 35, 8, 8, + 36, 3, 35, 1, 15, 3, 2, 1, 16, 15, 16, 2, 37, 1, 37, 1, + 1, 1, 6, 6, 38, 1, 38, 11, 1, 39, 39, 40, 11, 2, 41, 4, + 40, 1, 2, 4, 1, 1, 1, 41, 3, 1, 3, 1, 5, 7, 5, 7}, + + // value table + { 1, -1, 0, 1, 2, -1, -2, 1, -1, 3, -3, 1, -1, 2, -2, 4, + -4, 1, -1, 1, 5, -1, -5, 1, -1, 2, -2, 3, 1, -3, -1, 6, + -6, 0, 1, -1, 7, -7, 1, 2, -1, -2, 1, -1, 4, 8, -4, -8, + 1, -1, 3, -3, 9, -9, 1, 2, -2, -1, 1, 10, -10, -1, 5, -5, + 2, -2, 1, 11, -11, -1, 1, 3, 2, -1, -3, -2, 12, -12, 4, -4, + 2, -2, -6, 6, 13, -13, 1, -1, 14, -14, 1, -1, 3, -3, 7, -7, + 15, -15, 2, 1, -2, -1, 1, 5, -5, -1, -16, 2, 1, 16, -2, 4, + -4, -1, 1, 3, -3, -1, 17, -17, 1, 1, -8, 8, -1, -1, 2, 18, + -18, -2, 3, 1, -3, 19, -19, -1, 3, -3, 6, 1, -6, 20, 2, 9, + -9, -1, -20, -2, 4, -4, 1, -5, 21, 5, -21, -1, 1, -22, -1, 2, + 22, -2, 10, 1, -10, 23, 1, 4, -23, 1, 2, -1, -2, -4, -7, 1, + 7, -24, -1, 24, -1, -1, 1, 3, -1, -25, 25, 4, -3, -4, 11, -11, + 26, -26, 6, 1, 1, -6, -5, -3, 3, 5, -1, -27, 27, 1, 4, -4, + -1, -8, -1, 28, 2, 8, -12, -28, -2, -2, 2, 12, -1, 29, 1, -29, + 30, -30, 5, -5, 1, -31, -1, 3, 31, -1, 1, 1, -3, -13, 1, -7, + -1, -32, 13, 7, 32, 33, -33, -1, -9, -34, 9, 34, -6, 5, 6, -5} +},{ + // MapTab6 + 2, // _eobSym + 13, // _escSym + // run table + {1, 1, 0, 1, 1, 2, 2, 1, 1, 3, 3, 1, 1, 0, 2, 2, + 4, 1, 4, 1, 1, 1, 5, 5, 1, 1, 6, 6, 2, 2, 1, 1, + 3, 3, 7, 7, 1, 1, 8, 8, 1, 1, 2, 2, 1, 9, 1, 9, + 4, 4, 10, 1, 1, 10, 1, 1, 11, 11, 3, 3, 1, 2, 1, 2, + 1, 1, 12, 12, 5, 5, 1, 1, 13, 1, 1, 13, 2, 2, 1, 1, + 6, 6, 1, 1, 4, 14, 4, 14, 3, 1, 3, 1, 1, 1, 15, 7, + 15, 2, 2, 7, 1, 1, 1, 8, 1, 8, 16, 16, 1, 1, 1, 1, + 2, 1, 1, 2, 1, 1, 3, 5, 5, 3, 4, 1, 1, 4, 1, 1, + 17, 17, 9, 1, 1, 9, 2, 2, 1, 1, 10, 10, 1, 6, 1, 1, + 6, 18, 1, 1, 18, 1, 1, 1, 2, 2, 3, 1, 3, 1, 1, 1, + 4, 1, 19, 1, 19, 7, 1, 1, 20, 1, 4, 20, 1, 7, 11, 2, + 1, 11, 21, 2, 8, 5, 1, 8, 1, 5, 21, 1, 1, 1, 22, 1, + 1, 22, 1, 1, 3, 3, 1, 23, 2, 12, 24, 1, 1, 2, 1, 1, + 12, 23, 1, 1, 24, 1, 1, 1, 4, 1, 1, 1, 2, 1, 6, 6, + 4, 2, 1, 1, 1, 1, 1, 1, 1, 14, 13, 3, 1, 25, 9, 25, + 14, 1, 9, 3, 13, 1, 1, 1, 1, 1, 10, 1, 1, 2, 10, 2}, + + // value table + {-20, -1, 0, 2, -2, 1, -1, 3, -3, 1, -1, 4, -4, 0, 2, -2, + 1, 5, -1, -5, 6, -6, 1, -1, 7, -7, 1, -1, 3, -3, 8, -8, + 2, -2, 1, -1, 9, -9, 1, -1, 10, -10, 4, -4, 11, 1, -11, -1, + 2, -2, 1, 12, -12, -1, 13, -13, 1, -1, 3, -3, 14, 5, -14, -5, + -15, 15, -1, 1, 2, -2, 16, -16, 1, 17, -17, -1, 6, -6, 18, -18, + 2, -2, -19, 19, -3, 1, 3, -1, 4, 20, -4, 1, -21, 21, 1, 2, + -1, -7, 7, -2, 22, -22, 23, 2, -23, -2, 1, -1, -24, 24, -25, 25, + -8, -26, 26, 8, -27, 27, 5, 3, -3, -5, -4, 28, -28, 4, 29, -29, + 1, -1, -2, -30, 30, 2, 9, -9, -31, 31, 2, -2, -32, 3, 32, -33, + -3, 1, 33, -34, -1, 34, -35, 35, -10, 10, -6, 36, 6, -36, 37, -37, + -5, 38, 1, -38, -1, 3, 39, -39, -1, 40, 5, 1, -40, -3, 2, -11, + -41, -2, 1, 11, -3, -4, 41, 3, 42, 4, -1, -43, -42, 43, 1, -44, + 45, -1, 44, -45, -7, 7, -46, 1, -12, 2, 1, -47, 46, 12, 47, 48, + -2, -1, -48, 49, -1, -50, -49, 50, -6, -51, 51, 52, -13, 53, -4, 4, + 6, 13, -53, -52, -54, 55, 54, -55, -56, -2, 2, -8, 56, 1, -3, -1, + 2, 58, 3, 8, -2, 57, -58, -60, -59, -57, -3, 60, 59, -14, 3, 14} +},{ + // MapTab7 + 2, // _eobSym + 38, // _escSym + // run table + {1, 1, 0, 2, 2, 1, 1, 3, 3, 4, 4, 5, 5, 1, 1, 6, + 6, 2, 2, 7, 7, 8, 8, 1, 1, 3, 3, 9, 9, 10, 10, 1, + 1, 2, 2, 4, 4, 11, 0, 11, 12, 12, 13, 13, 1, 1, 5, 5, + 14, 14, 15, 16, 15, 16, 3, 3, 1, 6, 1, 6, 2, 2, 7, 7, + 8, 8, 17, 17, 1, 1, 4, 4, 18, 18, 2, 2, 1, 19, 1, 20, + 19, 20, 21, 21, 3, 3, 22, 22, 5, 5, 24, 1, 1, 23, 9, 23, + 24, 9, 2, 2, 10, 1, 1, 10, 6, 6, 25, 4, 4, 25, 7, 7, + 26, 8, 1, 8, 3, 1, 26, 3, 11, 11, 27, 27, 2, 28, 1, 2, + 28, 1, 12, 12, 5, 5, 29, 13, 13, 29, 32, 1, 1, 33, 31, 30, + 32, 4, 30, 33, 4, 31, 3, 14, 1, 1, 3, 34, 34, 2, 2, 14, + 6, 6, 35, 36, 35, 36, 1, 15, 1, 16, 16, 15, 7, 9, 7, 9, + 37, 8, 8, 37, 1, 1, 39, 2, 38, 39, 2, 40, 5, 38, 40, 5, + 3, 3, 4, 4, 10, 10, 1, 1, 1, 1, 41, 2, 41, 2, 6, 6, + 1, 1, 11, 42, 11, 43, 3, 42, 3, 17, 4, 43, 1, 17, 7, 1, + 8, 44, 4, 7, 44, 5, 8, 2, 5, 1, 2, 48, 45, 1, 12, 45, + 12, 48, 13, 13, 1, 9, 9, 46, 1, 46, 47, 47, 49, 18, 18, 49}, + + // value table + { 1, -1, 0, 1, -1, 2, -2, 1, -1, 1, -1, 1, -1, 3, -3, 1, + -1, -2, 2, 1, -1, 1, -1, 4, -4, -2, 2, 1, -1, 1, -1, 5, + -5, -3, 3, 2, -2, 1, 0, -1, 1, -1, 1, -1, 6, -6, 2, -2, + 1, -1, 1, 1, -1, -1, -3, 3, 7, 2, -7, -2, -4, 4, 2, -2, + 2, -2, 1, -1, 8, -8, 3, -3, 1, -1, -5, 5, 9, 1, -9, 1, + -1, -1, 1, -1, -4, 4, 1, -1, 3, -3, 1, -10, 10, 1, 2, -1, + -1, -2, 6, -6, 2, 11, -11, -2, 3, -3, 1, -4, 4, -1, 3, -3, + 1, 3, 12, -3, -5, -12, -1, 5, 2, -2, 1, -1, -7, 1, 13, 7, + -1, -13, 2, -2, 4, -4, 1, 2, -2, -1, 1, 14, -14, 1, 1, 1, + -1, -5, -1, -1, 5, -1, -6, 2, -15, 15, 6, 1, -1, -8, 8, -2, + -4, 4, 1, 1, -1, -1, 16, 2, -16, -2, 2, -2, 4, 3, -4, -3, + -1, -4, 4, 1, -17, 17, -1, -9, 1, 1, 9, 1, -5, -1, -1, 5, + -7, 7, 6, -6, 3, -3, 18, -18, 19, -19, 1, -10, -1, 10, -5, 5, + 20, -20, -3, 1, 3, 1, 8, -1, -8, 2, 7, -1, -21, -2, 5, 21, + 5, -1, -7, -5, 1, -6, -5, -11, 6, 22, 11, 1, 1, -22, -3, -1, + 3, -1, 3, -3, -23, 4, -4, 1, 23, -1, 1, -1, 1, -2, 2, -1} +},{ + // MapTab8 + 4, // _eobSym + 11, // _escSym + // run table + {1, 1, 1, 1, 0, 2, 2, 1, 1, 3, 3, 0, 1, 1, 2, 2, + 4, 4, 1, 1, 5, 5, 1, 1, 2, 2, 3, 3, 6, 6, 1, 1, + 7, 7, 8, 1, 8, 2, 2, 1, 4, 4, 1, 3, 1, 3, 9, 9, + 2, 2, 1, 5, 1, 5, 10, 10, 1, 1, 11, 11, 3, 6, 3, 4, + 4, 6, 2, 2, 1, 12, 1, 12, 7, 13, 7, 13, 1, 1, 8, 8, + 2, 2, 14, 14, 16, 15, 16, 5, 5, 1, 3, 15, 1, 3, 4, 4, + 1, 1, 17, 17, 2, 2, 6, 6, 1, 18, 1, 18, 22, 21, 22, 21, + 25, 24, 25, 19, 9, 20, 9, 23, 19, 24, 20, 3, 23, 7, 3, 1, + 1, 7, 28, 26, 29, 5, 28, 26, 5, 8, 29, 4, 8, 27, 2, 2, + 4, 27, 1, 1, 10, 36, 10, 33, 33, 36, 30, 1, 32, 32, 1, 30, + 6, 31, 31, 35, 3, 6, 11, 11, 3, 2, 35, 2, 34, 1, 34, 1, + 37, 37, 12, 7, 12, 5, 41, 5, 4, 7, 1, 8, 13, 4, 1, 41, + 13, 38, 8, 38, 9, 1, 40, 40, 9, 1, 39, 2, 2, 49, 39, 42, + 3, 3, 14, 16, 49, 14, 16, 42, 43, 43, 6, 6, 15, 1, 1, 15, + 44, 44, 1, 1, 50, 48, 4, 5, 4, 7, 5, 2, 10, 10, 48, 7, + 50, 45, 2, 1, 45, 8, 8, 1, 46, 46, 3, 47, 47, 3, 1, 1}, + + // value table + { 1, -1, 2, -2, 0, 1, -1, 3, -3, 1, -1, 0, 4, -4, 2, -2, + 1, -1, 5, -5, 1, -1, 6, -6, 3, -3, 2, -2, 1, -1, 7, -7, + 1, -1, 1, 8, -1, 4, -4, -8, 2, -2, 9, 3, -9, -3, 1, -1, + 5, -5, 10, 2, -10, -2, 1, -1, 11, -11, 1, -1, -4, 2, 4, 3, + -3, -2, 6, -6, 12, 1, -12, -1, 2, 1, -2, -1, 13, -13, 2, -2, + 7, -7, 1, -1, 1, 1, -1, 3, -3, 14, 5, -1, -14, -5, 4, -4, + 15, -15, 1, -1, 8, -8, -3, 3, 16, 1, -16, -1, 1, 1, -1, -1, + 1, 1, -1, 1, 2, 1, -2, 1, -1, -1, -1, 6, -1, 3, -6, 17, + -17, -3, 1, 1, 1, 4, -1, -1, -4, 3, -1, 5, -3, -1, -9, 9, + -5, 1, 18, -18, 2, 1, -2, 1, -1, -1, 1, 19, -1, 1, -19, -1, + 4, 1, -1, 1, 7, -4, -2, 2, -7, 10, -1, -10, 1, 20, -1, -20, + 1, -1, 2, 4, -2, 5, 1, -5, 6, -4, 21, 4, 2, -6, -21, -1, + -2, 1, -4, -1, -3, 22, -1, 1, 3, -22, -1, 11, -11, 1, 1, 1, + 8, -8, 2, 2, -1, -2, -2, -1, 1, -1, -5, 5, 2, 23, -23, -2, + 1, -1, 24, -24, -1, -1, 7, 6, -7, 5, -6, 12, -3, 3, 1, -5, + 1, 1, -12, 25, -1, -5, 5, -25, -1, 1, 9, 1, -1, -9, 26, -26} +} +}; + +} // End of namespace Indeo +} // End of namespace Image diff --git a/image/codecs/indeo/indeo.h b/image/codecs/indeo/indeo.h new file mode 100644 index 0000000000..3b0b1854f5 --- /dev/null +++ b/image/codecs/indeo/indeo.h @@ -0,0 +1,584 @@ +/* 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. + * + */ + +#include "common/scummsys.h" +#include "graphics/managed_surface.h" +#include "image/codecs/codec.h" + +/* Common structures, macros, and base class shared by both Indeo4 and + * Indeo5 decoders, derived from ffmpeg. We don't currently support Indeo5 + * decoding, but just in case we eventually need it, this is kept as a separate + * file like it is in ffmpeg. + * + * Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg + * written, produced, and directed by Alan Smithee + */ + +#ifndef IMAGE_CODECS_INDEO_INDEO_H +#define IMAGE_CODECS_INDEO_INDEO_H + +#include "image/codecs/indeo/get_bits.h" +#include "image/codecs/indeo/vlc.h" + +namespace Image { +namespace Indeo { + +/** + * Indeo 4 frame types. + */ +enum { + IVI4_FRAMETYPE_INTRA = 0, + IVI4_FRAMETYPE_INTRA1 = 1, ///< intra frame with slightly different bitstream coding + IVI4_FRAMETYPE_INTER = 2, ///< non-droppable P-frame + IVI4_FRAMETYPE_BIDIR = 3, ///< bidirectional frame + IVI4_FRAMETYPE_INTER_NOREF = 4, ///< droppable P-frame + IVI4_FRAMETYPE_NULL_FIRST = 5, ///< empty frame with no data + IVI4_FRAMETYPE_NULL_LAST = 6 ///< empty frame with no data +}; + +enum { + IVI_MB_HUFF = 0, /// Huffman table is used for coding macroblocks + IVI_BLK_HUFF = 1 /// Huffman table is used for coding blocks +}; + +/** + * Declare inverse transform function types + */ +typedef void (InvTransformPtr)(const int32 *in, int16 *out, uint32 pitch, const uint8 *flags); +typedef void (DCTransformPtr)(const int32 *in, int16 *out, uint32 pitch, int blkSize); + +typedef void (*IviMCFunc)(int16 *buf, const int16 *refBuf, uint32 pitch, int mcType); +typedef void (*IviMCAvgFunc)(int16 *buf, const int16 *refBuf1, const int16 *refBuf2, + uint32 pitch, int mcType, int mcType2); + +///< max number of bits of the ivi's huffman codes +#define IVI_VLC_BITS 13 +#define IVI5_IS_PROTECTED 0x20 + +/** + * convert unsigned values into signed ones (the sign is in the LSB) + */ +#define IVI_TOSIGNED(val) (-(((val) >> 1) ^ -((val) & 1))) + +/** + * calculate number of macroblocks in a tile + */ +#define IVI_MBs_PER_TILE(tileWidth, tileHeight, mbSize) \ + ((((tileWidth) + (mbSize) - 1) / (mbSize)) * (((tileHeight) + (mbSize) - 1) / (mbSize))) + +/** + * huffman codebook descriptor + */ +struct IVIHuffDesc { + int32 _numRows; + uint8 _xBits[16]; + + /** + * Generate a huffman codebook from the given descriptor + * and convert it into the FFmpeg VLC table. + * + * @param[out] vlc Where to place the generated VLC table + * @param[in] flag Flag: true - for static or false for dynamic tables + * @returns result code: 0 - OK, -1 = error (invalid codebook descriptor) + */ + int createHuffFromDesc(VLC *vlc, bool flag) const; + + /** + * Compare two huffman codebook descriptors. + * + * @param[in] desc2 Ptr to the 2nd descriptor to compare + * @returns comparison result: 0 - equal, 1 - not equal + */ + bool huffDescCompare(const IVIHuffDesc *desc2) const; + + /** + * Copy huffman codebook descriptors. + * + * @param[in] src ptr to the source descriptor + */ + void huffDescCopy(const IVIHuffDesc *src); +}; + +struct IVI45DecContext; + +/** + * Macroblock/block huffman table descriptor + */ +struct IVIHuffTab { +public: + int32 _tabSel; /// index of one of the predefined tables + /// or "7" for custom one + VLC * _tab; /// pointer to the table associated with tab_sel + + /// the following are used only when tab_sel == 7 + IVIHuffDesc _custDesc; /// custom Huffman codebook descriptor + VLC _custTab; /// vlc table for custom codebook + + /** + * Constructor + */ + IVIHuffTab(); + + /** + * Decode a huffman codebook descriptor from the bitstream + * and select specified huffman table. + * + * @param[in] ctx Decoder context + * @param[in] descCoded Flag signalling if table descriptor was coded + * @param[in] whichTab Codebook purpose (IVI_MB_HUFF or IVI_BLK_HUFF) + * @returns Zero on success, negative value otherwise + */ + int decodeHuffDesc(IVI45DecContext *ctx, int descCoded, int whichTab); +}; + +/** + * run-value (RLE) table descriptor + */ +struct RVMapDesc { + uint8 _eobSym; ///< end of block symbol + uint8 _escSym; ///< escape symbol + uint8 _runtab[256]; + int8 _valtab[256]; +}; + +/** + * information for Indeo macroblock (16x16, 8x8 or 4x4) + */ +struct IVIMbInfo { + int16 _xPos; + int16 _yPos; + uint32 _bufOffs; ///< address in the output buffer for this mb + uint8 _type; ///< macroblock type: 0 - INTRA, 1 - INTER + uint8 _cbp; ///< coded block pattern + int8 _qDelta; ///< quant delta + int8 _mvX; ///< motion vector (x component) + int8 _mvY; ///< motion vector (y component) + int8 _bMvX; ///< second motion vector (x component) + int8 _bMvY; ///< second motion vector (y component) + + IVIMbInfo(); +}; + +/** + * information for Indeo tile + */ +struct IVITile { + int _xPos; + int _yPos; + int _width; + int _height; + int _mbSize; + bool _isEmpty; + int _dataSize; ///< size of the data in bytes + int _numMBs; ///< number of macroblocks in this tile + IVIMbInfo * _mbs; ///< array of macroblock descriptors + IVIMbInfo * _refMbs; ///< ptr to the macroblock descriptors of the reference tile + + IVITile(); +}; + +/** + * information for Indeo wavelet band + */ +struct IVIBandDesc { + int _plane; ///< plane number this band belongs to + int _bandNum; ///< band number + int _width; + int _height; + int _aHeight; ///< aligned band height + const uint8 * _dataPtr; ///< ptr to the first byte of the band data + int _dataSize; ///< size of the band data + int16 * _buf; ///< pointer to the output buffer for this band + int16 * _refBuf; ///< pointer to the reference frame buffer (for motion compensation) + int16 * _bRefBuf; ///< pointer to the second reference frame buffer (for motion compensation) + int16 * _bufs[4]; ///< array of pointers to the band buffers + int _pitch; ///< _pitch associated with the buffers above + bool _isEmpty; + int _mbSize; ///< macroblock size + int _blkSize; ///< block size + uint8 _isHalfpel; ///< precision of the motion compensation: 0 - fullpel, 1 - halfpel + bool _inheritMv; ///< tells if motion vector is inherited from reference macroblock + bool _inheritQDelta; ///< tells if quantiser delta is inherited from reference macroblock + bool _qdeltaPresent; ///< tells if Qdelta signal is present in the bitstream (Indeo5 only) + int _quantMat; ///< dequant matrix index + int _globQuant; ///< quant base for this band + const uint8 * _scan; ///< ptr to the scan pattern + int _scanSize; ///< size of the scantable + + IVIHuffTab _blkVlc; ///< vlc table for decoding block data + + int _numCorr; ///< number of correction entries + uint8 _corr[61 * 2]; ///< rvmap correction pairs + int _rvmapSel; ///< rvmap table selector + RVMapDesc * _rvMap; ///< ptr to the RLE table for this band + int _numTiles; ///< number of tiles in this band + IVITile * _tiles; ///< array of tile descriptors + InvTransformPtr *_invTransform; + int _transformSize; + DCTransformPtr *_dcTransform; + bool _is2dTrans; + int32 _checksum; ///< for debug purposes + int _checksumPresent; + int _bufSize; ///< band buffer size in bytes + const uint16 * _intraBase; ///< quantization matrix for intra blocks + const uint16 * _interBase; ///< quantization matrix for inter blocks + const uint8 * _intraScale; ///< quantization coefficient for intra blocks + const uint8 * _interScale; ///< quantization coefficient for inter blocks + + IVIBandDesc(); + + int initTiles(IVITile *refTile, int p, int b, int tHeight, int tWidth); +}; + +struct IVIPicConfig { + uint16 _picWidth; + uint16 _picHeight; + uint16 _chromaWidth; + uint16 _chromaHeight; + uint16 _tileWidth; + uint16 _tileHeight; + uint8 _lumaBands; + uint8 _chromaBands; + + IVIPicConfig(); + + /** + * Compare some properties of two pictures + */ + bool ivi_pic_config_cmp(const IVIPicConfig &cfg2); +}; + +/** + * color plane (luma or chroma) information + */ +struct IVIPlaneDesc { + uint16 _width; + uint16 _height; + uint8 _numBands; ///< number of bands this plane subdivided into + IVIBandDesc * _bands; ///< array of band descriptors + + IVIPlaneDesc(); + + static int initPlanes(IVIPlaneDesc *planes, const IVIPicConfig *cfg, bool isIndeo4); + + static int initTiles(IVIPlaneDesc *planes, int tileWidth, int tileHeight); + + /** + * Free planes, bands and macroblocks buffers. + * + * @param[in] planes pointer to the array of the plane descriptors + */ + static void freeBuffers(IVIPlaneDesc *planes); + + /** + * Check if the given dimension of an image is valid, meaning that all + * bytes of the image can be addressed with a signed int. + * + * @param w the width of the picture + * @param h the height of the picture + * @param log_offset the offset to sum to the log level for logging with log_ctx + * @returns >= 0 if valid, a negative error code otherwise + */ + static int checkImageSize(unsigned int w, unsigned int h, int logOffset); +}; + +struct AVFrame { + /** + * Dimensions + */ + int _width, _height; + +#define AV_NUM_DATA_POINTERS 3 + /** + * pointer to the picture/channel planes. + * This might be different from the first allocated byte + * + * Some decoders access areas outside 0,0 - width,height, please + * see avcodec_align_dimensions2(). Some filters and swscale can read + * up to 16 bytes beyond the planes, if these filters are to be used, + * then 16 extra bytes must be allocated. + * + * NOTE: Except for hwaccel formats, pointers not needed by the format + * MUST be set to NULL. + */ + uint8 *_data[AV_NUM_DATA_POINTERS]; + + /** + * For video, size in bytes of each picture line. + * For audio, size in bytes of each plane. + * + * For audio, only linesize[0] may be set. For planar audio, each channel + * plane must be the same size. + * + * For video the linesizes should be multiples of the CPUs alignment + * preference, this is 16 or 32 for modern desktop CPUs. + * Some code requires such alignment other code can be slower without + * correct alignment, for yet other it makes no difference. + * + * @note The linesize may be larger than the size of usable data -- there + * may be extra padding present for performance reasons. + */ + int _linesize[AV_NUM_DATA_POINTERS]; + + /** + * Constructor + */ + AVFrame(); + + /** + * Destructor + */ + ~AVFrame() { freeFrame(); } + + /** + * Sets the frame dimensions + */ + int setDimensions(uint16 width, uint16 height); + + /** + * Get a buffer for a frame + */ + int getBuffer(int flags); + + /** + * Frees any data loaded for the frame + */ + void freeFrame(); +}; + +struct IVI45DecContext { + friend struct IVIHuffTab; +private: + VLC_TYPE _tableData[8192 * 16][2]; + VLC _iviMbVlcTabs[8]; ///< static macroblock Huffman tables + VLC _iviBlkVlcTabs[8]; ///< static block Huffman tables +public: + GetBits * _gb; + RVMapDesc _rvmapTabs[9]; ///< local corrected copy of the static rvmap tables + + uint32 _frameNum; + int _frameType; + int _prevFrameType; ///< frame type of the previous frame + uint32 _dataSize; ///< size of the frame data in bytes from picture header + int _isScalable; + const uint8 * _frameData; ///< input frame data pointer + int _interScal; ///< signals a sequence of scalable inter frames + uint32 _frameSize; ///< frame size in bytes + uint32 _picHdrSize; ///< picture header size in bytes + uint8 _frameFlags; + uint16 _checksum; ///< frame _checksum + + IVIPicConfig _picConf; + IVIPlaneDesc _planes[3]; ///< color planes + + int _bufSwitch; ///< used to switch between three buffers + int _dstBuf; ///< buffer index for the currently decoded frame + int _refBuf; ///< inter frame reference buffer index + int _ref2Buf; ///< temporal storage for switching buffers + int _bRefBuf; ///< second reference frame buffer index + + IVIHuffTab _mbVlc; ///< current macroblock table descriptor + IVIHuffTab _blkVlc; ///< current block table descriptor + + uint8 _rvmapSel; + bool _inImf; + bool _inQ; ///< flag for explicitly stored quantiser delta + uint8 _picGlobQuant; + uint8 _unknown1; + + uint16 _gopHdrSize; + uint8 _gopFlags; + uint32 _lockWord; + + bool _hasBFrames; + bool _hasTransp; ///< transparency mode enabled + bool _usesTiling; + bool _usesHaar; + bool _usesFullpel; + + bool _gopInvalid; + int _bufInvalid[4]; + + bool _isIndeo4; + + AVFrame * _pFrame; + bool _gotPFrame; + + IVI45DecContext(); +private: + /** + * Initial Run-value (RLE) tables. + */ + static const RVMapDesc _ff_ivi_rvmap_tabs[9]; +}; + +class IndeoDecoderBase : public Codec { +private: + /** + * Decode an Indeo 4 or 5 band. + * + * @param[in,out] band ptr to the band descriptor + * @returns result code: 0 = OK, -1 = error + */ + int decode_band(IVIBandDesc *band); + + /** + * Haar wavelet recomposition filter for Indeo 4 + * + * @param[in] plane Pointer to the descriptor of the plane being processed + * @param[out] dst pointer to the destination buffer + * @param[in] dstPitch Pitch of the destination buffer + */ + void recomposeHaar(const IVIPlaneDesc *plane, uint8 *dst, const int dstPitch); + + /** + * 5/3 wavelet recomposition filter for Indeo5 + * + * @param[in] plane Pointer to the descriptor of the plane being processed + * @param[out] dst Pointer to the destination buffer + * @param[in] dstPitch Pitch of the destination buffer + */ + void recompose53(const IVIPlaneDesc *plane, uint8 *dst, const int dstPitch); + + /* + * Convert and output the current plane. + * This conversion is done by adding back the bias value of 128 + * (subtracted in the encoder) and clipping the result. + * + * @param[in] plane Pointer to the descriptor of the plane being processed + * @param[out] dst Pointer to the buffer receiving converted pixels + * @param[in] dstPitch Pitch for moving to the next y line + */ + void outputPlane(IVIPlaneDesc *plane, uint8 *dst, int dstPitch); + + /** + * Handle empty tiles by performing data copying and motion + * compensation respectively. + * + * @param[in] band Pointer to the band descriptor + * @param[in] tile Pointer to the tile descriptor + * @param[in] mvScale Scaling factor for motion vectors + */ + int processEmptyTile(IVIBandDesc *band, IVITile *tile, int32 mvScale); + + /* + * Decode size of the tile data. + * The size is stored as a variable-length field having the following format: + * if (tile_data_size < 255) than this field is only one byte long + * if (tile_data_size >= 255) than this field four is byte long: 0xFF X1 X2 X3 + * where X1-X3 is size of the tile data + * + * @param[in,out] gb the GetBit context + * @returns Size of the tile data in bytes + */ + int decodeTileDataSize(GetBits *gb); + + /* + * Decode block data: + * extract huffman-coded transform coefficients from the bitstream, + * dequantize them, apply inverse transform and motion compensation + * in order to reconstruct the picture. + * + * @param[in,out] gb The GetBit context + * @param[in] band Pointer to the band descriptor + * @param[in] tile Pointer to the tile descriptor + * @returns Result code: 0 - OK, -1 = error (corrupted blocks data) + */ + int decodeBlocks(GetBits *gb, IVIBandDesc *band, IVITile *tile); + + int iviMc(IVIBandDesc *band, IviMCFunc mc, IviMCAvgFunc mcAvg, + int offs, int mvX, int mvY, int mvX2, int mvY2, int mcType, int mcType2); + + int ivi_decode_coded_blocks(GetBits *gb, IVIBandDesc *band, + IviMCFunc mc, IviMCAvgFunc mcAvg, int mvX, int mvY, + int mvX2, int mvY2, int *prevDc, int isIntra, + int mcType, int mcType2, uint32 quant, int offs); + + int ivi_dc_transform(IVIBandDesc *band, int *prevDc, int bufOffs, + int blkSize); +protected: + IVI45DecContext _ctx; + Graphics::PixelFormat _pixelFormat; + Graphics::ManagedSurface *_surface; + + /** + * Scan patterns shared between indeo4 and indeo5 + */ + static const uint8 _ffIviVerticalScan8x8[64]; + static const uint8 _ffIviHorizontalScan8x8[64]; + static const uint8 _ffIviDirectScan4x4[16]; +protected: + /** + * Returns the pixel format for the decoder's surface + */ + virtual Graphics::PixelFormat getPixelFormat() const { return _pixelFormat; } + + /** + * Decode the Indeo picture header. + * @returns 0 = Ok, negative number = error + */ + virtual int decodePictureHeader() = 0; + + /** + * Rearrange decoding and reference buffers. + */ + virtual void switchBuffers() = 0; + + virtual bool isNonNullFrame() const = 0; + + /** + * Decode Indeo band header. + * + * @param[in,out] band Pointer to the band descriptor + * @returns Result code: 0 = OK, negative number = error + */ + virtual int decodeBandHeader(IVIBandDesc *band) = 0; + + /** + * Decode information (block type, _cbp, quant delta, motion vector) + * for all macroblocks in the current tile. + * + * @param[in,out] band Pointer to the band descriptor + * @param[in,out] tile Pointer to the tile descriptor + * @returns Result code: 0 = OK, negative number = error + */ + virtual int decodeMbInfo(IVIBandDesc *band, IVITile *tile) = 0; + + /** + * Decodes the Indeo frame from the bit reader already + * loaded into the context + */ + int decodeIndeoFrame(); + + /** + * scale motion vector + */ + int scaleMV(int mv, int mvScale); +public: + IndeoDecoderBase(uint16 width, uint16 height); + virtual ~IndeoDecoderBase(); +}; + +} // End of namespace Indeo +} // End of namespace Image + +#endif diff --git a/image/codecs/indeo/indeo_dsp.cpp b/image/codecs/indeo/indeo_dsp.cpp new file mode 100644 index 0000000000..15626e9473 --- /dev/null +++ b/image/codecs/indeo/indeo_dsp.cpp @@ -0,0 +1,644 @@ +/* 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. + * + */ + +/* VLC code + * + * Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg + * written, produced, and directed by Alan Smithee + */ + +#include "image/codecs/indeo/indeo_dsp.h" + +namespace Image { +namespace Indeo { + +/** + * butterfly operation for the inverse Haar transform + */ +#define IVI_HAAR_BFLY(s1, s2, o1, o2, t) \ + t = ((s1) - (s2)) >> 1;\ + o1 = ((s1) + (s2)) >> 1;\ + o2 = (t);\ + +/** + * inverse 8-point Haar transform + */ +#define INV_HAAR8(s1, s5, s3, s7, s2, s4, s6, s8,\ + d1, d2, d3, d4, d5, d6, d7, d8,\ + t0, t1, t2, t3, t4, t5, t6, t7, t8) {\ + t1 = (s1) << 1; t5 = (s5) << 1;\ + IVI_HAAR_BFLY(t1, t5, t1, t5, t0); IVI_HAAR_BFLY(t1, s3, t1, t3, t0);\ + IVI_HAAR_BFLY(t5, s7, t5, t7, t0); IVI_HAAR_BFLY(t1, s2, t1, t2, t0);\ + IVI_HAAR_BFLY(t3, s4, t3, t4, t0); IVI_HAAR_BFLY(t5, s6, t5, t6, t0);\ + IVI_HAAR_BFLY(t7, s8, t7, t8, t0);\ + d1 = COMPENSATE(t1);\ + d2 = COMPENSATE(t2);\ + d3 = COMPENSATE(t3);\ + d4 = COMPENSATE(t4);\ + d5 = COMPENSATE(t5);\ + d6 = COMPENSATE(t6);\ + d7 = COMPENSATE(t7);\ + d8 = COMPENSATE(t8); } + +/** + * inverse 4-point Haar transform + */ +#define INV_HAAR4(s1, s3, s5, s7, d1, d2, d3, d4, t0, t1, t2, t3, t4) {\ + IVI_HAAR_BFLY(s1, s3, t0, t1, t4);\ + IVI_HAAR_BFLY(t0, s5, t2, t3, t4);\ + d1 = COMPENSATE(t2);\ + d2 = COMPENSATE(t3);\ + IVI_HAAR_BFLY(t1, s7, t2, t3, t4);\ + d3 = COMPENSATE(t2);\ + d4 = COMPENSATE(t3); } + +void IndeoDSP::ffIviInverseHaar8x8(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags) { + int i, shift, sp1, sp2, sp3, sp4; + const int32 *src; + int32 * dst; + int tmp[64]; + int t0, t1, t2, t3, t4, t5, t6, t7, t8; + + // apply the InvHaar8 to all columns +#define COMPENSATE(x) (x) + src = in; + dst = tmp; + for (i = 0; i < 8; i++) { + if (flags[i]) { + // pre-scaling + shift = !(i & 4); + sp1 = src[ 0] << shift; + sp2 = src[ 8] << shift; + sp3 = src[16] << shift; + sp4 = src[24] << shift; + INV_HAAR8( sp1, sp2, sp3, sp4, + src[32], src[40], src[48], src[56], + dst[ 0], dst[ 8], dst[16], dst[24], + dst[32], dst[40], dst[48], dst[56], + t0, t1, t2, t3, t4, t5, t6, t7, t8); + } else { + dst[ 0] = dst[ 8] = dst[16] = dst[24] = + dst[32] = dst[40] = dst[48] = dst[56] = 0; + } + + src++; + dst++; + } +#undef COMPENSATE + + // apply the InvHaar8 to all rows +#define COMPENSATE(x) (x) + src = tmp; + for (i = 0; i < 8; i++) { + if ( !src[0] && !src[1] && !src[2] && !src[3] + && !src[4] && !src[5] && !src[6] && !src[7]) { + memset(out, 0, 8 * sizeof(out[0])); + } else { + INV_HAAR8(src[0], src[1], src[2], src[3], + src[4], src[5], src[6], src[7], + out[0], out[1], out[2], out[3], + out[4], out[5], out[6], out[7], + t0, t1, t2, t3, t4, t5, t6, t7, t8); + } + src += 8; + out += pitch; + } +#undef COMPENSATE +} + +void IndeoDSP::ffIviRowHaar8(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags) { + int i; + int t0, t1, t2, t3, t4, t5, t6, t7, t8; + + // apply the InvHaar8 to all rows +#define COMPENSATE(x) (x) + for (i = 0; i < 8; i++) { + if ( !in[0] && !in[1] && !in[2] && !in[3] + && !in[4] && !in[5] && !in[6] && !in[7]) { + memset(out, 0, 8 * sizeof(out[0])); + } else { + INV_HAAR8(in[0], in[1], in[2], in[3], + in[4], in[5], in[6], in[7], + out[0], out[1], out[2], out[3], + out[4], out[5], out[6], out[7], + t0, t1, t2, t3, t4, t5, t6, t7, t8); + } + in += 8; + out += pitch; + } +#undef COMPENSATE +} + +void IndeoDSP::ffIviColHaar8(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags) { + int i; + int t0, t1, t2, t3, t4, t5, t6, t7, t8; + + // apply the InvHaar8 to all columns +#define COMPENSATE(x) (x) + for (i = 0; i < 8; i++) { + if (flags[i]) { + INV_HAAR8(in[ 0], in[ 8], in[16], in[24], + in[32], in[40], in[48], in[56], + out[0 * pitch], out[1 * pitch], + out[2 * pitch], out[3 * pitch], + out[4 * pitch], out[5 * pitch], + out[6 * pitch], out[7 * pitch], + t0, t1, t2, t3, t4, t5, t6, t7, t8); + } else { + out[0 * pitch] = out[1 * pitch] = + out[2 * pitch] = out[3 * pitch] = + out[4 * pitch] = out[5 * pitch] = + out[6 * pitch] = out[7 * pitch] = 0; + } + + in++; + out++; + } +#undef COMPENSATE +} + +void IndeoDSP::ffIviInverseHaar4x4(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags) { + int i, shift, sp1, sp2; + const int32 *src; + int32 * dst; + int tmp[16]; + int t0, t1, t2, t3, t4; + + // apply the InvHaar4 to all columns +#define COMPENSATE(x) (x) + src = in; + dst = tmp; + for (i = 0; i < 4; i++) { + if (flags[i]) { + // pre-scaling + shift = !(i & 2); + sp1 = src[0] << shift; + sp2 = src[4] << shift; + INV_HAAR4( sp1, sp2, src[8], src[12], + dst[0], dst[4], dst[8], dst[12], + t0, t1, t2, t3, t4); + } else { + dst[0] = dst[4] = dst[8] = dst[12] = 0; + } + + src++; + dst++; + } +#undef COMPENSATE + + // apply the InvHaar8 to all rows +#define COMPENSATE(x) (x) + src = tmp; + for (i = 0; i < 4; i++) { + if (!src[0] && !src[1] && !src[2] && !src[3]) { + memset(out, 0, 4 * sizeof(out[0])); + } else { + INV_HAAR4(src[0], src[1], src[2], src[3], + out[0], out[1], out[2], out[3], + t0, t1, t2, t3, t4); + } + src += 4; + out += pitch; + } +#undef COMPENSATE +} + +void IndeoDSP::ffIviRowHaar4(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags) { + int i; + int t0, t1, t2, t3, t4; + + // apply the InvHaar4 to all rows +#define COMPENSATE(x) (x) + for (i = 0; i < 4; i++) { + if (!in[0] && !in[1] && !in[2] && !in[3]) { + memset(out, 0, 4 * sizeof(out[0])); + } else { + INV_HAAR4(in[0], in[1], in[2], in[3], + out[0], out[1], out[2], out[3], + t0, t1, t2, t3, t4); + } + in += 4; + out += pitch; + } +#undef COMPENSATE +} + +void IndeoDSP::ffIviColHaar4(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags) { + int i; + int t0, t1, t2, t3, t4; + + // apply the InvHaar8 to all columns +#define COMPENSATE(x) (x) + for (i = 0; i < 4; i++) { + if (flags[i]) { + INV_HAAR4(in[0], in[4], in[8], in[12], + out[0 * pitch], out[1 * pitch], + out[2 * pitch], out[3 * pitch], + t0, t1, t2, t3, t4); + } else { + out[0 * pitch] = out[1 * pitch] = + out[2 * pitch] = out[3 * pitch] = 0; + } + + in++; + out++; + } +#undef COMPENSATE +} + +void IndeoDSP::ffIviDcHaar2d(const int32 *in, int16 *out, uint32 pitch, + int blkSize) { + int x, y; + int16 dcCoeff; + + dcCoeff = (*in + 0) >> 3; + + for (y = 0; y < blkSize; out += pitch, y++) { + for (x = 0; x < blkSize; x++) + out[x] = dcCoeff; + } +} + +//* butterfly operation for the inverse slant transform +#define IVI_SLANT_BFLY(s1, s2, o1, o2, t) \ + t = (s1) - (s2);\ + o1 = (s1) + (s2);\ + o2 = (t);\ + +//* This is a reflection a,b = 1/2, 5/4 for the inverse slant transform +#define IVI_IREFLECT(s1, s2, o1, o2, t) \ + t = (((s1) + (s2)*2 + 2) >> 2) + (s1);\ + o2 = (((s1)*2 - (s2) + 2) >> 2) - (s2);\ + o1 = (t);\ + +//* This is a reflection a,b = 1/2, 7/8 for the inverse slant transform +#define IVI_SLANT_PART4(s1, s2, o1, o2, t) \ + t = (s2) + (((s1)*4 - (s2) + 4) >> 3);\ + o2 = (s1) + ((-(s1) - (s2)*4 + 4) >> 3);\ + o1 = (t);\ + +//* inverse slant8 transform +#define IVI_INV_SLANT8(s1, s4, s8, s5, s2, s6, s3, s7,\ + d1, d2, d3, d4, d5, d6, d7, d8,\ + t0, t1, t2, t3, t4, t5, t6, t7, t8) {\ + IVI_SLANT_PART4(s4, s5, t4, t5, t0);\ +\ + IVI_SLANT_BFLY(s1, t5, t1, t5, t0); IVI_SLANT_BFLY(s2, s6, t2, t6, t0);\ + IVI_SLANT_BFLY(s7, s3, t7, t3, t0); IVI_SLANT_BFLY(t4, s8, t4, t8, t0);\ +\ + IVI_SLANT_BFLY(t1, t2, t1, t2, t0); IVI_IREFLECT (t4, t3, t4, t3, t0);\ + IVI_SLANT_BFLY(t5, t6, t5, t6, t0); IVI_IREFLECT (t8, t7, t8, t7, t0);\ + IVI_SLANT_BFLY(t1, t4, t1, t4, t0); IVI_SLANT_BFLY(t2, t3, t2, t3, t0);\ + IVI_SLANT_BFLY(t5, t8, t5, t8, t0); IVI_SLANT_BFLY(t6, t7, t6, t7, t0);\ + d1 = COMPENSATE(t1);\ + d2 = COMPENSATE(t2);\ + d3 = COMPENSATE(t3);\ + d4 = COMPENSATE(t4);\ + d5 = COMPENSATE(t5);\ + d6 = COMPENSATE(t6);\ + d7 = COMPENSATE(t7);\ + d8 = COMPENSATE(t8);} + +//* inverse slant4 transform +#define IVI_INV_SLANT4(s1, s4, s2, s3, d1, d2, d3, d4, t0, t1, t2, t3, t4) {\ + IVI_SLANT_BFLY(s1, s2, t1, t2, t0); IVI_IREFLECT (s4, s3, t4, t3, t0);\ +\ + IVI_SLANT_BFLY(t1, t4, t1, t4, t0); IVI_SLANT_BFLY(t2, t3, t2, t3, t0);\ + d1 = COMPENSATE(t1);\ + d2 = COMPENSATE(t2);\ + d3 = COMPENSATE(t3);\ + d4 = COMPENSATE(t4);} + +void IndeoDSP::ffIviInverseSlant8x8(const int32 *in, int16 *out, uint32 pitch, const uint8 *flags) { + int i; + const int32 *src; + int32 * dst; + int tmp[64]; + int t0, t1, t2, t3, t4, t5, t6, t7, t8; + +#define COMPENSATE(x) (x) + src = in; + dst = tmp; + for (i = 0; i < 8; i++) { + if (flags[i]) { + IVI_INV_SLANT8(src[0], src[8], src[16], src[24], src[32], src[40], src[48], src[56], + dst[0], dst[8], dst[16], dst[24], dst[32], dst[40], dst[48], dst[56], + t0, t1, t2, t3, t4, t5, t6, t7, t8); + } else { + dst[0] = dst[8] = dst[16] = dst[24] = dst[32] = dst[40] = dst[48] = dst[56] = 0; + } + + src++; + dst++; + } +#undef COMPENSATE + +#define COMPENSATE(x) (((x) + 1)>>1) + src = tmp; + for (i = 0; i < 8; i++) { + if (!src[0] && !src[1] && !src[2] && !src[3] && !src[4] && !src[5] && !src[6] && !src[7]) { + memset(out, 0, 8*sizeof(out[0])); + } else { + IVI_INV_SLANT8(src[0], src[1], src[2], src[3], src[4], src[5], src[6], src[7], + out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7], + t0, t1, t2, t3, t4, t5, t6, t7, t8); + } + src += 8; + out += pitch; + } +#undef COMPENSATE +} + +void IndeoDSP::ffIviInverseSlant4x4(const int32 *in, int16 *out, uint32 pitch, const uint8 *flags) { + int i; + const int32 *src; + int32 * dst; + int tmp[16]; + int t0, t1, t2, t3, t4; + +#define COMPENSATE(x) (x) + src = in; + dst = tmp; + for (i = 0; i < 4; i++) { + if (flags[i]) { + IVI_INV_SLANT4(src[0], src[4], src[8], src[12], + dst[0], dst[4], dst[8], dst[12], + t0, t1, t2, t3, t4); + } else { + dst[0] = dst[4] = dst[8] = dst[12] = 0; + } + src++; + dst++; + } +#undef COMPENSATE + +#define COMPENSATE(x) (((x) + 1)>>1) + src = tmp; + for (i = 0; i < 4; i++) { + if (!src[0] && !src[1] && !src[2] && !src[3]) { + out[0] = out[1] = out[2] = out[3] = 0; + } else { + IVI_INV_SLANT4(src[0], src[1], src[2], src[3], + out[0], out[1], out[2], out[3], + t0, t1, t2, t3, t4); + } + src += 4; + out += pitch; + } +#undef COMPENSATE +} + +void IndeoDSP::ffIviDcSlant2d(const int32 *in, int16 *out, uint32 pitch, + int blkSize) { + int x, y; + int16 dcCoeff; + + dcCoeff = (*in + 1) >> 1; + + for (y = 0; y < blkSize; out += pitch, y++) { + for (x = 0; x < blkSize; x++) + out[x] = dcCoeff; + } +} + +void IndeoDSP::ffIviRowSlant8(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags) { + int i; + int t0, t1, t2, t3, t4, t5, t6, t7, t8; + +#define COMPENSATE(x) (((x) + 1)>>1) + for (i = 0; i < 8; i++) { + if (!in[0] && !in[1] && !in[2] && !in[3] && !in[4] && !in[5] && !in[6] && !in[7]) { + memset(out, 0, 8*sizeof(out[0])); + } else { + IVI_INV_SLANT8( in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7], + out[0], out[1], out[2], out[3], out[4], out[5], out[6], out[7], + t0, t1, t2, t3, t4, t5, t6, t7, t8); + } + in += 8; + out += pitch; + } +#undef COMPENSATE +} + +void IndeoDSP::ffIviDcRowSlant(const int32 *in, int16 *out, uint32 pitch, int blkSize) { + int x, y; + int16 dcCoeff; + + dcCoeff = (*in + 1) >> 1; + + for (x = 0; x < blkSize; x++) + out[x] = dcCoeff; + + out += pitch; + + for (y = 1; y < blkSize; out += pitch, y++) { + for (x = 0; x < blkSize; x++) + out[x] = 0; + } +} + +void IndeoDSP::ffIviColSlant8(const int32 *in, int16 *out, uint32 pitch, const uint8 *flags) { + int i, row2, row4, row8; + int t0, t1, t2, t3, t4, t5, t6, t7, t8; + + row2 = pitch << 1; + row4 = pitch << 2; + row8 = pitch << 3; + +#define COMPENSATE(x) (((x) + 1)>>1) + for (i = 0; i < 8; i++) { + if (flags[i]) { + IVI_INV_SLANT8(in[0], in[8], in[16], in[24], in[32], in[40], in[48], in[56], + out[0], out[pitch], out[row2], out[row2 + pitch], out[row4], + out[row4 + pitch], out[row4 + row2], out[row8 - pitch], + t0, t1, t2, t3, t4, t5, t6, t7, t8); + } else { + out[0] = out[pitch] = out[row2] = out[row2 + pitch] = out[row4] = + out[row4 + pitch] = out[row4 + row2] = out[row8 - pitch] = 0; + } + + in++; + out++; + } +#undef COMPENSATE +} + +void IndeoDSP::ffIviDcColSlant(const int32 *in, int16 *out, uint32 pitch, int blkSize) { + int x, y; + int16 dcCoeff; + + dcCoeff = (*in + 1) >> 1; + + for (y = 0; y < blkSize; out += pitch, y++) { + out[0] = dcCoeff; + for (x = 1; x < blkSize; x++) + out[x] = 0; + } +} + +void IndeoDSP::ffIviRowSlant4(const int32 *in, int16 *out, + uint32 pitch, const uint8 *flags) { + int i; + int t0, t1, t2, t3, t4; + +#define COMPENSATE(x) (((x) + 1)>>1) + for (i = 0; i < 4; i++) { + if (!in[0] && !in[1] && !in[2] && !in[3]) { + memset(out, 0, 4*sizeof(out[0])); + } else { + IVI_INV_SLANT4( in[0], in[1], in[2], in[3], + out[0], out[1], out[2], out[3], + t0, t1, t2, t3, t4); + } + in += 4; + out += pitch; + } +#undef COMPENSATE +} + +void IndeoDSP::ffIviColSlant4(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags) { + int i, row2; + int t0, t1, t2, t3, t4; + + row2 = pitch << 1; + +#define COMPENSATE(x) (((x) + 1)>>1) + for (i = 0; i < 4; i++) { + if (flags[i]) { + IVI_INV_SLANT4(in[0], in[4], in[8], in[12], + out[0], out[pitch], out[row2], out[row2 + pitch], + t0, t1, t2, t3, t4); + } else { + out[0] = out[pitch] = out[row2] = out[row2 + pitch] = 0; + } + + in++; + out++; + } +#undef COMPENSATE +} + +void IndeoDSP::ffIviPutPixels8x8(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags) { + int x, y; + + for (y = 0; y < 8; out += pitch, in += 8, y++) + for (x = 0; x < 8; x++) + out[x] = in[x]; +} + +void IndeoDSP::ffIviPutDcPixel8x8(const int32 *in, int16 *out, uint32 pitch, + int blkSize) { + int y; + + out[0] = in[0]; + memset(out + 1, 0, 7*sizeof(out[0])); + out += pitch; + + for (y = 1; y < 8; out += pitch, y++) + memset(out, 0, 8*sizeof(out[0])); +} + +#define IVI_MC_TEMPLATE(size, suffix, OP) \ +static void iviMc ## size ##x## size ## suffix(int16 *buf, \ + uint32 dpitch, \ + const int16 *refBuf, \ + uint32 pitch, int mcType) \ +{ \ + int i, j; \ + const int16 *wptr; \ +\ + switch (mcType) { \ + case 0: /* fullpel (no interpolation) */ \ + for (i = 0; i < size; i++, buf += dpitch, refBuf += pitch) { \ + for (j = 0; j < size; j++) {\ + OP(buf[j], refBuf[j]); \ + } \ + } \ + break; \ + case 1: /* horizontal halfpel interpolation */ \ + for (i = 0; i < size; i++, buf += dpitch, refBuf += pitch) \ + for (j = 0; j < size; j++) \ + OP(buf[j], (refBuf[j] + refBuf[j+1]) >> 1); \ + break; \ + case 2: /* vertical halfpel interpolation */ \ + wptr = refBuf + pitch; \ + for (i = 0; i < size; i++, buf += dpitch, wptr += pitch, refBuf += pitch) \ + for (j = 0; j < size; j++) \ + OP(buf[j], (refBuf[j] + wptr[j]) >> 1); \ + break; \ + case 3: /* vertical and horizontal halfpel interpolation */ \ + wptr = refBuf + pitch; \ + for (i = 0; i < size; i++, buf += dpitch, wptr += pitch, refBuf += pitch) \ + for (j = 0; j < size; j++) \ + OP(buf[j], (refBuf[j] + refBuf[j+1] + wptr[j] + wptr[j+1]) >> 2); \ + break; \ + } \ +} \ +\ +void IndeoDSP::ffIviMc ## size ##x## size ## suffix(int16 *buf, const int16 *refBuf, \ + uint32 pitch, int mcType) \ +{ \ + iviMc ## size ##x## size ## suffix(buf, pitch, refBuf, pitch, mcType); \ +} + +#define IVI_MC_AVG_TEMPLATE(size, suffix, OP) \ +void IndeoDSP::ffIviMcAvg ## size ##x## size ## suffix(int16 *buf, \ + const int16 *refBuf, \ + const int16 *refBuf2, \ + uint32 pitch, \ + int mcType, int mcType2) \ +{ \ + int16 tmp[size * size]; \ + int i, j; \ +\ + iviMc ## size ##x## size ## NoDelta(tmp, size, refBuf, pitch, mcType); \ + iviMc ## size ##x## size ## Delta(tmp, size, refBuf2, pitch, mcType2); \ + for (i = 0; i < size; i++, buf += pitch) { \ + for (j = 0; j < size; j++) {\ + OP(buf[j], tmp[i * size + j] >> 1); \ + } \ + } \ +} + +#define OP_PUT(a, b) (a) = (b) +#define OP_ADD(a, b) (a) += (b) + +IVI_MC_TEMPLATE(8, NoDelta, OP_PUT) +IVI_MC_TEMPLATE(8, Delta, OP_ADD) +IVI_MC_TEMPLATE(4, NoDelta, OP_PUT) +IVI_MC_TEMPLATE(4, Delta, OP_ADD) +IVI_MC_AVG_TEMPLATE(8, NoDelta, OP_PUT) +IVI_MC_AVG_TEMPLATE(8, Delta, OP_ADD) +IVI_MC_AVG_TEMPLATE(4, NoDelta, OP_PUT) +IVI_MC_AVG_TEMPLATE(4, Delta, OP_ADD) + +} // End of namespace Indeo +} // End of namespace Image diff --git a/image/codecs/indeo/indeo_dsp.h b/image/codecs/indeo/indeo_dsp.h new file mode 100644 index 0000000000..8ea223b634 --- /dev/null +++ b/image/codecs/indeo/indeo_dsp.h @@ -0,0 +1,336 @@ +/* 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. + * + */ + +/* VLC code + * + * Original copyright note: + * DSP functions (inverse transforms, motion compensation, wavelet recompositions) + * for Indeo Video Interactive codecs. + */ + +#ifndef IMAGE_CODECS_INDEO_INDEO_DSP_H +#define IMAGE_CODECS_INDEO_INDEO_DSP_H + +#include "image/codecs/indeo/mem.h" +#include "image/codecs/indeo/indeo.h" + +namespace Image { +namespace Indeo { + +class IndeoDSP { +public: + /** + * two-dimensional inverse Haar 8x8 transform for Indeo 4 + * + * @param[in] in Pointer to the vector of transform coefficients + * @param[out] out Pointer to the output buffer (frame) + * @param[in] pitch Pitch to move to the next y line + * @param[in] flags Pointer to the array of column flags: + * != 0 - non_empty column, 0 - empty one + * (this array must be filled by caller) + */ + static void ffIviInverseHaar8x8(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags); + static void ffIviInverseHaar8x1(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags); + static void ffIviInverseHaar1x8(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags); + + /** + * one-dimensional inverse 8-point Haar transform on rows for Indeo 4 + * + * @param[in] in Pointer to the vector of transform coefficients + * @param[out] out Pointer to the output buffer (frame) + * @param[in] pitch Pitch to move to the next y line + * @param[in] flags Pointer to the array of column flags: + * != 0 - non_empty column, 0 - empty one + * (this array must be filled by caller) + */ + static void ffIviRowHaar8(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags); + + /** + * one-dimensional inverse 8-point Haar transform on columns for Indeo 4 + * + * @param[in] in Pointer to the vector of transform coefficients + * @param[out] out Pointer to the output buffer (frame) + * @param[in] pitch Pitch to move to the next y line + * @param[in] flags Pointer to the array of column flags: + * != 0 - non_empty column, 0 - empty one + * (this array must be filled by caller) + */ + static void ffIviColHaar8(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags); + + /** + * two-dimensional inverse Haar 4x4 transform for Indeo 4 + * + * @param[in] in Pointer to the vector of transform coefficients + * @param[out] out Pointer to the output buffer (frame) + * @param[in] pitch Pitch to move to the next y line + * @param[in] flags Pointer to the array of column flags: + * != 0 - non_empty column, 0 - empty one + * (this array must be filled by caller) + */ + static void ffIviInverseHaar4x4(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags); + + /** + * one-dimensional inverse 4-point Haar transform on rows for Indeo 4 + * + * @param[in] in Pointer to the vector of transform coefficients + * @param[out] out Pointer to the output buffer (frame) + * @param[in] pitch Pitch to move to the next y line + * @param[in] flags Pointer to the array of column flags: + * != 0 - non_empty column, 0 - empty one + * (this array must be filled by caller) + */ + static void ffIviRowHaar4(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags); + + /** + * one-dimensional inverse 4-point Haar transform on columns for Indeo 4 + * + * @param[in] in Pointer to the vector of transform coefficients + * @param[out] out Pointer to the output buffer (frame) + * @param[in] pitch Pitch to move to the next y line + * @param[in] flags Pointer to the array of column flags: + * != 0 - non_empty column, 0 - empty one + * (this array must be filled by caller) + */ + static void ffIviColHaar4(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags); + + /** + * DC-only two-dimensional inverse Haar transform for Indeo 4. + * Performing the inverse transform in this case is equivalent to + * spreading dcCoeff >> 3 over the whole block. + * + * @param[in] in Pointer to the dc coefficient + * @param[out] out Pointer to the output buffer (frame) + * @param[in] pitch Pitch to move to the next y line + * @param[in] blkSize Transform block size + */ + static void ffIviDcHaar2d(const int32 *in, int16 *out, uint32 pitch, + int blkSize); + + /** + * two-dimensional inverse slant 8x8 transform + * + * @param[in] in Pointer to the vector of transform coefficients + * @param[out] out Pointer to the output buffer (frame) + * @param[in] pitch Pitch to move to the next y line + * @param[in] flags Pointer to the array of column flags: + * != 0 - non_empty column, 0 - empty one + * (this array must be filled by caller) + */ + static void ffIviInverseSlant8x8(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags); + + /** + * two-dimensional inverse slant 4x4 transform + * + * @param[in] in Pointer to the vector of transform coefficients + * @param[out] out Pointer to the output buffer (frame) + * @param[in] pitch Pitch to move to the next y line + * @param[in] flags Pointer to the array of column flags: + * != 0 - non_empty column, 0 - empty one + * (this array must be filled by caller) + */ + static void ffIviInverseSlant4x4(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags); + + /** + * DC-only two-dimensional inverse slant transform. + * Performing the inverse slant transform in this case is equivalent to + * spreading (dcCoeff + 1)/2 over the whole block. + * It works much faster than performing the slant transform on a vector of zeroes. + * + * @param[in] in Pointer to the dc coefficient + * @param[out] out Pointer to the output buffer (frame) + * @param[in] pitch Pitch to move to the next y line + * @param[in] blkSize Transform block size + */ + static void ffIviDcSlant2d(const int32 *in, int16 *out, uint32 pitch, int blkSize); + + /** + * inverse 1D row slant transform + * + * @param[in] in Pointer to the vector of transform coefficients + * @param[out] out Pointer to the output buffer (frame) + * @param[in] pitch Pitch to move to the next y line + * @param[in] flags Pointer to the array of column flags (unused here) + */ + static void ffIviRowSlant8(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags); + + /** + * inverse 1D column slant transform + * + * @param[in] in Pointer to the vector of transform coefficients + * @param[out] out Pointer to the output buffer (frame) + * @param[in] pitch Pitch to move to the next y line + * @param[in] flags Pointer to the array of column flags: + * != 0 - non_empty column, 0 - empty one + * (this array must be filled by caller) + */ + static void ffIviColSlant8(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags); + + /** + * inverse 1D row slant transform + * + * @param[in] in Pointer to the vector of transform coefficients + * @param[out] out Pointer to the output buffer (frame) + * @param[in] pitch Pitch to move to the next y line + * @param[in] flags Pointer to the array of column flags (unused here) + */ + static void ffIviRowSlant4(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags); + + /** + * inverse 1D column slant transform + * + * @param[in] in Pointer to the vector of transform coefficients + * @param[out] out Pointer to the output buffer (frame) + * @param[in] pitch Pitch to move to the next y line + * @param[in] flags Pointer to the array of column flags: + * != 0 - non_empty column, 0 - empty one + * (this array must be filled by caller) + */ + static void ffIviColSlant4(const int32 *in, int16 *out, uint32 pitch, + const uint8 *flags); + + /** + * DC-only inverse row slant transform + */ + static void ffIviDcRowSlant(const int32 *in, int16 *out, uint32 pitch, int blkSize); + + /** + * DC-only inverse column slant transform + */ + static void ffIviDcColSlant(const int32 *in, int16 *out, uint32 pitch, int blkSize); + + /** + * Copy the pixels into the frame buffer. + */ + static void ffIviPutPixels8x8(const int32 *in, int16 *out, uint32 pitch, const uint8 *flags); + + /** + * Copy the DC coefficient into the first pixel of the block and + * zero all others. + */ + static void ffIviPutDcPixel8x8(const int32 *in, int16 *out, uint32 pitch, int blkSize); + + /** + * 8x8 block motion compensation with adding delta + * + * @param[in,out] buf Pointer to the block in the current frame buffer containing delta + * @param[in] refBuf Pointer to the corresponding block in the reference frame + * @param[in] pitch Pitch for moving to the next y line + * @param[in] mcType Interpolation type + */ + static void ffIviMc8x8Delta(int16 *buf, const int16 *refBuf, uint32 pitch, int mcType); + + /** + * 4x4 block motion compensation with adding delta + * + * @param[in,out] buf Pointer to the block in the current frame buffer containing delta + * @param[in] refBuf Pointer to the corresponding block in the reference frame + * @param[in] pitch Pitch for moving to the next y line + * @param[in] mcType Interpolation type + */ + static void ffIviMc4x4Delta(int16 *buf, const int16 *refBuf, uint32 pitch, int mcType); + + /** + * motion compensation without adding delta + * + * @param[in,out] buf Pointer to the block in the current frame buffer containing delta + * @param[in] refBuf Pointer to the corresponding block in the reference frame + * @param[in] pitch Pitch for moving to the next y line + * @param[in] mcType Interpolation type + */ + static void ffIviMc8x8NoDelta(int16 *buf, const int16 *refBuf, uint32 pitch, int mcType); + + /** + * 4x4 block motion compensation without adding delta + * + * @param[in,out] buf Pointer to the block in the current frame receiving the result + * @param[in] refBuf Pointer to the corresponding block in the reference frame + * @param[in] pitch Pitch for moving to the next y line + * @param[in] mcType Interpolation type + */ + static void ffIviMc4x4NoDelta(int16 *buf, const int16 *refBuf, uint32 pitch, int mcType); + + /** + * 8x8 block motion compensation with adding delta + * + * @param[in,out] buf Pointer to the block in the current frame buffer containing delta + * @param[in] refBuf Pointer to the corresponding block in the backward reference frame + * @param[in] refBuf2 Pointer to the corresponding block in the forward reference frame + * @param[in] pitch Pitch for moving to the next y line + * @param[in] mcType Interpolation type for backward reference + * @param[in] mcType2 Interpolation type for forward reference + */ + static void ffIviMcAvg8x8Delta(int16 *buf, const int16 *refBuf, const int16 *refBuf2, uint32 pitch, int mcType, int mcType2); + + /** + * 4x4 block motion compensation with adding delta + * + * @param[in,out] buf Pointer to the block in the current frame buffer containing delta + * @param[in] refBuf Pointer to the corresponding block in the backward reference frame + * @param[in] refBuf2 Pointer to the corresponding block in the forward reference frame + * @param[in] pitch Pitch for moving to the next y line + * @param[in] mcType Interpolation type for backward reference + * @param[in] mcType2 Interpolation type for forward reference + */ + static void ffIviMcAvg4x4Delta(int16 *buf, const int16 *refBuf, const int16 *refBuf2, uint32 pitch, int mcType, int mcType2); + + /** + * motion compensation without adding delta for B-frames + * + * @param[in,out] buf Pointer to the block in the current frame buffer containing delta + * @param[in] refBuf Pointer to the corresponding block in the backward reference frame + * @param[in] refBuf2 Pointer to the corresponding block in the forward reference frame + * @param[in] pitch Pitch for moving to the next y line + * @param[in] mcType Interpolation type for backward reference + * @param[in] mcType2 Interpolation type for forward reference + */ + static void ffIviMcAvg8x8NoDelta(int16 *buf, const int16 *refBuf, const int16 *refBuf2, uint32 pitch, int mcType, int mcType2); + + /** + * 4x4 block motion compensation without adding delta for B-frames + * + * @param[in,out] buf Pointer to the block in the current frame buffer containing delta + * @param[in] refBuf Pointer to the corresponding block in the backward reference frame + * @param[in] refBuf2 Pointer to the corresponding block in the forward reference frame + * @param[in] pitch Pitch for moving to the next y line + * @param[in] mcType Interpolation type for backward reference + * @param[in] mcType2 Interpolation type for forward reference + */ + static void ffIviMcAvg4x4NoDelta(int16 *buf, const int16 *refBuf, const int16 *refBuf2, uint32 pitch, int mcType, int mcType2); +}; + +} // End of namespace Indeo +} // End of namespace Image + +#endif diff --git a/image/codecs/indeo/mem.cpp b/image/codecs/indeo/mem.cpp new file mode 100644 index 0000000000..9db37ec601 --- /dev/null +++ b/image/codecs/indeo/mem.cpp @@ -0,0 +1,172 @@ +/* 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. + * + */ + +/* VLC code + * + * Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg + * written, produced, and directed by Alan Smithee + */ + +#include "image/codecs/indeo/mem.h" + +namespace Image { +namespace Indeo { + +const uint8 ffReverse[256] = { + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF, +}; + +const uint8 ffZigZagDirect[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +/*------------------------------------------------------------------------*/ + +/** + * Multiply two `size_t` values checking for overflow. + * + * @param[in] a,b Operands of multiplication + * @param[out] r Pointer to the result of the operation + * @return 0 on success, AVERROR(EINVAL) on overflow + */ +static inline int avSizeMult(size_t a, size_t b, size_t *r) { + size_t t = a * b; + + // Hack inspired from glibc: don't try the division if nelem and elsize + // are both less than sqrt(SIZE_MAX). + if ((a | b) >= ((size_t)1 << (sizeof(size_t) * 4)) && a && t / a != b) + return -1; + *r = t; + return 0; +} + +/*------------------------------------------------------------------------*/ + +void *avMallocZ(size_t size) { + void *ptr = malloc(size); + if (ptr) + memset(ptr, 0, size); + + return ptr; +} + +void *avMallocArray(size_t nmemb, size_t size) { + assert(size && nmemb < MAX_INTEGER / size); + return malloc(nmemb * size); +} + +void *avMallocZArray(size_t nmemb, size_t size) { + assert(size && nmemb < MAX_INTEGER / size); + return avMallocZ(nmemb * size); +} + +void avFreeP(void *arg) { + void **ptr = (void **)arg; + free(*ptr); + *ptr = nullptr; +} + +static void *avRealloc(void *ptr, size_t size) { + return realloc(ptr, size + !size); +} + +void *avReallocF(void *ptr, size_t nelem, size_t elsize) { + size_t size; + void *r; + + if (avSizeMult(elsize, nelem, &size)) { + free(ptr); + return nullptr; + } + r = avRealloc(ptr, size); + if (!r) + free(ptr); + + return r; +} + + +/** + * Swap the order of the bytes in the passed value + */ +uint32 bitswap32(uint32 x) { + return (uint32)ffReverse[x & 0xFF] << 24 | + (uint32)ffReverse[(x >> 8) & 0xFF] << 16 | + (uint32)ffReverse[(x >> 16) & 0xFF] << 8 | + (uint32)ffReverse[x >> 24]; +} + +/** + * Reverse "nbits" bits of the value "val" and return the result + * in the least significant bits. + */ +uint16 invertBits(uint16 val, int nbits) { + uint16 res; + + if (nbits <= 8) { + res = ffReverse[val] >> (8 - nbits); + } else { + res = ((ffReverse[val & 0xFF] << 8) + + (ffReverse[val >> 8])) >> (16 - nbits); + } + + return res; +} + +uint8 avClipUint8(int a) { + if (a & (~0xFF)) + return (-a) >> 31; + else + return a; +} + +unsigned avClipUintp2(int a, int p) { + if (a & ~((1 << p) - 1)) + return -a >> 31 & ((1 << p) - 1); + else + return a; +} + +} // End of namespace Indeo +} // End of namespace Image diff --git a/image/codecs/indeo/mem.h b/image/codecs/indeo/mem.h new file mode 100644 index 0000000000..8e889e5cbf --- /dev/null +++ b/image/codecs/indeo/mem.h @@ -0,0 +1,148 @@ +/* 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. + * + */ + +#include "common/scummsys.h" + +/* Common memory code used by the Indeo decoder + * + * Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg + * written, produced, and directed by Alan Smithee + */ + +#ifndef IMAGE_CODECS_INDEO_MEM_H +#define IMAGE_CODECS_INDEO_MEM_H + +namespace Image { +namespace Indeo { + +#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0])) +#define FFALIGN(x, a) (((x) + (a)-1) & ~((a)-1)) +#define FFSIGN(a) ((a) > 0 ? 1 : -1) +#define MAX_INTEGER 0x7ffffff + +/** + * Allocate a memory block with alignment suitable for all memory accesses + * (including vectors if available on the CPU) and zero all the bytes of the + * block. + * + * @param size Size in bytes for the memory block to be allocated + * @return Pointer to the allocated block, or `NULL` if it cannot be allocated + * @see av_malloc() + */ +extern void *avMallocZ(size_t size); + +/** + * Allocate a memory block for an array with av_malloc(). + * + * The allocated memory will have size `size * nmemb` bytes. + * + * @param nmemb Number of element + * @param size Size of a single element + * @return Pointer to the allocated block, or `NULL` if the block cannot + * be allocated + * @see av_malloc() + */ +extern void *avMallocArray(size_t nmemb, size_t size); + +/** + * Allocate a memory block for an array with av_mallocz(). + * + * The allocated memory will have size `size * nmemb` bytes. + * + * @param nmemb Number of elements + * @param size Size of the single element + * @return Pointer to the allocated block, or `NULL` if the block cannot + * be allocated + * + * @see av_mallocz() + * @see av_malloc_array() + */ +extern void *avMallocZArray(size_t nmemb, size_t size); + +/** + * Free a memory block which has been allocated with a function of av_malloc() + * or av_realloc() family, and set the pointer pointing to it to `NULL`. + * + * @param ptr Pointer to the pointer to the memory block which should be freed + * @note `*ptr = NULL` is safe and leads to no action. + */ +extern void avFreeP(void *arg); + + +/** + * Allocate, reallocate, or free a block of memory. + * + * This function does the same thing as av_realloc(), except: + * - It takes two size arguments and allocates `nelem * elsize` bytes, + * after checking the result of the multiplication for integer overflow. + * - It frees the input block in case of failure, thus avoiding the memory + * leak with the classic + * @code{.c} + * buf = realloc(buf); + * if (!buf) + * return -1; + * @endcode + * pattern. + */ +extern void *avReallocF(void *ptr, size_t nelem, size_t elsize); + +/** + * Reverse "nbits" bits of the value "val" and return the result + * in the least significant bits. + */ +extern uint16 invertBits(uint16 val, int nbits); + +/** + * Swap the order of the bytes in the passed value + */ +extern uint32 bitswap32(uint32 x); + +/** + * Clip a signed integer value into the 0-255 range. + * @param a value to clip + * @return clipped value + */ +extern uint8 avClipUint8(int a); + +/** + * Clip a signed integer to an unsigned power of two range. + * @param a value to clip + * @param p bit position to clip at + * @return clipped value + */ +extern unsigned avClipUintp2(int a, int p); + +/** +* Clip a signed integer value into the amin-amax range. +* @param a value to clip +* @param amin minimum value of the clip range +* @param amax maximum value of the clip range +* @return clipped value +*/ +#define av_clip CLIP + +extern const uint8 ffZigZagDirect[64]; + +} // End of namespace Indeo +} // End of namespace Image + +#endif diff --git a/image/codecs/indeo/vlc.cpp b/image/codecs/indeo/vlc.cpp new file mode 100644 index 0000000000..5b9e96cd96 --- /dev/null +++ b/image/codecs/indeo/vlc.cpp @@ -0,0 +1,336 @@ +/* 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. + * + */ + +/* VLC code + * + * Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg + * written, produced, and directed by Alan Smithee + */ + +#include "image/codecs/indeo/vlc.h" +#include "image/codecs/indeo/mem.h" +#include "common/textconsole.h" +#include "common/util.h" + +namespace Image { +namespace Indeo { + +/** + * Quicksort + * This sort is fast, and fully inplace but not stable and it is possible + * to construct input that requires O(n^2) time but this is very unlikely to + * happen with non constructed input. + */ +#define AV_QSORT(p, num, type, cmp) do {\ + void *stack[64][2];\ + int sp = 1;\ + stack[0][0] = p;\ + stack[0][1] = (p)+(num)-1;\ + while(sp){\ + type *start = (type *)stack[--sp][0];\ + type *end = (type *)stack[ sp][1];\ + while (start < end) {\ + if (start < end-1) {\ + int checksort = 0;\ + type *right = end - 2;\ + type *left = start + 1;\ + type *mid = start + ((end - start) >> 1);\ + if(cmp(start, end) > 0) {\ + if(cmp( end, mid) > 0) SWAP(*start, *mid);\ + else SWAP(*start, *end);\ + } else {\ + if(cmp(start, mid) > 0) SWAP(*start, *mid);\ + else checksort = 1;\ + }\ + if (cmp(mid, end) > 0) { \ + SWAP(*mid, *end);\ + checksort = 0;\ + }\ + if(start == end - 2) break;\ + SWAP(end[-1], *mid);\ + while (left <= right) {\ + while (left<=right && cmp(left, end - 1) < 0)\ + left++;\ + while (left<=right && cmp(right, end - 1) > 0)\ + right--;\ + if (left <= right) {\ + SWAP(*left, *right);\ + left++;\ + right--;\ + }\ + }\ + SWAP(end[-1], *left);\ + if(checksort && (mid == left - 1 || mid == left)){\ + mid= start;\ + while(mid<end && cmp(mid, mid+1) <= 0)\ + mid++;\ + if(mid==end)\ + break;\ + }\ + if (end - left < left - start){\ + stack[sp ][0] = start;\ + stack[sp++][1] = right;\ + start = left + 1;\ + } else {\ + stack[sp ][0] = left+1;\ + stack[sp++][1] = end;\ + end = right;\ + }\ + } else {\ + if (cmp(start, end) > 0)\ + SWAP(*start, *end);\ + break;\ + }\ + }\ + }\ +} while (0) + +#define COPY(condition)\ + for (i = 0; i < nbCodes; i++) { \ + buf[j].bits = getData(p_bits, i, bitsWrap, bitsSize); \ + if (!(condition)) \ + continue; \ + if (buf[j].bits > (3 * nbBits) || buf[j].bits > 32) { \ + warning("Too long VLC (%d) in init_vlc", buf[j].bits); \ + if (!(flags & INIT_VLC_USE_NEW_STATIC)) \ + free(buf); \ + return -1; \ + } \ + buf[j].code = getData(codes, i, codesWrap, codesSize); \ + if (buf[j].code >= (1LL << buf[j].bits)) { \ + warning("Invalid code %x for %d in init_vlc", buf[j].code, i); \ + if (!(flags & INIT_VLC_USE_NEW_STATIC)) \ + free(buf); \ + return -1; \ + } \ + if (flags & INIT_VLC_LE) \ + buf[j].code = bitswap32(buf[j].code); \ + else \ + buf[j].code <<= 32 - buf[j].bits; \ + if (symbols) \ + buf[j].symbol = getData(symbols, i, symbolsWrap, symbolsSize); \ + else \ + buf[j].symbol = i; \ + j++; \ + } + +/*------------------------------------------------------------------------*/ + +VLC::VLC() : _bits(0), _tableSize(0), _tableAllocated(0), _table(nullptr) { +} + +int VLC::init_vlc(int nbBits, int nbCodes, const void *bits, int bitsWrap, int bitsSize, + const void *codes, int codesWrap, int codesSize, int flags) { + return init_vlc(nbBits, nbCodes, bits, bitsWrap, bitsSize, codes, codesWrap, + codesSize, nullptr, 0, 0, flags); +} + +int VLC::init_vlc(int nbBits, int nbCodes, const void *p_bits, int bitsWrap, + int bitsSize, const void *codes, int codesWrap, int codesSize, + const void *symbols, int symbolsWrap, int symbolsSize, int flags) { + VLCcode *buf; + int i, j, ret; + VLCcode localbuf[1500]; // the maximum currently needed is 1296 by rv34 + VLC localvlc, *vlc; + + vlc = this; + vlc->_bits = nbBits; + if (flags & INIT_VLC_USE_NEW_STATIC) { + assert((nbCodes + 1) <= FF_ARRAY_ELEMS(localbuf)); + buf = localbuf; + localvlc = *this; + vlc = &localvlc; + vlc->_tableSize = 0; + } else { + vlc->_table = NULL; + vlc->_tableAllocated = 0; + vlc->_tableSize = 0; + + buf = (VLCcode *)avMallocArray((nbCodes + 1), sizeof(VLCcode)); + assert(buf); + } + + assert(symbolsSize <= 2 || !symbols); + j = 0; + + COPY(buf[j].bits > nbBits); + + // qsort is the slowest part of init_vlc, and could probably be improved or avoided + AV_QSORT(buf, j, VLCcode, compareVlcSpec); + COPY(buf[j].bits && buf[j].bits <= nbBits); + nbCodes = j; + + ret = vlc->buildTable(nbBits, nbCodes, buf, flags); + + if (flags & INIT_VLC_USE_NEW_STATIC) { + if (vlc->_tableSize != vlc->_tableAllocated) + warning("needed %d had %d", vlc->_tableSize, vlc->_tableAllocated); + + assert(ret >= 0); + *this = *vlc; + } else { + free(buf); + if (ret < 0) { + avFreeP(&vlc->_table); + return -1; + } + } + + return 0; +} + +void VLC::freeVlc() { + free(_table); +} + +int VLC::compareVlcSpec(const void *a, const void *b) { + const VLCcode *sa = (VLCcode *)a, *sb = (VLCcode *)b; + return (sa->code >> 1) - (sb->code >> 1); +} + +int VLC::buildTable(int tableNbBits, int nbCodes, + VLCcode *codes, int flags) { + VLC *vlc = this; + int tableSize, tableIndex, index, codePrefix, symbol, subtableBits; + int i, j, k, n, nb, inc; + uint32 code; + VLC_TYPE (*table)[2]; + + tableSize = 1 << tableNbBits; + if (tableNbBits > 30) + return -1; + tableIndex = allocTable(tableSize, flags & INIT_VLC_USE_NEW_STATIC); + //warning("new table index=%d size=%d", tableIndex, tableSize); + if (tableIndex < 0) + return tableIndex; + table = &vlc->_table[tableIndex]; + + // first pass: map codes and compute auxiliary table sizes + for (i = 0; i < nbCodes; i++) { + n = codes[i].bits; + code = codes[i].code; + symbol = codes[i].symbol; + //warning("i=%d n=%d code=0x%x", i, n, code); + + if (n <= tableNbBits) { + // no need to add another table + j = code >> (32 - tableNbBits); + nb = 1 << (tableNbBits - n); + inc = 1; + if (flags & INIT_VLC_LE) { + j = bitswap32(code); + inc = 1 << n; + } + for (k = 0; k < nb; k++) { + int bits = table[j][1]; + //warning("%4x: code=%d n=%d", j, i, n); + + if (bits != 0 && bits != n) { + warning("incorrect codes"); + return -1; + } + + table[j][1] = n; //bits + table[j][0] = symbol; + j += inc; + } + } else { + // fill auxiliary table recursively + n -= tableNbBits; + codePrefix = code >> (32 - tableNbBits); + subtableBits = n; + codes[i].bits = n; + codes[i].code = code << tableNbBits; + for (k = i + 1; k < nbCodes; k++) { + n = codes[k].bits - tableNbBits; + if (n <= 0) + break; + code = codes[k].code; + if (code >> (32 - tableNbBits) != (uint)codePrefix) + break; + codes[k].bits = n; + codes[k].code = code << tableNbBits; + subtableBits = MAX(subtableBits, n); + } + subtableBits = MIN(subtableBits, tableNbBits); + j = (flags & INIT_VLC_LE) ? bitswap32(codePrefix) >> (32 - tableNbBits) : codePrefix; + table[j][1] = -subtableBits; + //warning("%4x: n=%d (subtable)", j, codes[i].bits + tableNbBits); + index = vlc->buildTable(subtableBits, k - i, codes + i, flags); + if (index < 0) + return index; + + // note: realloc has been done, so reload tables + table = (VLC_TYPE (*)[2])&vlc->_table[tableIndex]; + table[j][0] = index; //code + i = k - 1; + } + } + + for (i = 0; i < tableSize; i++) { + if (table[i][1] == 0) //bits + table[i][0] = -1; //codes + } + + return tableIndex; +} + +int VLC::allocTable(int size, int useStatic) { + VLC *vlc = this; + int index = vlc->_tableSize; + + vlc->_tableSize += size; + if (vlc->_tableSize > vlc->_tableAllocated) { + // cannot do anything, init_vlc() is used with too little memory + assert(!useStatic); + + vlc->_tableAllocated += (1 << vlc->_bits); + vlc->_table = (int16(*)[2])avReallocF(vlc->_table, vlc->_tableAllocated, sizeof(VLC_TYPE) * 2); + if (!vlc->_table) { + vlc->_tableAllocated = 0; + vlc->_tableSize = 0; + return -2; + } + + memset(vlc->_table + vlc->_tableAllocated - (1 << vlc->_bits), 0, sizeof(VLC_TYPE) * 2 << vlc->_bits); + } + + return index; +} + +uint VLC::getData(const void *table, uint idx, uint wrap, uint size) { + const uint8 *ptr = (const uint8 *)table + idx * wrap; + + switch(size) { + case 1: + return *(const uint8 *)ptr; + + case 2: + return *(const uint16 *)ptr; + + default: + return *(const uint32 *)ptr; + } +} + +} // End of namespace Indeo +} // End of namespace Image diff --git a/image/codecs/indeo/vlc.h b/image/codecs/indeo/vlc.h new file mode 100644 index 0000000000..a6dd692732 --- /dev/null +++ b/image/codecs/indeo/vlc.h @@ -0,0 +1,135 @@ +/* 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. + * + */ + +#include "common/scummsys.h" + +/* VLC code + * + * Original copyright note: * Intel Indeo 4 (IV41, IV42, etc.) video decoder for ffmpeg + * written, produced, and directed by Alan Smithee + */ + +#ifndef IMAGE_CODECS_INDEO_VLC_H +#define IMAGE_CODECS_INDEO_VLC_H + +#include "image/codecs/indeo/get_bits.h" + +namespace Image { +namespace Indeo { + +#define VLC_TYPE int16 + +enum VLCFlag { + INIT_VLC_LE = 2, + INIT_VLC_USE_NEW_STATIC = 4 +}; + +struct VLCcode { + uint8 bits; + uint16 symbol; + + /** + * codeword, with the first bit-to-be-read in the msb + * (even if intended for a little-endian bitstream reader) + */ + uint32 code; +}; + +struct VLC { +private: + static int compareVlcSpec(const void *a, const void *b); + + /** + * Gets a value of a given size from a table + * @param table Table to get data from + * @param idx Index of value to retrieve + * @param wrap Size of elements with alignment + * @param size Size of elements + */ + static uint getData(const void *table, uint idx, uint wrap, uint size); +public: + int _bits; + VLC_TYPE (*_table)[2]; ///< code, bits + int _tableSize, _tableAllocated; + + VLC(); + + /* Build VLC decoding tables suitable for use with get_vlc(). + + 'nbBits' sets the decoding table size (2^nbBits) entries. The + bigger it is, the faster is the decoding. But it should not be too + big to save memory and L1 cache. '9' is a good compromise. + + 'nbCodes' : number of vlcs codes + + 'bits' : table which gives the size (in bits) of each vlc code. + + 'codes' : table which gives the bit pattern of of each vlc code. + + 'symbols' : table which gives the values to be returned from get_vlc(). + + 'xxx_wrap' : give the number of bytes between each entry of the + 'bits' or 'codes' tables. + + 'xxx_size' : gives the number of bytes of each entry of the 'bits' + or 'codes' tables. + + 'wrap' and 'size' make it possible to use any memory configuration and types + (byte/word/long) to store the 'bits', 'codes', and 'symbols' tables. + + 'useStatic' should be set to 1 for tables, which should be freed + with av_free_static(), 0 if freeVlc() will be used. + */ + int init_vlc(int nbBits, int nbCodes, const void *bits, int bitsWrap, + int bitsSize, const void *codes, int codesWrap, int codesSize, + const void *symbols, int symbolsWrap, int symbolsSize, int flags); + + int init_vlc(int nbBits, int nbCodes, const void *bits, int bitsWrap, int bitsSize, + const void *codes, int codesWrap, int codesSize, int flags); + + /** + * Free VLC data + */ + void freeVlc(); + + + /** + * Build VLC decoding tables suitable for use with get_vlc(). + * + * @param tableNbBits max length of vlc codes to store directly in this table + * (Longer codes are delegated to subtables.) + * + * @param nbCodes number of elements in codes[] + * + * @param codes descriptions of the vlc codes + * These must be ordered such that codes going into the same subtable are contiguous. + * Sorting by VLCcode.code is sufficient, though not necessary. + */ + int buildTable(int tableNbBits, int nbCodes, VLCcode *codes, int flags); + + int allocTable(int size, int useStatic); +}; + +} // End of namespace Indeo +} // End of namespace Image + +#endif diff --git a/image/codecs/indeo4.cpp b/image/codecs/indeo4.cpp new file mode 100644 index 0000000000..3e4c37bbab --- /dev/null +++ b/image/codecs/indeo4.cpp @@ -0,0 +1,951 @@ +/* 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. + * + */ + +/* Intel Indeo 4 decompressor, derived from ffmpeg. + * + * Original copyright note: * Intel Indeo 3 (IV41, IV42, etc.) video decoder for ffmpeg + * written, produced, and directed by Alan Smithee + */ + +#include "common/memstream.h" +#include "common/textconsole.h" +#include "graphics/yuv_to_rgb.h" +#include "image/codecs/indeo4.h" +#include "image/codecs/indeo/indeo_dsp.h" +#include "image/codecs/indeo/mem.h" + +namespace Image { + +#define IVI4_PIC_SIZE_ESC 7 + +Indeo4Decoder::Indeo4Decoder(uint16 width, uint16 height) : IndeoDecoderBase(width, height) { + _ctx._isIndeo4 = true; + _ctx._refBuf = 1; + _ctx._bRefBuf = 3; + _ctx._pFrame = new AVFrame(); +} + +bool Indeo4Decoder::isIndeo4(Common::SeekableReadStream &stream) { + // Less than 16 bytes? This can't be right + if (stream.size() < 16) + return false; + + // Read in the start of the data + byte buffer[16]; + stream.read(buffer, 16); + stream.seek(-16, SEEK_CUR); + + // Validate the first 18-bit word has the correct identifier + Indeo::GetBits gb(new Common::MemoryReadStream(buffer, 16 * 8), DisposeAfterUse::YES); + bool isIndeo4 = gb.getBits(18) == 0x3FFF8; + + return isIndeo4; +} + +const Graphics::Surface *Indeo4Decoder::decodeFrame(Common::SeekableReadStream &stream) { + // Not Indeo 4? Fail + if (!isIndeo4(stream)) + return nullptr; + + // Set up the frame data buffer + byte *frameData = new byte[stream.size()]; + stream.read(frameData, stream.size()); + _ctx._frameData = frameData; + _ctx._frameSize = stream.size(); + + // Set up the GetBits instance for reading the data + _ctx._gb = new GetBits(new Common::MemoryReadStream(_ctx._frameData, _ctx._frameSize * 8)); + + // Decode the frame + int err = decodeIndeoFrame(); + + // Free the bit reader and frame buffer + delete _ctx._gb; + _ctx._gb = nullptr; + delete[] frameData; + _ctx._frameData = nullptr; + _ctx._frameSize = 0; + + return (err < 0) ? nullptr : &_surface->rawSurface(); +} + +int Indeo4Decoder::decodePictureHeader() { + int pic_size_indx, i, p; + IVIPicConfig picConf; + + if (_ctx._gb->getBits(18) != 0x3FFF8) { + warning("Invalid picture start code!"); + return -1; + } + + _ctx._prevFrameType = _ctx._frameType; + _ctx._frameType = _ctx._gb->getBits(3); + if (_ctx._frameType == 7) { + warning("Invalid frame type: %d", _ctx._frameType); + return -1; + } + + if (_ctx._frameType == IVI4_FRAMETYPE_BIDIR) + _ctx._hasBFrames = true; + + _ctx._hasTransp = _ctx._gb->getBit(); + + // unknown bit: Mac decoder ignores this bit, XANIM returns error + if (_ctx._gb->getBit()) { + warning("Sync bit is set!"); + return -1; + } + + _ctx._dataSize = _ctx._gb->getBit() ? _ctx._gb->getBits(24) : 0; + + // null frames don't contain anything else so we just return + if (_ctx._frameType >= IVI4_FRAMETYPE_NULL_FIRST) { + warning("Null frame encountered!"); + return 0; + } + + // Check key lock status. If enabled - ignore lock word. + // Usually we have to prompt the user for the password, but + // we don't do that because Indeo 4 videos can be decoded anyway + if (_ctx._gb->getBit()) { + _ctx._gb->skip(32); + warning("Password-protected clip!"); + } + + pic_size_indx = _ctx._gb->getBits(3); + if (pic_size_indx == IVI4_PIC_SIZE_ESC) { + picConf._picHeight = _ctx._gb->getBits(16); + picConf._picWidth = _ctx._gb->getBits(16); + } else { + picConf._picHeight = _ivi4_common_pic_sizes[pic_size_indx * 2 + 1]; + picConf._picWidth = _ivi4_common_pic_sizes[pic_size_indx * 2]; + } + + // Decode tile dimensions. + _ctx._usesTiling = _ctx._gb->getBit(); + if (_ctx._usesTiling) { + picConf._tileHeight = scaleTileSize(picConf._picHeight, _ctx._gb->getBits(4)); + picConf._tileWidth = scaleTileSize(picConf._picWidth, _ctx._gb->getBits(4)); + } else { + picConf._tileHeight = picConf._picHeight; + picConf._tileWidth = picConf._picWidth; + } + + // Decode chroma subsampling. We support only 4:4 aka YVU9. + if (_ctx._gb->getBits(2)) { + warning("Only YVU9 picture format is supported!"); + return -1; + } + picConf._chromaHeight = (picConf._picHeight + 3) >> 2; + picConf._chromaWidth = (picConf._picWidth + 3) >> 2; + + // decode subdivision of the planes + picConf._lumaBands = decodePlaneSubdivision(); + picConf._chromaBands = 0; + if (picConf._lumaBands) + picConf._chromaBands = decodePlaneSubdivision(); + _ctx._isScalable = picConf._lumaBands != 1 || picConf._chromaBands != 1; + if (_ctx._isScalable && (picConf._lumaBands != 4 || picConf._chromaBands != 1)) { + warning("Scalability: unsupported subdivision! Luma bands: %d, chroma bands: %d", + picConf._lumaBands, picConf._chromaBands); + return -1; + } + + // check if picture layout was changed and reallocate buffers + if (picConf.ivi_pic_config_cmp(_ctx._picConf)) { + if (IVIPlaneDesc::initPlanes(_ctx._planes, &picConf, 1)) { + warning("Couldn't reallocate color planes!"); + _ctx._picConf._lumaBands = 0; + return -2; + } + + _ctx._picConf = picConf; + + // set default macroblock/block dimensions + for (p = 0; p <= 2; p++) { + for (i = 0; i < (!p ? picConf._lumaBands : picConf._chromaBands); i++) { + _ctx._planes[p]._bands[i]._mbSize = !p ? (!_ctx._isScalable ? 16 : 8) : 4; + _ctx._planes[p]._bands[i]._blkSize = !p ? 8 : 4; + } + } + + if (IVIPlaneDesc::initTiles(_ctx._planes, _ctx._picConf._tileWidth, + _ctx._picConf._tileHeight)) { + warning("Couldn't reallocate internal structures!"); + return -2; + } + } + + _ctx._frameNum = _ctx._gb->getBit() ? _ctx._gb->getBits(20) : 0; + + // skip decTimeEst field if present + if (_ctx._gb->getBit()) + _ctx._gb->skip(8); + + // decode macroblock and block huffman codebooks + if (_ctx._mbVlc.decodeHuffDesc(&_ctx, _ctx._gb->getBit(), IVI_MB_HUFF) || + _ctx._blkVlc.decodeHuffDesc(&_ctx, _ctx._gb->getBit(), IVI_BLK_HUFF)) + return -1; + + _ctx._rvmapSel = _ctx._gb->getBit() ? _ctx._gb->getBits(3) : 8; + + _ctx._inImf = _ctx._gb->getBit(); + _ctx._inQ = _ctx._gb->getBit(); + + _ctx._picGlobQuant = _ctx._gb->getBits(5); + + // TODO: ignore this parameter if unused + _ctx._unknown1 = _ctx._gb->getBit() ? _ctx._gb->getBits(3) : 0; + + _ctx._checksum = _ctx._gb->getBit() ? _ctx._gb->getBits(16) : 0; + + // skip picture header extension if any + while (_ctx._gb->getBit()) { + _ctx._gb->skip(8); + } + + if (_ctx._gb->getBit()) { + warning("Bad blocks bits encountered!"); + } + + _ctx._gb->align(); + + return 0; +} + +void Indeo4Decoder::switchBuffers() { + int isPrevRef = 0, isRef = 0; + + switch (_ctx._prevFrameType) { + case IVI4_FRAMETYPE_INTRA: + case IVI4_FRAMETYPE_INTRA1: + case IVI4_FRAMETYPE_INTER: + isPrevRef = 1; + break; + } + + switch (_ctx._frameType) { + case IVI4_FRAMETYPE_INTRA: + case IVI4_FRAMETYPE_INTRA1: + case IVI4_FRAMETYPE_INTER: + isRef = 1; + break; + + default: + break; + } + + if (isPrevRef && isRef) { + SWAP(_ctx._dstBuf, _ctx._refBuf); + } else if (isPrevRef) { + SWAP(_ctx._refBuf, _ctx._bRefBuf); + SWAP(_ctx._dstBuf, _ctx._refBuf); + } +} + +bool Indeo4Decoder::isNonNullFrame() const { + return _ctx._frameType < IVI4_FRAMETYPE_NULL_FIRST; +} + +int Indeo4Decoder::decodeBandHeader(IVIBandDesc *band) { + int plane, bandNum, indx, transformId, scanIndx; + int i; + int quantMat; + + plane = _ctx._gb->getBits(2); + bandNum = _ctx._gb->getBits(4); + if (band->_plane != plane || band->_bandNum != bandNum) { + warning("Invalid band header sequence!"); + return -1; + } + + band->_isEmpty = _ctx._gb->getBit(); + if (!band->_isEmpty) { + int old_blk_size = band->_blkSize; + // skip header size + // If header size is not given, header size is 4 bytes. + if (_ctx._gb->getBit()) + _ctx._gb->skip(16); + + band->_isHalfpel = _ctx._gb->getBits(2); + if (band->_isHalfpel >= 2) { + warning("Invalid/unsupported mv resolution: %d!", + band->_isHalfpel); + return -1; + } + if (!band->_isHalfpel) + _ctx._usesFullpel = true; + + band->_checksumPresent = _ctx._gb->getBit(); + if (band->_checksumPresent) + band->_checksum = _ctx._gb->getBits(16); + + indx = _ctx._gb->getBits(2); + if (indx == 3) { + warning("Invalid block size!"); + return -1; + } + band->_mbSize = 16 >> indx; + band->_blkSize = 8 >> (indx >> 1); + + band->_inheritMv = _ctx._gb->getBit(); + band->_inheritQDelta = _ctx._gb->getBit(); + + band->_globQuant = _ctx._gb->getBits(5); + + if (!_ctx._gb->getBit() || _ctx._frameType == IVI4_FRAMETYPE_INTRA) { + transformId = _ctx._gb->getBits(5); + if ((uint)transformId >= FF_ARRAY_ELEMS(_transforms) || + !_transforms[transformId]._invTrans) { + warning("Transform %d", transformId); + return -3; + } + if ((transformId >= 7 && transformId <= 9) || + transformId == 17) { + warning("DCT transform"); + return -3; + } + + if (transformId < 10 && band->_blkSize < 8) { + warning("wrong transform size!"); + return -1; + } + if ((transformId >= 0 && transformId <= 2) || transformId == 10) + _ctx._usesHaar = true; + + band->_invTransform = _transforms[transformId]._invTrans; + band->_dcTransform = _transforms[transformId]._dcTrans; + band->_is2dTrans = _transforms[transformId]._is2dTrans; + + if (transformId < 10) + band->_transformSize = 8; + else + band->_transformSize = 4; + + if (band->_blkSize != band->_transformSize) { + warning("transform and block size mismatch (%d != %d)", band->_transformSize, band->_blkSize); + return -1; + } + + scanIndx = _ctx._gb->getBits(4); + if (scanIndx == 15) { + warning("Custom scan pattern encountered!"); + return -1; + } + if (scanIndx > 4 && scanIndx < 10) { + if (band->_blkSize != 4) { + warning("mismatching scan table!"); + return -1; + } + } else if (band->_blkSize != 8) { + warning("mismatching scan table!"); + return -1; + } + + band->_scan = _scan_index_to_tab[scanIndx]; + band->_scanSize = band->_blkSize; + + quantMat = _ctx._gb->getBits(5); + if (quantMat == 31) { + warning("Custom quant matrix encountered!"); + return -1; + } + if ((uint)quantMat >= FF_ARRAY_ELEMS(_quant_index_to_tab)) { + warning("Quantization matrix %d", quantMat); + return -1; + } + band->_quantMat = quantMat; + } else { + if (old_blk_size != band->_blkSize) { + warning("The band block size does not match the configuration inherited"); + return -1; + } + } + if (_quant_index_to_tab[band->_quantMat] > 4 && band->_blkSize == 4) { + warning("Invalid quant matrix for 4x4 block encountered!"); + band->_quantMat = 0; + return -1; + } + if (band->_scanSize != band->_blkSize) { + warning("mismatching scan table!"); + return -1; + } + if (band->_transformSize == 8 && band->_blkSize < 8) { + warning("mismatching _transformSize!"); + return -1; + } + + // decode block huffman codebook + if (!_ctx._gb->getBit()) + band->_blkVlc._tab = _ctx._blkVlc._tab; + else + if (band->_blkVlc.decodeHuffDesc(&_ctx, 1, IVI_BLK_HUFF)) + return -1; + + // select appropriate rvmap table for this band + band->_rvmapSel = _ctx._gb->getBit() ? _ctx._gb->getBits(3) : 8; + + // decode rvmap probability corrections if any + band->_numCorr = 0; // there is no corrections + if (_ctx._gb->getBit()) { + band->_numCorr = _ctx._gb->getBits(8); // get number of correction pairs + if (band->_numCorr > 61) { + warning("Too many corrections: %d", + band->_numCorr); + return -1; + } + + // read correction pairs + for (i = 0; i < band->_numCorr * 2; i++) + band->_corr[i] = _ctx._gb->getBits(8); + } + } + + if (band->_blkSize == 8) { + band->_intraBase = &_ivi4_quant_8x8_intra[_quant_index_to_tab[band->_quantMat]][0]; + band->_interBase = &_ivi4_quant_8x8_inter[_quant_index_to_tab[band->_quantMat]][0]; + } else { + band->_intraBase = &_ivi4_quant_4x4_intra[_quant_index_to_tab[band->_quantMat]][0]; + band->_interBase = &_ivi4_quant_4x4_inter[_quant_index_to_tab[band->_quantMat]][0]; + } + + // Indeo 4 doesn't use scale tables + band->_intraScale = NULL; + band->_interScale = NULL; + + _ctx._gb->align(); + + if (!band->_scan) { + warning("band->_scan not set"); + return -1; + } + + return 0; +} + +int Indeo4Decoder::decodeMbInfo(IVIBandDesc *band, IVITile *tile) { + int x, y, mvX, mvY, mvDelta, offs, mbOffset, blksPerMb, + mvScale, mbTypeBits, s; + IVIMbInfo *mb, *refMb; + int row_offset = band->_mbSize * band->_pitch; + + mb = tile->_mbs; + refMb = tile->_refMbs; + offs = tile->_yPos * band->_pitch + tile->_xPos; + + blksPerMb = band->_mbSize != band->_blkSize ? 4 : 1; + mbTypeBits = _ctx._frameType == IVI4_FRAMETYPE_BIDIR ? 2 : 1; + + // scale factor for motion vectors + mvScale = (_ctx._planes[0]._bands[0]._mbSize >> 3) - (band->_mbSize >> 3); + mvX = mvY = 0; + + if (((tile->_width + band->_mbSize - 1) / band->_mbSize) * ((tile->_height + band->_mbSize - 1) / band->_mbSize) != tile->_numMBs) { + warning("numMBs mismatch %d %d %d %d", tile->_width, tile->_height, band->_mbSize, tile->_numMBs); + return -1; + } + + for (y = tile->_yPos; y < tile->_yPos + tile->_height; y += band->_mbSize) { + mbOffset = offs; + + for (x = tile->_xPos; x < tile->_xPos + tile->_width; x += band->_mbSize) { + mb->_xPos = x; + mb->_yPos = y; + mb->_bufOffs = mbOffset; + mb->_bMvX = mb->_bMvY = 0; + + if (_ctx._gb->getBit()) { + if (_ctx._frameType == IVI4_FRAMETYPE_INTRA) { + warning("Empty macroblock in an INTRA picture!"); + return -1; + } + mb->_type = 1; // empty macroblocks are always INTER + mb->_cbp = 0; // all blocks are empty + + mb->_qDelta = 0; + if (!band->_plane && !band->_bandNum && _ctx._inQ) { + mb->_qDelta = _ctx._gb->getVLC2(_ctx._mbVlc._tab->_table, + IVI_VLC_BITS, 1); + mb->_qDelta = IVI_TOSIGNED(mb->_qDelta); + } + + mb->_mvX = mb->_mvY = 0; // no motion vector coded + if (band->_inheritMv && refMb) { + // motion vector inheritance + if (mvScale) { + mb->_mvX = scaleMV(refMb->_mvX, mvScale); + mb->_mvY = scaleMV(refMb->_mvY, mvScale); + } else { + mb->_mvX = refMb->_mvX; + mb->_mvY = refMb->_mvY; + } + } + } else { + if (band->_inheritMv) { + // copy mb_type from corresponding reference mb + if (!refMb) { + warning("refMb unavailable"); + return -1; + } + mb->_type = refMb->_type; + } else if (_ctx._frameType == IVI4_FRAMETYPE_INTRA || + _ctx._frameType == IVI4_FRAMETYPE_INTRA1) { + mb->_type = 0; // mb_type is always INTRA for intra-frames + } else { + mb->_type = _ctx._gb->getBits(mbTypeBits); + } + + mb->_cbp = _ctx._gb->getBits(blksPerMb); + + mb->_qDelta = 0; + if (band->_inheritQDelta) { + if (refMb) mb->_qDelta = refMb->_qDelta; + } else if (mb->_cbp || (!band->_plane && !band->_bandNum && + _ctx._inQ)) { + mb->_qDelta = _ctx._gb->getVLC2(_ctx._mbVlc._tab->_table, + IVI_VLC_BITS, 1); + mb->_qDelta = IVI_TOSIGNED(mb->_qDelta); + } + + if (!mb->_type) { + mb->_mvX = mb->_mvY = 0; // there is no motion vector in intra-macroblocks + } else { + if (band->_inheritMv) { + if (refMb) { + // motion vector inheritance + if (mvScale) { + mb->_mvX = scaleMV(refMb->_mvX, mvScale); + mb->_mvY = scaleMV(refMb->_mvY, mvScale); + } else { + mb->_mvX = refMb->_mvX; + mb->_mvY = refMb->_mvY; + } + } + } else { + // decode motion vector deltas + mvDelta = _ctx._gb->getVLC2(_ctx._mbVlc._tab->_table, + IVI_VLC_BITS, 1); + mvY += IVI_TOSIGNED(mvDelta); + mvDelta = _ctx._gb->getVLC2(_ctx._mbVlc._tab->_table, + IVI_VLC_BITS, 1); + mvX += IVI_TOSIGNED(mvDelta); + mb->_mvX = mvX; + mb->_mvY = mvY; + if (mb->_type == 3) { + mvDelta = _ctx._gb->getVLC2( + _ctx._mbVlc._tab->_table, + IVI_VLC_BITS, 1); + mvY += IVI_TOSIGNED(mvDelta); + mvDelta = _ctx._gb->getVLC2( + _ctx._mbVlc._tab->_table, + IVI_VLC_BITS, 1); + mvX += IVI_TOSIGNED(mvDelta); + mb->_bMvX = -mvX; + mb->_bMvY = -mvY; + } + } + if (mb->_type == 2) { + mb->_bMvX = -mb->_mvX; + mb->_bMvY = -mb->_mvY; + mb->_mvX = 0; + mb->_mvY = 0; + } + } + } + + s = band->_isHalfpel; + if (mb->_type) + if (x + (mb->_mvX >> s) + (y + (mb->_mvY >> s))*band->_pitch < 0 || + x + ((mb->_mvX + s) >> s) + band->_mbSize - 1 + + (y + band->_mbSize - 1 + ((mb->_mvY + s) >> s))*band->_pitch > band->_bufSize - 1) { + warning("motion vector %d %d outside reference", x*s + mb->_mvX, y*s + mb->_mvY); + return -1; + } + + mb++; + if (refMb) + refMb++; + mbOffset += band->_mbSize; + } + + offs += row_offset; + } + + _ctx._gb->align(); + return 0; +} + +int Indeo4Decoder::scaleTileSize(int defSize, int sizeFactor) { + return sizeFactor == 15 ? defSize : (sizeFactor + 1) << 5; +} + +int Indeo4Decoder::decodePlaneSubdivision() { + int i; + + switch (_ctx._gb->getBits(2)) { + case 3: + return 1; + + case 2: + for (i = 0; i < 4; i++) + if (_ctx._gb->getBits(2) != 3) + return 0; + return 4; + + default: + return 0; + } +} + +/*------------------------------------------------------------------------*/ + +/** + * Indeo 4 8x8 scan (zigzag) patterns + */ +static const uint8 ivi4AlternateScan8x8[64] = { + 0, 8, 1, 9, 16, 24, 2, 3, 17, 25, 10, 11, 32, 40, 48, 56, + 4, 5, 6, 7, 33, 41, 49, 57, 18, 19, 26, 27, 12, 13, 14, 15, + 34, 35, 43, 42, 50, 51, 59, 58, 20, 21, 22, 23, 31, 30, 29, 28, + 36, 37, 38, 39, 47, 46, 45, 44, 52, 53, 54, 55, 63, 62, 61, 60 +}; + +static const uint8 ivi4AlternateScan4x4[16] = { + 0, 1, 4, 5, 8, 12, 2, 3, 9, 13, 6, 7, 10, 11, 14, 15 +}; + +static const uint8 ivi4VerticalScan4x4[16] = { + 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 +}; + +static const uint8 ivi4HorizontalScan4x4[16] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 +}; + +const uint Indeo4Decoder::_ivi4_common_pic_sizes[14] = { + 640, 480, 320, 240, 160, 120, 704, 480, 352, 240, 352, 288, 176, 144 +}; + +Indeo4Decoder::Transform Indeo4Decoder::_transforms[18] = { + { IndeoDSP::ffIviInverseHaar8x8, IndeoDSP::ffIviDcHaar2d, 1 }, + { IndeoDSP::ffIviRowHaar8, IndeoDSP::ffIviDcHaar2d, 0 }, + { IndeoDSP::ffIviColHaar8, IndeoDSP::ffIviDcHaar2d, 0 }, + { IndeoDSP::ffIviPutPixels8x8, IndeoDSP::ffIviPutDcPixel8x8, 1 }, + { IndeoDSP::ffIviInverseSlant8x8, IndeoDSP::ffIviDcSlant2d, 1 }, + { IndeoDSP::ffIviRowSlant8, IndeoDSP::ffIviDcRowSlant, 1 }, + { IndeoDSP::ffIviColSlant8, IndeoDSP::ffIviDcColSlant, 1 }, + { NULL, NULL, 0 }, // inverse DCT 8x8 + { NULL, NULL, 0 }, // inverse DCT 8x1 + { NULL, NULL, 0 }, // inverse DCT 1x8 + { IndeoDSP::ffIviInverseHaar4x4, IndeoDSP::ffIviDcHaar2d, 1 }, + { IndeoDSP::ffIviInverseSlant4x4, IndeoDSP::ffIviDcSlant2d, 1 }, + { NULL, NULL, 0 }, // no transform 4x4 + { IndeoDSP::ffIviRowHaar4, IndeoDSP::ffIviDcHaar2d, 0 }, + { IndeoDSP::ffIviColHaar4, IndeoDSP::ffIviDcHaar2d, 0 }, + { IndeoDSP::ffIviRowSlant4, IndeoDSP::ffIviDcRowSlant, 0 }, + { IndeoDSP::ffIviColSlant4, IndeoDSP::ffIviDcColSlant, 0 }, + { NULL, NULL, 0 }, // inverse DCT 4x4 +}; + +const uint8 *const Indeo4Decoder::_scan_index_to_tab[15] = { + // for 8x8 transforms + ffZigZagDirect, + ivi4AlternateScan8x8, + _ffIviHorizontalScan8x8, + _ffIviVerticalScan8x8, + ffZigZagDirect, + + // for 4x4 transforms + _ffIviDirectScan4x4, + ivi4AlternateScan4x4, + ivi4VerticalScan4x4, + ivi4HorizontalScan4x4, + _ffIviDirectScan4x4, + + // TODO: check if those are needed + _ffIviHorizontalScan8x8, + _ffIviHorizontalScan8x8, + _ffIviHorizontalScan8x8, + _ffIviHorizontalScan8x8, + _ffIviHorizontalScan8x8 +}; + +/** + * Indeo 4 dequant tables + */ +const uint16 Indeo4Decoder::_ivi4_quant_8x8_intra[9][64] = { + { + 43, 342, 385, 470, 555, 555, 598, 726, + 342, 342, 470, 513, 555, 598, 726, 769, + 385, 470, 555, 555, 598, 726, 726, 811, + 470, 470, 555, 555, 598, 726, 769, 854, + 470, 555, 555, 598, 683, 726, 854, 1025, + 555, 555, 598, 683, 726, 854, 1025, 1153, + 555, 555, 598, 726, 811, 982, 1195, 1451, + 555, 598, 726, 811, 982, 1195, 1451, 1793 + }, + { + 86, 1195, 2390, 2390, 4865, 4865, 4865, 4865, + 1195, 1195, 2390, 2390, 4865, 4865, 4865, 4865, + 2390, 2390, 4865, 4865, 6827, 6827, 6827, 6827, + 2390, 2390, 4865, 4865, 6827, 6827, 6827, 6827, + 4865, 4865, 6827, 6827, 6827, 6827, 6827, 6827, + 4865, 4865, 6827, 6827, 6827, 6827, 6827, 6827, + 4865, 4865, 6827, 6827, 6827, 6827, 6827, 6827, + 4865, 4865, 6827, 6827, 6827, 6827, 6827, 6827 + }, + { + 235, 1067, 1195, 1323, 1451, 1579, 1707, 1835, + 235, 1067, 1195, 1323, 1451, 1579, 1707, 1835, + 235, 1067, 1195, 1323, 1451, 1579, 1707, 1835, + 235, 1067, 1195, 1323, 1451, 1579, 1707, 1835, + 235, 1067, 1195, 1323, 1451, 1579, 1707, 1835, + 235, 1067, 1195, 1323, 1451, 1579, 1707, 1835, + 235, 1067, 1195, 1323, 1451, 1579, 1707, 1835, + 235, 1067, 1195, 1323, 1451, 1579, 1707, 1835 + }, + { + 1707, 1707, 3414, 3414, 3414, 3414, 3414, 3414, + 1707, 1707, 3414, 3414, 3414, 3414, 3414, 3414, + 1707, 1707, 3414, 3414, 3414, 3414, 3414, 3414, + 1707, 1707, 3414, 3414, 3414, 3414, 3414, 3414, + 1707, 1707, 3414, 3414, 3414, 3414, 3414, 3414, + 1707, 1707, 3414, 3414, 3414, 3414, 3414, 3414, + 1707, 1707, 3414, 3414, 3414, 3414, 3414, 3414, + 1707, 1707, 3414, 3414, 3414, 3414, 3414, 3414 + }, + { + 897, 897, 897, 897, 897, 897, 897, 897, + 1067, 1067, 1067, 1067, 1067, 1067, 1067, 1067, + 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, + 1409, 1409, 1409, 1409, 1409, 1409, 1409, 1409, + 1579, 1579, 1579, 1579, 1579, 1579, 1579, 1579, + 1750, 1750, 1750, 1750, 1750, 1750, 1750, 1750, + 1921, 1921, 1921, 1921, 1921, 1921, 1921, 1921, + 2091, 2091, 2091, 2091, 2091, 2091, 2091, 2091 + }, + { + 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, + 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, + 3414, 3414, 3414, 3414, 3414, 3414, 3414, 3414, + 3414, 3414, 3414, 3414, 3414, 3414, 3414, 3414, + 3414, 3414, 3414, 3414, 3414, 3414, 3414, 3414, + 3414, 3414, 3414, 3414, 3414, 3414, 3414, 3414, + 3414, 3414, 3414, 3414, 3414, 3414, 3414, 3414, + 3414, 3414, 3414, 3414, 3414, 3414, 3414, 3414 + }, + { + 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, + 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, + 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, + 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, + 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, + 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, + 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390, + 2390, 2390, 2390, 2390, 2390, 2390, 2390, 2390 + }, + { + 22, 171, 214, 257, 257, 299, 299, 342, + 171, 171, 257, 257, 299, 299, 342, 385, + 214, 257, 257, 299, 299, 342, 342, 385, + 257, 257, 257, 299, 299, 342, 385, 427, + 257, 257, 299, 299, 342, 385, 427, 513, + 257, 299, 299, 342, 385, 427, 513, 598, + 299, 299, 299, 385, 385, 470, 598, 726, + 299, 299, 385, 385, 470, 598, 726, 897 + }, + { + 86, 598, 1195, 1195, 2390, 2390, 2390, 2390, + 598, 598, 1195, 1195, 2390, 2390, 2390, 2390, + 1195, 1195, 2390, 2390, 3414, 3414, 3414, 3414, + 1195, 1195, 2390, 2390, 3414, 3414, 3414, 3414, + 2390, 2390, 3414, 3414, 3414, 3414, 3414, 3414, + 2390, 2390, 3414, 3414, 3414, 3414, 3414, 3414, + 2390, 2390, 3414, 3414, 3414, 3414, 3414, 3414, + 2390, 2390, 3414, 3414, 3414, 3414, 3414, 3414 + } +}; + +const uint16 Indeo4Decoder::_ivi4_quant_8x8_inter[9][64] = { + { + 427, 427, 470, 427, 427, 427, 470, 470, + 427, 427, 470, 427, 427, 427, 470, 470, + 470, 470, 470, 470, 470, 470, 470, 470, + 427, 427, 470, 470, 427, 427, 470, 470, + 427, 427, 470, 427, 427, 427, 470, 470, + 427, 427, 470, 427, 427, 427, 470, 470, + 470, 470, 470, 470, 470, 470, 470, 470, + 470, 470, 470, 470, 470, 470, 470, 470 + }, + { + 1707, 1707, 2433, 2433, 3414, 3414, 3414, 3414, + 1707, 1707, 2433, 2433, 3414, 3414, 3414, 3414, + 2433, 2433, 3414, 3414, 4822, 4822, 4822, 4822, + 2433, 2433, 3414, 3414, 4822, 4822, 4822, 4822, + 3414, 3414, 4822, 4822, 3414, 3414, 3414, 3414, + 3414, 3414, 4822, 4822, 3414, 3414, 3414, 3414, + 3414, 3414, 4822, 4822, 3414, 3414, 3414, 3414, + 3414, 3414, 4822, 4822, 3414, 3414, 3414, 3414 + }, + { + 1195, 1195, 1281, 1238, 1195, 1195, 1281, 1281, + 1195, 1195, 1281, 1238, 1195, 1195, 1281, 1281, + 1195, 1195, 1281, 1238, 1195, 1195, 1281, 1281, + 1195, 1195, 1281, 1238, 1195, 1195, 1281, 1281, + 1195, 1195, 1281, 1238, 1195, 1195, 1281, 1281, + 1195, 1195, 1281, 1238, 1195, 1195, 1281, 1281, + 1195, 1195, 1281, 1238, 1195, 1195, 1281, 1281, + 1195, 1195, 1281, 1238, 1195, 1195, 1281, 1281 + }, + { + 2433, 2433, 3414, 3414, 2433, 2433, 2433, 2433, + 2433, 2433, 3414, 3414, 2433, 2433, 2433, 2433, + 2433, 2433, 3414, 3414, 2433, 2433, 2433, 2433, + 2433, 2433, 3414, 3414, 2433, 2433, 2433, 2433, + 2433, 2433, 3414, 3414, 2433, 2433, 2433, 2433, + 2433, 2433, 3414, 3414, 2433, 2433, 2433, 2433, + 2433, 2433, 3414, 3414, 2433, 2433, 2433, 2433, + 2433, 2433, 3414, 3414, 2433, 2433, 2433, 2433 + }, + { + 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, + 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, + 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, + 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, + 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, + 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, + 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281, + 1281, 1281, 1281, 1281, 1281, 1281, 1281, 1281 + }, + { + 2433, 2433, 2433, 2433, 2433, 2433, 2433, 2433, + 2433, 2433, 2433, 2433, 2433, 2433, 2433, 2433, + 3414, 3414, 3414, 3414, 3414, 3414, 3414, 3414, + 3414, 3414, 3414, 3414, 3414, 3414, 3414, 3414, + 2433, 2433, 2433, 2433, 2433, 2433, 2433, 2433, + 2433, 2433, 2433, 2433, 2433, 2433, 2433, 2433, + 2433, 2433, 2433, 2433, 2433, 2433, 2433, 2433, + 2433, 2433, 2433, 2433, 2433, 2433, 2433, 2433 + }, + { + 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, + 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, + 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, + 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, + 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, + 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, + 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707, + 1707, 1707, 1707, 1707, 1707, 1707, 1707, 1707 + }, + { + 86, 171, 171, 214, 214, 214, 214, 257, + 171, 171, 214, 214, 214, 214, 257, 257, + 171, 214, 214, 214, 214, 257, 257, 257, + 214, 214, 214, 214, 257, 257, 257, 299, + 214, 214, 214, 257, 257, 257, 299, 299, + 214, 214, 257, 257, 257, 299, 299, 299, + 214, 257, 257, 257, 299, 299, 299, 342, + 257, 257, 257, 299, 299, 299, 342, 342 + }, + { + 854, 854, 1195, 1195, 1707, 1707, 1707, 1707, + 854, 854, 1195, 1195, 1707, 1707, 1707, 1707, + 1195, 1195, 1707, 1707, 2390, 2390, 2390, 2390, + 1195, 1195, 1707, 1707, 2390, 2390, 2390, 2390, + 1707, 1707, 2390, 2390, 1707, 1707, 1707, 1707, + 1707, 1707, 2390, 2390, 1707, 1707, 1707, 1707, + 1707, 1707, 2390, 2390, 1707, 1707, 1707, 1707, + 1707, 1707, 2390, 2390, 1707, 1707, 1707, 1707 + } +}; + +const uint16 Indeo4Decoder::_ivi4_quant_4x4_intra[5][16] = { + { + 22, 214, 257, 299, + 214, 257, 299, 342, + 257, 299, 342, 427, + 299, 342, 427, 513 + }, + { + 129, 1025, 1451, 1451, + 1025, 1025, 1451, 1451, + 1451, 1451, 2049, 2049, + 1451, 1451, 2049, 2049 + }, + { + 43, 171, 171, 171, + 43, 171, 171, 171, + 43, 171, 171, 171, + 43, 171, 171, 171 + }, + { + 43, 43, 43, 43, + 171, 171, 171, 171, + 171, 171, 171, 171, + 171, 171, 171, 171 + }, + { + 43, 43, 43, 43, + 43, 43, 43, 43, + 43, 43, 43, 43, + 43, 43, 43, 43 + } +}; + +const uint16 Indeo4Decoder::_ivi4_quant_4x4_inter[5][16] = { + { + 107, 214, 257, 299, + 214, 257, 299, 299, + 257, 299, 299, 342, + 299, 299, 342, 342 + }, + { + 513, 1025, 1238, 1238, + 1025, 1025, 1238, 1238, + 1238, 1238, 1451, 1451, + 1238, 1238, 1451, 1451 + }, + { + 43, 171, 171, 171, + 43, 171, 171, 171, + 43, 171, 171, 171, + 43, 171, 171, 171 + }, + { + 43, 43, 43, 43, + 171, 171, 171, 171, + 171, 171, 171, 171, + 171, 171, 171, 171 + }, + { + 43, 43, 43, 43, + 43, 43, 43, 43, + 43, 43, 43, 43, + 43, 43, 43, 43 + } +}; + +const uint8 Indeo4Decoder::_quant_index_to_tab[22] = { + 0, 1, 0, 2, 1, 3, 0, 4, 1, 5, 0, 1, 6, 7, 8, // for 8x8 quant matrixes + 0, 1, 2, 2, 3, 3, 4 // for 4x4 quant matrixes +}; + +} // End of namespace Image diff --git a/image/codecs/indeo4.h b/image/codecs/indeo4.h new file mode 100644 index 0000000000..4a6279db37 --- /dev/null +++ b/image/codecs/indeo4.h @@ -0,0 +1,140 @@ +/* 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. + * + */ + +/* Intel Indeo 4 decompressor, derived from ffmpeg. + * + * Original copyright note: + * Intel Indeo 4 (IV31, IV32, etc.) video decoder for ffmpeg + * written, produced, and directed by Alan Smithee + */ + +#ifndef IMAGE_CODECS_INDEO4_H +#define IMAGE_CODECS_INDEO4_H + +#include "image/codecs/indeo/get_bits.h" +#include "image/codecs/indeo/indeo.h" + +namespace Image { + +using namespace Indeo; + +/** + * Intel Indeo 4 decoder. + * + * Used by AVI. + * + * Used in video: + * - AVIDecoder + */ +class Indeo4Decoder : public IndeoDecoderBase { + struct Transform { + InvTransformPtr *_invTrans; + DCTransformPtr *_dcTrans; + bool _is2dTrans; + }; +public: + Indeo4Decoder(uint16 width, uint16 height); + virtual ~Indeo4Decoder() {} + + virtual const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream); + + static bool isIndeo4(Common::SeekableReadStream &stream); +protected: + /** + * Decode the Indeo 4 picture header. + * @returns 0 = Ok, negative number = error + */ + virtual int decodePictureHeader(); + + /** + * Rearrange decoding and reference buffers. + */ + virtual void switchBuffers(); + + virtual bool isNonNullFrame() const; + + /** + * Decode Indeo 4 band header. + * + * @param[in,out] band pointer to the band descriptor + * @returns result code: 0 = OK, negative number = error + */ + virtual int decodeBandHeader(IVIBandDesc *band); + + /** + * Decode information (block type, cbp, quant delta, motion vector) + * for all macroblocks in the current tile. + * + * @param[in,out] band pointer to the band descriptor + * @param[in,out] tile pointer to the tile descriptor + * @returns result code: 0 = OK, negative number = error + */ + virtual int decodeMbInfo(IVIBandDesc *band, IVITile *tile); +private: + int scaleTileSize(int defSize, int sizeFactor); + + /** + * Decode subdivision of a plane. + * This is a simplified version that checks for two supported subdivisions: + * - 1 wavelet band per plane, size factor 1:1, code pattern: 3 + * - 4 wavelet bands per plane, size factor 1:4, code pattern: 2,3,3,3,3 + * Anything else is either unsupported or corrupt. + * + * @param[in,out] gb The GetBit context + * @returns Number of wavelet bands or 0 on error + */ + int decodePlaneSubdivision(); + +private: + /** + * Standard picture dimensions + */ + static const uint _ivi4_common_pic_sizes[14]; + + /** + * Transformations list + */ + static Transform _transforms[18]; + + static const uint8 *const _scan_index_to_tab[15]; + + /** + * Indeo 4 dequant tables + */ + static const uint16 _ivi4_quant_8x8_intra[9][64]; + + static const uint16 _ivi4_quant_8x8_inter[9][64]; + + static const uint16 _ivi4_quant_4x4_intra[5][16]; + + static const uint16 _ivi4_quant_4x4_inter[5][16]; + + /** + * Table for mapping quant matrix index from the bitstream + * into internal quant table number. + */ + static const uint8 _quant_index_to_tab[22]; +}; + +} // End of namespace Image + +#endif diff --git a/image/codecs/indeo5.cpp b/image/codecs/indeo5.cpp new file mode 100644 index 0000000000..d5ce571bea --- /dev/null +++ b/image/codecs/indeo5.cpp @@ -0,0 +1,744 @@ +/* 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. + * + */ + +/* Intel Indeo 5 decompressor, derived from ffmpeg. + * + * Original copyright note: * Intel Indeo 3 (IV41, IV42, etc.) video decoder for ffmpeg + * written, produced, and directed by Alan Smithee + */ + +#include "common/memstream.h" +#include "common/textconsole.h" +#include "graphics/yuv_to_rgb.h" +#include "image/codecs/indeo5.h" +#include "image/codecs/indeo/indeo_dsp.h" +#include "image/codecs/indeo/mem.h" + +namespace Image { + +/** + * Indeo5 frame types. + */ +enum { + FRAMETYPE_INTRA = 0, + FRAMETYPE_INTER = 1, ///< non-droppable P-frame + FRAMETYPE_INTER_SCAL = 2, ///< droppable P-frame used in the scalability mode + FRAMETYPE_INTER_NOREF = 3, ///< droppable P-frame + FRAMETYPE_NULL = 4 ///< empty frame with no data +}; + +#define IVI5_PIC_SIZE_ESC 15 + +Indeo5Decoder::Indeo5Decoder(uint16 width, uint16 height) : IndeoDecoderBase(width, height) { + _ctx._isIndeo4 = false; + _ctx._refBuf = 1; + _ctx._bRefBuf = 3; + _ctx._pFrame = new AVFrame(); +} + +bool Indeo5Decoder::isIndeo5(Common::SeekableReadStream &stream) { + // Less than 16 bytes? This can't be right + if (stream.size() < 16) + return false; + + // Read in the start of the data + byte buffer[16]; + stream.read(buffer, 16); + stream.seek(-16, SEEK_CUR); + + // Validate the first 5-bit word has the correct identifier + Indeo::GetBits gb(new Common::MemoryReadStream(buffer, 16 * 8)); + bool isIndeo5 = gb.getBits(5) == 0x1F; + + return isIndeo5; +} + +const Graphics::Surface *Indeo5Decoder::decodeFrame(Common::SeekableReadStream &stream) { + // Not Indeo 5? Fail + if (!isIndeo5(stream)) + return nullptr; + + // Set up the frame data buffer + byte *frameData = new byte[stream.size()]; + stream.read(frameData, stream.size()); + _ctx._frameData = frameData; + _ctx._frameSize = stream.size(); + + // Set up the GetBits instance for reading the data + _ctx._gb = new GetBits(new Common::MemoryReadStream(_ctx._frameData, _ctx._frameSize * 8)); + + // Decode the frame + int err = decodeIndeoFrame(); + + // Free the bit reader and frame buffer + delete _ctx._gb; + _ctx._gb = nullptr; + delete[] frameData; + _ctx._frameData = nullptr; + _ctx._frameSize = 0; + + return (err < 0) ? nullptr : &_surface->rawSurface(); +} + +int Indeo5Decoder::decodePictureHeader() { + IVIPicConfig picConf; + int ret; + + if (_ctx._gb->getBits(5) != 0x1F) { + warning("Invalid picture start code!"); + return -1; + } + + _ctx._prevFrameType = _ctx._frameType; + _ctx._frameType = _ctx._gb->getBits(3); + if (_ctx._frameType >= 5) { + warning("Invalid frame type: %d", _ctx._frameType); + return -1; + } + + _ctx._frameNum = _ctx._gb->getBits(8); + + if (_ctx._frameType == FRAMETYPE_INTRA) { + if ((ret = decode_gop_header()) < 0) { + warning("Invalid GOP header, skipping frames."); + _ctx._gopInvalid = true; + return ret; + } + _ctx._gopInvalid = false; + } + + if (_ctx._frameType == FRAMETYPE_INTER_SCAL && !_ctx._isScalable) { + warning("Scalable inter frame in non scalable stream"); + _ctx._frameType = FRAMETYPE_INTER; + return -1; + } + + if (_ctx._frameType != FRAMETYPE_NULL) { + _ctx._frameFlags = _ctx._gb->getBits(8); + + _ctx._picHdrSize = (_ctx._frameFlags & 1) ? _ctx._gb->getBits(24) : 0; + + _ctx._checksum = (_ctx._frameFlags & 0x10) ? _ctx._gb->getBits(16) : 0; + + // skip unknown extension if any + if (_ctx._frameFlags & 0x20) + skip_hdr_extension(); // XXX: untested + + // decode macroblock huffman codebook + ret = _ctx._mbVlc.decodeHuffDesc(&_ctx, _ctx._frameFlags & 0x40, + IVI_MB_HUFF); + if (ret < 0) + return ret; + + _ctx._gb->skip(3); // FIXME: unknown meaning! + } + + _ctx._gb->align(); + return 0; +} + +void Indeo5Decoder::switchBuffers() { + switch (_ctx._prevFrameType) { + case FRAMETYPE_INTRA: + case FRAMETYPE_INTER: + _ctx._bufSwitch ^= 1; + _ctx._dstBuf = _ctx._bufSwitch; + _ctx._refBuf = _ctx._bufSwitch ^ 1; + break; + + case FRAMETYPE_INTER_SCAL: + if (!_ctx._interScal) { + _ctx._ref2Buf = 2; + _ctx._interScal = 1; + } + SWAP(_ctx._dstBuf, _ctx._ref2Buf); + _ctx._refBuf = _ctx._ref2Buf; + break; + + case FRAMETYPE_INTER_NOREF: + break; + } + + switch (_ctx._frameType) { + case FRAMETYPE_INTRA: + _ctx._bufSwitch = 0; + // FALLTHROUGH + case FRAMETYPE_INTER: + _ctx._interScal = 0; + _ctx._dstBuf = _ctx._bufSwitch; + _ctx._refBuf = _ctx._bufSwitch ^ 1; + break; + + case FRAMETYPE_INTER_SCAL: + case FRAMETYPE_INTER_NOREF: + case FRAMETYPE_NULL: + break; + } +} + +bool Indeo5Decoder::isNonNullFrame() const { + return _ctx._frameType != FRAMETYPE_NULL; +} + +int Indeo5Decoder::decodeBandHeader(IVIBandDesc *band) { + int i, ret; + uint8 bandFlags; + + bandFlags = _ctx._gb->getBits(8); + + if (bandFlags & 1) { + band->_isEmpty = true; + return 0; + } + + band->_dataSize = (_ctx._frameFlags & 0x80) ? _ctx._gb->getBits(24) : 0; + + band->_inheritMv = (bandFlags & 2) != 0; + band->_inheritQDelta = (bandFlags & 8) != 0; + band->_qdeltaPresent = (bandFlags & 4) != 0; + if (!band->_qdeltaPresent) + band->_inheritQDelta = 1; + + // decode rvmap probability corrections if any + band->_numCorr = 0; // there are no corrections + if (bandFlags & 0x10) { + band->_numCorr = _ctx._gb->getBits(8); // get number of correction pairs + if (band->_numCorr > 61) { + warning("Too many corrections: %d", band->_numCorr); + return -1; + } + + // read correction pairs + for (i = 0; i < band->_numCorr * 2; i++) + band->_corr[i] = _ctx._gb->getBits(8); + } + + // select appropriate rvmap table for this band + band->_rvmapSel = (bandFlags & 0x40) ? _ctx._gb->getBits(3) : 8; + + // decode block huffman codebook + ret = band->_blkVlc.decodeHuffDesc(&_ctx, bandFlags & 0x80, IVI_BLK_HUFF); + if (ret < 0) + return ret; + + band->_checksumPresent = _ctx._gb->getBit(); + if (band->_checksumPresent) + band->_checksum = _ctx._gb->getBits(16); + + band->_globQuant = _ctx._gb->getBits(5); + + // skip unknown extension if any + if (bandFlags & 0x20) { // XXX: untested + _ctx._gb->align(); + skip_hdr_extension(); + } + + _ctx._gb->align(); + + return 0; +} + +int Indeo5Decoder::decodeMbInfo(IVIBandDesc *band, IVITile *tile) { + int x, y, mvX, mvY, mvDelta, offs, mbOffset, mvScale, blksPerMb, s; + IVIMbInfo *mb, *refMb; + int rowOffset = band->_mbSize * band->_pitch; + + mb = tile->_mbs; + refMb = tile->_refMbs; + offs = tile->_yPos * band->_pitch + tile->_xPos; + + if (!refMb && + ((band->_qdeltaPresent && band->_inheritQDelta) || band->_inheritMv)) + return -1; + + if (tile->_numMBs != IVI_MBs_PER_TILE(tile->_width, tile->_height, band->_mbSize)) { + warning("Allocated tile size %d mismatches parameters %d", + tile->_numMBs, IVI_MBs_PER_TILE(tile->_width, tile->_height, band->_mbSize)); + return -1; + } + + // scale factor for motion vectors + mvScale = (_ctx._planes[0]._bands[0]._mbSize >> 3) - (band->_mbSize >> 3); + mvX = mvY = 0; + + for (y = tile->_yPos; y < (tile->_yPos + tile->_height); y += band->_mbSize) { + mbOffset = offs; + + for (x = tile->_xPos; x < (tile->_xPos + tile->_width); x += band->_mbSize) { + mb->_xPos = x; + mb->_yPos = y; + mb->_bufOffs = mbOffset; + + if (_ctx._gb->getBit()) { + if (_ctx._frameType == FRAMETYPE_INTRA) { + warning("Empty macroblock in an INTRA picture!"); + return -1; + } + mb->_type = 1; // empty macroblocks are always INTER + mb->_cbp = 0; // all blocks are empty + + mb->_qDelta = 0; + if (!band->_plane && !band->_bandNum && (_ctx._frameFlags & 8)) { + mb->_qDelta = _ctx._gb->getVLC2(_ctx._mbVlc._tab->_table, IVI_VLC_BITS, 1); + mb->_qDelta = IVI_TOSIGNED(mb->_qDelta); + } + + mb->_mvX = mb->_mvY = 0; // no motion vector coded + if (band->_inheritMv && refMb) { + // motion vector inheritance + if (mvScale) { + mb->_mvX = scaleMV(refMb->_mvX, mvScale); + mb->_mvY = scaleMV(refMb->_mvY, mvScale); + } else { + mb->_mvX = refMb->_mvX; + mb->_mvY = refMb->_mvY; + } + } + } else { + if (band->_inheritMv && refMb) { + mb->_type = refMb->_type; // copy mb_type from corresponding reference mb + } else if (_ctx._frameType == FRAMETYPE_INTRA) { + mb->_type = 0; // mb_type is always INTRA for intra-frames + } else { + mb->_type = _ctx._gb->getBit(); + } + + blksPerMb = band->_mbSize != band->_blkSize ? 4 : 1; + mb->_cbp = _ctx._gb->getBits(blksPerMb); + + mb->_qDelta = 0; + if (band->_qdeltaPresent) { + if (band->_inheritQDelta) { + if (refMb) mb->_qDelta = refMb->_qDelta; + } else if (mb->_cbp || (!band->_plane && !band->_bandNum && + (_ctx._frameFlags & 8))) { + mb->_qDelta = _ctx._gb->getVLC2(_ctx._mbVlc._tab->_table, IVI_VLC_BITS, 1); + mb->_qDelta = IVI_TOSIGNED(mb->_qDelta); + } + } + + if (!mb->_type) { + mb->_mvX = mb->_mvY = 0; // there is no motion vector in intra-macroblocks + } else { + if (band->_inheritMv && refMb) { + // motion vector inheritance + if (mvScale) { + mb->_mvX = scaleMV(refMb->_mvX, mvScale); + mb->_mvY = scaleMV(refMb->_mvY, mvScale); + } else { + mb->_mvX = refMb->_mvX; + mb->_mvY = refMb->_mvY; + } + } else { + // decode motion vector deltas + mvDelta = _ctx._gb->getVLC2(_ctx._mbVlc._tab->_table, IVI_VLC_BITS, 1); + mvY += IVI_TOSIGNED(mvDelta); + mvDelta = _ctx._gb->getVLC2(_ctx._mbVlc._tab->_table, IVI_VLC_BITS, 1); + mvX += IVI_TOSIGNED(mvDelta); + mb->_mvX = mvX; + mb->_mvY = mvY; + } + } + } + + s = band->_isHalfpel; + if (mb->_type) + if (x + (mb->_mvX >> s) + (y + (mb->_mvY >> s)) * band->_pitch < 0 || + x + ((mb->_mvX + s) >> s) + band->_mbSize - 1 + + (y + band->_mbSize - 1 + ((mb->_mvY + s) >> s)) * band->_pitch > band->_bufSize - 1) { + warning("motion vector %d %d outside reference", x*s + mb->_mvX, y * s + mb->_mvY); + return -1; + } + + mb++; + if (refMb) + refMb++; + mbOffset += band->_mbSize; + } + + offs += rowOffset; + } + + _ctx._gb->align(); + + return 0; +} + +int Indeo5Decoder::decode_gop_header() { + int result, i, p, tileSize, picSizeIndx, mbSize, blkSize, isScalable; + int quantMat; + bool blkSizeChanged = false; + IVIBandDesc *band, *band1, *band2; + IVIPicConfig picConf; + + _ctx._gopFlags = _ctx._gb->getBits(8); + + _ctx._gopHdrSize = (_ctx._gopFlags & 1) ? _ctx._gb->getBits(16) : 0; + + if (_ctx._gopFlags & IVI5_IS_PROTECTED) + _ctx._lockWord = _ctx._gb->getBits(32); + + tileSize = (_ctx._gopFlags & 0x40) ? 64 << _ctx._gb->getBits(2) : 0; + if (tileSize > 256) { + warning("Invalid tile size: %d", tileSize); + return -1; + } + + // decode number of wavelet bands + // num_levels * 3 + 1 + picConf._lumaBands = _ctx._gb->getBits(2) * 3 + 1; + picConf._chromaBands = _ctx._gb->getBit() * 3 + 1; + isScalable = picConf._lumaBands != 1 || picConf._chromaBands != 1; + if (isScalable && (picConf._lumaBands != 4 || picConf._chromaBands != 1)) { + warning("Scalability: unsupported subdivision! Luma bands: %d, chroma bands: %d", + picConf._lumaBands, picConf._chromaBands); + return -1; + } + + picSizeIndx = _ctx._gb->getBits(4); + if (picSizeIndx == IVI5_PIC_SIZE_ESC) { + picConf._picHeight = _ctx._gb->getBits(13); + picConf._picWidth = _ctx._gb->getBits(13); + } else { + picConf._picHeight = _commonPicSizes[picSizeIndx * 2 + 1] << 2; + picConf._picWidth = _commonPicSizes[picSizeIndx * 2] << 2; + } + + if (_ctx._gopFlags & 2) { + warning("YV12 picture format"); + return -2; + } + + picConf._chromaHeight = (picConf._picHeight + 3) >> 2; + picConf._chromaWidth = (picConf._picWidth + 3) >> 2; + + if (!tileSize) { + picConf._tileHeight = picConf._picHeight; + picConf._tileWidth = picConf._picWidth; + } else { + picConf._tileHeight = picConf._tileWidth = tileSize; + } + + // check if picture layout was changed and reallocate buffers + if (picConf.ivi_pic_config_cmp(_ctx._picConf) || _ctx._gopInvalid) { + result = IVIPlaneDesc::initPlanes(_ctx._planes, &picConf, 0); + if (result < 0) { + warning("Couldn't reallocate color planes!"); + return result; + } + _ctx._picConf = picConf; + _ctx._isScalable = isScalable; + blkSizeChanged = 1; // force reallocation of the internal structures + } + + for (p = 0; p <= 1; p++) { + for (i = 0; i < (!p ? picConf._lumaBands : picConf._chromaBands); i++) { + band = &_ctx._planes[p]._bands[i]; + + band->_isHalfpel = _ctx._gb->getBit(); + + mbSize = _ctx._gb->getBit(); + blkSize = 8 >> _ctx._gb->getBit(); + mbSize = blkSize << (!mbSize ? 1 : 0); + + if (p == 0 && blkSize == 4) { + warning("4x4 luma blocks are unsupported!"); + return -2; + } + + blkSizeChanged = mbSize != band->_mbSize || blkSize != band->_blkSize; + if (blkSizeChanged) { + band->_mbSize = mbSize; + band->_blkSize = blkSize; + } + + if (_ctx._gb->getBit()) { + warning("Extended transform info"); + return -2; + } + + // select transform function and scan pattern according to plane and band number + switch ((p << 2) + i) { + case 0: + band->_invTransform = IndeoDSP::ffIviInverseSlant8x8; + band->_dcTransform = IndeoDSP::ffIviDcSlant2d; + band->_scan = ffZigZagDirect; + band->_transformSize = 8; + break; + + case 1: + band->_invTransform = IndeoDSP::ffIviRowSlant8; + band->_dcTransform = IndeoDSP::ffIviDcRowSlant; + band->_scan = _ffIviVerticalScan8x8; + band->_transformSize = 8; + break; + + case 2: + band->_invTransform = IndeoDSP::ffIviColSlant8; + band->_dcTransform = IndeoDSP::ffIviDcColSlant; + band->_scan = _ffIviHorizontalScan8x8; + band->_transformSize = 8; + break; + + case 3: + band->_invTransform = IndeoDSP::ffIviPutPixels8x8; + band->_dcTransform = IndeoDSP::ffIviPutDcPixel8x8; + band->_scan = _ffIviHorizontalScan8x8; + band->_transformSize = 8; + break; + + case 4: + band->_invTransform = IndeoDSP::ffIviInverseSlant4x4; + band->_dcTransform = IndeoDSP::ffIviDcSlant2d; + band->_scan = _ffIviDirectScan4x4; + band->_transformSize = 4; + break; + } + + band->_is2dTrans = band->_invTransform == IndeoDSP::ffIviInverseSlant8x8 || + band->_invTransform == IndeoDSP::ffIviInverseSlant4x4; + + if (band->_transformSize != band->_blkSize) { + warning("transform and block size mismatch (%d != %d)", band->_transformSize, band->_blkSize); + return -1; + } + + // select dequant matrix according to plane and band number + if (!p) { + quantMat = (picConf._lumaBands > 1) ? i + 1 : 0; + } else { + quantMat = 5; + } + + if (band->_blkSize == 8) { + if (quantMat >= 5) { + warning("_quantMat %d too large!", quantMat); + return -1; + } + band->_intraBase = &_baseQuant8x8Intra[quantMat][0]; + band->_interBase = &_baseQuant8x8Inter[quantMat][0]; + band->_intraScale = &_scaleQuant8x8Intra[quantMat][0]; + band->_interScale = &_scaleQuant8x8Inter[quantMat][0]; + } else { + band->_intraBase = _baseQuant4x4Intra; + band->_interBase = _baseQuant4x4Inter; + band->_intraScale = _scaleQuant4x4Intra; + band->_interScale = _scaleQuant4x4Inter; + } + + if (_ctx._gb->getBits(2)) { + warning("End marker missing!"); + return -1; + } + } + } + + // copy chroma parameters into the 2nd chroma plane + for (i = 0; i < picConf._chromaBands; i++) { + band1 = &_ctx._planes[1]._bands[i]; + band2 = &_ctx._planes[2]._bands[i]; + + band2->_width = band1->_width; + band2->_height = band1->_height; + band2->_mbSize = band1->_mbSize; + band2->_blkSize = band1->_blkSize; + band2->_isHalfpel = band1->_isHalfpel; + band2->_intraBase = band1->_intraBase; + band2->_interBase = band1->_interBase; + band2->_intraScale = band1->_intraScale; + band2->_interScale = band1->_interScale; + band2->_scan = band1->_scan; + band2->_invTransform = band1->_invTransform; + band2->_dcTransform = band1->_dcTransform; + band2->_is2dTrans = band1->_is2dTrans; + band2->_transformSize = band1->_transformSize; + } + + // reallocate internal structures if needed + if (blkSizeChanged) { + result = IVIPlaneDesc::initTiles(_ctx._planes, picConf._tileWidth, + picConf._tileHeight); + if (result < 0) { + warning("Couldn't reallocate internal structures!"); + return result; + } + } + + if (_ctx._gopFlags & 8) { + if (_ctx._gb->getBits(3)) { + warning("Alignment bits are not zero!"); + return -1; + } + + if (_ctx._gb->getBit()) + _ctx._gb->skip(24); // skip transparency fill color + } + + _ctx._gb->align(); + + _ctx._gb->skip(23); // FIXME: unknown meaning + + // skip GOP extension if any + if (_ctx._gb->getBit()) { + do { + i = _ctx._gb->getBits(16); + } while (i & 0x8000); + } + + _ctx._gb->align(); + + return 0; +} + +int Indeo5Decoder::skip_hdr_extension() { + int i, len; + + do { + len = _ctx._gb->getBits(8); + if (_ctx._gb->eos()) + return -1; + for (i = 0; i < len; i++) + _ctx._gb->skip(8); + } while (len); + + return 0; +} + +/*------------------------------------------------------------------------*/ + +const uint8 Indeo5Decoder::_commonPicSizes[30] = { + 160, 120, 80, 60, 40, 30, 176, 120, 88, 60, 88, 72, 44, 36, 60, 45, 160, 60, + 176, 60, 20, 15, 22, 18, 0, 0, 0, 0, 0, 0 +}; + +const uint16 Indeo5Decoder::_baseQuant8x8Inter[5][64] = { + {0x26, 0x3a, 0x3e, 0x46, 0x4a, 0x4e, 0x52, 0x5a, 0x3a, 0x3e, 0x42, 0x46, 0x4a, 0x4e, 0x56, 0x5e, + 0x3e, 0x42, 0x46, 0x48, 0x4c, 0x52, 0x5a, 0x62, 0x46, 0x46, 0x48, 0x4a, 0x4e, 0x56, 0x5e, 0x66, + 0x4a, 0x4a, 0x4c, 0x4e, 0x52, 0x5a, 0x62, 0x6a, 0x4e, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x66, 0x6e, + 0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x72, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76, + }, + {0x26, 0x3a, 0x3e, 0x46, 0x4a, 0x4e, 0x52, 0x5a, 0x3a, 0x3e, 0x42, 0x46, 0x4a, 0x4e, 0x56, 0x5e, + 0x3e, 0x42, 0x46, 0x48, 0x4c, 0x52, 0x5a, 0x62, 0x46, 0x46, 0x48, 0x4a, 0x4e, 0x56, 0x5e, 0x66, + 0x4a, 0x4a, 0x4c, 0x4e, 0x52, 0x5a, 0x62, 0x6a, 0x4e, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x66, 0x6e, + 0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x72, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76, + }, + {0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, + 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, + 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, + 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, + }, + {0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, + 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, + 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, + }, + {0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + } +}; + +const uint16 Indeo5Decoder::_baseQuant8x8Intra[5][64] = { + {0x1a, 0x2e, 0x36, 0x42, 0x46, 0x4a, 0x4e, 0x5a, 0x2e, 0x32, 0x3e, 0x42, 0x46, 0x4e, 0x56, 0x6a, + 0x36, 0x3e, 0x3e, 0x44, 0x4a, 0x54, 0x66, 0x72, 0x42, 0x42, 0x44, 0x4a, 0x52, 0x62, 0x6c, 0x7a, + 0x46, 0x46, 0x4a, 0x52, 0x5e, 0x66, 0x72, 0x8e, 0x4a, 0x4e, 0x54, 0x62, 0x66, 0x6e, 0x86, 0xa6, + 0x4e, 0x56, 0x66, 0x6c, 0x72, 0x86, 0x9a, 0xca, 0x5a, 0x6a, 0x72, 0x7a, 0x8e, 0xa6, 0xca, 0xfe, + }, + {0x26, 0x3a, 0x3e, 0x46, 0x4a, 0x4e, 0x52, 0x5a, 0x3a, 0x3e, 0x42, 0x46, 0x4a, 0x4e, 0x56, 0x5e, + 0x3e, 0x42, 0x46, 0x48, 0x4c, 0x52, 0x5a, 0x62, 0x46, 0x46, 0x48, 0x4a, 0x4e, 0x56, 0x5e, 0x66, + 0x4a, 0x4a, 0x4c, 0x4e, 0x52, 0x5a, 0x62, 0x6a, 0x4e, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x66, 0x6e, + 0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x72, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76, + }, + {0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, + 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, + 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, + 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2, + }, + {0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, + 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, + 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, + }, + {0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, + } +}; + +const uint16 Indeo5Decoder::_baseQuant4x4Inter[16] = { + 0x1e, 0x3e, 0x4a, 0x52, 0x3e, 0x4a, 0x52, 0x56, 0x4a, 0x52, 0x56, 0x5e, 0x52, 0x56, 0x5e, 0x66 +}; + +const uint16 Indeo5Decoder::_baseQuant4x4Intra[16] = { + 0x1e, 0x3e, 0x4a, 0x52, 0x3e, 0x4a, 0x52, 0x5e, 0x4a, 0x52, 0x5e, 0x7a, 0x52, 0x5e, 0x7a, 0x92 +}; + + +const uint8 Indeo5Decoder::_scaleQuant8x8Inter[5][24] = { + {0x0b, 0x11, 0x13, 0x14, 0x15, 0x16, 0x18, 0x1a, 0x1b, 0x1d, 0x20, 0x22, + 0x23, 0x25, 0x28, 0x2a, 0x2e, 0x32, 0x35, 0x39, 0x3d, 0x41, 0x44, 0x4a, + }, + {0x07, 0x14, 0x16, 0x18, 0x1b, 0x1e, 0x22, 0x25, 0x29, 0x2d, 0x31, 0x35, + 0x3a, 0x3f, 0x44, 0x4a, 0x50, 0x56, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x7e, + }, + {0x15, 0x25, 0x28, 0x2d, 0x30, 0x34, 0x3a, 0x3d, 0x42, 0x48, 0x4c, 0x51, + 0x56, 0x5b, 0x60, 0x65, 0x6b, 0x70, 0x76, 0x7c, 0x82, 0x88, 0x8f, 0x97, + }, + {0x13, 0x1f, 0x20, 0x22, 0x25, 0x28, 0x2b, 0x2d, 0x30, 0x33, 0x36, 0x39, + 0x3c, 0x3f, 0x42, 0x45, 0x48, 0x4b, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x62, + }, + {0x3c, 0x52, 0x58, 0x5d, 0x63, 0x68, 0x68, 0x6d, 0x73, 0x78, 0x7c, 0x80, + 0x84, 0x89, 0x8e, 0x93, 0x98, 0x9d, 0xa3, 0xa9, 0xad, 0xb1, 0xb5, 0xba, + }, +}; + +const uint8 Indeo5Decoder::_scaleQuant8x8Intra[5][24] = { + {0x0b, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x20, + 0x22, 0x24, 0x27, 0x28, 0x2a, 0x2d, 0x2f, 0x31, 0x34, 0x37, 0x39, 0x3c, + }, + {0x01, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1b, 0x1e, 0x22, 0x25, 0x28, 0x2c, + 0x30, 0x34, 0x38, 0x3d, 0x42, 0x47, 0x4c, 0x52, 0x58, 0x5e, 0x65, 0x6c, + }, + {0x13, 0x22, 0x27, 0x2a, 0x2d, 0x33, 0x36, 0x3c, 0x41, 0x45, 0x49, 0x4e, + 0x53, 0x58, 0x5d, 0x63, 0x69, 0x6f, 0x75, 0x7c, 0x82, 0x88, 0x8e, 0x95, + }, + {0x13, 0x1f, 0x21, 0x24, 0x27, 0x29, 0x2d, 0x2f, 0x34, 0x37, 0x3a, 0x3d, + 0x40, 0x44, 0x48, 0x4c, 0x4f, 0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6b, + }, + {0x31, 0x42, 0x47, 0x47, 0x4d, 0x52, 0x58, 0x58, 0x5d, 0x63, 0x67, 0x6b, + 0x6f, 0x73, 0x78, 0x7c, 0x80, 0x84, 0x89, 0x8e, 0x93, 0x98, 0x9d, 0xa4, + } +}; + +const uint8 Indeo5Decoder::_scaleQuant4x4Inter[24] = { + 0x0b, 0x0d, 0x0d, 0x0e, 0x11, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, +}; + +const uint8 Indeo5Decoder::_scaleQuant4x4Intra[24] = { + 0x01, 0x0b, 0x0b, 0x0d, 0x0d, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20 +}; + +} // End of namespace Image diff --git a/image/codecs/indeo5.h b/image/codecs/indeo5.h new file mode 100644 index 0000000000..704bda5e4b --- /dev/null +++ b/image/codecs/indeo5.h @@ -0,0 +1,136 @@ +/* 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. + * + */ + +/* Intel Indeo 4 decompressor, derived from ffmpeg. + * + * Original copyright note: + * Intel Indeo 4 (IV31, IV32, etc.) video decoder for ffmpeg + * written, produced, and directed by Alan Smithee + */ + +#ifndef IMAGE_CODECS_INDEO5_H +#define IMAGE_CODECS_INDEO5_H + +#include "image/codecs/indeo/get_bits.h" +#include "image/codecs/indeo/indeo.h" + +namespace Image { + +using namespace Indeo; + +/** + * Intel Indeo 5 decoder. + * + * Used by AVI. + * + * Used in video: + * - AVIDecoder + */ +class Indeo5Decoder : public IndeoDecoderBase { + struct Transform { + InvTransformPtr *inv_trans; + DCTransformPtr *dc_trans; + int is_2d_trans; + }; +public: + Indeo5Decoder(uint16 width, uint16 height); + virtual ~Indeo5Decoder() {} + + virtual const Graphics::Surface *decodeFrame(Common::SeekableReadStream &stream); + + static bool isIndeo5(Common::SeekableReadStream &stream); +protected: + /** + * Decode the Indeo 5 picture header. + * @returns 0 = Ok, negative number = error + */ + virtual int decodePictureHeader(); + + /** + * Rearrange decoding and reference buffers. + */ + virtual void switchBuffers(); + + virtual bool isNonNullFrame() const; + + /** + * Decode Indeo 4 band header. + * + * @param[in,out] band pointer to the band descriptor + * @return result code: 0 = OK, negative number = error + */ + virtual int decodeBandHeader(IVIBandDesc *band); + + /** + * Decode information (block type, cbp, quant delta, motion vector) + * for all macroblocks in the current tile. + * + * @param[in,out] band pointer to the band descriptor + * @param[in,out] tile pointer to the tile descriptor + * @return result code: 0 = OK, negative number = error + */ + virtual int decodeMbInfo(IVIBandDesc *band, IVITile *tile); +private: + /** + * Decode Indeo5 GOP (Group of pictures) header. + * This header is present in key frames only. + * It defines parameters for all frames in a GOP. + * @returns result code: 0 = OK, -1 = error + */ + int decode_gop_header(); + + /** + * Skip a header extension. + */ + int skip_hdr_extension(); + +private: + /** + * standard picture dimensions (width, height divided by 4) + */ + static const uint8 _commonPicSizes[30]; + + /** + * Indeo5 dequantization matrixes consist of two tables: base table + * and scale table. The base table defines the dequantization matrix + * itself and the scale table tells how this matrix should be scaled + * for a particular quant level (0...24). + * + * ivi5_base_quant_bbb_ttt - base tables for block size 'bbb' of type 'ttt' + * ivi5_scale_quant_bbb_ttt - scale tables for block size 'bbb' of type 'ttt' + */ + static const uint16 _baseQuant8x8Inter[5][64]; + static const uint16 _baseQuant8x8Intra[5][64]; + + static const uint16 _baseQuant4x4Inter[16]; + static const uint16 _baseQuant4x4Intra[16]; + + static const uint8 _scaleQuant8x8Inter[5][24]; + static const uint8 _scaleQuant8x8Intra[5][24]; + + static const uint8 _scaleQuant4x4Inter[24]; + static const uint8 _scaleQuant4x4Intra[24]; +}; + +} // End of namespace Image + +#endif diff --git a/image/module.mk b/image/module.mk index 3742fb909f..2ede7c3bdb 100644 --- a/image/module.mk +++ b/image/module.mk @@ -13,6 +13,8 @@ MODULE_OBJS := \ codecs/cinepak.o \ codecs/codec.o \ codecs/indeo3.o \ + codecs/indeo4.o \ + codecs/indeo5.o \ codecs/mjpeg.o \ codecs/msrle.o \ codecs/msrle4.o \ @@ -21,7 +23,12 @@ MODULE_OBJS := \ codecs/rpza.o \ codecs/smc.o \ codecs/svq1.o \ - codecs/truemotion1.o + codecs/truemotion1.o \ + codecs/indeo/get_bits.o \ + codecs/indeo/indeo.o \ + codecs/indeo/indeo_dsp.o \ + codecs/indeo/mem.o \ + codecs/indeo/vlc.o ifdef USE_MPEG2 MODULE_OBJS += \ |