diff options
-rw-r--r-- | engines/glk/frotz/pics_decoder.cpp | 299 |
1 files changed, 105 insertions, 194 deletions
diff --git a/engines/glk/frotz/pics_decoder.cpp b/engines/glk/frotz/pics_decoder.cpp index dba1335295..73032df0d8 100644 --- a/engines/glk/frotz/pics_decoder.cpp +++ b/engines/glk/frotz/pics_decoder.cpp @@ -27,6 +27,71 @@ namespace Glk { namespace Frotz { +#define MAX_BIT 512 /* Must be less than or equal to CODE_TABLE_SIZE */ +#define CODE_SIZE 8 +#define CODE_TABLE_SIZE 4096 +#define PREFIX 0 +#define PIXEL 1 + +/** + * Support class used for picture decompression + */ +class Compress { +private: + byte _codeBuffer[CODE_TABLE_SIZE]; +public: + short _nextCode; + short _sLen; + short _sPtr; + short _tLen; + short _tPtr; + + Compress() : _nextCode(0), _sLen(0), _sPtr(0), _tLen(0), _tPtr(0) {} + + /** + * Read a code + */ + short readCode(Common::ReadStream &src); +}; + +static short MASK[16] = { + 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, + 0x00ff, 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff +}; + +short Compress::readCode(Common::ReadStream &src) { + short code, bsize, tlen, tptr; + + code = 0; + tlen = _tLen; + tptr = 0; + + while (tlen) { + if (_sLen == 0) { + if ((_sLen = src.read(_codeBuffer, MAX_BIT)) == 0) { + error("fread"); + } + _sLen *= 8; + _sPtr = 0; + } + bsize = ((_sPtr + 8) & ~7) - _sPtr; + bsize = (tlen > bsize) ? bsize : tlen; + code |= (((uint)_codeBuffer[_sPtr >> 3] >> (_sPtr & 7)) & MASK[bsize]) << tptr; + + tlen -= bsize; + tptr += bsize; + _sLen -= bsize; + _sPtr += bsize; + } + + if ((_nextCode == MASK[_tLen]) && (_tLen < 12)) + _tLen++; + + return code; +} + +/*--------------------------------------------------------------------------*/ + PictureDecoder::PictureDecoder() { _tableVal = new byte[3 * 3840]; _tableRef = (uint16 *)(_tableVal + 3840); @@ -38,219 +103,65 @@ PictureDecoder::~PictureDecoder() { Common::SeekableReadStream *PictureDecoder::decode(Common::ReadStream &src, uint flags, const Common::Array<byte> &palette, uint display, size_t width, size_t height) { - static const int raise_bits[4] = { 0x0100, 0x0300, 0x0700, 0x0000 }; Common::MemoryWriteStreamDynamic out(DisposeAfterUse::NO); - byte buf[512]; - byte transparent; - int colour_shift = 0; - int first_colour = 0; - int code, prev_code = 0; - int next_entry; - int bits_per_code; - int bits_shift; - int bits; - int bufpos = 0; + short code_table[CODE_TABLE_SIZE][2]; + byte buffer[CODE_TABLE_SIZE]; - /* - * Write out dimensions of image - */ + // Write out dimensions out.writeUint16LE(width); out.writeUint16LE(height); - /* Set up the color mapping. This is only used for MCGA pictures; the colour - * map affects every picture on the screen. The first colour to be defined is - * colour 2. Every map defines up to 14 colours (colour 2 to 15). These colours - * are not related to the standard Z-machine colour scheme which remains unchanged. - * (This is based on the Amiga interpreter which had to work with 16 colours. - * Colours 0 and 1 were used for text; changing the text colours actually changed - * palette entries 0 and 1. This interface uses the same trick in Amiga mode.) - */ - - switch (display) { - case kCGA: - colour_shift = -2; - break; - case kEGA: - colour_shift = 0; - break; - case kMCGA: - colour_shift = 32; - first_colour = 34; - break; - case kAmiga: - colour_shift = -1; - first_colour = 65; - break; - default: - error("Unsupported mode"); - break; - } - - // Note: we don't actually use paletted indexes, so adjust colour_shift - // relative to first_colour - colour_shift -= first_colour; - - out.writeUint16LE(palette.size() / 3); + // Write out palette + out.writeUint16LE(palette.size() / 3 + 2); + for (int idx = 0; idx < 6; ++idx) + out.writeByte(0); if (!palette.empty()) out.write(&palette[0], palette.size()); - /* Bit 0 of "flags" indicates that the picture uses a transparent colour, - * the top four bits tell us which colour it is. For CGA and MCGA pictures - * this is always 0; for EGA pictures it can be any colour between 0 and 15. - */ - transparent = 0xff; + byte transparent = 0xff; if (flags & 1) transparent = flags >> 12; out.writeByte(transparent); - /* The uncompressed picture is a long sequence of bytes. Every byte holds - * the colour of a pixel, starting at the top left, stopping at the bottom right. - * We keep track of our position in the current line. (There is a special case: - * CGA pictures with no transparent colour are stored as bit patterns, i.e. - * every byte holds the pattern for eight pixels. A pixel must be white if the - * corresponding bit is set, otherwise it must be black.) - */ -// current_x = 1 + width; -// current_y = 1 - 1; - - /* The compressed picture is a stream of bits. We read the file byte-wise, - * storing the current byte in the variable "bits". Several bits make one code; - * the variable "bits_shift" helps us to build the next code. - */ - bits_shift = 0; - bits = 0; - -reset_table: - /* Clear the table. We use a table of 3840 entries. Each entry consists of both - * a value and a reference to another table entry. Following these references - * we get a sequence of values. At the start of decompression all table entries - * are undefined. Later we see how entries are set and used. - */ - next_entry = 1; - - /* At the start of decompression 9 bits make one code; during the process this can - * rise to 12 bits per code. 9 bits are sufficient to address both 256 literal values - * and 256 table entries; 12 bits are sufficient to address both 256 literal values - * and all 3840 table entries. The number of bits per code rises with the number of - * table entries. When the table is cleared, the number of bits per code drops back to 9. - */ - bits_per_code = 9; - -next_code: - - /* Read the next code from the graphics file. This requires some confusing bit operations. - * Note that low bits always come first. Usually there are a few bits left over from - * the previous code; these bits must be used before further bits are read from the - * graphics file. - */ - code = bits >> (8 - bits_shift); - - do { - bits = src.readByte(); - code |= bits << bits_shift; - - bits_shift += 8; - } while (bits_shift < bits_per_code); - - bits_shift -= bits_per_code; - - code &= 0xfff >> (12 - bits_per_code); - - /* There are two codes with a special meaning. The first one is 256 which clears - * the table and sets the number of bits per code to 9. (This is necessary when - * the table is full.) The second one is 257 which marks the end of the picture. - * For the sake of efficiency, we drecement the code by 256. - */ - code -= 256; - - if (code == 0) - goto reset_table; - if (code == 1) { - bool t[256]; - // *******DEBUG******* - Common::fill(&t[0], &t[256], false); - for (uint idx = 0; idx < out.size(); ++idx) - t[*((byte *)out.getData() + idx)] = true; - - return new Common::MemoryReadStream(out.getData(), out.size(), DisposeAfterUse::YES); - } - - /* Codes from 0 to 255 are literals, i.e. they represent a plain byte value. - * Codes from 258 onwards are references to table entries, i.e. they represent - * a sequence of byte values (see the remarks on the table above). This means - * that for each code one or several byte values are added to the decompressed - * picture. But there is yet more work to do: Every time we read a code one - * table entry is set. As we said above, a table entry consist of both a value - * and a reference to another table entry. If the current code is a literal, - * then the value has to be set to this literal; but if the code refers to a - * sequence of byte values, then the value has to be set to the last byte of - * this sequence. In any case, the reference is set to the previous code. - * Finally, one should be aware that a code may legally refer to the table entry - * which is currently being set. This requires some extra care. - */ - _tableRef[next_entry] = prev_code; + int i; + short code, old = 0, first, clear_code; + Compress comp; - prev_code = code; + clear_code = 1 << CODE_SIZE; + comp._nextCode = clear_code + 2; + comp._tLen = CODE_SIZE + 1; + comp._tPtr = 0; - while (code >= 0) { - buf[bufpos++] = _tableVal[code]; - code = (short) _tableRef[code]; + for (i = 0; i < CODE_TABLE_SIZE; i++) { + code_table[i][PREFIX] = CODE_TABLE_SIZE; + code_table[i][PIXEL] = i; } - if (next_entry == prev_code) - buf[0] = code; - - _tableVal[next_entry] = code; - - /* The number of bits per code is incremented when the current number of bits - * no longer suffices to address all defined table entries; but in any case - * the number of bits may never be greater than 12. - */ - next_entry++; - - if (next_entry == raise_bits[bits_per_code - 9]) - bits_per_code++; - -reverse_buffer: - /* Output the sequence of byte values (pixels). The order of the sequence - * must be reversed. (This is why we have stored the sequence in a buffer; - * experiments show that a buffer of 512 bytes suffices.) - * - * Either add a single pixel or a pattern of eight bits (b/w CGA pictures without - * a transparent colour) to the current line. Increment our position by 1 or 8 - * respectively. The pixel may have to be painted several times if the scaling - * factor is greater than one. - */ - if (display == kCGA && transparent == 0xff) { - // TODO - } else { - byte v = code; - - if (v != transparent) { - v += colour_shift; - - if (display != kMCGA) { - // TODO - } else { - // position shift - } - - out.writeByte(v); - - if (display == kAmiga) { - // TODO - } + for (;;) { + if ((code = comp.readCode(src)) == (clear_code + 1)) + break; + if (code == clear_code) { + comp._tLen = CODE_SIZE + 1; + comp._nextCode = clear_code + 2; + code = comp.readCode(src); + } else { + first = (code == comp._nextCode) ? old : code; + while (code_table[first][PREFIX] != CODE_TABLE_SIZE) + first = code_table[first][PREFIX]; + code_table[comp._nextCode][PREFIX] = old; + code_table[comp._nextCode++][PIXEL] = code_table[first][PIXEL]; } + old = code; + i = 0; + do + buffer[i++] = (unsigned char)code_table[code][PIXEL]; + while ((code = code_table[code][PREFIX]) != CODE_TABLE_SIZE); + do + out.writeByte(buffer[--i]); + while (i > 0); } - /* If there are no more values in the buffer then read the next code from the file. - * Otherwise fetch the next byte value from the buffer and continue outputing the picture. - */ - if (bufpos == 0) - goto next_code; - - code = (code & ~0xff) | buf[--bufpos]; - goto reverse_buffer; + return new Common::MemoryReadStream(out.getData(), out.size(), DisposeAfterUse::YES); } } // End of namespace Frotz |