diff options
-rw-r--r-- | graphics/video/coktel_decoder.cpp | 171 | ||||
-rw-r--r-- | graphics/video/coktel_decoder.h | 15 |
2 files changed, 178 insertions, 8 deletions
diff --git a/graphics/video/coktel_decoder.cpp b/graphics/video/coktel_decoder.cpp index a6c79cc1df..0c5b637c2b 100644 --- a/graphics/video/coktel_decoder.cpp +++ b/graphics/video/coktel_decoder.cpp @@ -68,32 +68,191 @@ Common::Rational CoktelDecoder::getFrameRate() const { PreIMDDecoder::PreIMDDecoder(uint16 width, uint16 height, - Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType) { + Audio::Mixer &mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), + _stream(0), _videoBuffer(0), _videoBufferSize(0) { _width = width; _height = height; } PreIMDDecoder::~PreIMDDecoder() { + close(); } -bool PreIMDDecoder::seek(uint32 frame, int whence, bool restart) { - return false; +bool PreIMDDecoder::seek(int32 frame, int whence, bool restart) { + if (!isVideoLoaded()) + // Nothing to do + return false; + + // Find the frame to which to seek + if (whence == SEEK_CUR) + frame += _curFrame; + else if (whence == SEEK_END) + frame = _frameCount - frame - 1; + else if (whence == SEEK_SET) + frame--; + else + return false; + + if ((frame < -1) || (((uint32) frame) >= _frameCount)) + // Out of range + return false; + + if (frame == _curFrame) + // Nothing to do + return true; + + // Run through the frames + _curFrame = -1; + _stream->seek(2); + while (_curFrame != frame) { + uint16 frameSize = _stream->readUint16LE(); + + _stream->skip(frameSize + 2); + + _curFrame++; + } + + return true; } bool PreIMDDecoder::load(Common::SeekableReadStream &stream) { - return false; + // Since PreIMDs don't have any width and height values stored, + // we need them to be specified in the constructor + assert((_width > 0) && (_height > 0)); + + close(); + + _stream = &stream; + + _stream->seek(0); + + _frameCount = _stream->readUint16LE(); + + _surface.create(_width, _height, 1); + + _videoBufferSize = _width * _height; + _videoBuffer = new byte[_videoBufferSize]; + + memset(_videoBuffer, 0, _videoBufferSize); + + return true; } void PreIMDDecoder::close() { + reset(); + + _surface.free(); + + delete _stream; + + delete[] _videoBuffer; + + _stream = 0; + + _videoBuffer = 0; + _videoBufferSize = 0; } bool PreIMDDecoder::isVideoLoaded() const { - return false; + return _stream != 0; } Surface *PreIMDDecoder::decodeNextFrame() { - return 0; + if (!isVideoLoaded() || endOfVideo()) + return 0; + + processFrame(); + renderFrame(); + + _curFrame++; + + return &_surface; +} + +void PreIMDDecoder::processFrame() { + uint16 frameSize = _stream->readUint16LE(); + + uint32 nextFramePos = _stream->pos() + frameSize + 2; + + byte cmd; + + cmd = _stream->readByte(); + frameSize--; + + if (cmd == 0) { + // Palette. Ignored by Fascination, though + + _stream->skip(768); + + frameSize -= 769; + + cmd = _stream->readByte(); + } + + if (cmd != 2) { + // Partial frame data + + uint32 fSize = frameSize; + uint32 vidSize = _videoBufferSize; + + byte *vidBuffer = _videoBuffer; + + while ((fSize > 0) && (vidSize > 0)) { + uint32 n = _stream->readByte(); + fSize--; + + if ((n & 0x80) != 0) { + // Data + + n = MIN<uint32>((n & 0x7F) + 1, MIN(fSize, vidSize)); + + _stream->read(vidBuffer, n); + + vidBuffer += n; + vidSize -= n; + fSize -= n; + + } else { + // Skip + + n = MIN<uint32>(n + 1, vidSize); + + vidBuffer += n; + vidSize -= n; + } + } + + } else { + // Full direct frame + + uint32 vidSize = MIN<uint32>(_videoBufferSize, frameSize); + + _stream->read(_videoBuffer, vidSize); + } + + _stream->seek(nextFramePos); +} + +void PreIMDDecoder::renderFrame() { + uint16 w = MIN<uint16>(_surface.w, _width); + uint16 h = MIN<uint16>(_surface.h, _height); + + const byte *src = _videoBuffer; + byte *dst = (byte *) _surface.pixels; // + x/y + + uint32 frameDataSize = _videoBufferSize; + + while (h-- > 0) { + uint32 n = MIN<uint32>(w, frameDataSize); + + memcpy(dst, src, n); + + src += _width; + dst += _surface.pitch; + + frameDataSize -= n; + } } PixelFormat PreIMDDecoder::getPixelFormat() const { diff --git a/graphics/video/coktel_decoder.h b/graphics/video/coktel_decoder.h index acbd533c00..b354a5cd9d 100644 --- a/graphics/video/coktel_decoder.h +++ b/graphics/video/coktel_decoder.h @@ -62,7 +62,7 @@ public: Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); ~CoktelDecoder(); - virtual bool seek(uint32 frame, int whence = SEEK_SET, bool restart = false) = 0; + virtual bool seek(int32 frame, int whence = SEEK_SET, bool restart = false) = 0; // VideoDecoder interface @@ -98,7 +98,7 @@ public: Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); ~PreIMDDecoder(); - bool seek(uint32 frame, int whence = SEEK_SET, bool restart = false); + bool seek(int32 frame, int whence = SEEK_SET, bool restart = false); // VideoDecoder interface @@ -110,6 +110,17 @@ public: Surface *decodeNextFrame(); PixelFormat getPixelFormat() const; + +private: + Common::SeekableReadStream *_stream; + + byte *_videoBuffer; + uint32 _videoBufferSize; + + Surface _surface; + + void processFrame(); + void renderFrame(); }; } // End of namespace Graphics |