diff options
Diffstat (limited to 'video/flic_decoder.cpp')
-rw-r--r-- | video/flic_decoder.cpp | 302 |
1 files changed, 169 insertions, 133 deletions
diff --git a/video/flic_decoder.cpp b/video/flic_decoder.cpp index bdcdedc142..1a0627615b 100644 --- a/video/flic_decoder.cpp +++ b/video/flic_decoder.cpp @@ -26,13 +26,11 @@ #include "common/stream.h" #include "common/system.h" #include "common/textconsole.h" +#include "graphics/surface.h" namespace Video { FlicDecoder::FlicDecoder() { - _paletteChanged = false; - _fileStream = 0; - _surface = 0; } FlicDecoder::~FlicDecoder() { @@ -42,35 +40,59 @@ FlicDecoder::~FlicDecoder() { bool FlicDecoder::loadStream(Common::SeekableReadStream *stream) { close(); - _fileStream = stream; - - /* uint32 frameSize = */ _fileStream->readUint32LE(); - uint16 frameType = _fileStream->readUint16LE(); + /* uint32 frameSize = */ stream->readUint32LE(); + uint16 frameType = stream->readUint16LE(); // Check FLC magic number if (frameType != 0xAF12) { - warning("FlicDecoder::FlicDecoder(): attempted to load non-FLC data (type = 0x%04X)", frameType); - delete _fileStream; - _fileStream = 0; + warning("FlicDecoder::loadStream(): attempted to load non-FLC data (type = 0x%04X)", frameType); return false; } - - _frameCount = _fileStream->readUint16LE(); - uint16 width = _fileStream->readUint16LE(); - uint16 height = _fileStream->readUint16LE(); - uint16 colorDepth = _fileStream->readUint16LE(); + uint16 frameCount = stream->readUint16LE(); + uint16 width = stream->readUint16LE(); + uint16 height = stream->readUint16LE(); + uint16 colorDepth = stream->readUint16LE(); if (colorDepth != 8) { - warning("FlicDecoder::FlicDecoder(): attempted to load an FLC with a palette of color depth %d. Only 8-bit color palettes are supported", frameType); - delete _fileStream; - _fileStream = 0; + warning("FlicDecoder::loadStream(): attempted to load an FLC with a palette of color depth %d. Only 8-bit color palettes are supported", colorDepth); return false; } + addTrack(new FlicVideoTrack(stream, frameCount, width, height)); + return true; +} + +const Common::List<Common::Rect> *FlicDecoder::getDirtyRects() const { + const Track *track = getTrack(0); + + if (track) + return ((const FlicVideoTrack *)track)->getDirtyRects(); + + return 0; +} + +void FlicDecoder::clearDirtyRects() { + Track *track = getTrack(0); + + if (track) + ((FlicVideoTrack *)track)->clearDirtyRects(); +} + +void FlicDecoder::copyDirtyRectsToBuffer(uint8 *dst, uint pitch) { + Track *track = getTrack(0); + + if (track) + ((FlicVideoTrack *)track)->copyDirtyRectsToBuffer(dst, pitch); +} + +FlicDecoder::FlicVideoTrack::FlicVideoTrack(Common::SeekableReadStream *stream, uint16 frameCount, uint16 width, uint16 height) { + _fileStream = stream; + _frameCount = frameCount; + _fileStream->readUint16LE(); // flags // Note: The normal delay is a 32-bit integer (dword), whereas the overridden delay is a 16-bit integer (word) // the frame delay is the FLIC "speed", in milliseconds. - _frameRate = Common::Rational(1000, _fileStream->readUint32LE()); + _frameDelay = _startFrameDelay = _fileStream->readUint32LE(); _fileStream->seek(80); _offsetFrame1 = _fileStream->readUint32LE(); @@ -78,112 +100,53 @@ bool FlicDecoder::loadStream(Common::SeekableReadStream *stream) { _surface = new Graphics::Surface(); _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8()); - _palette = (byte *)malloc(3 * 256); + _palette = new byte[3 * 256]; memset(_palette, 0, 3 * 256); - _paletteChanged = false; + _dirtyPalette = false; + + _curFrame = -1; + _nextFrameStartTime = 0; + _atRingFrame = false; // Seek to the first frame _fileStream->seek(_offsetFrame1); - return true; } -void FlicDecoder::close() { - if (!_fileStream) - return; - +FlicDecoder::FlicVideoTrack::~FlicVideoTrack() { delete _fileStream; - _fileStream = 0; + delete[] _palette; _surface->free(); delete _surface; - _surface = 0; - - free(_palette); - _dirtyRects.clear(); - - reset(); } -void FlicDecoder::decodeByteRun(uint8 *data) { - byte *ptr = (byte *)_surface->pixels; - while ((int32)(ptr - (byte *)_surface->pixels) < (getWidth() * getHeight())) { - int chunks = *data++; - while (chunks--) { - int count = (int8)*data++; - if (count > 0) { - memset(ptr, *data++, count); - } else { - count = -count; - memcpy(ptr, data, count); - data += count; - } - ptr += count; - } - } - - // Redraw - _dirtyRects.clear(); - _dirtyRects.push_back(Common::Rect(0, 0, getWidth(), getHeight())); +bool FlicDecoder::FlicVideoTrack::endOfTrack() const { + return getCurFrame() >= getFrameCount() - 1; } -#define OP_PACKETCOUNT 0 -#define OP_UNDEFINED 1 -#define OP_LASTPIXEL 2 -#define OP_LINESKIPCOUNT 3 - -void FlicDecoder::decodeDeltaFLC(uint8 *data) { - uint16 linesInChunk = READ_LE_UINT16(data); data += 2; - uint16 currentLine = 0; - uint16 packetCount = 0; - - while (linesInChunk--) { - uint16 opcode; +bool FlicDecoder::FlicVideoTrack::rewind() { + _curFrame = -1; + _nextFrameStartTime = 0; - // First process all the opcodes. - do { - opcode = READ_LE_UINT16(data); data += 2; + if (endOfTrack() && _fileStream->pos() < _fileStream->size()) + _atRingFrame = true; + else + _fileStream->seek(_offsetFrame1); - switch ((opcode >> 14) & 3) { - case OP_PACKETCOUNT: - packetCount = opcode; - break; - case OP_UNDEFINED: - break; - case OP_LASTPIXEL: - *((byte *)_surface->pixels + currentLine * getWidth() + getWidth() - 1) = (opcode & 0xFF); - _dirtyRects.push_back(Common::Rect(getWidth() - 1, currentLine, getWidth(), currentLine + 1)); - break; - case OP_LINESKIPCOUNT: - currentLine += -(int16)opcode; - break; - } - } while (((opcode >> 14) & 3) != OP_PACKETCOUNT); + _frameDelay = _startFrameDelay; + return true; +} - uint16 column = 0; +uint16 FlicDecoder::FlicVideoTrack::getWidth() const { + return _surface->w; +} - // Now interpret the RLE data - while (packetCount--) { - column += *data++; - int rleCount = (int8)*data++; - if (rleCount > 0) { - memcpy((byte *)_surface->pixels + (currentLine * getWidth()) + column, data, rleCount * 2); - data += rleCount * 2; - _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1)); - } else if (rleCount < 0) { - rleCount = -rleCount; - uint16 dataWord = READ_UINT16(data); data += 2; - for (int i = 0; i < rleCount; ++i) { - WRITE_UINT16((byte *)_surface->pixels + currentLine * getWidth() + column + i * 2, dataWord); - } - _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1)); - } else { // End of cutscene ? - return; - } - column += rleCount * 2; - } +uint16 FlicDecoder::FlicVideoTrack::getHeight() const { + return _surface->h; +} - currentLine++; - } +Graphics::PixelFormat FlicDecoder::FlicVideoTrack::getPixelFormat() const { + return _surface->format; } #define FLI_SETPAL 4 @@ -192,7 +155,7 @@ void FlicDecoder::decodeDeltaFLC(uint8 *data) { #define PSTAMP 18 #define FRAME_TYPE 0xF1FA -const Graphics::Surface *FlicDecoder::decodeNextFrame() { +const Graphics::Surface *FlicDecoder::FlicVideoTrack::decodeNextFrame() { // Read chunk uint32 frameSize = _fileStream->readUint32LE(); uint16 frameType = _fileStream->readUint16LE(); @@ -201,15 +164,12 @@ const Graphics::Surface *FlicDecoder::decodeNextFrame() { switch (frameType) { case FRAME_TYPE: { - // FIXME: FLIC should be switched over to a variable frame rate VideoDecoder to handle - // this properly. - chunkCount = _fileStream->readUint16LE(); // Note: The overridden delay is a 16-bit integer (word), whereas the normal delay is a 32-bit integer (dword) // the frame delay is the FLIC "speed", in milliseconds. uint16 newFrameDelay = _fileStream->readUint16LE(); // "speed", in milliseconds if (newFrameDelay > 0) - _frameRate = Common::Rational(1000, newFrameDelay); + _frameDelay = newFrameDelay; _fileStream->readUint16LE(); // reserved, always 0 uint16 newWidth = _fileStream->readUint16LE(); @@ -240,10 +200,11 @@ const Graphics::Surface *FlicDecoder::decodeNextFrame() { frameType = _fileStream->readUint16LE(); uint8 *data = new uint8[frameSize - 6]; _fileStream->read(data, frameSize - 6); + switch (frameType) { case FLI_SETPAL: unpackPalette(data); - _paletteChanged = true; + _dirtyPalette = true; break; case FLI_SS2: decodeDeltaFLC(data); @@ -264,26 +225,111 @@ const Graphics::Surface *FlicDecoder::decodeNextFrame() { } _curFrame++; + _nextFrameStartTime += _frameDelay; - // If we just processed the ring frame, set the next frame - if (_curFrame == (int32)_frameCount) { - _curFrame = 0; + if (_atRingFrame) { + // If we decoded the ring frame, seek to the second frame + _atRingFrame = false; _fileStream->seek(_offsetFrame2); } - if (_curFrame == 0) - _startTime = g_system->getMillis(); - return _surface; } -void FlicDecoder::reset() { - FixedRateVideoDecoder::reset(); - if (_fileStream) - _fileStream->seek(_offsetFrame1); +void FlicDecoder::FlicVideoTrack::copyDirtyRectsToBuffer(uint8 *dst, uint pitch) { + for (Common::List<Common::Rect>::const_iterator it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) { + for (int y = (*it).top; y < (*it).bottom; ++y) { + const int x = (*it).left; + memcpy(dst + y * pitch + x, (byte *)_surface->pixels + y * getWidth() + x, (*it).right - x); + } + } + + clearDirtyRects(); } -void FlicDecoder::unpackPalette(uint8 *data) { +void FlicDecoder::FlicVideoTrack::decodeByteRun(uint8 *data) { + byte *ptr = (byte *)_surface->pixels; + while ((int32)(ptr - (byte *)_surface->pixels) < (getWidth() * getHeight())) { + int chunks = *data++; + while (chunks--) { + int count = (int8)*data++; + if (count > 0) { + memset(ptr, *data++, count); + } else { + count = -count; + memcpy(ptr, data, count); + data += count; + } + ptr += count; + } + } + + // Redraw + _dirtyRects.clear(); + _dirtyRects.push_back(Common::Rect(0, 0, getWidth(), getHeight())); +} + +#define OP_PACKETCOUNT 0 +#define OP_UNDEFINED 1 +#define OP_LASTPIXEL 2 +#define OP_LINESKIPCOUNT 3 + +void FlicDecoder::FlicVideoTrack::decodeDeltaFLC(uint8 *data) { + uint16 linesInChunk = READ_LE_UINT16(data); data += 2; + uint16 currentLine = 0; + uint16 packetCount = 0; + + while (linesInChunk--) { + uint16 opcode; + + // First process all the opcodes. + do { + opcode = READ_LE_UINT16(data); data += 2; + + switch ((opcode >> 14) & 3) { + case OP_PACKETCOUNT: + packetCount = opcode; + break; + case OP_UNDEFINED: + break; + case OP_LASTPIXEL: + *((byte *)_surface->pixels + currentLine * getWidth() + getWidth() - 1) = (opcode & 0xFF); + _dirtyRects.push_back(Common::Rect(getWidth() - 1, currentLine, getWidth(), currentLine + 1)); + break; + case OP_LINESKIPCOUNT: + currentLine += -(int16)opcode; + break; + } + } while (((opcode >> 14) & 3) != OP_PACKETCOUNT); + + uint16 column = 0; + + // Now interpret the RLE data + while (packetCount--) { + column += *data++; + int rleCount = (int8)*data++; + if (rleCount > 0) { + memcpy((byte *)_surface->pixels + (currentLine * getWidth()) + column, data, rleCount * 2); + data += rleCount * 2; + _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1)); + } else if (rleCount < 0) { + rleCount = -rleCount; + uint16 dataWord = READ_UINT16(data); data += 2; + for (int i = 0; i < rleCount; ++i) { + WRITE_UINT16((byte *)_surface->pixels + currentLine * getWidth() + column + i * 2, dataWord); + } + _dirtyRects.push_back(Common::Rect(column, currentLine, column + rleCount * 2, currentLine + 1)); + } else { // End of cutscene ? + return; + } + column += rleCount * 2; + } + + currentLine++; + } +} + +void FlicDecoder::FlicVideoTrack::unpackPalette(uint8 *data) { uint16 numPackets = READ_LE_UINT16(data); data += 2; if (0 == READ_LE_UINT16(data)) { //special case @@ -308,14 +354,4 @@ void FlicDecoder::unpackPalette(uint8 *data) { } } -void FlicDecoder::copyDirtyRectsToBuffer(uint8 *dst, uint pitch) { - for (Common::List<Common::Rect>::const_iterator it = _dirtyRects.begin(); it != _dirtyRects.end(); ++it) { - for (int y = (*it).top; y < (*it).bottom; ++y) { - const int x = (*it).left; - memcpy(dst + y * pitch + x, (byte *)_surface->pixels + y * getWidth() + x, (*it).right - x); - } - } - _dirtyRects.clear(); -} - } // End of namespace Video |