aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics
diff options
context:
space:
mode:
authorColin Snover2017-01-02 23:30:35 -0600
committerColin Snover2017-03-30 19:46:27 -0500
commit766d46153a285794d573d84d237aac3821431a01 (patch)
tree5d85232e856175fc20c4990ad48bf06a9db3d490 /engines/sci/graphics
parent65fe7bcfd8431888d9fdf345759bad1a78c455b7 (diff)
downloadscummvm-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.cpp258
-rw-r--r--engines/sci/graphics/video32.h105
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 &currentBuffer = 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 &currentBuffer = 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