From 9af92b8723bdba3b9e540d9e0ba3a9142bc2e480 Mon Sep 17 00:00:00 2001 From: richiesams Date: Tue, 20 Aug 2013 15:55:42 -0500 Subject: [PATCH] ZVISION: Handle rlf frame transitions internally Animations use incremental frame changes. That is, only a few frames are complete (I-frames), the rest are just the pixels that change between the current frame and both the previous frame and the next frame (B-frames). See https://en.wikipedia.org/wiki/Video_compression_picture_types --- engines/zvision/animation.cpp | 9 ++-- engines/zvision/rlf_animation.cpp | 86 ++++++++++++++++++++++++------- engines/zvision/rlf_animation.h | 24 ++++++++- 3 files changed, 93 insertions(+), 26 deletions(-) diff --git a/engines/zvision/animation.cpp b/engines/zvision/animation.cpp index 7c41c23a06e..8ca892dfdab 100644 --- a/engines/zvision/animation.cpp +++ b/engines/zvision/animation.cpp @@ -31,8 +31,6 @@ namespace ZVision { void ZVision::playAnimation(RlfAnimation *animation, uint16 x, uint16 y, DisposeAfterUse::Flag disposeAfterUse) { - uint currentFrame = 0; - uint lastFrame = animation->frameCount(); bool skip = false; uint32 frameTime = animation->frameTime(); uint width = animation->width(); @@ -44,7 +42,7 @@ void ZVision::playAnimation(RlfAnimation *animation, uint16 x, uint16 y, Dispose uint32 accumulatedTime = 0; // Only continue while the video is still playing - while (!shouldQuit() && !skip && currentFrame < lastFrame) { + while (!shouldQuit() && !skip && !animation->endOfAnimation()) { _clock.update(); uint32 currentTime = _clock.getLastMeasuredTime(); accumulatedTime += _clock.getDeltaTime(); @@ -69,11 +67,10 @@ void ZVision::playAnimation(RlfAnimation *animation, uint16 x, uint16 y, Dispose } } - if (accumulatedTime >= frameTime) { + while (accumulatedTime >= frameTime && !animation->endOfAnimation()) { accumulatedTime -= frameTime; - _system->copyRectToScreen(animation->getFrameData(currentFrame), width * sizeof(uint16), newX, newY, width, height); - currentFrame++; + _system->copyRectToScreen(animation->getNextFrame(), width * sizeof(uint16), newX, newY, width, height); } // Always update the screen so the mouse continues to render diff --git a/engines/zvision/rlf_animation.cpp b/engines/zvision/rlf_animation.cpp index 26ce2e217c8..90086e9bd00 100644 --- a/engines/zvision/rlf_animation.cpp +++ b/engines/zvision/rlf_animation.cpp @@ -34,7 +34,14 @@ namespace ZVision { RlfAnimation::RlfAnimation(const Common::String &fileName) - : _frames(0) { + : _frameCount(0), + _width(0), + _height(0), + _frameTime(0), + _frames(0), + _currentFrameBuffer(0), + _currentFrame(-1), + _frameBufferByteSize(0) { Common::File file; if (!file.open(fileName)) { warning("RLF animation file %s could not be opened", fileName.c_str()); @@ -87,8 +94,11 @@ RlfAnimation::RlfAnimation(const Common::String &fileName) file.readUint32LE(); // Unknown11 _frameTime = file.readUint32LE() / 10; // Frame time in microseconds + _frames = new Frame[_frameCount]; + _currentFrameBuffer = new uint16[_width * _height]; + _frameBufferByteSize = _width * _height * sizeof(uint16); + // Read in each frame - _frames = new uint16 *[_frameCount]; for (uint i = 0; i < _frameCount; i++) { file.readUint32BE(); // Magic number MARF uint32 size = file.readUint32LE(); // Size @@ -98,27 +108,19 @@ RlfAnimation::RlfAnimation(const Common::String &fileName) uint32 headerSize = file.readUint32LE(); // Offset from the beginning of this frame to the frame data. Should always be 28 file.readUint32LE(); // Unknown3 - int8 *buffer = new int8[size - headerSize]; - file.read(buffer, size - headerSize); - - _frames[i] = new uint16[_width * _height]; - uint frameByteSize = _width * _height * sizeof(uint16); - memset(_frames[i], 0x7C00, frameByteSize); - // Decode the data - debug("Decoding frame %u", i); + _frames[i].encodedSize = size - headerSize; + _frames[i].encodedData = new int8[_frames[i].encodedSize]; + file.read(_frames[i].encodedData, _frames[i].encodedSize); + if (type == MKTAG('E', 'L', 'H', 'D')) { - debug("Decoding with masked RLE"); - decodeMaskedRunLengthEncoding(buffer, (int8 *)_frames[i], size - headerSize, frameByteSize); + _frames[i].type = Masked; } else if (type == MKTAG('E', 'L', 'R', 'H')) { - debug("Decoding with simple RLE"); - decodeSimpleRunLengthEncoding(buffer, (int8 *)_frames[i], size - headerSize, frameByteSize); + _frames[i].type = Simple; + _completeFrames.push_back(i); } else { warning("Frame %u of %s doesn't have type that can be decoded", i, fileName.c_str()); return; } - - // Cleanup - delete[] buffer; } }; @@ -126,9 +128,58 @@ RlfAnimation::~RlfAnimation() { if (_frames != 0) { delete[] _frames; } + if (_currentFrameBuffer != 0) { + delete[] _currentFrameBuffer; + } } +const uint16 *RlfAnimation::getFrameData(uint frameNumber) { + assert(frameNumber < _frameCount && frameNumber >= 0); + if (frameNumber == _currentFrame) { + return _currentFrameBuffer; + } + + uint closestFrame = _currentFrame; + uint distance = ABS(_currentFrame - frameNumber); + for (Common::List::const_iterator iter = _completeFrames.begin(); iter != _completeFrames.end(); iter++) { + uint newDistance = ABS((*iter) - frameNumber); + if (closestFrame == -1 || newDistance < distance) { + closestFrame = (*iter); + distance = newDistance; + } + } + + bool forwards = frameNumber > closestFrame; + if (forwards) { + for (uint i = closestFrame; i <= frameNumber; i++) { + applyFrameToCurrent(i); + } + } else { + for (uint i = closestFrame; i >= frameNumber; i--) { + applyFrameToCurrent(i); + } + } + + return _currentFrameBuffer; +} + +const uint16 *RlfAnimation::getNextFrame() { + assert(_currentFrame + 1 < _frameCount); + + applyFrameToCurrent(_currentFrame + 1); + return _currentFrameBuffer; +} + +void RlfAnimation::applyFrameToCurrent(uint frameNumber) { + if (_frames[frameNumber].type == Masked) { + decodeMaskedRunLengthEncoding(_frames[frameNumber].encodedData, (int8 *)_currentFrameBuffer, _frames[frameNumber].encodedSize, _frameBufferByteSize); + } else if (_frames[frameNumber].type == Simple) { + decodeSimpleRunLengthEncoding(_frames[frameNumber].encodedData, (int8 *)_currentFrameBuffer, _frames[frameNumber].encodedSize, _frameBufferByteSize); + } + + _currentFrame = frameNumber; +} void RlfAnimation::decodeMaskedRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const { uint32 sourceOffset = 0; @@ -226,5 +277,4 @@ void RlfAnimation::decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint3 } } - } // End of namespace ZVision diff --git a/engines/zvision/rlf_animation.h b/engines/zvision/rlf_animation.h index 7ece4e50631..e204a500908 100644 --- a/engines/zvision/rlf_animation.h +++ b/engines/zvision/rlf_animation.h @@ -36,21 +36,41 @@ public: ~RlfAnimation(); private: + enum EncodingType { + Masked, + Simple + }; + + struct Frame { + EncodingType type; + int8 *encodedData; + uint32 encodedSize; + }; + private: uint _frameCount; uint _width; uint _height; uint32 _frameTime; // In milliseconds - uint16 **_frames; + Frame *_frames; + Common::List _completeFrames; + + int _currentFrame; + uint16 *_currentFrameBuffer; + uint32 _frameBufferByteSize; public: uint frameCount() { return _frameCount; } uint width() { return _width; } uint height() { return _height; } uint32 frameTime() { return _frameTime; } - const uint16 *getFrameData(uint frameNumber) const { return _frames[frameNumber]; } + const uint16 *getFrameData(uint frameNumber); + const uint16 *getNextFrame(); + bool endOfAnimation() { return _currentFrame == _frameCount - 1; } private: + void applyFrameToCurrent(uint frameNumber); + void decodeMaskedRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const; void decodeSimpleRunLengthEncoding(int8 *source, int8 *dest, uint32 sourceSize, uint32 destSize) const; };