From 5f0962696f97df2cce27d065d99b04243de59334 Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Mon, 5 Sep 2016 23:00:08 -0400 Subject: IMAGE: Added Indeo4Decoder decodePictureHeader, and lots of dependencies --- image/codecs/indeo/get_bits.cpp | 24 ++- image/codecs/indeo/get_bits.h | 19 +- image/codecs/indeo/indeo.cpp | 394 ++++++++++++++++++++++++++++++++++++++++ image/codecs/indeo/indeo.h | 350 +++++++++++++++++++++++++++++++++++ image/codecs/indeo/mem.cpp | 157 ++++++++++++++++ image/codecs/indeo/mem.h | 145 +++++++++++++++ image/codecs/indeo/vlc.cpp | 336 ++++++++++++++++++++++++++++++++++ image/codecs/indeo/vlc.h | 138 ++++++++++++++ image/codecs/indeo4.cpp | 198 +++++++++++++++++++- image/codecs/indeo4.h | 32 ++++ image/module.mk | 5 +- 11 files changed, 1788 insertions(+), 10 deletions(-) create mode 100644 image/codecs/indeo/indeo.cpp create mode 100644 image/codecs/indeo/indeo.h create mode 100644 image/codecs/indeo/mem.cpp create mode 100644 image/codecs/indeo/mem.h create mode 100644 image/codecs/indeo/vlc.cpp create mode 100644 image/codecs/indeo/vlc.h diff --git a/image/codecs/indeo/get_bits.cpp b/image/codecs/indeo/get_bits.cpp index 4878bc674f..98cef7facd 100644 --- a/image/codecs/indeo/get_bits.cpp +++ b/image/codecs/indeo/get_bits.cpp @@ -21,6 +21,7 @@ */ #include "image/codecs/indeo/get_bits.h" +#include "common/algorithm.h" #include "common/endian.h" #include "common/textconsole.h" @@ -160,14 +161,35 @@ GetBits::GetBits(const byte *buffer, size_t totalBits) { assert(buffer && totalBits < (INT_MAX - 7)); _buffer = buffer; + _disposeAfterUse = DisposeAfterUse::NO; _sizeInBits = totalBits; _sizeInBitsPlus8 = totalBits + 8; _index = 0; } +GetBits::GetBits(Common::SeekableReadStream &stream) { + byte *buffer = new byte[stream.size()]; + stream.read(buffer, stream.size()); + _buffer = buffer; + _disposeAfterUse = DisposeAfterUse::YES; + _sizeInBits = stream.size() * 8; + _sizeInBitsPlus8 = _sizeInBits + 8; + _index = 0; +} GetBits::GetBits(const GetBits &src) : _index(src._index), _buffer(src._buffer), - _sizeInBits(src._sizeInBits), _sizeInBitsPlus8(src._sizeInBitsPlus8) { + _sizeInBits(src._sizeInBits), _sizeInBitsPlus8(src._sizeInBitsPlus8), + _disposeAfterUse(src._disposeAfterUse) { + if (_disposeAfterUse == DisposeAfterUse::YES) { + byte *buffer = new byte[src._sizeInBits / 8]; + Common::copy(src._buffer, src._buffer + (src._sizeInBits / 8), buffer); + _buffer = buffer; + } +} + +GetBits::~GetBits() { + if (_disposeAfterUse == DisposeAfterUse::YES) + delete[] _buffer; } int GetBits::getXbits(int n) { diff --git a/image/codecs/indeo/get_bits.h b/image/codecs/indeo/get_bits.h index dd6ba55dd2..313fe7e2da 100644 --- a/image/codecs/indeo/get_bits.h +++ b/image/codecs/indeo/get_bits.h @@ -32,6 +32,8 @@ #define IMAGE_CODECS_INDEO_GET_BITS_H #include "common/scummsys.h" +#include "common/stream.h" +#include "common/types.h" namespace Image { namespace Indeo { @@ -44,13 +46,14 @@ namespace Indeo { class GetBits { private: const byte *_buffer; + DisposeAfterUse::Flag _disposeAfterUse; uint _index; uint _sizeInBits; uint _sizeInBitsPlus8; public: /** * Constructor - * @param buffer bitstream buffer, must be AV_INPUT_BUFFER_PADDING_SIZE bytes + * @param buffer Bitstream buffer, must be AV_INPUT_BUFFER_PADDING_SIZE bytes * larger than the actual read bits because some optimized bitstream * readers read 32 or 64 bit at once and could read over the end * @param bit_size the size of the buffer in bits @@ -58,8 +61,22 @@ public: */ GetBits(const byte *buffer, size_t totalBits); + /** + * Constructor + * @param stream Stream to get data from + */ + GetBits(Common::SeekableReadStream &stream); + + /** + * Copy constructor + */ GetBits(const GetBits &src); + /** + * Destructor + */ + ~GetBits(); + /** * Returns the number of bits read */ diff --git a/image/codecs/indeo/indeo.cpp b/image/codecs/indeo/indeo.cpp new file mode 100644 index 0000000000..9de32fa301 --- /dev/null +++ b/image/codecs/indeo/indeo.cpp @@ -0,0 +1,394 @@ +/* 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 and macros 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/mem.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, tile_size) (((stride) + (tile_size) - 1) / (tile_size)) + + +/** + * calculate number of macroblocks in a tile + */ +#define IVI_MBs_PER_TILE(tile_width, tile_height, mb_size) \ + ((((tile_width) + (mb_size) - 1) / (mb_size)) * (((tile_height) + (mb_size) - 1) / (mb_size))) + +/*------------------------------------------------------------------------*/ + +int IVIHuffDesc::ivi_create_huff_from_desc(VLC *vlc, int flag) const { + int pos, i, j, codes_per_row, prefix, not_last_row; + uint16 codewords[256]; + uint8 bits[256]; + + pos = 0; // current position = 0 + + for (i = 0; i < num_rows; i++) { + codes_per_row = 1 << xbits[i]; + not_last_row = (i != num_rows - 1); + prefix = ((1 << i) - 1) << (xbits[i] + not_last_row); + + for (j = 0; j < codes_per_row; 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] + not_last_row; + if (bits[pos] > IVI_VLC_BITS) + return -1; // invalid descriptor + + codewords[pos] = inv_bits((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::ivi_huff_desc_cmp(const IVIHuffDesc *desc2) const { + return num_rows != desc2->num_rows || + memcmp(xbits, desc2->xbits, num_rows); +} + +void IVIHuffDesc::ivi_huff_desc_copy(const IVIHuffDesc *src) { + num_rows = src->num_rows; + memcpy(xbits, src->xbits, src->num_rows); +} + +/*------------------------------------------------------------------------*/ + +IVIHuffTab::IVIHuffTab() { + tab = nullptr; + + for (int i = 0; i < 8; i++) { + ivi_mb_vlc_tabs[i].table = table_data + i * 2 * 8192; + ivi_mb_vlc_tabs[i].table_allocated = 8192; + ivi_mb_huff_desc[i].ivi_create_huff_from_desc(&ivi_mb_vlc_tabs[i], 1); + ivi_blk_vlc_tabs[i].table = table_data + (i * 2 + 1) * 8192; + ivi_blk_vlc_tabs[i].table_allocated = 8192; + ivi_blk_huff_desc[i].ivi_create_huff_from_desc(&ivi_blk_vlc_tabs[i], 1); + } +} + +int IVIHuffTab::ff_ivi_dec_huff_desc(GetBits *gb, int desc_coded, int which_tab) { + int i, result; + IVIHuffDesc new_huff; + + if (!desc_coded) { + // select default table + tab = (which_tab) ? &ivi_blk_vlc_tabs[7] + : &ivi_mb_vlc_tabs[7]; + return 0; + } + + tab_sel = gb->getBits(3); + if (tab_sel == 7) { + // custom huffman table (explicitly encoded) + new_huff.num_rows = gb->getBits(4); + if (!new_huff.num_rows) { + warning("Empty custom Huffman table!"); + return -1; + } + + for (i = 0; i < new_huff.num_rows; i++) + new_huff.xbits[i] = gb->getBits(4); + + // Have we got the same custom table? Rebuild if not. + if (new_huff.ivi_huff_desc_cmp(&cust_desc) || !cust_tab.table) { + cust_desc.ivi_huff_desc_copy(&new_huff); + + if (cust_tab.table) + cust_tab.ff_free_vlc(); + result = cust_desc.ivi_create_huff_from_desc(&cust_tab, 0); + if (result) { + // reset faulty description + cust_desc.num_rows = 0; + warning("Error while initializing custom vlc table!"); + return result; + } + } + tab = &cust_tab; + } else { + // select one of predefined tables + tab = (which_tab) ? &ivi_blk_vlc_tabs[tab_sel] + : &ivi_mb_vlc_tabs[tab_sel]; + } + + return 0; +} + +/*------------------------------------------------------------------------*/ + +bool IVIPicConfig::ivi_pic_config_cmp(const IVIPicConfig &cfg2) { + return pic_width != cfg2.pic_width || pic_height != cfg2.pic_height || + chroma_width != cfg2.chroma_width || chroma_height != cfg2.chroma_height || + tile_width != cfg2.tile_width || tile_height != cfg2.tile_height || + luma_bands != cfg2.luma_bands || chroma_bands != cfg2.chroma_bands; +} + +/*------------------------------------------------------------------------*/ + +int IVIPlaneDesc::ff_ivi_init_planes(IVIPlaneDesc *planes, const IVIPicConfig *cfg, bool is_indeo4) { + int p, b; + uint32 b_width, b_height, align_fac, width_aligned, + height_aligned, buf_size; + IVIBandDesc *band; + + ivi_free_buffers(planes); + + if (av_image_check_size(cfg->pic_width, cfg->pic_height, 0, NULL) < 0 || + cfg->luma_bands < 1 || cfg->chroma_bands < 1) + return -1; + + // fill in the descriptor of the luminance plane + planes[0].width = cfg->pic_width; + planes[0].height = cfg->pic_height; + planes[0].num_bands = cfg->luma_bands; + + // fill in the descriptors of the chrominance planes + planes[1].width = planes[2].width = (cfg->pic_width + 3) >> 2; + planes[1].height = planes[2].height = (cfg->pic_height + 3) >> 2; + planes[1].num_bands = planes[2].num_bands = cfg->chroma_bands; + + for (p = 0; p < 3; p++) { + planes[p].bands = (IVIBandDesc *)av_mallocz_array(planes[p].num_bands, 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].num_bands == 1 ? planes[p].width + : (planes[p].width + 1) >> 1; + b_height = planes[p].num_bands == 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); + buf_size = width_aligned * height_aligned * sizeof(int16); + + for (b = 0; b < planes[p].num_bands; b++) { + band = &planes[p].bands[b]; // select appropriate plane/band + band->plane = p; + band->band_num = b; + band->width = b_width; + band->height = b_height; + band->pitch = width_aligned; + band->aheight = height_aligned; + band->bufs[0] = (int16 *)av_mallocz(buf_size); + band->bufs[1] = (int16 *)av_mallocz(buf_size); + band->bufsize = buf_size / 2; + if (!band->bufs[0] || !band->bufs[1]) + return -2; + + // allocate the 3rd band buffer for scalability mode + if (cfg->luma_bands > 1) { + band->bufs[2] = (int16 *)av_mallocz(buf_size); + if (!band->bufs[2]) + return -2; + } + if (is_indeo4) { + band->bufs[3] = (int16 *)av_mallocz(buf_size); + if (!band->bufs[3]) + return -2; + } + // reset custom vlc + planes[p].bands[0].blk_vlc.cust_desc.num_rows = 0; + } + } + + return 0; +} + +int IVIPlaneDesc::ff_ivi_init_tiles(IVIPlaneDesc *planes, + int tile_width, int tile_height) { + int p, b, x_tiles, y_tiles, t_width, t_height, ret; + IVIBandDesc *band; + + for (p = 0; p < 3; p++) { + t_width = !p ? tile_width : (tile_width + 3) >> 2; + t_height = !p ? tile_height : (tile_height + 3) >> 2; + + if (!p && planes[0].num_bands == 4) { + t_width >>= 1; + t_height >>= 1; + } + if (t_width <= 0 || t_height <= 0) + return -3; + + for (b = 0; b < planes[p].num_bands; b++) { + band = &planes[p].bands[b]; + x_tiles = IVI_NUM_TILES(band->width, t_width); + y_tiles = IVI_NUM_TILES(band->height, t_height); + band->num_tiles = x_tiles * y_tiles; + + av_freep(&band->tiles); + band->tiles = (IVITile *)av_mallocz_array(band->num_tiles, sizeof(IVITile)); + if (!band->tiles) + return -2; + + // use the first luma band as reference for motion vectors + // and quant + ret = band->ivi_init_tiles(planes[0].bands[0].tiles, + p, b, t_height, t_width); + if (ret < 0) + return ret; + } + } + + return 0; +} + +void IVIPlaneDesc::ivi_free_buffers(IVIPlaneDesc *planes) { + int p, b, t; + + for (p = 0; p < 3; p++) { + if (planes[p].bands) + for (b = 0; b < planes[p].num_bands; b++) { + av_freep(&planes[p].bands[b].bufs[0]); + av_freep(&planes[p].bands[b].bufs[1]); + av_freep(&planes[p].bands[b].bufs[2]); + av_freep(&planes[p].bands[b].bufs[3]); + + if (planes[p].bands[b].blk_vlc.cust_tab.table) + planes[p].bands[b].blk_vlc.cust_tab.ff_free_vlc(); + for (t = 0; t < planes[p].bands[b].num_tiles; t++) + av_freep(&planes[p].bands[b].tiles[t].mbs); + av_freep(&planes[p].bands[b].tiles); + } + av_freep(&planes[p].bands); + planes[p].num_bands = 0; + } +} + +/*------------------------------------------------------------------------*/ + +int IVIBandDesc::ivi_init_tiles(IVITile *ref_tile, int p, int b, int t_height, int t_width) { + int x, y; + IVITile *tile = tiles; + + for (y = 0; y < height; y += t_height) { + for (x = 0; x < width; x += t_width) { + tile->xpos = x; + tile->ypos = y; + tile->mb_size = mb_size; + tile->width = MIN(width - x, t_width); + tile->height = MIN(height - y, t_height); + tile->is_empty = tile->data_size = 0; + // calculate number of macroblocks + tile->num_MBs = IVI_MBs_PER_TILE(tile->width, tile->height, + mb_size); + + av_freep(&tile->mbs); + tile->mbs = (IVIMbInfo *)av_mallocz_array(tile->num_MBs, sizeof(IVIMbInfo)); + if (!tile->mbs) + return -2; + + tile->ref_mbs = 0; + if (p || b) { + if (tile->num_MBs != ref_tile->num_MBs) { + warning("ref_tile mismatch"); + return -1; + } + tile->ref_mbs = ref_tile->mbs; + ref_tile++; + } + tile++; + } + } + + return 0; +} + +/*------------------------------------------------------------------------*/ + +int av_image_check_size(unsigned int w, unsigned int h, int log_offset, void *log_ctx) { + if (((w + 128) * (uint64)(h + 128)) < (INT_MAX / 8)) + return 0; + + error("Picture size %ux%u is invalid", w, h); +} + +} // 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..d689994b03 --- /dev/null +++ b/image/codecs/indeo/indeo.h @@ -0,0 +1,350 @@ +/* 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 structures and macros 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 +}; + +#define IVI_VLC_BITS 13 ///< max number of bits of the ivi's huffman codes +#define IVI5_IS_PROTECTED 0x20 + +/** + * huffman codebook descriptor + */ +struct IVIHuffDesc { + int32 num_rows; + 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: 1 - for static or 0 for dynamic tables + * @return result code: 0 - OK, -1 = error (invalid codebook descriptor) + */ + int ivi_create_huff_from_desc(VLC *vlc, int flag) const; + + /* + * Compare two huffman codebook descriptors. + * + * @param[in] desc2 ptr to the 2nd descriptor to compare + * @return comparison result: 0 - equal, 1 - not equal + */ + bool ivi_huff_desc_cmp(const IVIHuffDesc *desc2) const; + + /* + * Copy huffman codebook descriptors. + * + * @param[in] src ptr to the source descriptor + */ + void ivi_huff_desc_copy(const IVIHuffDesc *src); +}; + +/** + * macroblock/block huffman table descriptor + */ +struct IVIHuffTab { +private: + VLC_TYPE table_data[8192 * 16][2]; + VLC ivi_mb_vlc_tabs[8]; ///< static macroblock Huffman tables + VLC ivi_blk_vlc_tabs[8]; ///< static block Huffman tables +public: + int32 tab_sel; /// 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 cust_desc; /// custom Huffman codebook descriptor + VLC cust_tab; /// vlc table for custom codebook + + /** + * Constructor + */ + IVIHuffTab(); + + int ff_ivi_dec_huff_desc(GetBits *gb, int desc_coded, int which_tab); +}; + +enum { + IVI_MB_HUFF = 0, /// Huffman table is used for coding macroblocks + IVI_BLK_HUFF = 1 /// Huffman table is used for coding blocks +}; + + +/** + * Common scan patterns (defined in ivi_common.c) + */ +//extern const uint8 ff_ivi_vertical_scan_8x8[64]; +//extern const uint8 ff_ivi_horizontal_scan_8x8[64]; +//extern const uint8 ff_ivi_direct_scan_4x4[16]; + + +/** + * 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 blk_size); + + +/** + * run-value (RLE) table descriptor + */ +struct RVMapDesc { + uint8 eob_sym; ///< end of block symbol + uint8 esc_sym; ///< escape symbol + uint8 runtab[256]; + int8 valtab[256]; +}; + +extern const RVMapDesc ff_ivi_rvmap_tabs[9]; + + +/** + * information for Indeo macroblock (16x16, 8x8 or 4x4) + */ +struct IVIMbInfo { + int16 xpos; + int16 ypos; + uint32 buf_offs; ///< address in the output buffer for this mb + uint8 type; ///< macroblock type: 0 - INTRA, 1 - INTER + uint8 cbp; ///< coded block pattern + int8 q_delta; ///< quant delta + int8 mv_x; ///< motion vector (x component) + int8 mv_y; ///< motion vector (y component) + int8 b_mv_x; ///< second motion vector (x component) + int8 b_mv_y; ///< second motion vector (y component) +}; + + +/** + * information for Indeo tile + */ +struct IVITile { + int xpos; + int ypos; + int width; + int height; + int mb_size; + int is_empty; ///< = 1 if this tile doesn't contain any data + int data_size; ///< size of the data in bytes + int num_MBs; ///< number of macroblocks in this tile + IVIMbInfo * mbs; ///< array of macroblock descriptors + IVIMbInfo * ref_mbs; ///< ptr to the macroblock descriptors of the reference tile +}; + + +/** + * information for Indeo wavelet band + */ +struct IVIBandDesc { + int plane; ///< plane number this band belongs to + int band_num; ///< band number + int width; + int height; + int aheight; ///< aligned band height + const uint8 * data_ptr; ///< ptr to the first byte of the band data + int data_size; ///< size of the band data + int16 * buf; ///< pointer to the output buffer for this band + int16 * ref_buf; ///< pointer to the reference frame buffer (for motion compensation) + int16 * b_ref_buf; ///< 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 + int is_empty; ///< = 1 if this band doesn't contain any data + int mb_size; ///< macroblock size + int blk_size; ///< block size + int is_halfpel; ///< precision of the motion compensation: 0 - fullpel, 1 - halfpel + int inherit_mv; ///< tells if motion vector is inherited from reference macroblock + int inherit_qdelta; ///< tells if quantiser delta is inherited from reference macroblock + int qdelta_present; ///< tells if Qdelta signal is present in the bitstream (Indeo5 only) + int quant_mat; ///< dequant matrix index + int glob_quant; ///< quant base for this band + const uint8 * scan; ///< ptr to the scan pattern + int scan_size; ///< size of the scantable + + IVIHuffTab blk_vlc; ///< vlc table for decoding block data + + int num_corr; ///< number of correction entries + uint8 corr[61 * 2]; ///< rvmap correction pairs + int rvmap_sel; ///< rvmap table selector + RVMapDesc * rv_map; ///< ptr to the RLE table for this band + int num_tiles; ///< number of tiles in this band + IVITile * tiles; ///< array of tile descriptors + InvTransformPtr *inv_transform; + int transform_size; + DCTransformPtr *dc_transform; + int is_2d_trans; ///< 1 indicates that the two-dimensional inverse transform is used + int32 checksum; ///< for debug purposes + int checksum_present; + int bufsize; ///< band buffer size in bytes + const uint16 * intra_base; ///< quantization matrix for intra blocks + const uint16 * inter_base; ///< quantization matrix for inter blocks + const uint8 * intra_scale; ///< quantization coefficient for intra blocks + const uint8 * inter_scale; ///< quantization coefficient for inter blocks + + int ivi_init_tiles(IVITile *ref_tile, int p, int b, int t_height, int t_width); +}; + +struct IVIPicConfig { + uint16 pic_width; + uint16 pic_height; + uint16 chroma_width; + uint16 chroma_height; + uint16 tile_width; + uint16 tile_height; + uint8 luma_bands; + uint8 chroma_bands; + + /** + * 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 num_bands; ///< number of bands this plane subdivided into + IVIBandDesc *bands; ///< array of band descriptors + + static int ff_ivi_init_planes(IVIPlaneDesc *planes, const IVIPicConfig *cfg, bool is_indeo4); + + static int ff_ivi_init_tiles(IVIPlaneDesc *planes, int tile_width, int tile_height); + + /* + * Free planes, bands and macroblocks buffers. + * + * @param[in] planes pointer to the array of the plane descriptors + */ + static void ivi_free_buffers(IVIPlaneDesc *planes); +}; + +struct IVI45DecContext { + GetBits * gb; + RVMapDesc rvmap_tabs[9]; ///< local corrected copy of the static rvmap tables + + uint32 frame_num; + int frame_type; + int prev_frame_type; ///< frame type of the previous frame + uint32 data_size; ///< size of the frame data in bytes from picture header + int is_scalable; + const uint8 * frame_data; ///< input frame data pointer + int inter_scal; ///< signals a sequence of scalable inter frames + uint32 frame_size; ///< frame size in bytes + uint32 pic_hdr_size; ///< picture header size in bytes + uint8 frame_flags; + uint16 checksum; ///< frame checksum + + IVIPicConfig pic_conf; + IVIPlaneDesc planes[3]; ///< color planes + + int buf_switch; ///< used to switch between three buffers + int dst_buf; ///< buffer index for the currently decoded frame + int ref_buf; ///< inter frame reference buffer index + int ref2_buf; ///< temporal storage for switching buffers + int b_ref_buf; ///< second reference frame buffer index + + IVIHuffTab mb_vlc; ///< current macroblock table descriptor + IVIHuffTab blk_vlc; ///< current block table descriptor + + uint8 rvmap_sel; + uint8 in_imf; + uint8 in_q; ///< flag for explicitly stored quantiser delta + uint8 pic_glob_quant; + uint8 unknown1; + + uint16 gop_hdr_size; + uint8 gop_flags; + uint32 lock_word; + + int show_indeo4_info; + uint8 has_b_frames; + uint8 has_transp; ///< transparency mode status: 1 - enabled + uint8 uses_tiling; + uint8 uses_haar; + uint8 uses_fullpel; + +// int (*decode_pic_hdr) (struct IVI45DecContext *ctx, AVCodecContext *avctx); +// int (*decode_band_hdr) (struct IVI45DecContext *ctx, IVIBandDesc *band, AVCodecContext *avctx); +// int (*decode_mb_info) (struct IVI45DecContext *ctx, IVIBandDesc *band, IVITile *tile, AVCodecContext *avctx); +// void (*switch_buffers) (struct IVI45DecContext *ctx); +// int (*is_nonnull_frame)(struct IVI45DecContext *ctx); + + int gop_invalid; + int buf_invalid[4]; + + int is_indeo4; + +// AVFrame * p_frame; + int got_p_frame; +}; + +/*------------------------------------------------------------------------*/ + +/** + * 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 +*/ +extern int av_image_check_size(unsigned int w, unsigned int h, int log_offset, void *log_ctx); + + +} // 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..03a39cabb5 --- /dev/null +++ b/image/codecs/indeo/mem.cpp @@ -0,0 +1,157 @@ +/* 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 ff_reverse[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, +}; + +/*------------------------------------------------------------------------*/ + +/** + * 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 av_size_mult(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 *av_malloc(size_t size) { + return malloc(size); +} + +void *av_mallocz(size_t size) { + void *ptr = av_malloc(size); + if (ptr) + memset(ptr, 0, size); + + return ptr; +} + +void *av_malloc_array(size_t nmemb, size_t size) { + if (!size || nmemb >= INT_MAX / size) + return nullptr; + return malloc(nmemb * size); +} + +void *av_mallocz_array(size_t nmemb, size_t size) { + if (!size || nmemb >= INT_MAX / size) + return NULL; + + return av_mallocz(nmemb * size); +} + +void av_free(void *ptr) { + free(ptr); +} + +void av_freep(void *arg) { + void **ptr = (void **)arg; + free(*ptr); + *ptr = nullptr; +} + +static void *av_realloc(void *ptr, size_t size) { + return realloc(ptr, size + !size); +} + +void *av_realloc_f(void *ptr, size_t nelem, size_t elsize) { + size_t size; + void *r; + + if (av_size_mult(elsize, nelem, &size)) { + av_free(ptr); + return nullptr; + } + r = av_realloc(ptr, size); + if (!r) + av_free(ptr); + + return r; +} + + +/** + * Swap the order of the bytes in the passed value + */ +uint32 bitswap_32(uint32 x) { + return (uint32)ff_reverse[x & 0xFF] << 24 | + (uint32)ff_reverse[(x >> 8) & 0xFF] << 16 | + (uint32)ff_reverse[(x >> 16) & 0xFF] << 8 | + (uint32)ff_reverse[x >> 24]; +} + +/** + * Reverse "nbits" bits of the value "val" and return the result + * in the least significant bits. + */ +uint16 inv_bits(uint16 val, int nbits) { + uint16 res; + + if (nbits <= 8) { + res = ff_reverse[val] >> (8 - nbits); + } else + res = ((ff_reverse[val & 0xFF] << 8) + + (ff_reverse[val >> 8])) >> (16 - nbits); + + return res; +} + +} // 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..4f9ebf016d --- /dev/null +++ b/image/codecs/indeo/mem.h @@ -0,0 +1,145 @@ +/* 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 FFSWAP(type,a,b) do{type SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0) + +/** + * Allocate a memory block with alignment suitable for all memory accesses + * (including vectors if available on the CPU). + * + * @param size Size in bytes for the memory block to be allocated + * @return Pointer to the allocated block, or `NULL` if the block cannot + * be allocated + * @see av_mallocz() + */ +extern void *av_malloc(size_t size); + +/** + * 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 *av_mallocz(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 *av_malloc_array(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 *av_mallocz_array(size_t nmemb, size_t size); + +/** + * Free a memory block which has been allocated with a function of av_malloc() + * or av_realloc() family. + * + * @param ptr Pointer to the memory block which should be freed. + * + * @note `ptr = NULL` is explicitly allowed. + * @note It is recommended that you use av_freep() instead, to prevent leaving + * behind dangling pointers. + * @see av_freep() + */ +extern void av_free(void *ptr); + +/** + * 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 av_freep(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 *av_realloc_f(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 inv_bits(uint16 val, int nbits); + +/** + * Swap the order of the bytes in the passed value + */ +extern uint32 bitswap_32(uint32 x); + +} // 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..441596585e --- /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) FFSWAP(type, *start, *mid);\ + else FFSWAP(type, *start, *end);\ + }else{\ + if(cmp(start, mid) > 0) FFSWAP(type, *start, *mid);\ + else checksort= 1;\ + }\ + if(cmp(mid, end) > 0){ \ + FFSWAP(type, *mid, *end);\ + checksort=0;\ + }\ + if(start == end-2) break;\ + FFSWAP(type, 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){\ + FFSWAP(type, *left, *right);\ + left++;\ + right--;\ + }\ + }\ + FFSWAP(type, end[-1], *left);\ + if(checksort && (mid == left-1 || mid == left)){\ + mid= start;\ + while(mid 0)\ + FFSWAP(type, *start, *end);\ + break;\ + }\ + }\ + }\ +} while (0) + +/** + * VLC decoding + */ +#define GET_DATA(v, table, i, wrap, size) \ +{ \ + const uint8 *ptr = (const uint8 *)table + i * wrap; \ + switch(size) { \ + case 1: \ + v = *(const uint8 *)ptr; \ + break; \ + case 2: \ + v = *(const uint16 *)ptr; \ + break; \ + default: \ + v = *(const uint32 *)ptr; \ + break; \ + } \ +} + +/*------------------------------------------------------------------------*/ + +VLC::VLC() : bits(0), table_size(0), table_allocated(0), table(nullptr) { +} + +int VLC::init_vlc(int nb_bits, int nb_codes, const void *bits, int bits_wrap, int bits_size, + const void *codes, int codes_wrap, int codes_size, int flags) { + return init_vlc(nb_bits, nb_codes, bits, bits_wrap, bits_size, codes, codes_wrap, + codes_size, nullptr, 0, 0, flags); +} + +int VLC::init_vlc(int nb_bits, int nb_codes, const void *p_bits, int bits_wrap, + int bits_size, const void *codes, int codes_wrap, int codes_size, + const void *symbols, int symbols_wrap, int symbols_size, 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 = nb_bits; + if (flags & INIT_VLC_USE_NEW_STATIC) { + assert((nb_codes + 1) <= FF_ARRAY_ELEMS(localbuf)); + buf = localbuf; + localvlc = *this; + vlc = &localvlc; + vlc->table_size = 0; + } else { + vlc->table = NULL; + vlc->table_allocated = 0; + vlc->table_size = 0; + + buf = (VLCcode *)av_malloc_array((nb_codes + 1), sizeof(VLCcode)); + assert(buf); + } + + assert(symbols_size <= 2 || !symbols); + j = 0; +#define COPY(condition)\ + for (i = 0; i < nb_codes; i++) { \ + GET_DATA(buf[j].bits, p_bits, i, bits_wrap, bits_size); \ + if (!(condition)) \ + continue; \ + if (buf[j].bits > 3*nb_bits || 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; \ + } \ + GET_DATA(buf[j].code, codes, i, codes_wrap, codes_size); \ + if (buf[j].code >= (1LL< nb_bits); + + // qsort is the slowest part of init_vlc, and could probably be improved or avoided + AV_QSORT(buf, j, VLCcode, compare_vlcspec); + COPY(buf[j].bits && buf[j].bits <= nb_bits); + nb_codes = j; + + ret = vlc->build_table(nb_bits, nb_codes, buf, flags); + + if (flags & INIT_VLC_USE_NEW_STATIC) { + if (vlc->table_size != vlc->table_allocated) + warning("needed %d had %d", table_size, table_allocated); + + assert(ret >= 0); + *this = *vlc; + } else { + free(buf); + if (ret < 0) { + av_freep(&vlc->table); + return -1; + } + } + + return 0; +} + +void VLC::ff_free_vlc() { + free(table); +} + +int VLC::compare_vlcspec(const void *a, const void *b) { + const VLCcode *sa = (VLCcode *)a, *sb = (VLCcode *)b; + return (sa->code >> 1) - (sb->code >> 1); +} + +int VLC::build_table(int table_nb_bits, int nb_codes, + VLCcode *codes, int flags) { + VLC *vlc = this; + int table_size, table_index, index, code_prefix, symbol, subtable_bits; + int i, j, k, n, nb, inc; + uint32 code; + // the double volatile is needed to prevent an internal compiler error in gcc 4.2 + volatile VLC_TYPE(*volatile table)[2]; + + + table_size = 1 << table_nb_bits; + if (table_nb_bits > 30) + return -1; + table_index = alloc_table(table_size, flags & INIT_VLC_USE_NEW_STATIC); + warning("new table index=%d size=%d", table_index, table_size); + if (table_index < 0) + return table_index; + table = (volatile VLC_TYPE(*)[2])&vlc->table[table_index]; + + /* first pass: map codes and compute auxiliary table sizes */ + for (i = 0; i < nb_codes; 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 <= table_nb_bits) { + /* no need to add another table */ + j = code >> (32 - table_nb_bits); + nb = 1 << (table_nb_bits - n); + inc = 1; + if (flags & INIT_VLC_LE) { + j = bitswap_32(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 -= table_nb_bits; + code_prefix = code >> (32 - table_nb_bits); + subtable_bits = n; + codes[i].bits = n; + codes[i].code = code << table_nb_bits; + for (k = i + 1; k < nb_codes; k++) { + n = codes[k].bits - table_nb_bits; + if (n <= 0) + break; + code = codes[k].code; + if (code >> (32 - table_nb_bits) != code_prefix) + break; + codes[k].bits = n; + codes[k].code = code << table_nb_bits; + subtable_bits = MAX(subtable_bits, n); + } + subtable_bits = MIN(subtable_bits, table_nb_bits); + j = (flags & INIT_VLC_LE) ? bitswap_32(code_prefix) >> (32 - table_nb_bits) : code_prefix; + table[j][1] = -subtable_bits; + warning("%4x: n=%d (subtable)", j, codes[i].bits + table_nb_bits); + index = vlc->build_table(subtable_bits, k - i, codes + i, flags); + if (index < 0) + return index; + /* note: realloc has been done, so reload tables */ + table = (volatile VLC_TYPE(*)[2])&vlc->table[table_index]; + table[j][0] = index; //code + i = k - 1; + } + } + + for (i = 0; i < table_size; i++) { + if (table[i][1] == 0) //bits + table[i][0] = -1; //codes + } + + return table_index; +} + +int VLC::alloc_table(int size, int use_static) { + VLC *vlc = this; + int index = vlc->table_size; + + vlc->table_size += size; + if (vlc->table_size > vlc->table_allocated) { + // cannot do anything, init_vlc() is used with too little memory + assert(!use_static); + + vlc->table_allocated += (1 << vlc->bits); + vlc->table = (int16(*)[2])av_realloc_f(vlc->table, vlc->table_allocated, sizeof(VLC_TYPE) * 2); + if (!vlc->table) { + vlc->table_allocated = 0; + vlc->table_size = 0; + return -2; + } + + memset(vlc->table + vlc->table_allocated - (1 << vlc->bits), 0, sizeof(VLC_TYPE) * 2 << vlc->bits); + } + return index; +} + +} // 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..682be66e4a --- /dev/null +++ b/image/codecs/indeo/vlc.h @@ -0,0 +1,138 @@ +/* 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 +#define INIT_VLC_LE 2 +#define 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 compare_vlcspec(const void *a, const void *b); +public: + int bits; + VLC_TYPE (*table)[2]; ///< code, bits + int table_size, table_allocated; + + VLC(); + ~VLC() { ff_free_vlc(); } + + + /* Build VLC decoding tables suitable for use with get_vlc(). + + 'nb_bits' sets the decoding table size (2^nb_bits) 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. + + 'nb_codes' : 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. + + 'use_static' should be set to 1 for tables, which should be freed + with av_free_static(), 0 if ff_free_vlc() will be used. + */ + int init_vlc(int nb_bits, int nb_codes, const void *bits, int bits_wrap, + int bits_size, const void *codes, int codes_wrap, int codes_size, + const void *symbols, int symbols_wrap, int symbols_size, int flags); + + int init_vlc(int nb_bits, int nb_codes, const void *bits, int bits_wrap, int bits_size, + const void *codes, int codes_wrap, int codes_size, int flags); + + /** + * Free VLC data + */ + void ff_free_vlc(); + + + /** + * Build VLC decoding tables suitable for use with get_vlc(). + * + * @param table_nb_bits max length of vlc codes to store directly in this table + * (Longer codes are delegated to subtables.) + * + * @param nb_codes 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 build_table(int table_nb_bits, int nb_codes, + VLCcode *codes, int flags); + + int alloc_table(int size, int use_static); +}; + + +/** + * Reverse "nbits" bits of the value "val" and return the result + * in the least significant bits. + */ +extern uint16 inv_bits(uint16 val, int nbits); + +/** + * Swap the order of the bytes in the passed value + */ +extern uint32 bitswap_32(uint32 x); + +} // End of namespace Indeo +} // End of namespace Image + +#endif diff --git a/image/codecs/indeo4.cpp b/image/codecs/indeo4.cpp index 465e3adc49..bce9b5ad78 100644 --- a/image/codecs/indeo4.cpp +++ b/image/codecs/indeo4.cpp @@ -24,7 +24,7 @@ /* Intel Indeo 4 decompressor, derived from ffmpeg. * - * Original copyright note: * Intel Indeo 3 (IV31, IV32, etc.) video decoder for ffmpeg + * Original copyright note: * Intel Indeo 3 (IV41, IV42, etc.) video decoder for ffmpeg * written, produced, and directed by Alan Smithee */ @@ -33,19 +33,22 @@ #include "common/stream.h" #include "common/textconsole.h" #include "common/util.h" - #include "graphics/yuv_to_rgb.h" - #include "image/codecs/indeo4.h" -#include "image/codecs/indeo/get_bits.h" namespace Image { +#define IVI4_PIC_SIZE_ESC 7 + Indeo4Decoder::Indeo4Decoder(uint16 width, uint16 height) { _pixelFormat = g_system->getScreenFormat(); _surface = new Graphics::ManagedSurface(); _surface->create(width, height, _pixelFormat); - + _ctx.gb = nullptr; + _ctx.pic_conf.pic_width = _ctx.pic_conf.pic_height = 0; + _ctx.is_indeo4 = true; + _ctx.show_indeo4_info = false; + _ctx.b_ref_buf = 3; // buffer 2 is used for scalability mode } Indeo4Decoder::~Indeo4Decoder() { @@ -72,10 +75,191 @@ bool Indeo4Decoder::isIndeo4(Common::SeekableReadStream &stream) { const Graphics::Surface *Indeo4Decoder::decodeFrame(Common::SeekableReadStream &stream) { // Not Indeo 4? Fail if (!isIndeo4(stream)) - return 0; + return nullptr; + + // Set up the GetBits instance for reading the stream + _ctx.gb = new GetBits(stream); + + // Decode the header + int err = decodePictureHeader(); + + delete _ctx.gb; + _ctx.gb = nullptr; // TODO - return nullptr; + err = -1; + return (err < 0) ? nullptr : &_surface->rawSurface(); } +int Indeo4Decoder::decodePictureHeader() { + int pic_size_indx, i, p; + IVIPicConfig pic_conf; + + if (_ctx.gb->getBits(18) != 0x3FFF8) { + warning("Invalid picture start code!"); + return -1; + } + + _ctx.prev_frame_type = _ctx.frame_type; + _ctx.frame_type = _ctx.gb->getBits(3); + if (_ctx.frame_type == 7) { + warning("Invalid frame type: %d", _ctx.frame_type); + return -1; + } + + if (_ctx.frame_type == IVI4_FRAMETYPE_BIDIR) + _ctx.has_b_frames = 1; + + _ctx.has_transp = _ctx.gb->getBits1(); + + // unknown bit: Mac decoder ignores this bit, XANIM returns error + if (_ctx.gb->getBits1()) { + warning("Sync bit is set!"); + return -1; + } + + _ctx.data_size = _ctx.gb->getBits1() ? _ctx.gb->getBits(24) : 0; + + // null frames don't contain anything else so we just return + if (_ctx.frame_type >= 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->getBits1()) { + _ctx.gb->skipBitsLong(32); + warning("Password-protected clip!"); + } + + pic_size_indx = _ctx.gb->getBits(3); + if (pic_size_indx == IVI4_PIC_SIZE_ESC) { + pic_conf.pic_height = _ctx.gb->getBits(16); + pic_conf.pic_width = _ctx.gb->getBits(16); + } else { + pic_conf.pic_height = _ivi4_common_pic_sizes[pic_size_indx * 2 + 1]; + pic_conf.pic_width = _ivi4_common_pic_sizes[pic_size_indx * 2]; + } + + // Decode tile dimensions. + _ctx.uses_tiling = _ctx.gb->getBits1(); + if (_ctx.uses_tiling) { + pic_conf.tile_height = scaleTileSize(pic_conf.pic_height, _ctx.gb->getBits(4)); + pic_conf.tile_width = scaleTileSize(pic_conf.pic_width, _ctx.gb->getBits(4)); + } else { + pic_conf.tile_height = pic_conf.pic_height; + pic_conf.tile_width = pic_conf.pic_width; + } + + // Decode chroma subsampling. We support only 4:4 aka YVU9. + if (_ctx.gb->getBits(2)) { + warning("Only YVU9 picture format is supported!"); + return -1; + } + pic_conf.chroma_height = (pic_conf.pic_height + 3) >> 2; + pic_conf.chroma_width = (pic_conf.pic_width + 3) >> 2; + + // decode subdivision of the planes + pic_conf.luma_bands = decodePlaneSubdivision(); + pic_conf.chroma_bands = 0; + if (pic_conf.luma_bands) + pic_conf.chroma_bands = decodePlaneSubdivision(); + _ctx.is_scalable = pic_conf.luma_bands != 1 || pic_conf.chroma_bands != 1; + if (_ctx.is_scalable && (pic_conf.luma_bands != 4 || pic_conf.chroma_bands != 1)) { + warning("Scalability: unsupported subdivision! Luma bands: %d, chroma bands: %d", + pic_conf.luma_bands, pic_conf.chroma_bands); + return -1; + } + + // check if picture layout was changed and reallocate buffers + if (pic_conf.ivi_pic_config_cmp(_ctx.pic_conf)) { + if (IVIPlaneDesc::ff_ivi_init_planes(_ctx.planes, &pic_conf, 1)) { + warning("Couldn't reallocate color planes!"); + _ctx.pic_conf.luma_bands = 0; + return -2; + } + + _ctx.pic_conf = pic_conf; + + // set default macroblock/block dimensions + for (p = 0; p <= 2; p++) { + for (i = 0; i < (!p ? pic_conf.luma_bands : pic_conf.chroma_bands); i++) { + _ctx.planes[p].bands[i].mb_size = !p ? (!_ctx.is_scalable ? 16 : 8) : 4; + _ctx.planes[p].bands[i].blk_size = !p ? 8 : 4; + } + } + + if (IVIPlaneDesc::ff_ivi_init_tiles(_ctx.planes, _ctx.pic_conf.tile_width, + _ctx.pic_conf.tile_height)) { + warning("Couldn't reallocate internal structures!"); + return -2; + } + } + + _ctx.frame_num = _ctx.gb->getBits1() ? _ctx.gb->getBits(20) : 0; + + // skip decTimeEst field if present + if (_ctx.gb->getBits1()) + _ctx.gb->skipBits(8); + + // decode macroblock and block huffman codebooks + if (_ctx.mb_vlc.ff_ivi_dec_huff_desc(_ctx.gb, _ctx.gb->getBits1(), IVI_MB_HUFF) || + _ctx.blk_vlc.ff_ivi_dec_huff_desc(_ctx.gb, _ctx.gb->getBits1(), IVI_BLK_HUFF)) + return -1; + + _ctx.rvmap_sel = _ctx.gb->getBits1() ? _ctx.gb->getBits(3) : 8; + + _ctx.in_imf = _ctx.gb->getBits1(); + _ctx.in_q = _ctx.gb->getBits1(); + + _ctx.pic_glob_quant = _ctx.gb->getBits(5); + + // TODO: ignore this parameter if unused + _ctx.unknown1 = _ctx.gb->getBits1() ? _ctx.gb->getBits(3) : 0; + + _ctx.checksum = _ctx.gb->getBits1() ? _ctx.gb->getBits(16) : 0; + + // skip picture header extension if any + while (_ctx.gb->getBits1()) { + warning("Pic hdr extension encountered!"); + _ctx.gb->skipBits(8); + } + + if (_ctx.gb->getBits1()) { + warning("Bad blocks bits encountered!"); + } + + _ctx.gb->alignGetBits(); + + return 0; +} + +int Indeo4Decoder::scaleTileSize(int def_size, int size_factor) { + return size_factor == 15 ? def_size : (size_factor + 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; + } +} + +/*------------------------------------------------------------------------*/ + +const uint Indeo4Decoder::_ivi4_common_pic_sizes[14] = { + 640, 480, 320, 240, 160, 120, 704, 480, 352, 240, 352, 288, 176, 144 +}; + } // End of namespace Image diff --git a/image/codecs/indeo4.h b/image/codecs/indeo4.h index 44007e08b8..838b0c3aef 100644 --- a/image/codecs/indeo4.h +++ b/image/codecs/indeo4.h @@ -21,6 +21,7 @@ */ #include "common/scummsys.h" +#include "image/codecs/indeo/get_bits.h" /* Intel Indeo 4 decompressor, derived from ffmpeg. * @@ -33,10 +34,14 @@ #define IMAGE_CODECS_INDEO4_H #include "image/codecs/codec.h" +#include "image/codecs/indeo/get_bits.h" +#include "image/codecs/indeo/indeo.h" #include "graphics/managed_surface.h" namespace Image { +using namespace Indeo; + /** * Intel Indeo 4 decoder. * @@ -57,6 +62,33 @@ public: private: Graphics::PixelFormat _pixelFormat; Graphics::ManagedSurface *_surface; + IVI45DecContext _ctx; + + /** + * Decode the Indeo 4 picture header. + * @returns 0 = Ok, negative number = error + */ + int decodePictureHeader(); + + int scaleTileSize(int def_size, int size_factor); + + /** + * 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 + * @return number of wavelet bands or 0 on error + */ + int decodePlaneSubdivision(); + +private: + /** + * Standard picture dimensions + */ + static const uint _ivi4_common_pic_sizes[14]; }; } // End of namespace Image diff --git a/image/module.mk b/image/module.mk index af4284dcdb..6d55b17240 100644 --- a/image/module.mk +++ b/image/module.mk @@ -23,7 +23,10 @@ MODULE_OBJS := \ codecs/smc.o \ codecs/svq1.o \ codecs/truemotion1.o \ - codecs/indeo/get_bits.o + codecs/indeo/get_bits.o \ + codecs/indeo/indeo.o \ + codecs/indeo/mem.o \ + codecs/indeo/vlc.o ifdef USE_MPEG2 MODULE_OBJS += \ -- cgit v1.2.3