aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Snover2017-07-03 20:11:02 -0500
committerColin Snover2017-07-06 19:12:38 -0500
commit8cb35442c073b5ed5a0f3fa7d5e627bdd85af229 (patch)
tree73378e68056f20a3567b2aff8efdca6e55709701
parente9bef896462d6f99c4c89672de90c99e879415a2 (diff)
downloadscummvm-rg350-8cb35442c073b5ed5a0f3fa7d5e627bdd85af229.tar.gz
scummvm-rg350-8cb35442c073b5ed5a0f3fa7d5e627bdd85af229.tar.bz2
scummvm-rg350-8cb35442c073b5ed5a0f3fa7d5e627bdd85af229.zip
SCI32: Improve kShowMovieWin (AVI) rendering
1. Added a new game option for linear interpolation when scaling video in ScummVM builds with USE_RGB_COLOR; 2. 8bpp videos that put black in a palette index other than 0 (KQ7) should now always render correctly without the earlier game-specific workarounds which did not work very well; 3. Data from game scripts regarding video size and position are now ignored, since games always just try to show videos in the middle of the screen, but frequently get this a little bit wrong, causing either bad aspect ratios or off-center videos; 4. Builds without USE_RGB_COLOR support will not crash when attempting to play >8bpp AVIs, like those from KQ7 2.00b. Fixes Trac#9843, Trac#9762.
-rw-r--r--engines/sci/detection.cpp12
-rw-r--r--engines/sci/detection_tables.h31
-rw-r--r--engines/sci/engine/kvideo.cpp17
-rw-r--r--engines/sci/graphics/video32.cpp350
-rw-r--r--engines/sci/graphics/video32.h55
-rw-r--r--engines/sci/sci.h1
6 files changed, 201 insertions, 265 deletions
diff --git a/engines/sci/detection.cpp b/engines/sci/detection.cpp
index 7f8424787b..fe95b0cf2f 100644
--- a/engines/sci/detection.cpp
+++ b/engines/sci/detection.cpp
@@ -424,6 +424,18 @@ static const ADExtraGuiOptionsMap optionsList[] = {
}
},
+#ifdef USE_RGB_COLOR
+ {
+ GAMEOPTION_HQ_VIDEO,
+ {
+ _s("Use high-quality video scaling"),
+ _s("Use linear interpolation when upscaling videos, where possible"),
+ "enable_hq_video",
+ true
+ }
+ },
+#endif
+
{
GAMEOPTION_PREFER_DIGITAL_SFX,
{
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index 1ba3af4ec5..b5c426d836 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -724,9 +724,13 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#ifdef ENABLE_SCI32
#define GUIO_GK1_FLOPPY GUIO2(GUIO_NOSPEECH, \
GAMEOPTION_ORIGINAL_SAVELOAD)
-#define GUIO_GK1_CD GUIO3(GUIO_LINKSPEECHTOSFX, \
+#define GUIO_GK1_CD_DOS GUIO3(GUIO_LINKSPEECHTOSFX, \
GAMEOPTION_ORIGINAL_SAVELOAD, \
GAMEOPTION_HIGH_RESOLUTION_GRAPHICS)
+#define GUIO_GK1_CD_WIN GUIO4(GUIO_LINKSPEECHTOSFX, \
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_HIGH_RESOLUTION_GRAPHICS, \
+ GAMEOPTION_HQ_VIDEO)
#define GUIO_GK1_MAC GUIO_GK1_FLOPPY
// Gabriel Knight - English DOS Floppy
@@ -775,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"
@@ -783,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
@@ -791,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"
@@ -799,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"
@@ -807,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"
@@ -815,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
@@ -830,16 +834,18 @@ 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 GUIO7(GUIO_NOSUBTITLES, \
+#define GUIO_GK2_DEMO GUIO8(GUIO_NOSUBTITLES, \
GUIO_NOMUSIC, \
GUIO_NOSFX, \
GUIO_NOSPEECH, \
GUIO_NOMIDI, \
GUIO_NOLAUNCHLOAD, \
- GUIO_NOASPECT)
+ GUIO_NOASPECT, \
+ GAMEOPTION_HQ_VIDEO)
#define GUIO_GK2 GUIO7(GUIO_NOSUBTITLES, \
GUIO_NOSFX, \
GUIO_NOSPEECHVOLUME, \
@@ -1824,9 +1830,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
GUIO_LINKMUSICTOSFX, \
GUIO_LINKSPEECHTOSFX, \
GUIO_NOASPECT)
-#define GUIO_KQ7 GUIO3(GUIO_NOASPECT, \
+#define GUIO_KQ7 GUIO4(GUIO_NOASPECT, \
GUIO_LINKMUSICTOSFX, \
- GUIO_LINKSPEECHTOSFX)
+ GUIO_LINKSPEECHTOSFX, \
+ GAMEOPTION_HQ_VIDEO)
// King's Quest 7 - English Windows (from the King's Quest Collection)
// Executable scanning reports "2.100.002", VERSION file reports "1.4"
diff --git a/engines/sci/engine/kvideo.cpp b/engines/sci/engine/kvideo.cpp
index 9398e6d75e..37e4eeeab3 100644
--- a/engines/sci/engine/kvideo.cpp
+++ b/engines/sci/engine/kvideo.cpp
@@ -332,11 +332,12 @@ reg_t kShowMovieWinInit(EngineState *s, int argc, reg_t *argv) {
--argc;
}
- const int16 x = argv[0].toSint16();
- const int16 y = argv[1].toSint16();
- const int16 width = argc > 3 ? argv[2].toSint16() : 0;
- const int16 height = argc > 3 ? argv[3].toSint16() : 0;
- return make_reg(0, g_sci->_video32->getAVIPlayer().init1x(x, y, width, height));
+ // argv[0] is a broken x-coordinate
+ // argv[1] is a broken y-coordinate
+ // argv[2] is an optional broken width
+ // argv[3] is an optional broken height
+ const bool pixelDouble = argc > 3 && argv[2].toSint16() && argv[3].toSint16();
+ return make_reg(0, g_sci->_video32->getAVIPlayer().init(pixelDouble));
}
reg_t kShowMovieWinPlay(EngineState *s, int argc, reg_t *argv) {
@@ -386,9 +387,9 @@ reg_t kShowMovieWinPlayUntilEvent(EngineState *s, int argc, reg_t *argv) {
reg_t kShowMovieWinInitDouble(EngineState *s, int argc, reg_t *argv) {
// argv[0] is a broken movie ID
- const int16 x = argv[1].toSint16();
- const int16 y = argv[2].toSint16();
- return make_reg(0, g_sci->_video32->getAVIPlayer().init2x(x, y));
+ // argv[1] is a broken x-coordinate
+ // argv[2] is a broken y-coordinate
+ return make_reg(0, g_sci->_video32->getAVIPlayer().init(true));
}
reg_t kPlayVMD(EngineState *s, int argc, reg_t *argv) {
diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp
index fc35d40e1f..ecde85b622 100644
--- a/engines/sci/graphics/video32.cpp
+++ b/engines/sci/graphics/video32.cpp
@@ -23,9 +23,15 @@
#include "audio/mixer.h" // for Audio::Mixer::kSFXSoundType
#include "common/config-manager.h" // for ConfMan
#include "common/textconsole.h" // for warning, error
+#ifndef USE_RGB_COLOR
+#include "common/translation.h" // for _
+#endif
#include "common/util.h" // for ARRAYSIZE
#include "common/system.h" // for g_system
#include "engine.h" // for Engine, g_engine
+#include "graphics/colormasks.h" // for createPixelFormat
+#include "graphics/palette.h" // for PaletteManager
+#include "graphics/transparent_surface.h" // for TransparentSurface
#include "sci/console.h" // for Console
#include "sci/engine/features.h" // for GameFeatures
#include "sci/engine/state.h" // for EngineState
@@ -176,14 +182,9 @@ void SEQPlayer::renderFrame(SciBitmap &bitmap) const {
#pragma mark -
#pragma mark AVIPlayer
-AVIPlayer::AVIPlayer(SegManager *segMan, EventManager *eventMan) :
- _segMan(segMan),
+AVIPlayer::AVIPlayer(EventManager *eventMan) :
_eventMan(eventMan),
_decoder(new Video::AVIDecoder(Audio::Mixer::kSFXSoundType)),
- _scaleBuffer(nullptr),
- _plane(nullptr),
- _screenItem(nullptr),
- _bitmap(NULL_REG),
_status(kAVINotOpen) {}
AVIPlayer::~AVIPlayer() {
@@ -200,125 +201,99 @@ AVIPlayer::IOStatus AVIPlayer::open(const Common::String &fileName) {
return kIOFileNotFound;
}
- _status = kAVIOpen;
- return kIOSuccess;
-}
-
-AVIPlayer::IOStatus AVIPlayer::init1x(const int16 x, const int16 y, int16 width, int16 height) {
- if (_status == kAVINotOpen) {
+#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
- _pixelDouble = false;
-
- if (!width || !height) {
- const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
- const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
- const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
- const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
- const Ratio screenToScriptX(scriptWidth, screenWidth);
- const Ratio screenToScriptY(scriptHeight, screenHeight);
- width = (_decoder->getWidth() * screenToScriptX).toInt();
- height = (_decoder->getHeight() * screenToScriptY).toInt();
- }
-
- // QFG4CD gives non-multiple-of-2 values for width and height of the intro
- // video, which would normally be OK except the source video is a pixel
- // bigger in each dimension so it just causes part of the video to get cut
- // off
- width = (width + 1) & ~1;
- height = (height + 1) & ~1;
-
- // GK1 CREDITS.AVI is not rendered correctly in SSCI because it is a 640x480
- // video and the game script gives the wrong dimensions.
- // Since this is the only high-resolution AVI ever used by any SCI game,
- // just set the draw rectangle to draw across the entire screen
- if (g_sci->getGameId() == GID_GK1 && _decoder->getWidth() > 320) {
- _drawRect.left = 0;
- _drawRect.top = 0;
- _drawRect.right = 320;
- _drawRect.bottom = 200;
- } else {
- _drawRect.left = x;
- _drawRect.top = y;
- _drawRect.right = x + width;
- _drawRect.bottom = y + height;
- }
-
- init();
-
+ _status = kAVIOpen;
return kIOSuccess;
}
-AVIPlayer::IOStatus AVIPlayer::init2x(const int16 x, const int16 y) {
+AVIPlayer::IOStatus AVIPlayer::init(const bool pixelDouble) {
+ // 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
+ // original resolution, or
+ // * kShowMovie(WinInit, x, y, w, h) to render the video at (x,y) with
+ // rescaling to the given width and height, or
+ // * kShowMovie(WinInitDouble, x, y) to render the video at (x,y) with
+ // rescaling to double the original resolution.
+ //
+ // Unfortunately, the values passed by game scripts are frequently wrong:
+ //
+ // * KQ7 passes origin coordinates that cause videos to be misaligned on the
+ // Y-axis;
+ // * GK1 passes width and height that change the aspect ratio of the videos,
+ // even though they were rendered with square pixels (and in the case of
+ // CREDITS.AVI, cause the video to be badly downscaled);
+ // * The GK2 demo does all of these things at the same time.
+ //
+ // Fortunately, whenever all of these games play an AVI, they are just
+ // trying to play a video at the center of the screen. So, we ignore the
+ // values that the game sends, and instead calculate the correct dimensions
+ // and origin based on the video data, only allowing games to specify
+ // whether or not the videos should be scaled up 2x.
+
if (_status == kAVINotOpen) {
return kIOFileNotFound;
}
- _drawRect.left = x;
- _drawRect.top = y;
- _drawRect.right = x + _decoder->getWidth() * 2;
- _drawRect.bottom = y + _decoder->getHeight() * 2;
- _pixelDouble = true;
-
- init();
-
- return kIOSuccess;
-}
-
-void AVIPlayer::init() {
- int16 xRes;
- int16 yRes;
-
- // GK1 CREDITS.AVI or KQ7 1.51 half-size videos
- if ((g_sci->_gfxFrameout->_isHiRes && _decoder->getWidth() > 320) ||
- (g_sci->getGameId() == GID_KQ7 && getSciVersion() == SCI_VERSION_2_1_EARLY && _drawRect.width() <= 160)) {
- xRes = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
- yRes = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
- } else {
- xRes = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
-
- const Ratio videoRatio(_decoder->getWidth(), _decoder->getHeight());
- const Ratio screenRatio(4, 3);
+ g_sci->_gfxCursor32->hide();
- // Videos that already have a 4:3 aspect ratio should not receive any
- // aspect ratio correction
- if (videoRatio == screenRatio) {
- yRes = 240;
- } else {
- yRes = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
- }
+ int16 width = _decoder->getWidth();
+ int16 height = _decoder->getHeight();
+ if (pixelDouble) {
+ width *= 2;
+ height *= 2;
}
- _plane = new Plane(_drawRect);
- g_sci->_gfxFrameout->addPlane(*_plane);
-
- if (_decoder->getPixelFormat().bytesPerPixel == 1) {
- SciBitmap &bitmap = *_segMan->allocateBitmap(&_bitmap, _decoder->getWidth(), _decoder->getHeight(), kDefaultSkipColor, 0, 0, xRes, yRes, 0, false, false);
- bitmap.getBuffer().fillRect(Common::Rect(_decoder->getWidth(), _decoder->getHeight()), 0);
-
- CelInfo32 celInfo;
- celInfo.type = kCelTypeMem;
- celInfo.bitmap = _bitmap;
-
- _screenItem = new ScreenItem(_plane->_object, celInfo, Common::Point(), ScaleInfo());
- g_sci->_gfxFrameout->addScreenItem(*_screenItem);
- g_sci->_gfxFrameout->frameOut(true);
+ const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
+
+ // When scaling videos, they must not grow larger than the hardware screen
+ // or else the engine will crash. This is particularly important for the GK1
+ // CREDITS.AVI since the game sends extra width/height arguments, causing it
+ // to be treated as needing upscaling even though it does not.
+ width = MIN<int16>(width, screenWidth);
+ height = MIN<int16>(height, screenHeight);
+
+ _drawRect.left = (screenWidth - width) / 2;
+ _drawRect.top = (screenHeight - height) / 2;
+ _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 {
- // Attempting to draw a palettized cursor into a 24bpp surface will
- // cause memory corruption, so hide the cursor in this mode (SCI did not
- // have a 24bpp mode but just directed VFW to display videos instead)
- g_sci->_gfxCursor32->hide();
-
+#else
+ {
+#endif
const Graphics::PixelFormat format = _decoder->getPixelFormat();
g_sci->_gfxFrameout->setPixelFormat(format);
-
- if (_pixelDouble) {
- const int16 width = _drawRect.width();
- const int16 height = _drawRect.height();
- _scaleBuffer = calloc(1, width * height * format.bytesPerPixel);
- }
}
+
+ return kIOSuccess;
}
AVIPlayer::IOStatus AVIPlayer::play(const int16 from, const int16 to, const int16, const bool async) {
@@ -344,6 +319,7 @@ AVIPlayer::IOStatus AVIPlayer::play(const int16 from, const int16 to, const int1
void AVIPlayer::renderVideo() const {
_decoder->start();
+
while (!g_engine->shouldQuit() && !_decoder->endOfVideo()) {
g_sci->sleep(_decoder->getTimeToNextFrame());
while (_decoder->needsUpdate()) {
@@ -357,24 +333,18 @@ AVIPlayer::IOStatus AVIPlayer::close() {
return kIOSuccess;
}
- free(_scaleBuffer);
- _scaleBuffer = nullptr;
-
- if (_decoder->getPixelFormat().bytesPerPixel != 1) {
+#ifdef USE_RGB_COLOR
+ if (g_system->getScreenFormat().bytesPerPixel != 1) {
const Graphics::PixelFormat format = Graphics::PixelFormat::createFormatCLUT8();
g_sci->_gfxFrameout->setPixelFormat(format);
- g_sci->_gfxCursor32->unhide();
}
+#endif
+
+ g_system->fillScreen(0);
+ g_sci->_gfxCursor32->unhide();
_decoder->close();
_status = kAVINotOpen;
- if (_bitmap != NULL_REG) {
- _segMan->freeBitmap(_bitmap);
- _bitmap = NULL_REG;
- }
- g_sci->_gfxFrameout->deletePlane(*_plane);
- _plane = nullptr;
- _screenItem = nullptr;
return kIOSuccess;
}
@@ -395,91 +365,77 @@ uint16 AVIPlayer::getDuration() const {
return _decoder->getFrameCount();
}
-void AVIPlayer::renderFrame() const {
- const Graphics::Surface *surface = _decoder->decodeNextFrame();
+template <Graphics::TFilteringMode MODE>
+static void writeFrameToSystem(const Graphics::Surface *nextFrame, Video::VideoDecoder *decoder, const Common::Rect &drawRect) {
+ assert(nextFrame);
- if (surface->format.bytesPerPixel == 1) {
- SciBitmap &bitmap = *_segMan->lookupBitmap(_bitmap);
- if (surface->w > bitmap.getWidth() || surface->h > bitmap.getHeight()) {
- warning("Attempted to draw a video frame larger than the destination bitmap");
- return;
+ bool freeConvertedFrame;
+ Graphics::Surface *convertedFrame;
+ // Avoid creating a duplicate copy of the surface when it is not necessary
+ if (decoder->getPixelFormat() == g_system->getScreenFormat()) {
+ freeConvertedFrame = false;
+ convertedFrame = const_cast<Graphics::Surface *>(nextFrame);
+ } else {
+ freeConvertedFrame = true;
+ convertedFrame = nextFrame->convertTo(g_system->getScreenFormat(), decoder->getPalette());
+ }
+ assert(convertedFrame);
+
+ if (decoder->getWidth() != drawRect.width() || decoder->getHeight() != drawRect.height()) {
+ Graphics::Surface *const unscaledFrame(convertedFrame);
+ const Graphics::TransparentSurface tsUnscaledFrame(*unscaledFrame);
+ convertedFrame = tsUnscaledFrame.scaleT<MODE>(drawRect.width(), drawRect.height());
+ assert(convertedFrame);
+ if (freeConvertedFrame) {
+ unscaledFrame->free();
+ delete unscaledFrame;
}
+ freeConvertedFrame = true;
+ }
- // KQ7 1.51 encodes videos with palette entry 0 as white, which makes
- // the area around the video turn white too, since it is coded to use
- // palette entry 0. This happens to work in the original game because
- // the video is rendered by VfW, not in the engine itself. To fix this,
- // we just modify the incoming pixel data from the video so if a pixel
- // is using entry 0, we change it to use entry 255, which is guaranteed
- // to always be white
- if (getSciVersion() == SCI_VERSION_2_1_EARLY && g_sci->getGameId() == GID_KQ7) {
- uint8 *target = bitmap.getPixels();
- const uint8 *source = (const uint8 *)surface->getPixels();
- const uint8 *end = (const uint8 *)surface->getPixels() + surface->w * surface->h;
-
- while (source != end) {
- const uint8 value = *source++;
- *target++ = value == 0 ? 255 : value;
- }
- } else {
- bitmap.getBuffer().copyRectToSurface(*surface, 0, 0, Common::Rect(surface->w, surface->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;
+ }
+}
- const bool dirtyPalette = _decoder->hasDirtyPalette();
- if (dirtyPalette) {
- Palette palette;
- const byte *rawPalette = _decoder->getPalette();
- for (int i = 0; i < ARRAYSIZE(palette.colors); ++i) {
- palette.colors[i].r = *rawPalette++;
- palette.colors[i].g = *rawPalette++;
- palette.colors[i].b = *rawPalette++;
- palette.colors[i].used = true;
+void AVIPlayer::renderFrame() const {
+ // TODO: Improve efficiency by making changes to common Graphics code that
+ // allow reuse of a single conversion surface for all frames
+
+ const Graphics::Surface *nextFrame = _decoder->decodeNextFrame();
+ assert(nextFrame);
+
+ if (g_system->getScreenFormat().bytesPerPixel == 1 && _decoder->hasDirtyPalette()) {
+ const uint8 *palette = _decoder->getPalette();
+ 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;
}
-
- // Prevent KQ7 1.51 from setting entry 0 to white
- palette.colors[0].used = false;
-
- g_sci->_gfxPalette32->submit(palette);
+ palette += 3;
}
+ }
- g_sci->_gfxFrameout->updateScreenItem(*_screenItem);
- g_sci->_gfxFrameout->frameOut(true);
+#ifdef USE_RGB_COLOR
+ if (g_system->getScreenFormat().bytesPerPixel != 1) {
+ writeFrameToSystem<Graphics::FILTER_BILINEAR>(nextFrame, _decoder, _drawRect);
} else {
- assert(surface->format.bytesPerPixel == 4);
-
- const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
- const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
- const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
- const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
-
- Common::Rect drawRect(_drawRect);
- mulru(drawRect, Ratio(screenWidth, scriptWidth), Ratio(screenHeight, scriptHeight), 1);
-
- if (_pixelDouble) {
- const uint32 *source = (const uint32 *)surface->getPixels();
- uint32 *target = (uint32 *)_scaleBuffer;
- // target pitch here is in uint32s, not bytes, because the surface
- // bpp is 4
- const uint16 pitch = surface->pitch / 2;
- for (int y = 0; y < surface->h; ++y) {
- for (int x = 0; x < surface->w; ++x) {
- const uint32 value = *source++;
-
- target[0] = value;
- target[1] = value;
- target[pitch] = value;
- target[pitch + 1] = value;
- target += 2;
- }
- target += pitch;
- }
-
- 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_sci->_gfxFrameout->updateScreen();
+#else
+ {
+#endif
+ writeFrameToSystem<Graphics::FILTER_NEAREST>(nextFrame, _decoder, _drawRect);
}
}
diff --git a/engines/sci/graphics/video32.h b/engines/sci/graphics/video32.h
index 41e3016f12..a023fd4cd9 100644
--- a/engines/sci/graphics/video32.h
+++ b/engines/sci/graphics/video32.h
@@ -23,6 +23,9 @@
#ifndef SCI_GRAPHICS_VIDEO32_H
#define SCI_GRAPHICS_VIDEO32_H
+#ifdef USE_RGB_COLOR
+#include "common/config-manager.h" // for ConfMan
+#endif
#include "common/rect.h" // for Rect
#include "common/scummsys.h" // for int16, uint8, uint16, int32
#include "common/str.h" // for String
@@ -110,7 +113,7 @@ public:
kEventFlagHotRectangle = 8
};
- AVIPlayer(SegManager *segMan, EventManager *eventMan);
+ AVIPlayer(EventManager *eventMan);
~AVIPlayer();
/**
@@ -122,14 +125,7 @@ public:
* Initializes the AVI rendering parameters for the
* current AVI. This must be called after `open`.
*/
- IOStatus init1x(const int16 x, const int16 y, const int16 width, const int16 height);
-
- /**
- * Initializes the AVI rendering parameters for the
- * current AVI, in pixel-doubling mode. This must
- * be called after `open`.
- */
- IOStatus init2x(const int16 x, const int16 y);
+ IOStatus init(const bool pixelDouble);
/**
* Begins playback of the current AVI.
@@ -160,7 +156,6 @@ public:
private:
typedef Common::HashMap<uint16, AVIStatus> StatusMap;
- SegManager *_segMan;
EventManager *_eventMan;
Video::AVIDecoder *_decoder;
@@ -170,48 +165,12 @@ private:
AVIStatus _status;
/**
- * The plane where the AVI will be drawn.
- */
- Plane *_plane;
-
- /**
- * The screen item representing the AVI surface,
- * in 8bpp mode. In 24bpp mode, video is drawn
- * directly to the screen.
- */
- ScreenItem *_screenItem;
-
- /**
- * The bitmap used to render video output in
- * 8bpp mode.
- */
- reg_t _bitmap;
-
- /**
* The rectangle where the video will be drawn,
- * in game script coordinates.
+ * in screen coordinates.
*/
Common::Rect _drawRect;
/**
- * The scale buffer for pixel-doubled videos
- * drawn in 24bpp mode.
- */
- void *_scaleBuffer;
-
- /**
- * In SCI2.1, whether or not the video should
- * be pixel doubled for playback.
- */
- bool _pixelDouble;
-
- /**
- * Performs common initialisation for both
- * scaled and unscaled videos.
- */
- void init();
-
- /**
* Renders video without event input until the
* video is complete.
*/
@@ -657,7 +616,7 @@ class Video32 : public Common::Serializable {
public:
Video32(SegManager *segMan, EventManager *eventMan) :
_SEQPlayer(segMan, eventMan),
- _AVIPlayer(segMan, eventMan),
+ _AVIPlayer(eventMan),
_VMDPlayer(segMan, eventMan),
_robotPlayer(segMan),
_duckPlayer(segMan, eventMan) {}
diff --git a/engines/sci/sci.h b/engines/sci/sci.h
index 8109317cdf..ff3b515dcc 100644
--- a/engines/sci/sci.h
+++ b/engines/sci/sci.h
@@ -56,6 +56,7 @@ namespace Sci {
// HIGH_RESOLUTION_GRAPHICS availability is checked for in SciEngine::run()
#define GAMEOPTION_HIGH_RESOLUTION_GRAPHICS GUIO_GAMEOPTIONS8
#define GAMEOPTION_ENABLE_BLACK_LINED_VIDEO GUIO_GAMEOPTIONS9
+#define GAMEOPTION_HQ_VIDEO GUIO_GAMEOPTIONS10
struct EngineState;
class Vocabulary;