diff options
author | Colin Snover | 2017-01-02 23:30:35 -0600 |
---|---|---|
committer | Colin Snover | 2017-03-30 19:46:27 -0500 |
commit | 766d46153a285794d573d84d237aac3821431a01 (patch) | |
tree | 5d85232e856175fc20c4990ad48bf06a9db3d490 /engines/sci/graphics | |
parent | 65fe7bcfd8431888d9fdf345759bad1a78c455b7 (diff) | |
download | scummvm-rg350-766d46153a285794d573d84d237aac3821431a01.tar.gz scummvm-rg350-766d46153a285794d573d84d237aac3821431a01.tar.bz2 scummvm-rg350-766d46153a285794d573d84d237aac3821431a01.zip |
SCI32: Implement known-used portions of kPlayDuck
Diffstat (limited to 'engines/sci/graphics')
-rw-r--r-- | engines/sci/graphics/video32.cpp | 258 | ||||
-rw-r--r-- | engines/sci/graphics/video32.h | 105 |
2 files changed, 351 insertions, 12 deletions
diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp index f4eb2c2da0..8cd6fc7089 100644 --- a/engines/sci/graphics/video32.cpp +++ b/engines/sci/graphics/video32.cpp @@ -51,6 +51,25 @@ namespace Graphics { struct Surface; } namespace Sci { +/** + * @returns true if the player should quit + */ +static bool flushEvents(EventManager *eventMan) { + // Flushing all the keyboard and mouse events out of the event manager + // keeps events queued from before the start of playback from accidentally + // activating a video stop flag + for (;;) { + const SciEvent event = eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_MOUSE_PRESS | SCI_EVENT_MOUSE_RELEASE | SCI_EVENT_HOT_RECTANGLE | SCI_EVENT_QUIT); + if (event.type == SCI_EVENT_NONE) { + break; + } else if (event.type == SCI_EVENT_QUIT) { + return true; + } + } + + return false; +} + #pragma mark SEQPlayer SEQPlayer::SEQPlayer(SegManager *segMan) : @@ -672,16 +691,8 @@ VMDPlayer::EventFlags VMDPlayer::kernelPlayUntilEvent(const EventFlags flags, co } VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) { - // Flushing all the keyboard and mouse events out of the event manager - // keeps events queued from before the start of playback from accidentally - // activating a video stop flag - for (;;) { - const SciEvent event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_MOUSE_PRESS | SCI_EVENT_MOUSE_RELEASE | SCI_EVENT_HOT_RECTANGLE | SCI_EVENT_QUIT); - if (event.type == SCI_EVENT_NONE) { - break; - } else if (event.type == SCI_EVENT_QUIT) { - return kEventFlagEnd; - } + if (flushEvents(_eventMan)) { + return kEventFlagEnd; } if (flags & kEventFlagReverse) { @@ -914,4 +925,231 @@ void VMDPlayer::restrictPalette(const uint8 startColor, const int16 endColor) { _endColor = MIN<int16>(255, endColor); } +#pragma mark - +#pragma mark DuckPlayer + +DuckPlayer::DuckPlayer(SegManager *segMan, EventManager *eventMan) : + _segMan(segMan), + _eventMan(eventMan), + _decoder(new Video::AVIDecoder(Audio::Mixer::kSFXSoundType)), + _plane(nullptr), + _status(kDuckClosed), + _drawRect(), + _volume(Audio::Mixer::kMaxChannelVolume), + _doFrameOut(false), + _pixelDouble(false), + _scaleBuffer(nullptr) {} + +DuckPlayer::~DuckPlayer() { + close(); + delete _decoder; +} + +void DuckPlayer::open(const GuiResourceId resourceId, const int displayMode, const int16 x, const int16 y) { + if (_status != kDuckClosed) { + error("Attempted to play %u.duk, but another video was loaded", resourceId); + } + + const Common::String fileName = Common::String::format("%u.duk", resourceId); + if (!_decoder->loadFile(fileName)) { + error("Can't open %s", fileName.c_str()); + } + + _decoder->setVolume(_volume); + _pixelDouble = displayMode != 0; + + const int16 scale = _pixelDouble ? 2 : 1; + _drawRect = Common::Rect(x, y, + (x + _decoder->getWidth()) * scale, + (y + _decoder->getHeight()) * scale); + + g_sci->_gfxCursor32->hide(); + + if (_doFrameOut) { + _plane = new Plane(_drawRect, kPlanePicColored); + g_sci->_gfxFrameout->addPlane(*_plane); + g_sci->_gfxFrameout->frameOut(true); + } + + const Buffer ¤tBuffer = g_sci->_gfxFrameout->getCurrentBuffer(); + const Graphics::PixelFormat format = _decoder->getPixelFormat(); + + if (_pixelDouble) { + assert(_scaleBuffer == nullptr); + _scaleBuffer = new byte[_drawRect.width() * _drawRect.height() * format.bytesPerPixel]; + } + + initGraphics(currentBuffer.screenWidth, currentBuffer.screenHeight, true, &format); + + _status = kDuckOpen; +} + +void DuckPlayer::play(const int lastFrameNo) { + flushEvents(_eventMan); + + if (_status != kDuckPlaying) { + _status = kDuckPlaying; + _decoder->start(); + } + + while (!g_engine->shouldQuit()) { + if (_decoder->endOfVideo() || (lastFrameNo != -1 && _decoder->getCurFrame() >= lastFrameNo)) { + break; + } + + g_sci->sleep(_decoder->getTimeToNextFrame()); + while (_decoder->needsUpdate()) { + renderFrame(); + } + + SciEvent event = _eventMan->getSciEvent(SCI_EVENT_MOUSE_PRESS | SCI_EVENT_PEEK); + if (event.type == SCI_EVENT_MOUSE_PRESS) { + flushEvents(_eventMan); + break; + } + + event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK); + if (event.type == SCI_EVENT_KEYBOARD) { + bool stop = false; + while ((event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD)), + event.type != SCI_EVENT_NONE) { + if (event.character == SCI_KEY_ESC) { + stop = true; + break; + } + } + + if (stop) { + flushEvents(_eventMan); + break; + } + } + } +} + +void DuckPlayer::close() { + if (_status == kDuckClosed) { + return; + } + + _decoder->close(); + + const Buffer ¤tBuffer = g_sci->_gfxFrameout->getCurrentBuffer(); + const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8(); + initGraphics(currentBuffer.screenWidth, currentBuffer.screenHeight, true, &format); + + g_sci->_gfxCursor32->unhide(); + + if (_doFrameOut) { + g_sci->_gfxFrameout->deletePlane(*_plane); + g_sci->_gfxFrameout->frameOut(true); + _plane = nullptr; + } + + _pixelDouble = false; + delete[] _scaleBuffer; + _scaleBuffer = nullptr; + + _status = kDuckClosed; +} + +static inline uint16 interpolate(const Graphics::PixelFormat &format, const uint16 p1, const uint16 p2) { + uint8 r1, g1, b1, r2, g2, b2; + format.colorToRGB(p1, r1, g1, b1); + format.colorToRGB(p2, r2, g2, b2); + return format.RGBToColor((r1 + r2) >> 1, (g1 + g2) >> 1, (b1 + b2) >> 1); +} + +void DuckPlayer::renderFrame() const { + const Graphics::Surface *surface = _decoder->decodeNextFrame(); + + // Audio-only or non-updated frame + if (surface == nullptr) { + return; + } + + assert(surface->format.bytesPerPixel == 2); + + if (_pixelDouble) { + const uint16 *source = (const uint16 *)surface->getPixels(); + const Graphics::PixelFormat &format = surface->format; + uint16 *target = (uint16 *)_scaleBuffer; + +#ifndef SCI_DUCK_NO_INTERPOLATION + // divide by 2 gets pixel pitch instead of byte pitch for source + const uint16 sourcePitch = surface->pitch >> 1; +#endif + + const uint16 targetPitch = surface->pitch; + const bool blackLined = ConfMan.getBool("enable_black_lined_video"); + for (int y = 0; y < surface->h - 1; ++y) { + for (int x = 0; x < surface->w - 1; ++x) { +#ifndef SCI_DUCK_NO_INTERPOLATION + const uint16 a = source[0]; + const uint16 b = source[1]; + const uint16 c = source[sourcePitch]; + const uint16 d = source[sourcePitch + 1]; + + target[0] = a; + target[1] = interpolate(format, a, b); +#else + const uint16 value = *source; + target[0] = value; + target[1] = value; +#endif + if (!blackLined) { +#ifndef SCI_DUCK_NO_INTERPOLATION + target[targetPitch] = interpolate(format, a, c); + target[targetPitch + 1] = interpolate(format, target[1], interpolate(format, c, d)); +#else + target[targetPitch] = value; + target[targetPitch + 1] = value; +#endif + } + + target += 2; + ++source; + } + + const uint16 value = *source++; + target[0] = value; + target[1] = value; + if (!blackLined) { + target[targetPitch] = value; + target[targetPitch + 1] = value; + } + target += 2; + + if (blackLined) { + memset(target, 0, targetPitch * format.bytesPerPixel); + } + + target += targetPitch; + } + + for (int x = 0; x < surface->w; ++x) { + const uint16 lastValue = *source++; + target[0] = lastValue; + target[1] = lastValue; + + if (!blackLined) { + target[targetPitch] = lastValue; + target[targetPitch + 1] = lastValue; + target += 2; + } + } + + if (blackLined) { + memset(target, 0, targetPitch); + } + + g_system->copyRectToScreen(_scaleBuffer, surface->pitch * 2, _drawRect.left, _drawRect.top, _drawRect.width(), _drawRect.height()); + } else { + g_system->copyRectToScreen(surface->getPixels(), surface->pitch, _drawRect.left, _drawRect.top, surface->w, surface->h); + } + + g_system->updateScreen(); + g_sci->getSciDebugger()->onFrame(); +} + } // End of namespace Sci diff --git a/engines/sci/graphics/video32.h b/engines/sci/graphics/video32.h index 5ed8fd954a..fae5cafbbe 100644 --- a/engines/sci/graphics/video32.h +++ b/engines/sci/graphics/video32.h @@ -28,10 +28,11 @@ #include "common/str.h" // for String #include "sci/engine/vm_types.h" // for reg_t #include "sci/video/robot_decoder.h" // for RobotDecoder +#include "sci/sound/audio32.h" // for Audio32::kMaxVolume +#include "video/avi_decoder.h" // for AVIDecoder::setVolume namespace Video { class AdvancedVMDDecoder; -class AVIDecoder; } namespace Sci { class EventManager; @@ -531,6 +532,103 @@ private: bool _showCursor; }; +#pragma mark - +#pragma mark DuckPlayer + +class DuckPlayer { +public: + enum DuckStatus { + kDuckClosed = 0, + kDuckOpen = 1, + kDuckPlaying = 2, + kDuckPaused = 3 + }; + + DuckPlayer(SegManager *segMan, EventManager *eventMan); + + ~DuckPlayer(); + + /** + * Opens a stream to a Duck resource. + */ + void open(const GuiResourceId resourceId, const int displayMode, const int16 x, const int16 y); + + /** + * Stops playback and closes the currently open Duck stream. + */ + void close(); + + /** + * Begins playback of the current Duck video. + */ + void play(const int lastFrameNo); + + /** + * Sets a flag indicating that an opaque plane should be added + * to the graphics manager underneath the video surface during + * playback. + */ + void setDoFrameOut(const bool value) { _doFrameOut = value; } + + /** + * Sets the volume of the decoder. + */ + void setVolume(const uint8 value) { + _volume = (uint)value * Audio::Mixer::kMaxChannelVolume / Audio32::kMaxVolume; + _decoder->setVolume(_volume); + } + +private: + SegManager *_segMan; + EventManager *_eventMan; + Video::AVIDecoder *_decoder; + + /** + * An empty plane drawn behind the video when the doFrameOut + * flag is true. + */ + Plane *_plane; + + /** + * The player status. + */ + DuckStatus _status; + + /** + * The screen rect where the video should be drawn. + */ + Common::Rect _drawRect; + + /** + * The playback volume for the player. + */ + uint8 _volume; + + /** + * If true, frameOut will be called during Duck video playback to update + * other parts of the screen. + */ + bool _doFrameOut; + + /** + * If true, the video will be pixel doubled during playback. + */ + bool _pixelDouble; + + /** + * The buffer used to perform scaling of the video. + */ + byte *_scaleBuffer; + + /** + * Renders the current frame to the system video buffer. + */ + void renderFrame() const; +}; + +#pragma mark - +#pragma mark Video32 + /** * Video32 provides facilities for playing back * video in SCI engine. @@ -541,18 +639,21 @@ public: _SEQPlayer(segMan), _AVIPlayer(segMan, eventMan), _VMDPlayer(segMan, eventMan), - _robotPlayer(segMan) {} + _robotPlayer(segMan), + _duckPlayer(segMan, eventMan) {} SEQPlayer &getSEQPlayer() { return _SEQPlayer; } AVIPlayer &getAVIPlayer() { return _AVIPlayer; } VMDPlayer &getVMDPlayer() { return _VMDPlayer; } RobotDecoder &getRobotPlayer() { return _robotPlayer; } + DuckPlayer &getDuckPlayer() { return _duckPlayer; } private: SEQPlayer _SEQPlayer; AVIPlayer _AVIPlayer; VMDPlayer _VMDPlayer; RobotDecoder _robotPlayer; + DuckPlayer _duckPlayer; }; } // End of namespace Sci |