aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorColin Snover2017-07-05 00:30:30 -0500
committerColin Snover2017-07-06 19:12:39 -0500
commitf15f9e3b7c9dd7594a60aa230fed05b965a7a587 (patch)
treec23123d033aff719e74828fab1c2d54071efb864 /engines
parent71256a0d3c2136d21d943513766ec2acd623f6c1 (diff)
downloadscummvm-rg350-f15f9e3b7c9dd7594a60aa230fed05b965a7a587.tar.gz
scummvm-rg350-f15f9e3b7c9dd7594a60aa230fed05b965a7a587.tar.bz2
scummvm-rg350-f15f9e3b7c9dd7594a60aa230fed05b965a7a587.zip
SCI32: Refactor Video32 code to reduce code & feature duplication
Diffstat (limited to 'engines')
-rw-r--r--engines/sci/detection_tables.h20
-rw-r--r--engines/sci/graphics/video32.cpp657
-rw-r--r--engines/sci/graphics/video32.h429
-rw-r--r--engines/sci/sci.cpp5
4 files changed, 532 insertions, 579 deletions
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index 4230a3bb0f..506187659a 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -724,10 +724,13 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#ifdef ENABLE_SCI32
#define GUIO_GK1_FLOPPY GUIO2(GUIO_NOSPEECH, \
GAMEOPTION_ORIGINAL_SAVELOAD)
-#define GUIO_GK1_CD GUIO4(GUIO_LINKSPEECHTOSFX, \
+#define GUIO_GK1_CD_DOS GUIO4(GUIO_LINKSPEECHTOSFX, \
GAMEOPTION_ORIGINAL_SAVELOAD, \
GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, \
GAMEOPTION_HQ_VIDEO)
+#define GUIO_GK1_CD_WIN GUIO3(GUIO_LINKSPEECHTOSFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_HQ_VIDEO)
#define GUIO_GK1_MAC GUIO_GK1_FLOPPY
// Gabriel Knight - English DOS Floppy
@@ -776,7 +779,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "372d059f75856afa6d73dd84cbb8913d", 10996},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 12581736},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformDOS, ADGF_CD | ADGF_TESTING, GUIO_GK1_CD },
+ Common::EN_ANY, Common::kPlatformDOS, ADGF_CD | ADGF_TESTING, GUIO_GK1_CD_DOS },
// Gabriel Knight - English Windows CD (from jvprat)
// Executable scanning reports "2.000.000", VERSION file reports "01.100.000"
@@ -784,7 +787,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "372d059f75856afa6d73dd84cbb8913d", 10996},
{"resource.000", 0, "69b7516962510f780d38519cc15fcc7c", 12581736},
AD_LISTEND},
- Common::EN_ANY, Common::kPlatformWindows, ADGF_CD | ADGF_TESTING, GUIO_GK1_CD },
+ Common::EN_ANY, Common::kPlatformWindows, ADGF_CD | ADGF_TESTING, GUIO_GK1_CD_WIN },
// Gabriel Knight - German DOS CD (from Tobis87)
// SCI interpreter version 2.000.000
@@ -792,7 +795,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "a7d3e55114c65647310373cb390815ba", 11392},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13400497},
AD_LISTEND},
- Common::DE_DEU, Common::kPlatformDOS, ADGF_CD | ADGF_TESTING, GUIO_GK1_CD },
+ Common::DE_DEU, Common::kPlatformDOS, ADGF_CD | ADGF_TESTING, GUIO_GK1_CD_DOS },
// Gabriel Knight - Spanish DOS CD (from jvprat)
// Executable scanning reports "2.000.000", VERSION file reports "1.000.000, April 13, 1995"
@@ -800,7 +803,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "7cb6e9bba15b544ec7a635c45bde9953", 11404},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13381599},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformDOS, ADGF_CD | ADGF_TESTING, GUIO_GK1_CD },
+ Common::ES_ESP, Common::kPlatformDOS, ADGF_CD | ADGF_TESTING, GUIO_GK1_CD_DOS },
// Gabriel Knight - French DOS CD (from Hkz)
// VERSION file reports "1.000.000, May 3, 1994"
@@ -808,7 +811,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "55f909ba93a2515042a08d8a2da8414e", 11392},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13325145},
AD_LISTEND},
- Common::FR_FRA, Common::kPlatformDOS, ADGF_CD | ADGF_TESTING, GUIO_GK1_CD },
+ Common::FR_FRA, Common::kPlatformDOS, ADGF_CD | ADGF_TESTING, GUIO_GK1_CD_DOS },
// Gabriel Knight - Spanish Windows CD (from jvprat)
// Executable scanning reports "2.000.000", VERSION file reports "1.000.000, April 13, 1995"
@@ -816,7 +819,7 @@ static const struct ADGameDescription SciGameDescriptions[] = {
{"resource.map", 0, "7cb6e9bba15b544ec7a635c45bde9953", 11404},
{"resource.000", 0, "091cf08910780feabc56f8551b09cb36", 13381599},
AD_LISTEND},
- Common::ES_ESP, Common::kPlatformWindows, ADGF_CD | ADGF_TESTING, GUIO_GK1_CD },
+ Common::ES_ESP, Common::kPlatformWindows, ADGF_CD | ADGF_TESTING, GUIO_GK1_CD_WIN },
// Gabriel Knight - English Macintosh (Floppy!)
// This version is hi-res ONLY, so it should NOT get GAMEOPTION_HIGH_RESOLUTION_GRAPHICS
@@ -831,7 +834,8 @@ static const struct ADGameDescription SciGameDescriptions[] = {
Common::EN_ANY, Common::kPlatformMacintosh, ADGF_MACRESFORK | ADGF_UNSTABLE, GUIO_GK1_MAC },
#undef GUIO_GK1_FLOPPY
-#undef GUIO_GK1_CD
+#undef GUIO_GK1_CD_DOS
+#undef GUIO_GK1_CD_WIN
#undef GUIO_GK1_MAC
#define GUIO_GK2_DEMO GUIO8(GUIO_NOSUBTITLES, \
diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp
index 7d2daf787a..ec37d1c292 100644
--- a/engines/sci/graphics/video32.cpp
+++ b/engines/sci/graphics/video32.cpp
@@ -56,80 +56,191 @@ namespace Graphics { struct Surface; }
namespace Sci {
-/**
- * @returns true if the player should quit
- */
-static bool flushEvents(EventManager *eventMan) {
+static void 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);
+ const SciEvent event = eventMan->getSciEvent(SCI_EVENT_ANY & ~SCI_EVENT_QUIT);
if (event.type == SCI_EVENT_NONE) {
break;
- } else if (event.type == SCI_EVENT_QUIT) {
- return true;
}
}
+}
+
+bool VideoPlayer::open(const Common::String &fileName) {
+ if (!_decoder->loadFile(fileName)) {
+ warning("Failed to load %s", fileName.c_str());
+ return false;
+ }
+
+#ifndef USE_RGB_COLOR
+ // KQ7 2.00b videos are compressed in 24bpp Cinepak, so cannot play on
+ // a system with no RGB support
+ if (_decoder->getPixelFormat().bytesPerPixel != 1) {
+ void showScummVMDialog(const Common::String &message);
+ showScummVMDialog(Common::String::format(_("Cannot play back %dbpp video on a system with maximum color depth of 8bpp"), _decoder->getPixelFormat().bpp()));
+ _decoder->close();
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+bool VideoPlayer::startHQVideo() {
+#ifdef USE_RGB_COLOR
+ // Optimize rendering performance for unscaled videos, and allow
+ // better-than-NN interpolation for videos that are scaled
+ if (shouldStartHQVideo()) {
+ // TODO: Search for and use the best supported format (which may be
+ // lower than 32bpp) once the scaling code in Graphics supports
+ // 16bpp/24bpp, and once the SDL backend can correctly communicate
+ // supported pixel formats above whatever format is currently used by
+ // _hwsurface. Right now, this will either show an error dialog (OpenGL)
+ // or just crash entirely (SDL) if the backend does not support this
+ // 32bpp pixel format, which sucks since this code really ought to be
+ // able to fall back to NN scaling for games with 256-color videos
+ // without any error.
+ const Graphics::PixelFormat format(4, 8, 8, 8, 8, 24, 16, 8, 0);
+ g_sci->_gfxFrameout->setPixelFormat(format);
+ _hqVideoMode = (g_system->getScreenFormat() == format);
+ return _hqVideoMode;
+ } else {
+ _hqVideoMode = false;
+ }
+#endif
return false;
}
-static void directWriteToSystem(Video::VideoDecoder *decoder, const Common::Rect &drawRect, const bool setSystemPalette, const Graphics::Surface *nextFrame = nullptr) {
+bool VideoPlayer::endHQVideo() {
+#ifdef USE_RGB_COLOR
+ if (g_system->getScreenFormat().bytesPerPixel != 1) {
+ const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
+ g_sci->_gfxFrameout->setPixelFormat(format);
+ assert(g_system->getScreenFormat() == format);
+ _hqVideoMode = false;
+ return true;
+ }
+#endif
- // VMDPlayer needs to decode the frame early so it can submit palette
- // updates; calling decodeNextFrame again loses frames
- if (!nextFrame) {
- nextFrame = decoder->decodeNextFrame();
+ return false;
+}
+
+VideoPlayer::EventFlags VideoPlayer::playUntilEvent(const EventFlags flags, const uint32 maxSleepMs) {
+ flushEvents(_eventMan);
+ _decoder->start();
+
+ EventFlags stopFlag = kEventFlagNone;
+ for (;;) {
+ g_sci->sleep(MIN(_decoder->getTimeToNextFrame(), maxSleepMs));
+ const Graphics::Surface *nextFrame = nullptr;
+ // If a decoder needs more than one update per loop, this means we are
+ // running behind and should skip rendering these frames (but must still
+ // submit any palettes from skipped frames)
+ while (_decoder->needsUpdate()) {
+ nextFrame = _decoder->decodeNextFrame();
+ if (_decoder->hasDirtyPalette()) {
+ submitPalette(_decoder->getPalette());
+ }
+ }
+
+ // Some frames may contain only audio and/or palette data; this occurs
+ // with Duck videos and is not an error
+ if (nextFrame) {
+ renderFrame(*nextFrame);
+ }
+
+ stopFlag = checkForEvent(flags);
+ if (stopFlag != kEventFlagNone) {
+ break;
+ }
}
- assert(nextFrame);
- if (setSystemPalette &&
- g_system->getScreenFormat().bytesPerPixel == 1 &&
- decoder->hasDirtyPalette()) {
+ return stopFlag;
+}
- const uint8 *palette = decoder->getPalette();
- assert(palette);
- g_system->getPaletteManager()->setPalette(palette, 0, 256);
+VideoPlayer::EventFlags VideoPlayer::checkForEvent(const EventFlags flags) {
+ if (g_engine->shouldQuit() || _decoder->endOfVideo()) {
+ return kEventFlagEnd;
+ }
- // KQ7 1.x has videos encoded using Microsoft Video 1 where palette 0 is
- // white and 255 is black, which is basically the opposite of DOS/Win
- // SCI palettes. So, when drawing to an 8bpp hwscreen, whenever a new
- // palette is seen, the screen must be re-filled with the new black
- // entry to ensure areas outside the video are always black and not some
- // other color
- for (int color = 0; color < 256; ++color) {
- if (palette[0] == 0 && palette[1] == 0 && palette[2] == 0) {
- g_system->fillScreen(color);
- break;
+ SciEvent event = _eventMan->getSciEvent(SCI_EVENT_MOUSE_PRESS | SCI_EVENT_PEEK);
+ if ((flags & kEventFlagMouseDown) && event.type == SCI_EVENT_MOUSE_PRESS) {
+ return kEventFlagMouseDown;
+ }
+
+ event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK);
+ if ((flags & kEventFlagEscapeKey) && event.type == SCI_EVENT_KEYBOARD) {
+ if (getSciVersion() < SCI_VERSION_3) {
+ while ((event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD)),
+ event.type != SCI_EVENT_NONE) {
+ if (event.character == SCI_KEY_ESC) {
+ return kEventFlagEscapeKey;
+ }
}
- palette += 3;
+ } else if (event.character == SCI_KEY_ESC) {
+ return kEventFlagEscapeKey;
}
}
+ return kEventFlagNone;
+}
+
+void VideoPlayer::submitPalette(const uint8 palette[256 * 3]) const {
+#ifdef USE_RGB_COLOR
+ if (g_system->getScreenFormat().bytesPerPixel != 1) {
+ return;
+ }
+#endif
+
+ assert(palette);
+ g_system->getPaletteManager()->setPalette(palette, 0, 256);
+
+ // KQ7 1.x has videos encoded using Microsoft Video 1 where palette 0 is
+ // white and 255 is black, which is basically the opposite of DOS/Win
+ // SCI palettes. So, when drawing to an 8bpp hwscreen, whenever a new
+ // palette is seen, the screen must be re-filled with the new black
+ // entry to ensure areas outside the video are always black and not some
+ // other color
+ for (int color = 0; color < 256; ++color) {
+ if (palette[0] == 0 && palette[1] == 0 && palette[2] == 0) {
+ g_system->fillScreen(color);
+ break;
+ }
+ palette += 3;
+ }
+}
+
+void VideoPlayer::renderFrame(const Graphics::Surface &nextFrame) const {
bool freeConvertedFrame;
Graphics::Surface *convertedFrame;
// Avoid creating a duplicate copy of the surface when it is not necessary
- if (decoder->getPixelFormat() == g_system->getScreenFormat()) {
+ if (_decoder->getPixelFormat() == g_system->getScreenFormat()) {
freeConvertedFrame = false;
- convertedFrame = const_cast<Graphics::Surface *>(nextFrame);
+ convertedFrame = const_cast<Graphics::Surface *>(&nextFrame);
} else {
freeConvertedFrame = true;
- convertedFrame = nextFrame->convertTo(g_system->getScreenFormat(), decoder->getPalette());
+ convertedFrame = nextFrame.convertTo(g_system->getScreenFormat(), _decoder->getPalette());
}
assert(convertedFrame);
- if (decoder->getWidth() != drawRect.width() || decoder->getHeight() != drawRect.height()) {
+ if (_decoder->getWidth() != _drawRect.width() || _decoder->getHeight() != _drawRect.height()) {
Graphics::Surface *const unscaledFrame(convertedFrame);
+ // TODO: The only reason TransparentSurface is used here because it is
+ // where common scaler code is right now.
const Graphics::TransparentSurface tsUnscaledFrame(*unscaledFrame);
#ifdef USE_RGB_COLOR
- if (g_system->getScreenFormat().bytesPerPixel != 1) {
- convertedFrame = tsUnscaledFrame.scaleT<Graphics::FILTER_BILINEAR>(drawRect.width(), drawRect.height());
+ if (_hqVideoMode) {
+ convertedFrame = tsUnscaledFrame.scaleT<Graphics::FILTER_BILINEAR>(_drawRect.width(), _drawRect.height());
} else {
-#else
+#elif 1
{
+#else
+ }
#endif
- convertedFrame = tsUnscaledFrame.scaleT<Graphics::FILTER_NEAREST>(drawRect.width(), drawRect.height());
+ convertedFrame = tsUnscaledFrame.scaleT<Graphics::FILTER_NEAREST>(_drawRect.width(), _drawRect.height());
}
assert(convertedFrame);
if (freeConvertedFrame) {
@@ -139,29 +250,50 @@ static void directWriteToSystem(Video::VideoDecoder *decoder, const Common::Rect
freeConvertedFrame = true;
}
- g_system->copyRectToScreen(convertedFrame->getPixels(), convertedFrame->pitch, drawRect.left, drawRect.top, convertedFrame->w, convertedFrame->h);
+ g_system->copyRectToScreen(convertedFrame->getPixels(), convertedFrame->pitch, _drawRect.left, _drawRect.top, convertedFrame->w, convertedFrame->h);
g_sci->_gfxFrameout->updateScreen();
+
if (freeConvertedFrame) {
convertedFrame->free();
delete convertedFrame;
}
}
+template <typename PixelType>
+void VideoPlayer::renderLQToSurface(Graphics::Surface &out, const Graphics::Surface &nextFrame, const bool doublePixels, const bool blackLines) const {
+
+ const int lineCount = blackLines ? 2 : 1;
+ if (doublePixels) {
+ for (int16 y = 0; y < nextFrame.h * 2; y += lineCount) {
+ const PixelType *source = (const PixelType *)nextFrame.getBasePtr(0, y >> 1);
+ PixelType *target = (PixelType *)out.getBasePtr(0, y);
+ for (int16 x = 0; x < nextFrame.w; ++x) {
+ *target++ = *source;
+ *target++ = *source++;
+ }
+ }
+ } else if (blackLines) {
+ for (int16 y = 0; y < nextFrame.h; y += lineCount) {
+ const PixelType *source = (const PixelType *)nextFrame.getBasePtr(0, y);
+ PixelType *target = (PixelType *)out.getBasePtr(0, y);
+ memcpy(target, source, out.w * sizeof(PixelType));
+ }
+ } else {
+ out.copyRectToSurface(nextFrame.getPixels(), nextFrame.pitch, 0, 0, nextFrame.w, nextFrame.h);
+ }
+}
+
#pragma mark SEQPlayer
-SEQPlayer::SEQPlayer(SegManager *segMan, EventManager *eventMan) :
- _segMan(segMan),
- _eventMan(eventMan),
- _decoder(nullptr) {}
+SEQPlayer::SEQPlayer(EventManager *eventMan) :
+ VideoPlayer(eventMan) {}
-void SEQPlayer::play(const Common::String &fileName, const int16 numTicks, const int16 x, const int16 y) {
+void SEQPlayer::play(const Common::String &fileName, const int16 numTicks, const int16, const int16) {
- close();
+ _decoder.reset(new SEQDecoder(numTicks));
- _decoder = new SEQDecoder(numTicks);
- if (!_decoder->loadFile(fileName)) {
- warning("[SEQPlayer::play]: Failed to load %s", fileName.c_str());
- delete _decoder;
+ if (!VideoPlayer::open(fileName)) {
+ _decoder.reset();
return;
}
@@ -183,113 +315,34 @@ void SEQPlayer::play(const Common::String &fileName, const int16 numTicks, const
_drawRect.setWidth(scaledWidth);
_drawRect.setHeight(scaledHeight);
-#ifdef USE_RGB_COLOR
- // Optimize rendering performance for unscaled videos, and allow
- // better-than-NN interpolation for videos that are scaled
- if (ConfMan.getBool("enable_hq_video") &&
- (_decoder->getWidth() != scaledWidth || _decoder->getHeight() != scaledHeight)) {
- // TODO: Search for and use the best supported format (which may be
- // lower than 32bpp) once the scaling code in Graphics supports
- // 16bpp/24bpp, and once the SDL backend can correctly communicate
- // supported pixel formats above whatever format is currently used by
- // _hwsurface. Right now, this will just crash ScummVM if the backend
- // does not support a 32bpp pixel format, which sucks since this code
- // really ought to be able to fall back to NN scaling for games with
- // 256-color videos.
- const Graphics::PixelFormat format = Graphics::createPixelFormat<8888>();
- g_sci->_gfxFrameout->setPixelFormat(format);
- }
-#endif
-
- _decoder->start();
-
- while (!g_engine->shouldQuit() && !_decoder->endOfVideo()) {
- g_sci->sleep(_decoder->getTimeToNextFrame());
- while (_decoder->needsUpdate()) {
- renderFrame();
- }
-
- // SSCI did not allow SEQ animations to be bypassed like this
- SciEvent event = _eventMan->getSciEvent(SCI_EVENT_MOUSE_PRESS | SCI_EVENT_PEEK);
- if (event.type == SCI_EVENT_MOUSE_PRESS) {
- 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) {
- break;
- }
- }
- }
-
- close();
-}
-
-void SEQPlayer::renderFrame() const {
- directWriteToSystem(_decoder, _drawRect, true);
-}
-
-void SEQPlayer::close() {
-#ifdef USE_RGB_COLOR
- if (g_system->getScreenFormat().bytesPerPixel != 1) {
- const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
- g_sci->_gfxFrameout->setPixelFormat(format);
- }
-#endif
-
+ startHQVideo();
+ playUntilEvent(kEventFlagMouseDown | kEventFlagEscapeKey);
+ endHQVideo();
g_system->fillScreen(0);
- delete _decoder;
- _decoder = nullptr;
+ _decoder.reset();
}
#pragma mark -
#pragma mark AVIPlayer
AVIPlayer::AVIPlayer(EventManager *eventMan) :
- _eventMan(eventMan),
- _decoder(new Video::AVIDecoder(Audio::Mixer::kSFXSoundType)),
+ VideoPlayer(eventMan, new Video::AVIDecoder(Audio::Mixer::kSFXSoundType)),
_status(kAVINotOpen) {}
-AVIPlayer::~AVIPlayer() {
- close();
- delete _decoder;
-}
-
AVIPlayer::IOStatus AVIPlayer::open(const Common::String &fileName) {
if (_status != kAVINotOpen) {
close();
}
- if (!_decoder->loadFile(fileName)) {
+ if (!VideoPlayer::open(fileName)) {
return kIOFileNotFound;
}
-#ifndef USE_RGB_COLOR
- // KQ7 2.00b videos are compressed in 24bpp Cinepak, so cannot play on
- // a system with no RGB support
- if (_decoder->getPixelFormat().bytesPerPixel != 1) {
- void showScummVMDialog(const Common::String &message);
- showScummVMDialog(Common::String::format(_("Cannot play back %dbpp video on a system with maximum color depth of 8bpp"), _decoder->getPixelFormat().bpp()));
- _decoder->close();
- return kIOFileNotFound;
- }
-#endif
-
_status = kAVIOpen;
return kIOSuccess;
}
-AVIPlayer::IOStatus AVIPlayer::init(const bool pixelDouble) {
+AVIPlayer::IOStatus AVIPlayer::init(const bool doublePixels) {
// Calls to initialize the AVI player in SCI can be made in a few ways:
//
// * kShowMovie(WinInit, x, y) to render the video at (x,y) using its
@@ -322,7 +375,7 @@ AVIPlayer::IOStatus AVIPlayer::init(const bool pixelDouble) {
int16 width = _decoder->getWidth();
int16 height = _decoder->getHeight();
- if (pixelDouble) {
+ if (doublePixels) {
width *= 2;
height *= 2;
}
@@ -342,28 +395,8 @@ AVIPlayer::IOStatus AVIPlayer::init(const bool pixelDouble) {
_drawRect.setWidth(width);
_drawRect.setHeight(height);
-#ifdef USE_RGB_COLOR
- // Optimize rendering performance for unscaled videos, and allow
- // better-than-NN interpolation for videos that are scaled
- if (ConfMan.getBool("enable_hq_video") &&
- (_decoder->getWidth() != width || _decoder->getHeight() != height)) {
-
- // TODO: Search for and use the best supported format (which may be
- // lower than 32bpp) once the scaling code in Graphics supports
- // 16bpp/24bpp, and once the SDL backend can correctly communicate
- // supported pixel formats above whatever format is currently used by
- // _hwsurface. Right now, this will just crash ScummVM if the backend
- // does not support a 32bpp pixel format, which sucks since this code
- // really ought to be able to fall back to NN scaling for games with
- // 256-color videos.
- const Graphics::PixelFormat format = Graphics::createPixelFormat<8888>();
- g_sci->_gfxFrameout->setPixelFormat(format);
- } else {
-#else
- {
-#endif
- const Graphics::PixelFormat format = _decoder->getPixelFormat();
- g_sci->_gfxFrameout->setPixelFormat(format);
+ if (!startHQVideo() && _decoder->getPixelFormat().bytesPerPixel != 1) {
+ g_sci->_gfxFrameout->setPixelFormat(_decoder->getPixelFormat());
}
return kIOSuccess;
@@ -379,10 +412,8 @@ AVIPlayer::IOStatus AVIPlayer::play(const int16 from, const int16 to, const int1
_decoder->setEndFrame(to);
}
- if (!async) {
- renderVideo();
- } else if (getSciVersion() == SCI_VERSION_2_1_EARLY) {
- playUntilEvent((EventFlags)(kEventFlagEnd | kEventFlagEscapeKey));
+ if (!async || getSciVersion() == SCI_VERSION_2_1_EARLY) {
+ playUntilEvent(kEventFlagNone);
} else {
_status = kAVIPlaying;
}
@@ -390,15 +421,11 @@ AVIPlayer::IOStatus AVIPlayer::play(const int16 from, const int16 to, const int1
return kIOSuccess;
}
-void AVIPlayer::renderVideo() const {
- _decoder->start();
-
- while (!g_engine->shouldQuit() && !_decoder->endOfVideo()) {
- g_sci->sleep(_decoder->getTimeToNextFrame());
- while (_decoder->needsUpdate()) {
- renderFrame();
- }
- }
+AVIPlayer::EventFlags AVIPlayer::playUntilEvent(const EventFlags flags, const uint32 maxSleepMs) {
+ // NOTE: In SSCI, whether or not a video could be skipped was controlled by
+ // game scripts; here, we always allow skipping video with the mouse or
+ // escape key, to improve the user experience
+ return VideoPlayer::playUntilEvent(flags | kEventFlagMouseDown | kEventFlagEscapeKey, maxSleepMs);
}
AVIPlayer::IOStatus AVIPlayer::close() {
@@ -406,16 +433,15 @@ AVIPlayer::IOStatus AVIPlayer::close() {
return kIOSuccess;
}
-#ifdef USE_RGB_COLOR
- if (g_system->getScreenFormat().bytesPerPixel != 1) {
- const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
- g_sci->_gfxFrameout->setPixelFormat(format);
+ if (!endHQVideo()) {
+ // This fixes a single-frame white flash after playback of the KQ7 1.x
+ // videos, which replace palette entry 0 with white
+ const uint8 black[3] = { 0, 0, 0 };
+ g_system->getPaletteManager()->setPalette(black, 0, 1);
}
-#endif
g_system->fillScreen(0);
g_sci->_gfxCursor32->unhide();
-
_decoder->close();
_status = kAVINotOpen;
return kIOSuccess;
@@ -438,59 +464,12 @@ uint16 AVIPlayer::getDuration() const {
return _decoder->getFrameCount();
}
-void AVIPlayer::renderFrame() const {
- directWriteToSystem(_decoder, _drawRect, true);
-}
-
-AVIPlayer::EventFlags AVIPlayer::playUntilEvent(EventFlags flags) {
- _decoder->start();
-
- EventFlags stopFlag = kEventFlagNone;
- while (!g_engine->shouldQuit()) {
- if (_decoder->endOfVideo()) {
- stopFlag = kEventFlagEnd;
- break;
- }
-
- g_sci->sleep(_decoder->getTimeToNextFrame());
- while (_decoder->needsUpdate()) {
- renderFrame();
- }
-
- SciEvent event = _eventMan->getSciEvent(SCI_EVENT_MOUSE_PRESS | SCI_EVENT_PEEK);
- if ((flags & kEventFlagMouseDown) && event.type == SCI_EVENT_MOUSE_PRESS) {
- stopFlag = kEventFlagMouseDown;
- break;
- }
-
- event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK);
- if ((flags & kEventFlagEscapeKey) && 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) {
- stopFlag = kEventFlagEscapeKey;
- break;
- }
- }
- }
-
- return stopFlag;
-}
-
#pragma mark -
#pragma mark VMDPlayer
-VMDPlayer::VMDPlayer(SegManager *segMan, EventManager *eventMan) :
+VMDPlayer::VMDPlayer(EventManager *eventMan, SegManager *segMan) :
+ VideoPlayer(eventMan, new Video::AdvancedVMDDecoder(Audio::Mixer::kSFXSoundType)),
_segMan(segMan),
- _eventMan(eventMan),
- _decoder(new Video::AdvancedVMDDecoder(Audio::Mixer::kSFXSoundType)),
_isOpen(false),
_isInitialized(false),
@@ -525,7 +504,6 @@ VMDPlayer::VMDPlayer(SegManager *segMan, EventManager *eventMan) :
VMDPlayer::~VMDPlayer() {
close();
- delete _decoder;
}
#pragma mark -
@@ -673,14 +651,11 @@ VMDPlayer::EventFlags VMDPlayer::kernelPlayUntilEvent(const EventFlags flags, co
return playUntilEvent(flags);
}
-VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) {
- if (flushEvents(_eventMan)) {
- return kEventFlagEnd;
- }
-
+VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags, const uint32) {
if (flags & kEventFlagReverse) {
// NOTE: This flag may not work properly since SSCI does not care
// if a video has audio, but the VMD decoder does.
+ warning("VMD reverse playback flag was set. Please report this event to the bug tracker");
const bool success = _decoder->setReverse(true);
assert(success);
_decoder->setVolume(0);
@@ -700,86 +675,53 @@ VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) {
if (shouldUseCompositing()) {
_isComposited = true;
- _usingHighColor = false;
initComposited();
} else {
_isComposited = false;
- _usingHighColor = shouldUseHighColor();
initOverlay();
}
-
- _decoder->start();
}
- EventFlags stopFlag = kEventFlagNone;
- while (!g_engine->shouldQuit()) {
- if (_decoder->endOfVideo()) {
- stopFlag = kEventFlagEnd;
- break;
- }
-
- // Sleeping any more than 1/60th of a second will make the mouse feel
- // very sluggish during VMD action sequences because the frame rate of
- // VMDs is usually only 15fps
- g_sci->sleep(MIN<uint32>(10, _decoder->getTimeToNextFrame()));
- while (_decoder->needsUpdate()) {
- renderFrame();
- }
+ // Sleeping any more than 1/60th of a second will make the mouse feel
+ // very sluggish during VMD action sequences because the frame rate of
+ // VMDs is usually only 15fps
+ return VideoPlayer::playUntilEvent(flags, 10);
+}
- const int currentFrameNo = _decoder->getCurFrame();
+VMDPlayer::EventFlags VMDPlayer::checkForEvent(const EventFlags flags) {
+ const int currentFrameNo = _decoder->getCurFrame();
- if (currentFrameNo == _yieldFrame) {
- stopFlag = kEventFlagEnd;
- break;
- }
+ if (currentFrameNo == _yieldFrame) {
+ return kEventFlagEnd;
+ }
- if (_yieldInterval > 0 &&
- currentFrameNo != _lastYieldedFrameNo &&
- (currentFrameNo % _yieldInterval) == 0
- ) {
- _lastYieldedFrameNo = currentFrameNo;
- stopFlag = kEventFlagYieldToVM;
- break;
- }
+ if (_yieldInterval > 0 &&
+ currentFrameNo != _lastYieldedFrameNo &&
+ (currentFrameNo % _yieldInterval) == 0) {
- SciEvent event = _eventMan->getSciEvent(SCI_EVENT_MOUSE_PRESS | SCI_EVENT_PEEK);
- if ((flags & kEventFlagMouseDown) && event.type == SCI_EVENT_MOUSE_PRESS) {
- stopFlag = kEventFlagMouseDown;
- break;
- }
-
- event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK);
- if ((flags & kEventFlagEscapeKey) && event.type == SCI_EVENT_KEYBOARD) {
- bool stop = false;
- if (getSciVersion() < SCI_VERSION_3) {
- while ((event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD)),
- event.type != SCI_EVENT_NONE) {
- if (event.character == SCI_KEY_ESC) {
- stop = true;
- break;
- }
- }
- } else {
- stop = (event.character == SCI_KEY_ESC);
- }
+ _lastYieldedFrameNo = currentFrameNo;
+ return kEventFlagYieldToVM;
+ }
- if (stop) {
- stopFlag = kEventFlagEscapeKey;
- break;
- }
- }
+ EventFlags stopFlag = VideoPlayer::checkForEvent(flags);
+ if (stopFlag) {
+ return stopFlag;
+ }
- event = _eventMan->getSciEvent(SCI_EVENT_HOT_RECTANGLE | SCI_EVENT_PEEK);
- if ((flags & kEventFlagHotRectangle) && event.type == SCI_EVENT_HOT_RECTANGLE) {
- stopFlag = kEventFlagHotRectangle;
- break;
- }
+ const SciEvent event = _eventMan->getSciEvent(SCI_EVENT_HOT_RECTANGLE | SCI_EVENT_PEEK);
+ if ((flags & kEventFlagHotRectangle) && event.type == SCI_EVENT_HOT_RECTANGLE) {
+ return kEventFlagHotRectangle;
}
- return stopFlag;
+ return kEventFlagNone;
}
void VMDPlayer::initOverlay() {
+ // Make sure that any pending graphics changes from the game are submitted
+ // before starting playback, since if they aren't, and the video player
+ // yields back to the VM in the middle of playback, there may be a flash of
+ // content that draws over the video. (This happens when subtitles are
+ // enabled in Shivers.)
g_sci->_gfxFrameout->frameOut(true);
#ifdef USE_RGB_COLOR
@@ -787,11 +729,7 @@ void VMDPlayer::initOverlay() {
// writing to an intermediate 4bpp surface and using that surface during
// cursor drawing, or by promoting the cursor code to use CursorMan, if
// possible
- if (_usingHighColor) {
- // TODO: 8888 is used here because 4bpp is the only format currently
- // supported by the common scaling code
- const Graphics::PixelFormat format = Graphics::createPixelFormat<8888>();
- g_sci->_gfxFrameout->setPixelFormat(format);
+ if (startHQVideo()) {
redrawGameScreen();
}
#endif
@@ -799,7 +737,12 @@ void VMDPlayer::initOverlay() {
#ifdef USE_RGB_COLOR
void VMDPlayer::redrawGameScreen() const {
+ if (!_hqVideoMode) {
+ return;
+ }
+
Graphics::Surface *game = g_sci->_gfxFrameout->getCurrentBuffer().convertTo(g_system->getScreenFormat(), g_sci->_gfxPalette32->getHardwarePalette());
+ assert(game);
Common::Rect rects[4];
int splitCount = splitRects(Common::Rect(game->w, game->h), _drawRect, rects);
@@ -815,51 +758,22 @@ void VMDPlayer::redrawGameScreen() const {
}
#endif
-void VMDPlayer::renderOverlay() const {
- const Graphics::Surface *nextFrame = _decoder->decodeNextFrame();
-
+void VMDPlayer::renderOverlay(const Graphics::Surface &nextFrame) const {
#ifdef USE_RGB_COLOR
- if (_usingHighColor) {
- if (updatePalette()) {
- redrawGameScreen();
- }
-
- directWriteToSystem(_decoder, _drawRect, false, nextFrame);
- } else {
-#else
- {
+ if (_hqVideoMode) {
+ VideoPlayer::renderFrame(nextFrame);
+ return;
+ }
#endif
- updatePalette();
-
- Graphics::Surface out = g_sci->_gfxFrameout->getCurrentBuffer().getSubArea(_drawRect);
-
- const int lineCount = _blackLines ? 2 : 1;
- if (_doublePixels) {
- for (int16 y = 0; y < _drawRect.height(); y += lineCount) {
- const uint8 *source = (uint8 *)nextFrame->getBasePtr(0, y >> 1);
- uint8 *target = (uint8 *)out.getBasePtr(0, y);
- for (int16 x = 0; x < _decoder->getWidth(); ++x) {
- *target++ = *source;
- *target++ = *source++;
- }
- }
- } else if (_blackLines) {
- for (int16 y = 0; y < _drawRect.height(); y += lineCount) {
- const uint8 *source = (uint8 *)nextFrame->getBasePtr(0, y);
- uint8 *target = (uint8 *)out.getBasePtr(0, y);
- memcpy(target, source, _drawRect.width());
- }
- } else {
- out.copyRectToSurface(nextFrame->getPixels(), nextFrame->pitch, 0, 0, nextFrame->w, nextFrame->h);
- }
- g_sci->_gfxFrameout->directFrameOut(_drawRect);
- }
+ Graphics::Surface out = g_sci->_gfxFrameout->getCurrentBuffer().getSubArea(_drawRect);
+ renderLQToSurface<uint8>(out, nextFrame, _doublePixels, _blackLines);
+ g_sci->_gfxFrameout->directFrameOut(_drawRect);
}
-bool VMDPlayer::updatePalette() const {
- if (_ignorePalettes || !_decoder->hasDirtyPalette()) {
- return false;
+void VMDPlayer::submitPalette(const uint8 rawPalette[256 * 3]) const {
+ if (_ignorePalettes) {
+ return;
}
Palette palette;
@@ -877,13 +791,15 @@ bool VMDPlayer::updatePalette() const {
}
} else
#endif
- fillPalette(palette);
+ fillPalette(rawPalette, palette);
if (_isComposited) {
SciBitmap *bitmap = _segMan->lookupBitmap(_bitmapId);
bitmap->setPalette(palette);
- g_sci->_gfxFrameout->updateScreenItem(*_screenItem);
- g_sci->_gfxFrameout->frameOut(true);
+ // NOTE: SSCI calls updateScreenItem and frameOut here, but this should
+ // not be necessary in ScummVM since the new palette gets submitted
+ // before the next frame is rendered, and the frame rendering call will
+ // perform the same operations.
} else {
g_sci->_gfxPalette32->submit(palette);
g_sci->_gfxPalette32->updateForFrame();
@@ -892,7 +808,7 @@ bool VMDPlayer::updatePalette() const {
#if SCI_VMD_BLACK_PALETTE
if (_blackPalette) {
- fillPalette(palette);
+ fillPalette(rawPalette, palette);
if (_isComposited) {
SciBitmap *bitmap = _segMan->lookupBitmap(_bitmapId);
bitmap->setPalette(palette);
@@ -903,20 +819,25 @@ bool VMDPlayer::updatePalette() const {
}
#endif
- return true;
+#ifdef USE_RGB_COLOR
+ // Changes to the palette may affect areas outside of the video; when the
+ // engine is rendering video in high color, palette changes will only take
+ // effect once the entire screen is redrawn to the high color surface
+ redrawGameScreen();
+#endif
}
void VMDPlayer::closeOverlay() {
#ifdef USE_RGB_COLOR
- if (_usingHighColor) {
- g_sci->_gfxFrameout->setPixelFormat(Graphics::PixelFormat::createFormatCLUT8());
- g_sci->_gfxFrameout->resetHardware();
- } else {
-#else
- {
-#endif
- g_sci->_gfxFrameout->frameOut(true, _drawRect);
+ if (_hqVideoMode) {
+ if (endHQVideo()) {
+ g_sci->_gfxFrameout->resetHardware();
+ }
+ return;
}
+#endif
+
+ g_sci->_gfxFrameout->frameOut(true, _drawRect);
}
void VMDPlayer::initComposited() {
@@ -940,7 +861,10 @@ void VMDPlayer::initComposited() {
CelInfo32 vmdCelInfo;
vmdCelInfo.bitmap = _bitmapId;
- _decoder->setSurfaceMemory(vmdBitmap.getPixels(), vmdBitmap.getWidth(), vmdBitmap.getHeight(), 1);
+
+ Video::AdvancedVMDDecoder *decoder = dynamic_cast<Video::AdvancedVMDDecoder *>(_decoder.get());
+ assert(decoder);
+ decoder->setSurfaceMemory(vmdBitmap.getPixels(), vmdBitmap.getWidth(), vmdBitmap.getHeight(), 1);
if (_planeIsOwned) {
_plane = new Plane(_drawRect, kPlanePicColored);
@@ -967,13 +891,8 @@ void VMDPlayer::initComposited() {
}
void VMDPlayer::renderComposited() const {
- // This writes directly to the CelObjMem we already created,
- // so no need to take its return value
- _decoder->decodeNextFrame();
- if (!updatePalette()) {
- g_sci->_gfxFrameout->updateScreenItem(*_screenItem);
- g_sci->_gfxFrameout->frameOut(true);
- }
+ g_sci->_gfxFrameout->updateScreenItem(*_screenItem);
+ g_sci->_gfxFrameout->frameOut(true);
}
void VMDPlayer::closeComposited() {
@@ -999,16 +918,16 @@ void VMDPlayer::closeComposited() {
#pragma mark -
#pragma mark VMDPlayer - Rendering
-void VMDPlayer::renderFrame() const {
+void VMDPlayer::renderFrame(const Graphics::Surface &nextFrame) const {
if (_isComposited) {
renderComposited();
} else {
- renderOverlay();
+ renderOverlay(nextFrame);
}
}
-void VMDPlayer::fillPalette(Palette &palette) const {
- const byte *vmdPalette = _decoder->getPalette() + _startColor * 3;
+void VMDPlayer::fillPalette(const uint8 rawPalette[256 * 3], Palette &outPalette) const {
+ const byte *vmdPalette = rawPalette + _startColor * 3;
for (uint16 i = _startColor; i <= _endColor; ++i) {
uint8 r = *vmdPalette++;
uint8 g = *vmdPalette++;
@@ -1020,10 +939,10 @@ void VMDPlayer::fillPalette(Palette &palette) const {
b = CLIP(b * _boostPercent / 100, 0, 255);
}
- palette.colors[i].r = r;
- palette.colors[i].g = g;
- palette.colors[i].b = b;
- palette.colors[i].used = true;
+ outPalette.colors[i].r = r;
+ outPalette.colors[i].g = g;
+ outPalette.colors[i].b = b;
+ outPalette.colors[i].used = true;
}
}
@@ -1051,7 +970,7 @@ void VMDPlayer::restrictPalette(const uint8 startColor, const int16 endColor) {
#pragma mark DuckPlayer
DuckPlayer::DuckPlayer(SegManager *segMan, EventManager *eventMan) :
- _eventMan(eventMan),
+ VideoPlayer(eventMan),
_decoder(new Video::AVIDecoder(Audio::Mixer::kSFXSoundType)),
_plane(nullptr),
_status(kDuckClosed),
diff --git a/engines/sci/graphics/video32.h b/engines/sci/graphics/video32.h
index 474851cdf5..bf3cc84123 100644
--- a/engines/sci/graphics/video32.h
+++ b/engines/sci/graphics/video32.h
@@ -26,6 +26,7 @@
#ifdef USE_RGB_COLOR
#include "common/config-manager.h" // for ConfMan
#endif
+#include "common/ptr.h"
#include "common/rect.h" // for Rect
#include "common/scummsys.h" // for int16, uint8, uint16, int32
#include "common/str.h" // for String
@@ -45,53 +46,160 @@ class SegManager;
class SEQDecoder;
struct Palette;
-#pragma mark SEQPlayer
-
/**
- * SEQPlayer is used to play SEQ animations.
- * Used by DOS versions of GK1 and QFG4CD.
+ * An abstract class implementing common video playback functionality for SCI
+ * engine.
*/
-class SEQPlayer {
+class VideoPlayer {
public:
- SEQPlayer(SegManager *segMan, EventManager *eventMan);
+ enum EventFlags {
+ kEventFlagNone = 0,
+ kEventFlagEnd = 1,
+ kEventFlagEscapeKey = 2,
+ kEventFlagMouseDown = 4,
+ kEventFlagHotRectangle = 8,
+ kEventFlagToFrame = 0x10,
+ kEventFlagYieldToVM = 0x20,
+ kEventFlagReverse = 0x80
+ };
+
+ friend EventFlags operator|(const EventFlags a, const EventFlags b) {
+ return static_cast<EventFlags>((int)a | (int)b);
+ }
+
+ VideoPlayer(EventManager *eventMan, Video::VideoDecoder *decoder = nullptr) :
+ _eventMan(eventMan),
+ _decoder(decoder)
+#ifdef USE_RGB_MODE
+ ,
+ _hqVideoMode(false)
+#endif
+ {}
+
+ virtual ~VideoPlayer() {}
+
+protected:
+ EventManager *_eventMan;
/**
- * Plays a SEQ animation with the given
- * file name, with each frame being displayed
- * for `numTicks` ticks.
+ * The video decoder to use for video playback by this player.
*/
- void play(const Common::String &fileName, const int16 numTicks, const int16 x, const int16 y);
+ Common::ScopedPtr<Video::VideoDecoder> _decoder;
-private:
- SegManager *_segMan;
- EventManager *_eventMan;
- SEQDecoder *_decoder;
+ /**
+ * Attempts to open a video by filename and performs basic validation to
+ * ensure that the current system is actually capable of playing back the
+ * video.
+ */
+ bool open(const Common::String &fileName);
/**
- * Renders a single frame of video.
+ * Reinitializes the system hardware surface for playback of high-quality
+ * scaled video if the current video meets the necessary criteria for this
+ * playback mode.
+ *
+ * @returns whether or not the system surface was reinitialized for
+ * high-quality scaled video.
*/
- void renderFrame() const;
+ bool startHQVideo();
/**
- * Stops playback and closes the currently open SEQ stream.
+ * Determines whether or not the currently loaded video meets the criteria
+ * for high-quality scaled output.
*/
- void close();
+ virtual bool shouldStartHQVideo() const {
+#ifdef USE_RGB_COLOR
+ if (!ConfMan.getBool("enable_hq_video")) {
+ return false;
+ }
+
+ if (_decoder->getWidth() == _drawRect.width() &&
+ _decoder->getHeight() == _drawRect.height()) {
+ return false;
+ }
+
+ return true;
+#else
+ return false;
+#endif
+ }
/**
- * The rectangle where the video will be drawn,
- * in screen coordinates.
+ * Restores the hardware surface back to CLUT8 after video playback.
+ */
+ bool endHQVideo();
+
+ /**
+ * Plays a video until an event in the given `flags` is encountered, or
+ * until the end of the video is reached.
+ *
+ * @param maxSleepMs An optional parameter defining the maximum number of
+ * milliseconds that the video player should sleep between video frames.
+ */
+ virtual EventFlags playUntilEvent(const EventFlags flags, const uint32 maxSleepMs = 0xFFFFFFFF);
+
+ /**
+ * Checks to see if an event has occurred that should cause the video player
+ * to yield back to the VM.
+ */
+ virtual EventFlags checkForEvent(const EventFlags flags);
+
+ /**
+ * Submits a palette from the video to the system.
+ */
+ virtual void submitPalette(const uint8 palette[256 * 3]) const;
+
+ /**
+ * Renders a video frame to the system.
+ */
+ virtual void renderFrame(const Graphics::Surface &nextFrame) const;
+
+ /**
+ * Renders a video frame to an intermediate surface using low-quality
+ * scaling, black-lining, or direct copy, depending upon the passed flags.
+ */
+ template <typename PixelType>
+ void renderLQToSurface(Graphics::Surface &out, const Graphics::Surface &nextFrame, const bool doublePixels, const bool blackLines) const;
+
+ /**
+ * The rectangle where the video will be drawn, in screen coordinates.
*/
Common::Rect _drawRect;
+
+#ifdef USE_RGB_COLOR
+ /**
+ * Whether or not the player is currently in high-quality video rendering
+ * mode.
+ */
+ bool _hqVideoMode;
+#endif
+};
+
+#pragma mark SEQPlayer
+
+/**
+ * SEQPlayer is used to play SEQ animations.
+ * Used by DOS versions of GK1 and QFG4CD.
+ */
+class SEQPlayer : public VideoPlayer {
+public:
+ SEQPlayer(EventManager *eventMan);
+
+ /**
+ * Plays a SEQ animation with the given file name, with each frame being
+ * displayed for `numTicks` ticks.
+ */
+ void play(const Common::String &fileName, const int16 numTicks, const int16 x, const int16 y);
};
#pragma mark -
#pragma mark AVIPlayer
/**
- * AVIPlayer is used to play AVI videos. Used by
- * Windows versions of GK1CD, KQ7, and QFG4CD.
+ * AVIPlayer is used to play AVI videos.
+ * Used by Windows versions of GK1CD, KQ7, and QFG4CD.
*/
-class AVIPlayer {
+class AVIPlayer : public VideoPlayer {
public:
enum IOStatus {
kIOSuccess = 0,
@@ -106,16 +214,7 @@ public:
kAVIPaused = 3
};
- enum EventFlags {
- kEventFlagNone = 0,
- kEventFlagEnd = 1,
- kEventFlagEscapeKey = 2,
- kEventFlagMouseDown = 4,
- kEventFlagHotRectangle = 8
- };
-
AVIPlayer(EventManager *eventMan);
- ~AVIPlayer();
/**
* Opens a stream to an AVI resource.
@@ -123,16 +222,18 @@ public:
IOStatus open(const Common::String &fileName);
/**
- * Initializes the AVI rendering parameters for the
- * current AVI. This must be called after `open`.
+ * Initializes the AVI rendering parameters for the current AVI. This must
+ * be called after `open`.
*/
- IOStatus init(const bool pixelDouble);
+ IOStatus init(const bool doublePixels);
/**
* Begins playback of the current AVI.
*/
IOStatus play(const int16 from, const int16 to, const int16 showStyle, const bool cue);
+ virtual EventFlags playUntilEvent(const EventFlags flags, const uint32 maxSleepMs = 0xFFFFFFFF) override;
+
/**
* Stops playback and closes the currently open AVI stream.
*/
@@ -148,39 +249,11 @@ public:
*/
uint16 getDuration() const;
- /**
- * Plays the AVI until an event occurs (e.g. user
- * presses escape, clicks, etc.).
- */
- EventFlags playUntilEvent(const EventFlags flags);
-
private:
- typedef Common::HashMap<uint16, AVIStatus> StatusMap;
-
- EventManager *_eventMan;
- Video::AVIDecoder *_decoder;
-
/**
* Playback status of the player.
*/
AVIStatus _status;
-
- /**
- * The rectangle where the video will be drawn,
- * in screen coordinates.
- */
- Common::Rect _drawRect;
-
- /**
- * Renders video without event input until the
- * video is complete.
- */
- void renderVideo() const;
-
- /**
- * Renders a single frame of video.
- */
- void renderFrame() const;
};
#pragma mark -
@@ -188,10 +261,10 @@ private:
/**
* VMDPlayer is used to play VMD videos.
- * Used by Phant1, GK2, PQ:SWAT, Shivers, SQ6,
- * Torin, and Lighthouse.
+ * Used by LSL7, Phant1, GK2, PQ:SWAT, Shivers, SQ6, Rama, Torin, and
+ * Lighthouse.
*/
-class VMDPlayer {
+class VMDPlayer : public VideoPlayer {
public:
enum OpenFlags {
kOpenFlagNone = 0,
@@ -214,17 +287,6 @@ public:
kPlayFlagStretchVertical = 0x100
};
- enum EventFlags {
- kEventFlagNone = 0,
- kEventFlagEnd = 1,
- kEventFlagEscapeKey = 2,
- kEventFlagMouseDown = 4,
- kEventFlagHotRectangle = 8,
- kEventFlagToFrame = 0x10,
- kEventFlagYieldToVM = 0x20,
- kEventFlagReverse = 0x80
- };
-
enum VMDStatus {
kVMDNotOpen = 0,
kVMDOpen = 1,
@@ -234,13 +296,11 @@ public:
kVMDFinished = 5
};
- VMDPlayer(SegManager *segMan, EventManager *eventMan);
- ~VMDPlayer();
+ VMDPlayer(EventManager *eventMan, SegManager *segMan);
+ virtual ~VMDPlayer();
private:
SegManager *_segMan;
- EventManager *_eventMan;
- Video::AdvancedVMDDecoder *_decoder;
#pragma mark -
#pragma mark VMDPlayer - Playback
@@ -251,8 +311,8 @@ public:
IOStatus open(const Common::String &fileName, const OpenFlags flags);
/**
- * Initializes the VMD rendering parameters for the
- * current VMD. This must be called after `open`.
+ * Initializes the VMD rendering parameters for the current VMD. This must
+ * be called after `open`.
*/
void init(const int16 x, const int16 y, const PlayFlags flags, const int16 boostPercent, const int16 boostStartColor, const int16 boostEndColor);
@@ -271,64 +331,48 @@ public:
private:
/**
- * Whether or not a VMD stream has been opened with
- * `open`.
+ * Whether or not a VMD stream has been opened with `open`.
*/
bool _isOpen;
/**
- * Whether or not a VMD player has been initialised
- * with `init`.
+ * Whether or not a VMD player has been initialized with `init`.
*/
bool _isInitialized;
/**
- * The Resource object for VMDs that are read out
- * of a resource bundle instead of being streamed
- * from the filesystem.
+ * The Resource object for VMDs that are read out of a resource bundle
+ * instead of being streamed from the filesystem.
*/
Resource *_bundledVmd;
/**
- * For VMDs played with the `kEventFlagToFrame` flag,
- * the target frame for yielding back to the SCI VM.
+ * For VMDs played with the `kEventFlagToFrame` flag, the target frame for
+ * yielding back to the SCI VM.
*/
int32 _yieldFrame;
/**
- * For VMDs played with the `kEventFlagYieldToVM` flag,
- * the number of frames that should be rendered until
- * yielding back to the SCI VM.
+ * For VMDs played with the `kEventFlagYieldToVM` flag, the number of frames
+ * that should be rendered until yielding back to the SCI VM.
*/
int32 _yieldInterval;
/**
- * For VMDs played with the `kEventFlagYieldToVM` flag,
- * the last frame when control of the main thread was
- * yielded back to the SCI VM.
+ * For VMDs played with the `kEventFlagYieldToVM` flag, the last frame when
+ * control of the main thread was yielded back to the SCI VM.
*/
int _lastYieldedFrameNo;
- void initOverlay();
- void renderOverlay() const;
- void closeOverlay();
-
- void initComposited();
- void renderComposited() const;
- void closeComposited();
-
- /**
- * Plays the VMD until an event occurs (e.g. user
- * presses escape, clicks, etc.).
- */
- EventFlags playUntilEvent(const EventFlags flags);
+ virtual EventFlags playUntilEvent(const EventFlags flags, const uint32 = 0xFFFFFFFF) override;
+ virtual EventFlags checkForEvent(const EventFlags flags) override;
#pragma mark -
#pragma mark VMDPlayer - Rendering
public:
/**
- * Causes the VMD player to ignore all palettes in
- * the currently playing video.
+ * Causes the VMD player to ignore all palettes in the currently playing
+ * video.
*/
void ignorePalettes() { _ignorePalettes = true; }
@@ -337,12 +381,18 @@ public:
*/
void setPlane(const int16 priority, const reg_t planeId);
-private:
+protected:
/**
- * The rectangle where the video will be drawn, in screen coordinates.
+ * Renders a frame of video to the output bitmap.
*/
- Common::Rect _drawRect;
+ virtual void renderFrame(const Graphics::Surface &nextFrame) const override;
+
+ /**
+ * Updates the system with palette data from the video.
+ */
+ virtual void submitPalette(const uint8 palette[256 * 3]) const override;
+private:
/**
* The plane where the VMD will be drawn.
*/
@@ -358,11 +408,9 @@ private:
*/
reg_t _bitmapId;
- // TODO: planeIsOwned and priority are used in SCI3+ only
-
/**
- * If true, the plane for this VMD was set
- * externally and is not owned by this VMDPlayer.
+ * If true, the plane for this VMD was set externally and is not owned by
+ * this VMDPlayer.
*/
bool _planeIsOwned;
@@ -378,26 +426,24 @@ private:
bool _doublePixels;
/**
- * Whether or not the video should be pixel doubled
- * vertically only.
+ * Whether or not the video should be pixel doubled vertically only.
*/
bool _stretchVertical;
/**
- * Whether or not black lines should be rendered
- * across the video.
+ * Whether or not black lines should be rendered across the video.
*/
bool _blackLines;
/**
- * Whether or not the playback area of the VMD
- * should be left black at the end of playback.
+ * Whether or not the playback area of the VMD should be left black at the
+ * end of playback.
*/
bool _leaveScreenBlack;
/**
- * Whether or not the area of the VMD should be left
- * displaying the final frame of the video.
+ * Whether or not the area of the VMD should be left displaying the final
+ * frame of the video.
*/
bool _leaveLastFrame;
@@ -412,35 +458,18 @@ private:
bool _isComposited;
/**
- * Whether or not rendering of the video is being performed in high color or
- * better.
- */
- bool _usingHighColor;
-
- /**
- * Renders a frame of video to the output bitmap.
- */
- void renderFrame() const;
-
- /**
- * Updates the system with palette data from the video.
+ * Fills the given palette with RGB values from the VMD palette, applying
+ * brightness boost if it is enabled.
*/
- bool updatePalette() const;
-
- /**
- * Fills the given palette with RGB values from
- * the VMD palette, applying brightness boost if
- * it is enabled.
- */
- void fillPalette(Palette &palette) const;
+ void fillPalette(const uint8 rawPalette[256 * 3], Palette &outPalette) const;
#ifdef USE_RGB_COLOR
/**
* Redraws areas of the screen outside of the video to the system buffer.
- * This is used when
+ * This is used whenever palette changes occur and the video is rendering in
+ * high color mode.
*/
void redrawGameScreen() const;
-#endif
/**
* Determines whether or not the VMD player should upgrade the renderer to
@@ -450,18 +479,18 @@ private:
* video, but this will require additional work in GfxFrameout and
* GfxCursor32 since the internal buffer and cursor code are 8bpp only.
*/
- bool shouldUseHighColor() const {
-#ifdef USE_RGB_COLOR
- return ConfMan.getBool("enable_hq_video") &&
- _priority == 0 &&
- (_doublePixels || _stretchVertical) &&
- !_leaveLastFrame &&
- !_showCursor &&
- !_blackLines;
-#else
- return false;
-#endif
+ virtual bool shouldStartHQVideo() const override {
+ if (!VideoPlayer::shouldStartHQVideo()) {
+ return false;
+ }
+
+ if (_priority != 0 || _leaveLastFrame || _showCursor || _blackLines) {
+ return false;
+ }
+
+ return true;
}
+#endif
/**
* Determines whether or not the video should use the compositing renderer
@@ -469,18 +498,26 @@ private:
*/
bool shouldUseCompositing() const {
#ifdef USE_RGB_COLOR
- return getSciVersion() == SCI_VERSION_3 && !shouldUseHighColor();
+ return getSciVersion() == SCI_VERSION_3 && !shouldStartHQVideo();
#else
return getSciVersion() == SCI_VERSION_3;
#endif
}
+ void initOverlay();
+ void renderOverlay(const Graphics::Surface &nextFrame) const;
+ void closeOverlay();
+
+ void initComposited();
+ void renderComposited() const;
+ void closeComposited();
+
#pragma mark -
#pragma mark VMDPlayer - Blackout
public:
/**
- * Sets the area of the screen that should be blacked out
- * during VMD playback.
+ * Sets the area of the screen that should be blacked out during VMD
+ * playback.
*/
void setBlackoutArea(const Common::Rect &rect) { _blackoutRect = rect; }
@@ -491,8 +528,8 @@ private:
Common::Rect _blackoutRect;
/**
- * An optional plane that will be used to black out
- * areas of the screen outside of the VMD surface.
+ * An optional plane that will be used to black out areas of the screen
+ * outside of the VMD surface.
*/
Plane *_blackoutPlane;
@@ -500,37 +537,33 @@ private:
#pragma mark VMDPlayer - Palette
public:
/**
- * Restricts use of the system palette by VMD playback to
- * the given range of palette indexes.
+ * Restricts use of the system palette by VMD playback to the given range of
+ * palette indexes.
*/
void restrictPalette(const uint8 startColor, const int16 endColor);
private:
/**
- * The first color in the system palette that the VMD
- * can write to.
+ * The first color in the system palette that the VMD can write to.
*/
uint8 _startColor;
/**
- * The last color in the system palette that the VMD
- * can write to.
+ * The last color in the system palette that the VMD can write to.
*/
uint8 _endColor;
/**
- * If true, video frames are rendered after a blank
- * palette is submitted to the palette manager,
- * which is then restored after the video pixels
- * have already been rendered.
+ * If true, video frames are rendered after a blank palette is submitted to
+ * the palette manager, which is then restored after the video pixels have
+ * already been rendered.
*
- * This functionality is currently disabled because it seems like
- * it was designed for a different graphics architecture where
- * pixel data could be rendered before the video card's palette
- * had been updated. This is not possible in ScummVM because the
- * palette & pixel data are rendered simultaneously when
- * OSystem::updateScreen is called, rather than immediately
- * after they are sent to the backend.
+ * This functionality is currently disabled because it seems like it was
+ * designed for a different graphics architecture where pixel data could be
+ * rendered before the video card's palette had been updated. This is not
+ * possible in ScummVM because the palette & pixel data are rendered
+ * simultaneously when OSystem::updateScreen is called, rather than
+ * immediately after they are sent to the backend.
*/
#ifdef SCI_VMD_BLACK_PALETTE
bool _blackPalette;
@@ -540,21 +573,18 @@ private:
#pragma mark VMDPlayer - Brightness boost
private:
/**
- * The amount of brightness boost for the video.
- * Values above 100 increase brightness; values below
- * 100 reduce it.
+ * The amount of brightness boost for the video. Values above 100 increase
+ * brightness; values below 100 reduce it.
*/
int16 _boostPercent;
/**
- * The first color in the palette that should be
- * brightness boosted.
+ * The first color in the palette that should be brightness boosted.
*/
uint8 _boostStartColor;
/**
- * The last color in the palette that should be
- * brightness boosted.
+ * The last color in the palette that should be brightness boosted.
*/
uint8 _boostEndColor;
@@ -562,17 +592,15 @@ private:
#pragma mark VMDPlayer - Mouse cursor
public:
/**
- * Sets whether or not the mouse cursor should be drawn.
- * This does not have any effect during playback, but can
- * be used to prevent the mouse cursor from being shown
- * again after the video has finished.
+ * Sets whether or not the mouse cursor should be drawn. This does not have
+ * any effect during playback, but can be used to prevent the mouse cursor
+ * from being shown again after the video has finished.
*/
void setShowCursor(const bool shouldShow) { _showCursor = shouldShow; }
private:
/**
- * Whether or not the mouse cursor should be shown
- * during playback.
+ * Whether or not the mouse cursor should be shown during playback.
*/
bool _showCursor;
};
@@ -580,7 +608,7 @@ private:
#pragma mark -
#pragma mark DuckPlayer
-class DuckPlayer {
+class DuckPlayer : public VideoPlayer {
public:
enum DuckStatus {
kDuckClosed = 0,
@@ -674,15 +702,14 @@ private:
#pragma mark Video32
/**
- * Video32 provides facilities for playing back
- * video in SCI engine.
+ * Video32 provides facilities for playing back video in SCI engine.
*/
class Video32 : public Common::Serializable {
public:
Video32(SegManager *segMan, EventManager *eventMan) :
- _SEQPlayer(segMan, eventMan),
+ _SEQPlayer(eventMan),
_AVIPlayer(eventMan),
- _VMDPlayer(segMan, eventMan),
+ _VMDPlayer(eventMan, segMan),
_robotPlayer(segMan),
_duckPlayer(segMan, eventMan) {}
diff --git a/engines/sci/sci.cpp b/engines/sci/sci.cpp
index 634652cd3f..f6bc48f1fa 100644
--- a/engines/sci/sci.cpp
+++ b/engines/sci/sci.cpp
@@ -838,6 +838,10 @@ int SciEngine::inQfGImportRoom() const {
}
void SciEngine::sleep(uint32 msecs) {
+ if (!msecs) {
+ return;
+ }
+
uint32 time;
const uint32 wakeUpTime = g_system->getMillis() + msecs;
@@ -860,7 +864,6 @@ void SciEngine::sleep(uint32 msecs) {
g_system->delayMillis(wakeUpTime - time);
break;
}
-
}
}