mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-22 01:39:57 +00:00
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:
parent
610b563790
commit
9af92b8723
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user