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
This commit is contained in:
richiesams 2013-08-20 15:55:42 -05:00
parent 610b563790
commit 9af92b8723
3 changed files with 93 additions and 26 deletions

View File

@ -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

View File

@ -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

View File

@ -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;
};