From 8cb35442c073b5ed5a0f3fa7d5e627bdd85af229 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Mon, 3 Jul 2017 20:11:02 -0500 Subject: 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. --- engines/sci/detection.cpp | 12 ++ engines/sci/detection_tables.h | 31 ++-- engines/sci/engine/kvideo.cpp | 17 +- engines/sci/graphics/video32.cpp | 350 +++++++++++++++++---------------------- engines/sci/graphics/video32.h | 55 +----- engines/sci/sci.h | 1 + 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(width, screenWidth); + height = MIN(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 +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(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(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(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(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 StatusMap; - SegManager *_segMan; EventManager *_eventMan; Video::AVIDecoder *_decoder; @@ -169,48 +164,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; -- cgit v1.2.3