diff options
author | Eugene Sandulenko | 2016-10-04 23:21:41 +0200 |
---|---|---|
committer | GitHub | 2016-10-04 23:21:41 +0200 |
commit | 42c518238def133be776b7b857572bb54bc235ed (patch) | |
tree | 65aae1e613558f89387ced9d4680cbc49aefc29f | |
parent | 43d5ed570ef7863b892979e903ccfd20f4282c15 (diff) | |
parent | 054bfb3c196ac5a02d149c758a2ee0a4711b55e4 (diff) | |
download | scummvm-rg350-42c518238def133be776b7b857572bb54bc235ed.tar.gz scummvm-rg350-42c518238def133be776b7b857572bb54bc235ed.tar.bz2 scummvm-rg350-42c518238def133be776b7b857572bb54bc235ed.zip |
Merge pull request #839 from dreammaster/video
VIDEO: AVIDecoder changes for Titanic videos
-rw-r--r-- | engines/titanic/module.mk | 2 | ||||
-rw-r--r-- | engines/titanic/support/avi_surface.cpp | 130 | ||||
-rw-r--r-- | engines/titanic/support/avi_surface.h | 22 | ||||
-rw-r--r-- | engines/titanic/support/mouse_cursor.cpp | 43 | ||||
-rw-r--r-- | engines/titanic/support/mouse_cursor.h | 4 | ||||
-rw-r--r-- | engines/titanic/support/movie.cpp | 2 | ||||
-rw-r--r-- | engines/titanic/support/movie.h | 10 | ||||
-rw-r--r-- | engines/titanic/support/raw_surface.cpp | 134 | ||||
-rw-r--r-- | engines/titanic/support/transparency_surface.cpp | 83 | ||||
-rw-r--r-- | engines/titanic/support/transparency_surface.h (renamed from engines/titanic/support/raw_surface.h) | 26 | ||||
-rw-r--r-- | engines/titanic/support/video_surface.cpp | 77 | ||||
-rw-r--r-- | engines/titanic/support/video_surface.h | 11 | ||||
-rw-r--r-- | video/avi_decoder.cpp | 107 | ||||
-rw-r--r-- | video/avi_decoder.h | 16 | ||||
-rw-r--r-- | video/video_decoder.h | 2 |
15 files changed, 318 insertions, 351 deletions
diff --git a/engines/titanic/module.mk b/engines/titanic/module.mk index f1a6c751df..e802456c5a 100644 --- a/engines/titanic/module.mk +++ b/engines/titanic/module.mk @@ -474,7 +474,7 @@ MODULE_OBJS := \ support/movie_range_info.o \ support/movie_manager.o \ support/credit_text.o \ - support/raw_surface.o \ + support/transparency_surface.o \ support/rect.o \ support/screen_manager.o \ support/simple_file.o \ diff --git a/engines/titanic/support/avi_surface.cpp b/engines/titanic/support/avi_surface.cpp index 7fbb05ed58..5b11c7799e 100644 --- a/engines/titanic/support/avi_surface.cpp +++ b/engines/titanic/support/avi_surface.cpp @@ -30,28 +30,10 @@ namespace Titanic { -Video::AVIDecoder::AVIVideoTrack &AVIDecoder::getVideoTrack() { - for (TrackListIterator it = getTrackListBegin(); it != getTrackListEnd(); it++) - if ((*it)->getTrackType() == Track::kTrackTypeVideo) - return *dynamic_cast<AVIVideoTrack *>(*it); - - error("Could not find video track"); -} - -/** - * Track filter for AVIDecoder that filters out any secondary - * video track some videos have to hold transparency masks - */ -static bool primaryTrackSelect(bool isVideo, int trackCounter) { - return !isVideo || trackCounter == 0; -} - -/** - * Track filter for AVIDecoder that only accepts the secondary - * transparency msak video track for a video, if present - */ -static bool secondaryTrackSelect(bool isVideo, int trackCounter) { - return isVideo && trackCounter > 0; +Video::AVIDecoder::AVIVideoTrack &AVIDecoder::getVideoTrack(uint idx) { + assert(idx < _videoTracks.size()); + AVIVideoTrack *track = static_cast<AVIVideoTrack *>(_videoTracks[idx].track); + return *track; } AVISurface::AVISurface(const CResourceKey &key) { @@ -60,28 +42,18 @@ AVISurface::AVISurface(const CResourceKey &key) { _movieFrameSurface[0] = _movieFrameSurface[1] = nullptr; _framePixels = nullptr; - // Reset current frame. We need to keep track of frames separately from the decoders, + // Reset current frame. We need to keep track of frames separately from the decoder, // since it needs to be able to go beyond the frame count or to negative to allow // correct detection of when range playbacks have finished _currentFrame = -1; _isReversed = false; - // Create a decoder for the audio (if any) and primary video track - _decoders[0] = new AVIDecoder(Audio::Mixer::kPlainSoundType, primaryTrackSelect); - if (!_decoders[0]->loadFile(key.getString())) + // Create a decoder + _decoder = new AVIDecoder(Audio::Mixer::kPlainSoundType); + if (!_decoder->loadFile(key.getString())) error("Could not open video - %s", key.getString().c_str()); - _streamCount = 1; - - // Create a decoder for any secondary video track - AVIDecoder *decoder2 = new AVIDecoder(Audio::Mixer::kPlainSoundType, secondaryTrackSelect); - if (decoder2->loadFile(key.getString())) { - _decoders[1] = decoder2; - ++_streamCount; - } else { - delete decoder2; - _decoders[1] = nullptr; - } + _streamCount = _decoder->videoTrackCount(); } AVISurface::~AVISurface() { @@ -90,15 +62,14 @@ AVISurface::~AVISurface() { delete _framePixels; delete _movieFrameSurface[0]; delete _movieFrameSurface[1]; - delete _decoders[0]; - delete _decoders[1]; + delete _decoder; } bool AVISurface::play(uint flags, CGameObject *obj) { if (flags & MOVIE_REVERSE) - return play(_decoders[0]->getFrameCount() - 1, 0, flags, obj); + return play(_decoder->getFrameCount() - 1, 0, flags, obj); else - return play(0, _decoders[0]->getFrameCount() - 1, flags, obj); + return play(0, _decoder->getFrameCount() - 1, flags, obj); } bool AVISurface::play(int startFrame, int endFrame, uint flags, CGameObject *obj) { @@ -139,10 +110,7 @@ bool AVISurface::play(int startFrame, int endFrame, int initialFrame, uint flags } void AVISurface::stop() { - _decoders[0]->stop(); - if (_decoders[1]) - _decoders[1]->stop(); - + _decoder->stop(); _movieRangeInfo.destroyContents(); } @@ -160,19 +128,14 @@ bool AVISurface::startAtFrame(int frameNumber) { renderFrame(); // Start the playback - _decoders[0]->start(); - if (_decoders[1]) - _decoders[1]->start(); - + _decoder->start(); + return true; } void AVISurface::seekToFrame(uint frameNumber) { if ((int)frameNumber != getFrame()) { - _decoders[0]->seekToFrame(frameNumber); - if (_decoders[1]) - _decoders[1]->seekToFrame(frameNumber); - + _decoder->seekToFrame(frameNumber); _currentFrame = (int)frameNumber; } @@ -180,10 +143,7 @@ void AVISurface::seekToFrame(uint frameNumber) { } void AVISurface::setReversed(bool isReversed) { - _decoders[0]->setReverse(isReversed); - if (_decoders[1]) - _decoders[1]->setReverse(isReversed); - + _decoder->setReverse(isReversed); _isReversed = isReversed; } @@ -234,8 +194,8 @@ void AVISurface::setVideoSurface(CVideoSurface *surface) { _videoSurface = surface; // Handling for secondary video stream - if (_decoders[1]) { - const Common::String &streamName = _decoders[1]->getVideoTrack().getName(); + if (_streamCount == 2) { + const Common::String &streamName = _decoder->getVideoTrack(1).getName(); if (streamName == "mask0") { _videoSurface->_transparencyMode = TRANS_MASK0; @@ -252,18 +212,18 @@ void AVISurface::setVideoSurface(CVideoSurface *surface) { } void AVISurface::setupDecompressor() { - for (int idx = 0; idx < 2; ++idx) { - if (!_decoders[idx]) - continue; - AVIDecoder &decoder = *_decoders[idx]; + if (!_decoder) + return; + for (int idx = 0; idx < _streamCount; ++idx) { // Setup frame surface - _movieFrameSurface[idx] = new Graphics::ManagedSurface(decoder.getWidth(), decoder.getHeight(), - decoder.getVideoTrack().getPixelFormat()); + _movieFrameSurface[idx] = new Graphics::ManagedSurface(_decoder->getWidth(), _decoder->getHeight(), + _decoder->getVideoTrack(idx).getPixelFormat()); bool flag = false; - if (idx == 0 && _videoSurface) { - const Graphics::PixelFormat &ff = decoder.getVideoTrack().getPixelFormat(); + if (idx == 0 && _videoSurface && + _videoSurface->getPitch() == _movieFrameSurface[idx]->pitch) { + const Graphics::PixelFormat &ff = _decoder->getVideoTrack(0).getPixelFormat(); const int vDepth = _videoSurface->getPixelDepth(); switch (ff.bpp()) { @@ -285,8 +245,8 @@ void AVISurface::setupDecompressor() { } if (!flag) { - _framePixels = new Graphics::ManagedSurface(decoder.getWidth(), decoder.getHeight(), - decoder.getVideoTrack().getPixelFormat()); + _framePixels = new Graphics::ManagedSurface(_decoder->getWidth(), _decoder->getHeight(), + _decoder->getVideoTrack(0).getPixelFormat()); } else if (idx == 0) { _videoSurface->_transBlitFlag = true; } @@ -294,11 +254,11 @@ void AVISurface::setupDecompressor() { } uint AVISurface::getWidth() const { - return _decoders[0]->getWidth(); + return _decoder->getWidth(); } uint AVISurface::getHeight() const { - return _decoders[0]->getHeight(); + return _decoder->getHeight(); } void AVISurface::setFrame(int frameNumber) { @@ -307,25 +267,26 @@ void AVISurface::setFrame(int frameNumber) { stop(); // Ensure the frame number is valid - if (frameNumber >= (int)_decoders[0]->getFrameCount()) - frameNumber = _decoders[0]->getFrameCount() - 1; + if (frameNumber >= (int)_decoder->getFrameCount()) + frameNumber = _decoder->getFrameCount() - 1; seekToFrame(frameNumber); renderFrame(); } bool AVISurface::isNextFrame() const { - return _decoders[0]->getTimeToNextFrame() == 0; + return _decoder->getTimeToNextFrame() == 0; } bool AVISurface::renderFrame() { // Check there's a frame ready for display - if (!_decoders[0]->needsUpdate()) + if (!_decoder->needsUpdate()) return false; // Make a copy of each decoder's video frame for (int idx = 0; idx < _streamCount; ++idx) { - const Graphics::Surface *frame = _decoders[idx]->decodeNextFrame(); + const Graphics::Surface *frame = (idx == 0) ? + _decoder->decodeNextFrame() : _decoder->decodeNextTransparency(); assert(_movieFrameSurface[idx]->format == frame->format); _movieFrameSurface[idx]->blitFrom(*frame); @@ -333,17 +294,16 @@ bool AVISurface::renderFrame() { if (!_framePixels) { if (_videoSurface->lock()) { - if (_streamCount == 1) { - // Original seems to call a stubbed empty method here. - // Likely this form of blitting to surface wasn't needed - } - + // Blit the frame directly to the video surface + assert(_streamCount == 1); + _videoSurface->blitFrom(Point(0, 0), &_movieFrameSurface[0]->rawSurface()); + _videoSurface->unlock(); } } else { // Blit the primary video track's frame to the video surface Graphics::Surface *s = _movieFrameSurface[0]->rawSurface().convertTo( - g_system->getScreenFormat(), _decoders[0]->getPalette()); + g_system->getScreenFormat(), _decoder->getPalette()); _videoSurface->lock(); _videoSurface->getRawSurface()->blitFrom(*s); _videoSurface->unlock(); @@ -375,9 +335,7 @@ bool AVISurface::addEvent(int frameNumber, CGameObject *obj) { } void AVISurface::setFrameRate(double rate) { - _decoders[0]->setRate(Common::Rational((int)rate)); - if (_decoders[1]) - _decoders[1]->setRate(Common::Rational((int)rate)); + _decoder->setRate(Common::Rational((int)rate)); } Graphics::ManagedSurface *AVISurface::getSecondarySurface() { @@ -403,7 +361,7 @@ void AVISurface::playCutscene(const Rect &r, uint startFrame, uint endFrame) { while (_currentFrame < (int)endFrame && !g_vm->shouldQuit()) { if (isNextFrame()) { renderFrame(); - _currentFrame = _decoders[0]->getCurFrame(); + _currentFrame = _decoder->getCurFrame(); if (isDifferent) { // Clear the destination area, and use the transBlitFrom method, diff --git a/engines/titanic/support/avi_surface.h b/engines/titanic/support/avi_surface.h index c0cf0af60c..0bb14ceb27 100644 --- a/engines/titanic/support/avi_surface.h +++ b/engines/titanic/support/avi_surface.h @@ -40,17 +40,25 @@ enum MovieFlag { class AVIDecoder : public Video::AVIDecoder { public: - AVIDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType, SelectTrackFn trackFn = nullptr) : - Video::AVIDecoder(soundType, trackFn) {} - AVIDecoder(const Common::Rational &frameRateOverride, Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType, - SelectTrackFn trackFn = nullptr) : Video::AVIDecoder(frameRateOverride, soundType, trackFn) {} + AVIDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType) : + Video::AVIDecoder(soundType) {} + AVIDecoder(const Common::Rational &frameRateOverride, Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType) : + Video::AVIDecoder(frameRateOverride, soundType) {} - Video::AVIDecoder::AVIVideoTrack &getVideoTrack(); + /** + * Returns the number of video tracks the decoder has + */ + uint videoTrackCount() const { return _videoTracks.size(); } + + /** + * Returns the specified video track + */ + Video::AVIDecoder::AVIVideoTrack &getVideoTrack(uint idx); }; class AVISurface { private: - AVIDecoder *_decoders[2]; + AVIDecoder *_decoder; CVideoSurface *_videoSurface; CMovieRangeInfoList _movieRangeInfo; int _streamCount; @@ -114,7 +122,7 @@ public: /** * Return true if a video is currently playing */ - virtual bool isPlaying() const { return _decoders[0]->isPlaying(); } + virtual bool isPlaying() const { return _decoder->isPlaying(); } /** * Handle any movie events relevent for the frame diff --git a/engines/titanic/support/mouse_cursor.cpp b/engines/titanic/support/mouse_cursor.cpp index 0cefc368fa..3983e9fe60 100644 --- a/engines/titanic/support/mouse_cursor.cpp +++ b/engines/titanic/support/mouse_cursor.cpp @@ -20,18 +20,15 @@ * */ -#include "common/memstream.h" -#include "common/textconsole.h" #include "graphics/cursorman.h" #include "titanic/support/mouse_cursor.h" -#include "titanic/support/movie.h" -#include "titanic/support/screen_manager.h" #include "titanic/support/video_surface.h" -#include "titanic/core/resource_key.h" #include "titanic/titanic.h" namespace Titanic { +#define CURSOR_SIZE 64 + static const int CURSOR_DATA[NUM_CURSORS][4] = { { 1, 136, 19, 18 }, { 2, 139, 1, 1 }, @@ -52,7 +49,7 @@ static const int CURSOR_DATA[NUM_CURSORS][4] = { CMouseCursor::CursorEntry::~CursorEntry() { delete _videoSurface; - delete _frameSurface; + delete _transSurface; } CMouseCursor::CMouseCursor(CScreenManager *screenManager) : @@ -75,16 +72,16 @@ void CMouseCursor::loadCursorImages() { CURSOR_DATA[idx][3]); // Create the surface - CVideoSurface *surface = _screenManager->createSurface(64, 64); + CVideoSurface *surface = _screenManager->createSurface(CURSOR_SIZE, CURSOR_SIZE); _cursors[idx]._videoSurface = surface; // Open the cursors video and move to the given frame OSMovie movie(key, surface); movie.setFrame(idx); - Graphics::ManagedSurface *frameSurface = movie.duplicateFrame(); - _cursors[idx]._frameSurface = frameSurface; - surface->setTransparencySurface(frameSurface); + Graphics::ManagedSurface *transSurface = movie.duplicateTransparency(); + _cursors[idx]._transSurface = transSurface; + surface->setTransparencySurface(transSurface); } } @@ -100,15 +97,31 @@ void CMouseCursor::setCursor(CursorId cursorId) { ++_setCursorCount; if (cursorId != _cursorId) { + // The original cursors supported partial alpha when rendering the cursor. + // Since we're using the ScummVM CursorMan, we can't do that, so we need + // to build up a surface of the cursor with even partially transparent + // pixels as wholy transparent CursorEntry &ce = _cursors[cursorId - 1]; - CVideoSurface &surface = *ce._videoSurface; - surface.lock(); + CVideoSurface &srcSurface = *ce._videoSurface; + srcSurface.lock(); + + Graphics::ManagedSurface surface(CURSOR_SIZE, CURSOR_SIZE, g_system->getScreenFormat()); + const uint16 *srcP = srcSurface.getPixels(); + const byte *maskP = (const byte *)ce._transSurface->getPixels(); + uint16 *destP = (uint16 *)surface.getPixels(); + + for (int y = 0; y < CURSOR_SIZE; ++y) { + for (int x = 0; x < CURSOR_SIZE; ++x, ++srcP, ++maskP, ++destP) { + *destP = ((*maskP >> 4) == 0) ? srcSurface.getTransparencyColor() : *srcP; + } + } - CursorMan.replaceCursor(surface.getPixels(), surface.getWidth(), surface.getHeight(), - ce._centroid.x, ce._centroid.y, 0, false, &g_vm->_screen->format); - surface.unlock(); + srcSurface.unlock(); + // Set the cursor _cursorId = cursorId; + CursorMan.replaceCursor(surface.getPixels(), CURSOR_SIZE, CURSOR_SIZE, + ce._centroid.x, ce._centroid.y, srcSurface.getTransparencyColor(), false, &g_vm->_screen->format); } } diff --git a/engines/titanic/support/mouse_cursor.h b/engines/titanic/support/mouse_cursor.h index 74fb1f6113..5bf6895a67 100644 --- a/engines/titanic/support/mouse_cursor.h +++ b/engines/titanic/support/mouse_cursor.h @@ -55,10 +55,10 @@ class CVideoSurface; class CMouseCursor { struct CursorEntry { CVideoSurface *_videoSurface; - Graphics::ManagedSurface *_frameSurface; + Graphics::ManagedSurface *_transSurface; Common::Point _centroid; - CursorEntry() : _videoSurface(nullptr), _frameSurface(nullptr) {} + CursorEntry() : _videoSurface(nullptr), _transSurface(nullptr) {} ~CursorEntry(); }; private: diff --git a/engines/titanic/support/movie.cpp b/engines/titanic/support/movie.cpp index a605cc3465..eff0e0c754 100644 --- a/engines/titanic/support/movie.cpp +++ b/engines/titanic/support/movie.cpp @@ -203,7 +203,7 @@ void OSMovie::setFrameRate(double rate) { _aviSurface.setFrameRate(rate); } -Graphics::ManagedSurface *OSMovie::duplicateFrame() const { +Graphics::ManagedSurface *OSMovie::duplicateTransparency() const { return _aviSurface.duplicateTransparency(); } diff --git a/engines/titanic/support/movie.h b/engines/titanic/support/movie.h index 8c89f9e6dd..be34244d96 100644 --- a/engines/titanic/support/movie.h +++ b/engines/titanic/support/movie.h @@ -130,9 +130,9 @@ public: virtual void setFrameRate(double rate) = 0; /** - * Creates a duplicate of the movie's frame - */ - virtual Graphics::ManagedSurface *duplicateFrame() const = 0; + * Creates a duplicate of the transparency surface + */ + virtual Graphics::ManagedSurface *duplicateTransparency() const = 0; /** * Removes the movie from the list of currently playing movies @@ -229,9 +229,9 @@ public: virtual void setFrameRate(double rate); /** - * Creates a duplicate of the frame info + * Creates a duplicate of the transparency surface */ - virtual Graphics::ManagedSurface *duplicateFrame() const; + virtual Graphics::ManagedSurface *duplicateTransparency() const; }; } // End of namespace Titanic diff --git a/engines/titanic/support/raw_surface.cpp b/engines/titanic/support/raw_surface.cpp deleted file mode 100644 index 4123b86e68..0000000000 --- a/engines/titanic/support/raw_surface.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* ScummVM - Graphic Adventure Engine - * - * ScummVM is the legal property of its developers, whose names - * are too numerous to list here. Please refer to the COPYRIGHT - * file distributed with this source distribution. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - */ - -#include "titanic/support/raw_surface.h" -#include "common/algorithm.h" - -namespace Titanic { - -CRawSurface::CRawSurface(const Graphics::Surface *surface, TransparencyMode transMode) { - _width = surface->w; - _pixelsBaseP = (byte *)surface->getPixels(); - _pixelsP = nullptr; - _pitch = 0; - _runLength = 0; - _flag = false; - _flag1 = false; - _flag2 = true; - - switch (transMode) { - case TRANS_MASK0: - case TRANS_ALPHA0: - _flag2 = false; - _flag1 = true; - break; - case TRANS_MASK255: - case TRANS_ALPHA255: - _flag2 = true; - _flag1 = false; - break; - case TRANS_DEFAULT: - if ((_pixelsBaseP[0] == 0 && _pixelsBaseP[2] < 0x80) || - (_pixelsBaseP[0] != 0 && _pixelsBaseP[1] < 0x80)) { - _flag1 = true; - _flag2 = false; - } - break; - default: - break; - } -} - -void CRawSurface::setRow(int yp) { - for (int y = 0; y < yp; ++yp) { - resetPitch(); - skipPitch(); - } -} - -void CRawSurface::setCol(int xp) { - while (xp > 0) - xp -= moveX(xp); -} - -void CRawSurface::skipPitch() { - setCol(_pitch); -} - -int CRawSurface::moveX(int xp) { - if (_runLength) { - if (!_flag) { - --_runLength; - --_pitch; - ++_pixelsP; - return 1; - } - } else { - while (!*_pixelsBaseP) { - _runLength = *++_pixelsBaseP; - ++_pixelsBaseP; - - if (_runLength) { - _pixelsP = _pixelsBaseP; - _pixelsBaseP += _runLength; - if (_runLength & 1) - ++_pixelsBaseP; - - _flag = false; - --_pitch; - --_runLength; - return 1; - } - } - - _runLength = *_pixelsBaseP; - _pixelsP = _pixelsBaseP + 1; - _pixelsBaseP += 2; - _flag = true; - } - - if (xp < 0 || xp > _pitch) - xp = _pitch; - - int len = MIN(_runLength, xp); - _pitch -= len; - _runLength -= len; - return len; -} - -uint CRawSurface::getPixel() const { - return _flag1 ? 0xFF - *_pixelsP : *_pixelsP; -} - -bool CRawSurface::isPixelTransparent1() const { - return _flag1 ? *_pixelsP == 0xF0 : *_pixelsP == 0x10; -} - -bool CRawSurface::isPixelTransparent2() const { - return _flag2 ? *_pixelsP == 0xF0 : *_pixelsP == 0x10; -} - -void CRawSurface::resetPitch() { - _pitch = _width; -} - -} // End of namespace Titanic diff --git a/engines/titanic/support/transparency_surface.cpp b/engines/titanic/support/transparency_surface.cpp new file mode 100644 index 0000000000..f917dfb48b --- /dev/null +++ b/engines/titanic/support/transparency_surface.cpp @@ -0,0 +1,83 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "titanic/support/transparency_surface.h" +#include "common/algorithm.h" +#include "common/textconsole.h" + +namespace Titanic { + +CTransparencySurface::CTransparencySurface(const Graphics::Surface *surface, + TransparencyMode transMode) : _surface(surface) { + _pitch = 0; + _runLength = 0; + _flag = false; + _flag1 = false; + _flag2 = true; + + switch (transMode) { + case TRANS_MASK0: + case TRANS_ALPHA0: + _flag2 = false; + _flag1 = true; + break; + case TRANS_MASK255: + case TRANS_ALPHA255: + _flag2 = true; + _flag1 = false; + break; + case TRANS_DEFAULT: + if (*(byte *)surface->getPixels() < 0x80) { + _flag1 = true; + _flag2 = false; + } + break; + default: + break; + } +} + +int CTransparencySurface::moveX() { + if (++_pos.x >= _surface->w) { + _pos.x = 0; + ++_pos.y; + } + + return 1; +} + +uint CTransparencySurface::getPixel() const { + const byte *pixelP = (const byte *)_surface->getBasePtr(_pos.x, _pos.y); + return _flag1 ? 0xFF - *pixelP : *pixelP; +} + +bool CTransparencySurface::isPixelTransparent1() const { + const byte *pixelP = (const byte *)_surface->getBasePtr(_pos.x, _pos.y); + return _flag1 ? *pixelP == 0xF0 : *pixelP == 0x10; +} + +bool CTransparencySurface::isPixelTransparent2() const { + const byte *pixelP = (const byte *)_surface->getBasePtr(_pos.x, _pos.y); + return _flag2 ? *pixelP == 0xF0 : *pixelP == 0x10; +} + +} // End of namespace Titanic diff --git a/engines/titanic/support/raw_surface.h b/engines/titanic/support/transparency_surface.h index 69efc1e3db..5593dfa66d 100644 --- a/engines/titanic/support/raw_surface.h +++ b/engines/titanic/support/transparency_surface.h @@ -20,9 +20,10 @@ * */ -#ifndef TITANIC_RAW_SURFACE_H -#define TITANIC_RAW_SURFACE_H +#ifndef TITANIC_TRANSPARENCY_SURFACE_H +#define TITANIC_TRANSPARENCY_SURFACE_H +#include "common/rect.h" #include "graphics/surface.h" namespace Titanic { @@ -32,24 +33,21 @@ enum TransparencyMode { TRANS_ALPHA255 = 3, TRANS_DEFAULT = 4 }; -class CRawSurface { +class CTransparencySurface { private: - const byte *_pixelsBaseP; - const byte *_pixelsP; + const Graphics::Surface *_surface; + Common::Point _pos; int _pitch; int _runLength; bool _flag; - int _width; bool _flag1; bool _flag2; public: - CRawSurface(const Graphics::Surface *surface, TransparencyMode transMode); + CTransparencySurface(const Graphics::Surface *surface, TransparencyMode transMode); - void setRow(int yp); + void setRow(int yp) { _pos.y = yp; } - void setCol(int xp); - - void skipPitch(); + void setCol(int xp) { _pos.x = xp; } uint getPixel() const; @@ -57,11 +55,9 @@ public: bool isPixelTransparent2() const; - void resetPitch(); - - int moveX(int xp); + int moveX(); }; } // End of namespace Titanic -#endif /* TITANIC_RAW_SURFACE_H */ +#endif /* TITANIC_TRANSPARENCY_SURFACE_H */ diff --git a/engines/titanic/support/video_surface.cpp b/engines/titanic/support/video_surface.cpp index fa87e6df0c..bcaaad4492 100644 --- a/engines/titanic/support/video_surface.cpp +++ b/engines/titanic/support/video_surface.cpp @@ -22,8 +22,8 @@ #include "titanic/support/video_surface.h" #include "titanic/support/image_decoders.h" -#include "titanic/support/raw_surface.h" #include "titanic/support/screen_manager.h" +#include "titanic/support/transparency_surface.h" #include "titanic/titanic.h" namespace Titanic { @@ -184,65 +184,46 @@ void CVideoSurface::blitRect2(const Rect &srcRect, const Rect &destRect, CVideoS } void CVideoSurface::transBlitRect(const Rect &srcRect, const Rect &destRect, CVideoSurface *src, bool flipFlag) { + assert(srcRect.width() == destRect.width() && srcRect.height() == destRect.height()); + if (lock()) { if (src->lock()) { Graphics::ManagedSurface *srcSurface = src->_rawSurface; Graphics::ManagedSurface *destSurface = _rawSurface; Graphics::Surface destArea = destSurface->getSubArea(destRect); - const uint16 *srcPtr = flipFlag ? - (const uint16 *)srcSurface->getBasePtr(srcRect.left, srcRect.top) : - (const uint16 *)srcSurface->getBasePtr(srcRect.left, srcRect.bottom); - uint16 *destPtr = (uint16 *)destSurface->getBasePtr(destArea.w, destArea.h - 1); + const uint16 *srcPtr = (const uint16 *)srcSurface->getBasePtr( + srcRect.left, flipFlag ? srcRect.top : srcRect.bottom - 1); + uint16 *destPtr = (uint16 *)destSurface->getBasePtr(0, destArea.h - 1); bool is16Bit = src->getPixelDepth() == 2; bool isAlpha = src->_transparencyMode == TRANS_ALPHA0 || src->_transparencyMode == TRANS_ALPHA255; - CRawSurface rawSurface(src->getTransparencySurface(), src->_transparencyMode); - if (flipFlag) - rawSurface.setRow(srcRect.top); - else - rawSurface.setRow(src->getHeight() - srcRect.bottom); + CTransparencySurface transSurface(src->getTransparencySurface(), src->_transparencyMode); - for (int srcY = srcRect.top; srcY < srcRect.bottom; ++srcY) { + for (int yCtr = 0; yCtr < srcRect.height(); ++yCtr) { // Prepare for copying the line const uint16 *lineSrcP = srcPtr; uint16 *lineDestP = destPtr; - rawSurface.resetPitch(); - rawSurface.setCol(srcRect.left); - - int srcWidth = srcRect.width(); - while (srcWidth > 0) { - int move = rawSurface.moveX(0); - - if (move <= 1) { - if (!rawSurface.isPixelTransparent2()) { - copyPixel(lineDestP, lineSrcP, rawSurface.getPixel() >> 3, - is16Bit, isAlpha); - } - } else { - if (move > srcWidth) - move = srcWidth; - - if (rawSurface.isPixelTransparent1()) { - Common::copy(lineSrcP, lineSrcP + move, lineDestP); - } else if (!rawSurface.isPixelTransparent2()) { - byte transVal = rawSurface.getPixel() >> 3; - for (int idx = 0; idx < move; ++idx) { - copyPixel(lineDestP + idx, lineSrcP + idx, transVal, is16Bit, isAlpha); - } - } + transSurface.setRow(flipFlag ? srcRect.top + yCtr : srcRect.bottom - yCtr - 1); + transSurface.setCol(srcRect.left); + + for (int srcX = srcRect.left; srcX < srcRect.right; ++srcX) { + transSurface.moveX(); + + if (!transSurface.isPixelTransparent2()) { + copyPixel(lineDestP, lineSrcP, transSurface.getPixel() >> 3, + is16Bit, isAlpha); } - lineSrcP += move; - lineDestP += move; - srcWidth -= move; + ++lineSrcP; + ++lineDestP; } // Move to next line - rawSurface.skipPitch(); - srcPtr = flipFlag ? srcPtr + getWidth() : srcPtr - getWidth(); - destPtr -= destArea.w; + srcPtr = flipFlag ? srcPtr + (src->getPitch() / 2) : + srcPtr - (src->getPitch() / 2); + destPtr -= destArea.pitch / 2; } src->unlock(); @@ -487,13 +468,11 @@ uint16 OSVideoSurface::getPixel(const Common::Point &pt) { if (pt.x >= 0 && pt.y >= 0 && pt.x < getWidth() && pt.y < getHeight()) { if (_transparencySurface) { - CRawSurface rawSurface(&_transparencySurface->rawSurface(), _transparencyMode); - rawSurface.setRow(_transBlitFlag ? pt.y : getHeight() - pt.y - 1); - rawSurface.resetPitch(); - rawSurface.setCol(pt.x); - rawSurface.moveX(0); + CTransparencySurface transSurface(&_transparencySurface->rawSurface(), _transparencyMode); + transSurface.setRow(_transBlitFlag ? pt.y : getHeight() - pt.y - 1); + transSurface.setCol(pt.x); - if (rawSurface.isPixelTransparent2()) + if (transSurface.isPixelTransparent2()) return getTransparencyColor(); } @@ -637,8 +616,8 @@ void OSVideoSurface::transPixelate() { unlock(); } -Graphics::ManagedSurface *OSVideoSurface::dupMovieFrame() const { - return _movie ? _movie->duplicateFrame() : nullptr; +Graphics::ManagedSurface *OSVideoSurface::dupMovieTransparency() const { + return _movie ? _movie->duplicateTransparency() : nullptr; } int OSVideoSurface::freeSurface() { diff --git a/engines/titanic/support/video_surface.h b/engines/titanic/support/video_surface.h index 37ff7917dc..3625a3555c 100644 --- a/engines/titanic/support/video_surface.h +++ b/engines/titanic/support/video_surface.h @@ -30,8 +30,8 @@ #include "titanic/support/direct_draw.h" #include "titanic/support/movie.h" #include "titanic/support/movie_range_info.h" -#include "titanic/support/raw_surface.h" #include "titanic/support/rect.h" +#include "titanic/support/transparency_surface.h" #include "titanic/core/list.h" #include "titanic/core/resource_key.h" @@ -277,9 +277,9 @@ public: virtual bool hasFrame(); /** - * Duplicates movie frame surface + * Duplicates movie transparency surface */ - virtual Graphics::ManagedSurface *dupMovieFrame() const = 0; + virtual Graphics::ManagedSurface *dupMovieTransparency() const = 0; /** * Frees the underlying surface @@ -509,10 +509,9 @@ public: virtual void transPixelate(); /** - * Duplicates movie frame surface + * Duplicates movie transparency surface */ - virtual Graphics::ManagedSurface *dupMovieFrame() const; - + virtual Graphics::ManagedSurface *dupMovieTransparency() const; /** * Frees the underlying surface diff --git a/video/avi_decoder.cpp b/video/avi_decoder.cpp index 980ce3a3ea..c4b091d240 100644 --- a/video/avi_decoder.cpp +++ b/video/avi_decoder.cpp @@ -76,13 +76,13 @@ enum { }; -AVIDecoder::AVIDecoder(Audio::Mixer::SoundType soundType, SelectTrackFn trackFn) : - _frameRateOverride(0), _soundType(soundType), _selectTrackFn(trackFn) { +AVIDecoder::AVIDecoder(Audio::Mixer::SoundType soundType) : + _frameRateOverride(0), _soundType(soundType) { initCommon(); } -AVIDecoder::AVIDecoder(const Common::Rational &frameRateOverride, Audio::Mixer::SoundType soundType, - SelectTrackFn trackFn) : _frameRateOverride(frameRateOverride), _soundType(soundType), _selectTrackFn(trackFn) { +AVIDecoder::AVIDecoder(const Common::Rational &frameRateOverride, Audio::Mixer::SoundType soundType) : + _frameRateOverride(frameRateOverride), _soundType(soundType) { initCommon(); } @@ -94,6 +94,23 @@ AVIDecoder::AVIAudioTrack *AVIDecoder::createAudioTrack(AVIStreamHeader sHeader, return new AVIAudioTrack(sHeader, wvInfo, _soundType); } +bool AVIDecoder::seekToFrame(uint frame) { + if (!isSeekable()) + return false; + + // If we didn't find a video track, we can't seek by frame (of course) + if (_videoTracks.empty()) + return false; + + AVIVideoTrack *track = static_cast<AVIVideoTrack *>(_videoTracks.front().track); + Audio::Timestamp time = track->getFrameTime(frame); + + if (time < 0) + return false; + + return seek(time); +} + void AVIDecoder::initCommon() { _decodedHeader = false; _foundMovieList = false; @@ -111,6 +128,14 @@ bool AVIDecoder::isSeekable() const { return isVideoLoaded() && !_indexEntries.empty(); } +const Graphics::Surface *AVIDecoder::decodeNextTransparency() { + if (_videoTracks.size() != 2) + return nullptr; + + AVIVideoTrack *track = static_cast<AVIVideoTrack *>(_videoTracks[1].track); + return track->decodeNextFrame(); +} + bool AVIDecoder::parseNextChunk() { uint32 tag = _fileStream->readUint32BE(); uint32 size = _fileStream->readUint32LE(); @@ -293,14 +318,8 @@ void AVIDecoder::handleStreamHeader(uint32 size) { } void AVIDecoder::addTrack(Track *track, bool isExternal) { - if (!_selectTrackFn || - (dynamic_cast<AVIVideoTrack *>(track) && _selectTrackFn(true, _videoTrackCounter++)) || - (dynamic_cast<AVIAudioTrack *>(track) && _selectTrackFn(false, _audioTrackCounter++))) { - VideoDecoder::addTrack(track, isExternal); - _lastAddedTrack = track; - } else { - _lastAddedTrack = nullptr; - } + VideoDecoder::addTrack(track, isExternal); + _lastAddedTrack = track; } void AVIDecoder::readStreamName(uint32 size) { @@ -366,17 +385,30 @@ bool AVIDecoder::loadStream(Common::SeekableReadStream *stream) { TrackStatus status; status.track = *it; status.index = index; - status.chunkSearchOffset = _movieListStart; + status.chunkSearchOffset = 0; - if ((*it)->getTrackType() == Track::kTrackTypeVideo) - _videoTracks.push_back(status); - else + if ((*it)->getTrackType() == Track::kTrackTypeAudio) { _audioTracks.push_back(status); - } + } else if (_videoTracks.empty()) { + _videoTracks.push_back(status); + } else { + // Secondary video track. Figure out the starting chunk offset, + // by iteratiing through the index + assert(_videoTracks.size() == 1); + + // Find the index entry for the frame and move to it + for (uint idx = 0; idx < _indexEntries.size(); ++idx) { + if (_indexEntries[idx].id != ID_REC && + getStreamIndex(_indexEntries[idx].id) == index) { + status.chunkSearchOffset = _indexEntries[idx].offset; + break; + } + } + assert(status.chunkSearchOffset != 0); - if (_videoTracks.size() != 1) { - close(); - return false; + // Add the video track to the list + _videoTracks.push_back(status); + } } // Check if this is a special Duck Truemotion video @@ -407,12 +439,13 @@ void AVIDecoder::readNextPacket() { if (_videoTracks.empty()) return; - // Get the video frame first - handleNextPacket(_videoTracks[0]); + // Handle the video first + for (uint idx = 0; idx < _videoTracks.size(); ++idx) + handleNextPacket(_videoTracks[idx]); // Handle audio tracks next - for (uint32 i = 0; i < _audioTracks.size(); i++) - handleNextPacket(_audioTracks[i]); + for (uint idx = 0; idx < _audioTracks.size(); ++idx) + handleNextPacket(_audioTracks[idx]); } void AVIDecoder::handleNextPacket(TrackStatus &status) { @@ -666,6 +699,32 @@ bool AVIDecoder::seekIntern(const Audio::Timestamp &time) { videoTrack->decodeFrame(chunk); } + // Update any secondary video track for transparencies + if (_videoTracks.size() == 2) { + AVIVideoTrack *videoTrack2 = static_cast<AVIVideoTrack *>(_videoTracks.back().track); + + // Set it's frame number + videoTrack2->setCurFrame((int)frame - 1); + + // Find the index entry for the frame and move to it + for (uint i = 0, frameNum = 0; i < _indexEntries.size(); ++i) { + if (_indexEntries[i].id != ID_REC && + getStreamIndex(_indexEntries[i].id) == _videoTracks.back().index) { + if (frameNum++ == frame) { + Common::SeekableReadStream *chunk = nullptr; + _fileStream->seek(_indexEntries[i].offset + 8); + _videoTracks.back().chunkSearchOffset = _indexEntries[i].offset; + + if (_indexEntries[i].size != 0) + chunk = _fileStream->readStream(_indexEntries[i].size); + + videoTrack2->decodeFrame(chunk); + break; + } + } + } + } + // Set the video track's frame videoTrack->setCurFrame((int)frame - 1); diff --git a/video/avi_decoder.h b/video/avi_decoder.h index a3733b579c..3581b65409 100644 --- a/video/avi_decoder.h +++ b/video/avi_decoder.h @@ -62,10 +62,8 @@ namespace Video { */ class AVIDecoder : public VideoDecoder { public: - typedef bool(*SelectTrackFn)(bool isVideo, int trackNumber); - AVIDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType, SelectTrackFn trackFn = nullptr); - AVIDecoder(const Common::Rational &frameRateOverride, Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType, - SelectTrackFn trackFn = nullptr); + AVIDecoder(Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); + AVIDecoder(const Common::Rational &frameRateOverride, Audio::Mixer::SoundType soundType = Audio::Mixer::kPlainSoundType); virtual ~AVIDecoder(); bool loadStream(Common::SeekableReadStream *stream); @@ -77,6 +75,7 @@ public: bool isRewindable() const { return true; } bool isSeekable() const; + const Graphics::Surface *decodeNextTransparency(); protected: // VideoDecoder API void readNextPacket(); @@ -287,7 +286,6 @@ protected: int _videoTrackCounter, _audioTrackCounter; Track *_lastAddedTrack; - SelectTrackFn _selectTrackFn; void initCommon(); @@ -306,6 +304,14 @@ protected: public: virtual AVIAudioTrack *createAudioTrack(AVIStreamHeader sHeader, PCMWaveFormat wvInfo); + + /** + * Seek to a given frame. + * + * This only works when the video track(s) supports getFrameTime(). + * This calls seek() internally. + */ + virtual bool seekToFrame(uint frame); }; } // End of namespace Video diff --git a/video/video_decoder.h b/video/video_decoder.h index eca15e7265..a415a70724 100644 --- a/video/video_decoder.h +++ b/video/video_decoder.h @@ -184,7 +184,7 @@ public: * This only works when one video track is present, and that track * supports getFrameTime(). This calls seek() internally. */ - bool seekToFrame(uint frame); + virtual bool seekToFrame(uint frame); /** * Pause or resume the video. This should stop/resume any audio playback |