aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrichiesams2013-08-20 15:55:42 -0500
committerrichiesams2013-08-20 15:55:42 -0500
commit9af92b8723bdba3b9e540d9e0ba3a9142bc2e480 (patch)
tree5b3e05a86635e45d67823dd5d1382bee12053086
parent610b563790ded5c43f757675a9acdc49ce555a1d (diff)
downloadscummvm-rg350-9af92b8723bdba3b9e540d9e0ba3a9142bc2e480.tar.gz
scummvm-rg350-9af92b8723bdba3b9e540d9e0ba3a9142bc2e480.tar.bz2
scummvm-rg350-9af92b8723bdba3b9e540d9e0ba3a9142bc2e480.zip
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
-rw-r--r--engines/zvision/animation.cpp9
-rw-r--r--engines/zvision/rlf_animation.cpp86
-rw-r--r--engines/zvision/rlf_animation.h24
3 files changed, 93 insertions, 26 deletions
diff --git a/engines/zvision/animation.cpp b/engines/zvision/animation.cpp
index 7c41c23a06..8ca892dfda 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 26ce2e217c..90086e9bd0 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<uint>::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 7ece4e5063..e204a50090 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<uint> _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;
};