aboutsummaryrefslogtreecommitdiff
path: root/engines/sci
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sci')
-rw-r--r--engines/sci/detection_tables.h50
-rw-r--r--engines/sci/engine/segment.h25
-rw-r--r--engines/sci/graphics/frameout.cpp68
-rw-r--r--engines/sci/graphics/frameout.h13
-rw-r--r--engines/sci/graphics/helpers.h42
-rw-r--r--engines/sci/graphics/palette32.cpp40
-rw-r--r--engines/sci/graphics/palette32.h27
-rw-r--r--engines/sci/graphics/plane32.cpp3
-rw-r--r--engines/sci/graphics/video32.cpp367
-rw-r--r--engines/sci/graphics/video32.h70
10 files changed, 505 insertions, 200 deletions
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h
index b5c426d836..9378f2b1ec 100644
--- a/engines/sci/detection_tables.h
+++ b/engines/sci/detection_tables.h
@@ -846,13 +846,14 @@ static const struct ADGameDescription SciGameDescriptions[] = {
GUIO_NOLAUNCHLOAD, \
GUIO_NOASPECT, \
GAMEOPTION_HQ_VIDEO)
-#define GUIO_GK2 GUIO7(GUIO_NOSUBTITLES, \
+#define GUIO_GK2 GUIO8(GUIO_NOSUBTITLES, \
GUIO_NOSFX, \
GUIO_NOSPEECHVOLUME, \
GUIO_NOMIDI, \
GUIO_NOASPECT, \
GAMEOPTION_ORIGINAL_SAVELOAD, \
- GAMEOPTION_ENABLE_BLACK_LINED_VIDEO)
+ GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GAMEOPTION_HQ_VIDEO)
#define GUIO_GK2_MAC GUIO_GK2
// Gabriel Knight 2 - English Windows Non-Interactive Demo
@@ -2695,10 +2696,11 @@ static const struct ADGameDescription SciGameDescriptions[] = {
GUIO_NOMIDI, \
GUIO_NOLAUNCHLOAD, \
GAMEOPTION_ORIGINAL_SAVELOAD)
-#define GUIO_LSL7 GUIO4(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+#define GUIO_LSL7 GUIO5(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
GUIO_NOASPECT, \
GUIO_NOMIDI, \
- GAMEOPTION_ORIGINAL_SAVELOAD)
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_HQ_VIDEO)
// Larry 7 - English DOS Demo (provided by richiefs in bug report #2670691)
// SCI interpreter version 2.100.002
@@ -2758,9 +2760,10 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#define GUIO_LIGHTHOUSE_DEMO GUIO3(GUIO_NOSPEECH, \
GUIO_NOASPECT, \
GAMEOPTION_ORIGINAL_SAVELOAD)
-#define GUIO_LIGHTHOUSE GUIO3(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+#define GUIO_LIGHTHOUSE GUIO4(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
GUIO_NOASPECT, \
- GAMEOPTION_ORIGINAL_SAVELOAD)
+ GAMEOPTION_ORIGINAL_SAVELOAD, \
+ GAMEOPTION_HQ_VIDEO)
// Lighthouse - English Windows Demo (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.00"
@@ -2979,11 +2982,12 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#ifdef ENABLE_SCI32
-#define GUIO_PHANTASMAGORIA_DEMO GUIO5(GUIO_NOSUBTITLES, \
+#define GUIO_PHANTASMAGORIA_DEMO GUIO6(GUIO_NOSUBTITLES, \
GUIO_NOASPECT, \
GUIO_NOLAUNCHLOAD, \
GUIO_LINKSPEECHTOSFX, \
- GAMEOPTION_ENABLE_BLACK_LINED_VIDEO)
+ GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GAMEOPTION_HQ_VIDEO)
#define GUIO_PHANTASMAGORIA GUIO_PHANTASMAGORIA_DEMO
#define GUIO_PHANTASMAGORIA_MAC GUIO_PHANTASMAGORIA_DEMO
@@ -3509,12 +3513,13 @@ static const struct ADGameDescription SciGameDescriptions[] = {
GUIO_LINKSPEECHTOSFX, \
GUIO_NOASPECT, \
GUIO_NOLAUNCHLOAD)
-#define GUIO_PQSWAT GUIO6(GUIO_NOSUBTITLES, \
+#define GUIO_PQSWAT GUIO7(GUIO_NOSUBTITLES, \
GUIO_NOMIDI, \
GUIO_LINKMUSICTOSFX, \
GUIO_LINKSPEECHTOSFX, \
GUIO_NOASPECT, \
- GAMEOPTION_ENABLE_BLACK_LINED_VIDEO)
+ GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GAMEOPTION_HQ_VIDEO)
// Police Quest: SWAT - English DOS/Windows Demo (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "0.001.200"
@@ -3957,14 +3962,16 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#undef GUIO_QFG4_CD
// TODO: Correct GUIOs
-#define GUIO_RAMA_DEMO GUIO4(GUIO_NOMIDI, \
+#define GUIO_RAMA_DEMO GUIO5(GUIO_NOMIDI, \
GUIO_NOLAUNCHLOAD, \
GUIO_NOASPECT, \
- GAMEOPTION_ENABLE_BLACK_LINED_VIDEO)
-#define GUIO_RAMA GUIO4(GUIO_NOMIDI, \
+ GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GAMEOPTION_HQ_VIDEO)
+#define GUIO_RAMA GUIO5(GUIO_NOMIDI, \
GUIO_NOASPECT, \
GAMEOPTION_ORIGINAL_SAVELOAD, \
- GAMEOPTION_ENABLE_BLACK_LINED_VIDEO)
+ GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GAMEOPTION_HQ_VIDEO)
// RAMA - English DOS/Windows Demo
// Executable scanning reports "2.100.002", VERSION file reports "000.000.008"
@@ -4040,11 +4047,12 @@ static const struct ADGameDescription SciGameDescriptions[] = {
GUIO_LINKSPEECHTOSFX, \
GUIO_LINKMUSICTOSFX, \
GUIO_NOASPECT)
-#define GUIO_SHIVERS GUIO5(GUIO_NOMIDI, \
+#define GUIO_SHIVERS GUIO6(GUIO_NOMIDI, \
GUIO_LINKSPEECHTOSFX, \
GUIO_LINKMUSICTOSFX, \
GUIO_NOASPECT, \
- GAMEOPTION_ENABLE_BLACK_LINED_VIDEO)
+ GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GAMEOPTION_HQ_VIDEO)
// Shivers - English Windows (from jvprat)
// Executable scanning reports "2.100.002", VERSION file reports "1.02"
@@ -4661,10 +4669,11 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#define GUIO_SQ6_DEMO GUIO3(GUIO_NOLAUNCHLOAD, \
GUIO_LINKSPEECHTOSFX, \
GUIO_NOASPECT)
-#define GUIO_SQ6 GUIO4(GUIO_LINKSPEECHTOSFX, \
+#define GUIO_SQ6 GUIO5(GUIO_LINKSPEECHTOSFX, \
GUIO_NOASPECT, \
GAMEOPTION_ORIGINAL_SAVELOAD, \
- GAMEOPTION_ENABLE_BLACK_LINED_VIDEO)
+ GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GAMEOPTION_HQ_VIDEO)
// Space Quest 6 - English DOS/Win3.11 CD (from the Space Quest Collection)
// Executable scanning reports "2.100.002", VERSION file reports "1.0"
@@ -4740,10 +4749,11 @@ static const struct ADGameDescription SciGameDescriptions[] = {
#define GUIO_TORIN_DEMO GUIO3(GUIO_NOMIDI, \
GUIO_NOLAUNCHLOAD, \
GUIO_NOASPECT)
-#define GUIO_TORIN GUIO4(GUIO_NOMIDI, \
+#define GUIO_TORIN GUIO5(GUIO_NOMIDI, \
GUIO_NOASPECT, \
GAMEOPTION_ORIGINAL_SAVELOAD, \
- GAMEOPTION_ENABLE_BLACK_LINED_VIDEO)
+ GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \
+ GAMEOPTION_HQ_VIDEO)
#define GUIO_TORIN_MAC GUIO_TORIN
// Torin's Passage - English Windows Interactive Demo
diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h
index adc8f138be..8eca6713b3 100644
--- a/engines/sci/engine/segment.h
+++ b/engines/sci/engine/segment.h
@@ -29,6 +29,9 @@
#include "sci/engine/vm.h"
#include "sci/engine/vm_types.h" // for reg_t
#include "sci/util.h"
+#ifdef ENABLE_SCI32
+#include "sci/graphics/palette32.h"
+#endif
namespace Sci {
@@ -1026,7 +1029,7 @@ public:
setRemap(remap);
setDataSize(width * height);
WRITE_SCI11ENDIAN_UINT32(_data + 16, 0);
- setHunkPaletteOffset(paletteSize > 0 ? (width * height) : 0);
+ setHunkPaletteOffset(paletteSize > 0 ? (bitmapHeaderSize + width * height) : 0);
setDataOffset(bitmapHeaderSize);
setUncompressedDataOffset(bitmapHeaderSize);
setControlOffset(0);
@@ -1099,17 +1102,7 @@ public:
BITMAP_PROPERTY(32, DataSize, 12);
- inline uint32 getHunkPaletteOffset() const {
- return READ_SCI11ENDIAN_UINT32(_data + 20);
- }
-
- inline void setHunkPaletteOffset(uint32 hunkPaletteOffset) {
- if (hunkPaletteOffset) {
- hunkPaletteOffset += getBitmapHeaderSize();
- }
-
- WRITE_SCI11ENDIAN_UINT32(_data + 20, hunkPaletteOffset);
- }
+ BITMAP_PROPERTY(32, HunkPaletteOffset, 20);
BITMAP_PROPERTY(32, DataOffset, 24);
@@ -1162,6 +1155,14 @@ public:
return _data + getHunkPaletteOffset();
}
+ inline void setPalette(const Palette &palette) {
+ byte *paletteData = getHunkPalette();
+ if (paletteData != nullptr) {
+ SciSpan<byte> paletteSpan(paletteData, getRawSize() - getHunkPaletteOffset());
+ HunkPalette::write(paletteSpan, palette);
+ }
+ }
+
virtual void saveLoadWithSerializer(Common::Serializer &ser);
void applyRemap(SciArray &clut) {
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index 80800da074..7415979f5c 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -46,6 +46,7 @@
#include "sci/graphics/cursor32.h"
#include "sci/graphics/font.h"
#include "sci/graphics/frameout.h"
+#include "sci/graphics/helpers.h"
#include "sci/graphics/paint32.h"
#include "sci/graphics/palette32.h"
#include "sci/graphics/plane32.h"
@@ -548,47 +549,20 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *show
showBits();
}
-/**
- * Determines the parts of `r` that aren't overlapped by `other`.
- * Returns -1 if `r` and `other` have no intersection.
- * Returns number of returned parts (in `outRects`) otherwise.
- * (In particular, this returns 0 if `r` is contained in `other`.)
- */
-int splitRects(Common::Rect r, const Common::Rect &other, Common::Rect(&outRects)[4]) {
- if (!r.intersects(other)) {
- return -1;
- }
-
- int splitCount = 0;
- if (r.top < other.top) {
- Common::Rect &t = outRects[splitCount++];
- t = r;
- t.bottom = other.top;
- r.top = other.top;
- }
-
- if (r.bottom > other.bottom) {
- Common::Rect &t = outRects[splitCount++];
- t = r;
- t.top = other.bottom;
- r.bottom = other.bottom;
- }
-
- if (r.left < other.left) {
- Common::Rect &t = outRects[splitCount++];
- t = r;
- t.right = other.left;
- r.left = other.left;
- }
-
- if (r.right > other.right) {
- Common::Rect &t = outRects[splitCount++];
- t = r;
- t.left = other.right;
- }
+void GfxFrameout::directFrameOut(const Common::Rect &showRect) {
+ updateMousePositionForRendering();
+ _showList.add(showRect);
+ showBits();
+}
- return splitCount;
+#ifdef USE_RGB_COLOR
+void GfxFrameout::resetHardware() {
+ updateMousePositionForRendering();
+ _showList.add(Common::Rect(getCurrentBuffer().screenWidth, getCurrentBuffer().screenHeight));
+ g_system->getPaletteManager()->setPalette(_palette->getHardwarePalette(), 0, 256);
+ showBits();
}
+#endif
/**
* Determines the parts of `middleRect` that aren't overlapped
@@ -1041,7 +1015,21 @@ void GfxFrameout::showBits() {
continue;
}
- g_system->copyRectToScreen(sourceBuffer, _currentBuffer.screenWidth, rounded.left, rounded.top, rounded.width(), rounded.height());
+#ifdef USE_RGB_COLOR
+ if (g_system->getScreenFormat() != _currentBuffer.format) {
+ // This happens (at least) when playing a video in Shivers with
+ // HQ video on & subtitles on
+ Graphics::Surface *screenSurface = _currentBuffer.getSubArea(rounded).convertTo(g_system->getScreenFormat(), _palette->getHardwarePalette());
+ assert(screenSurface);
+ g_system->copyRectToScreen(screenSurface->getPixels(), screenSurface->pitch, rounded.left, rounded.top, screenSurface->w, screenSurface->h);
+ screenSurface->free();
+ delete screenSurface;
+ } else {
+#else
+ {
+#endif
+ g_system->copyRectToScreen(sourceBuffer, _currentBuffer.screenWidth, rounded.left, rounded.top, rounded.width(), rounded.height());
+ }
}
_cursor->donePainting();
diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h
index 4bdb172e5e..26732d5c01 100644
--- a/engines/sci/graphics/frameout.h
+++ b/engines/sci/graphics/frameout.h
@@ -340,6 +340,19 @@ public:
void palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *showStyle);
/**
+ * Draws the given rect from the internal screen buffer to hardware without
+ * processing any other graphics updates except for cursor changes.
+ */
+ void directFrameOut(const Common::Rect &showRect);
+
+#ifdef USE_RGB_COLOR
+ /**
+ * Sends the entire internal screen buffer and palette to hardware.
+ */
+ void resetHardware();
+#endif
+
+ /**
* Modifies the raw pixel data for the next frame with
* new palette indexes based on matched style ranges.
*/
diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h
index 1da3749c90..52699c6242 100644
--- a/engines/sci/graphics/helpers.h
+++ b/engines/sci/graphics/helpers.h
@@ -187,6 +187,48 @@ inline void mulru(Common::Rect &rect, const Common::Rational &ratioX, const Comm
rect.bottom = mulru(rect.bottom - 1, ratioY, extra) + 1;
}
+/**
+ * Determines the parts of `r` that aren't overlapped by `other`.
+ * Returns -1 if `r` and `other` have no intersection.
+ * Returns number of returned parts (in `outRects`) otherwise.
+ * (In particular, this returns 0 if `r` is contained in `other`.)
+ */
+inline int splitRects(Common::Rect r, const Common::Rect &other, Common::Rect(&outRects)[4]) {
+ if (!r.intersects(other)) {
+ return -1;
+ }
+
+ int splitCount = 0;
+ if (r.top < other.top) {
+ Common::Rect &t = outRects[splitCount++];
+ t = r;
+ t.bottom = other.top;
+ r.top = other.top;
+ }
+
+ if (r.bottom > other.bottom) {
+ Common::Rect &t = outRects[splitCount++];
+ t = r;
+ t.top = other.bottom;
+ r.bottom = other.bottom;
+ }
+
+ if (r.left < other.left) {
+ Common::Rect &t = outRects[splitCount++];
+ t = r;
+ t.right = other.left;
+ r.left = other.left;
+ }
+
+ if (r.right > other.right) {
+ Common::Rect &t = outRects[splitCount++];
+ t = r;
+ t.left = other.right;
+ }
+
+ return splitCount;
+}
+
struct Buffer : public Graphics::Surface {
uint16 screenWidth;
uint16 screenHeight;
diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp
index acf63bc396..339461d157 100644
--- a/engines/sci/graphics/palette32.cpp
+++ b/engines/sci/graphics/palette32.cpp
@@ -53,6 +53,29 @@ HunkPalette::HunkPalette(const SciSpan<const byte> &rawPalette) :
}
}
+void HunkPalette::write(SciSpan<byte> &out, const Palette &palette) {
+ const uint8 numPalettes = 1;
+ const uint16 paletteOffset = kHunkPaletteHeaderSize + 2 * numPalettes;
+
+ out[kNumPaletteEntriesOffset] = numPalettes;
+ out[kHunkPaletteHeaderSize + 2] = paletteOffset;
+
+ SciSpan<byte> entry = out.subspan(paletteOffset);
+ entry[kEntryStartColorOffset] = 0;
+ entry.setUint16SEAt(kEntryNumColorsOffset, ARRAYSIZE(palette.colors));
+ entry[kEntryUsedOffset] = 1;
+ entry[kEntrySharedUsedOffset] = 0;
+ entry.setUint32SEAt(kEntryVersionOffset, 1);
+
+ SciSpan<byte> paletteData = entry.subspan(kEntryHeaderSize);
+ for (uint i = 0; i < ARRAYSIZE(palette.colors); ++i) {
+ *paletteData++ = palette.colors[i].used;
+ *paletteData++ = palette.colors[i].r;
+ *paletteData++ = palette.colors[i].g;
+ *paletteData++ = palette.colors[i].b;
+ }
+}
+
void HunkPalette::setVersion(const uint32 version) const {
if (_numPalettes != _data.getUint8At(kNumPaletteEntriesOffset)) {
error("Invalid HunkPalette");
@@ -333,6 +356,9 @@ static const uint8 gammaTables[GfxPalette32::numGammaTables][256] = {
// Palette versioning
_version(1),
_needsUpdate(false),
+#ifdef USE_RGB_COLOR
+ _hardwarePalette(),
+#endif
_currentPalette(),
_sourcePalette(),
_nextPalette(),
@@ -459,7 +485,11 @@ void GfxPalette32::updateHardware() {
return;
}
- byte bpal[3 * 256];
+#ifdef USE_RGB_COLOR
+ uint8 *bpal = _hardwarePalette;
+#else
+ uint8 bpal[256 * 3];
+#endif
// HACK: There are resources in a couple of Windows-only games that seem to
// include bogus palette entries above 236. SSCI does a lot of extra work
@@ -508,7 +538,13 @@ void GfxPalette32::updateHardware() {
bpal[255 * 3 + 2] = 0;
}
- g_system->getPaletteManager()->setPalette(bpal, 0, 256);
+ // If the system is in a high color mode, which can happen during video
+ // playback, attempting to send the palette to OSystem is illegal and will
+ // result in a crash
+ if (g_system->getScreenFormat().bytesPerPixel == 1) {
+ g_system->getPaletteManager()->setPalette(bpal, 0, 256);
+ }
+
_gammaChanged = false;
}
diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h
index fb0b226130..b8ba85eb4a 100644
--- a/engines/sci/graphics/palette32.h
+++ b/engines/sci/graphics/palette32.h
@@ -37,6 +37,16 @@ class HunkPalette {
public:
HunkPalette(const SciSpan<const byte> &rawPalette);
+ static void write(SciSpan<byte> &out, const Palette &palette);
+
+ static uint32 calculateHunkPaletteSize(const uint16 numIndexes = 256, const bool sharedUsed = true) {
+ const int numPalettes = 1;
+ return kHunkPaletteHeaderSize +
+ /* slack bytes between hunk header & palette offset table */ 2 +
+ /* palette offset table */ 2 * numPalettes +
+ /* palette data */ (kEntryHeaderSize + numIndexes * (/* RGB */ 3 + !sharedUsed)) * numPalettes;
+ }
+
/**
* Gets the version of the palette. Used to avoid resubmitting a HunkPalette
* which has already been submitted for the next frame.
@@ -237,6 +247,14 @@ public:
*/
inline const Palette &getCurrentPalette() const { return _currentPalette; };
+#ifdef USE_RGB_COLOR
+ /**
+ * Gets the raw hardware palette in RGB format. This should be used instead
+ * of `::PaletteManager::grabPalette` when the OSystem screen is >8bpp.
+ */
+ inline const uint8 *getHardwarePalette() const { return _hardwarePalette; };
+#endif
+
/**
* Loads a palette into GfxPalette32 with the given resource ID.
*/
@@ -288,6 +306,15 @@ private:
*/
bool _needsUpdate;
+#ifdef USE_RGB_COLOR
+ /**
+ * A local copy of the hardware palette. Used when the backend is in a true
+ * color mode and a change to the game's internal framebuffer occurs that
+ * needs to be reconverted from 8bpp to the backend's bit depth.
+ */
+ uint8 _hardwarePalette[256 * 3];
+#endif
+
/**
* The currently displayed palette.
*/
diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp
index 0a69ab2a98..47d41bc752 100644
--- a/engines/sci/graphics/plane32.cpp
+++ b/engines/sci/graphics/plane32.cpp
@@ -26,6 +26,7 @@
#include "sci/engine/selector.h"
#include "sci/engine/state.h"
#include "sci/graphics/frameout.h"
+#include "sci/graphics/helpers.h"
#include "sci/graphics/lists32.h"
#include "sci/graphics/plane32.h"
#include "sci/graphics/remap32.h"
@@ -262,8 +263,6 @@ void Plane::deleteAllPics() {
#pragma mark -
#pragma mark Plane - Rendering
-extern int splitRects(Common::Rect r, const Common::Rect &other, Common::Rect(&outRects)[4]);
-
void Plane::breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList) const {
const int nextPlaneIndex = planeList.findIndexByObject(_object) + 1;
const PlaneList::size_type planeCount = planeList.size();
diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp
index ecde85b622..b05b9e6868 100644
--- a/engines/sci/graphics/video32.cpp
+++ b/engines/sci/graphics/video32.cpp
@@ -562,9 +562,10 @@ VMDPlayer::IOStatus VMDPlayer::open(const Common::String &fileName, const OpenFl
return kIOError;
}
-void VMDPlayer::init(const int16 x, const int16 y, const PlayFlags flags, const int16 boostPercent, const int16 boostStartColor, const int16 boostEndColor) {
- _x = getSciVersion() >= SCI_VERSION_3 ? x : (x & ~1);
- _y = y;
+void VMDPlayer::init(int16 x, const int16 y, const PlayFlags flags, const int16 boostPercent, const int16 boostStartColor, const int16 boostEndColor) {
+ if (getSciVersion() < SCI_VERSION_3) {
+ x &= ~1;
+ }
_doublePixels = flags & kPlayFlagDoublePixels;
_blackLines = ConfMan.getBool("enable_black_lined_video") && (flags & kPlayFlagBlackLines);
// If ScummVM has been configured to disable black lines on video playback,
@@ -579,6 +580,11 @@ void VMDPlayer::init(const int16 x, const int16 y, const PlayFlags flags, const
_blackPalette = flags & kPlayFlagBlackPalette;
#endif
_stretchVertical = flags & kPlayFlagStretchVertical;
+
+ _drawRect = Common::Rect(x,
+ y,
+ x + (_decoder->getWidth() << _doublePixels),
+ y + (_decoder->getHeight() << (_doublePixels || _stretchVertical)));
}
VMDPlayer::IOStatus VMDPlayer::close() {
@@ -586,32 +592,10 @@ VMDPlayer::IOStatus VMDPlayer::close() {
return kIOSuccess;
}
- _decoder->close();
- _isOpen = false;
- _isInitialized = false;
- _ignorePalettes = false;
-
- if (_bundledVmd) {
- g_sci->getResMan()->unlockResource(_bundledVmd);
- _bundledVmd = nullptr;
- }
-
- if (_bitmapId != NULL_REG) {
- _segMan->freeBitmap(_bitmapId);
- _bitmapId = NULL_REG;
- }
-
- if (!_planeIsOwned && _screenItem != nullptr) {
- g_sci->_gfxFrameout->deleteScreenItem(*_screenItem);
- _screenItem = nullptr;
- } else if (_plane != nullptr) {
- g_sci->_gfxFrameout->deletePlane(*_plane);
- _plane = nullptr;
- }
-
- if (!_leaveLastFrame && _leaveScreenBlack) {
- // This call *actually* deletes the plane/screen item
- g_sci->_gfxFrameout->frameOut(true);
+ if (_isComposited) {
+ closeComposited();
+ } else {
+ closeOverlay();
}
if (_blackoutPlane != nullptr) {
@@ -624,13 +608,24 @@ VMDPlayer::IOStatus VMDPlayer::close() {
g_sci->_gfxFrameout->frameOut(true);
}
+ _decoder->close();
+
+ if (_bundledVmd) {
+ g_sci->getResMan()->unlockResource(_bundledVmd);
+ _bundledVmd = nullptr;
+ }
+
if (!_showCursor) {
g_sci->_gfxCursor32->unhide();
}
+ _isOpen = false;
+ _isInitialized = false;
+ _ignorePalettes = false;
_lastYieldedFrameNo = 0;
_planeIsOwned = true;
_priority = 0;
+ _drawRect = Common::Rect();
return kIOSuccess;
}
@@ -695,70 +690,21 @@ VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) {
g_sci->_gfxCursor32->hide();
}
- Common::Rect vmdRect(_x,
- _y,
- _x + _decoder->getWidth(),
- _y + _decoder->getHeight());
- ScaleInfo vmdScaleInfo;
-
if (!_blackoutRect.isEmpty() && _planeIsOwned) {
_blackoutPlane = new Plane(_blackoutRect);
g_sci->_gfxFrameout->addPlane(*_blackoutPlane);
}
- if (_doublePixels) {
- vmdScaleInfo.x = 256;
- vmdScaleInfo.y = 256;
- vmdScaleInfo.signal = kScaleSignalManual;
- vmdRect.right += vmdRect.width();
- vmdRect.bottom += vmdRect.height();
- } else if (_stretchVertical) {
- vmdScaleInfo.y = 256;
- vmdScaleInfo.signal = kScaleSignalManual;
- vmdRect.bottom += vmdRect.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;
-
- SciBitmap &vmdBitmap = *_segMan->allocateBitmap(&_bitmapId, vmdRect.width(), vmdRect.height(), 255, 0, 0, screenWidth, screenHeight, 0, false, false);
- vmdBitmap.getBuffer().fillRect(Common::Rect(vmdRect.width(), vmdRect.height()), 0);
-
- if (screenWidth != scriptWidth || screenHeight != scriptHeight) {
- mulru(vmdRect, Ratio(scriptWidth, screenWidth), Ratio(scriptHeight, screenHeight), 1);
- }
-
- CelInfo32 vmdCelInfo;
- vmdCelInfo.bitmap = _bitmapId;
- _decoder->setSurfaceMemory(vmdBitmap.getPixels(), vmdBitmap.getWidth(), vmdBitmap.getHeight(), 1);
-
- if (_planeIsOwned) {
- _x = 0;
- _y = 0;
- _plane = new Plane(vmdRect, kPlanePicColored);
- if (_priority) {
- _plane->_priority = _priority;
- }
- g_sci->_gfxFrameout->addPlane(*_plane);
- _screenItem = new ScreenItem(_plane->_object, vmdCelInfo, Common::Point(), vmdScaleInfo);
+ if (shouldUseCompositing()) {
+ _isComposited = true;
+ _usingHighColor = false;
+ initComposited();
} else {
- _screenItem = new ScreenItem(_plane->_object, vmdCelInfo, Common::Point(_x, _y), vmdScaleInfo);
- if (_priority) {
- _screenItem->_priority = _priority;
- }
- }
-
- if (_blackLines) {
- _screenItem->_drawBlackLines = true;
+ _isComposited = false;
+ _usingHighColor = shouldUseHighColor();
+ initOverlay();
}
- // NOTE: There was code for positioning the screen item using insetRect
- // here, but none of the game scripts seem to use this functionality.
-
- g_sci->_gfxFrameout->addScreenItem(*_screenItem);
-
_decoder->start();
}
@@ -830,66 +776,245 @@ VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) {
return stopFlag;
}
-#pragma mark -
-#pragma mark VMDPlayer - Rendering
+void VMDPlayer::initOverlay() {
+ g_sci->_gfxFrameout->frameOut(true);
-void VMDPlayer::renderFrame() const {
- // This writes directly to the CelObjMem we already created,
- // so no need to take its return value
- _decoder->decodeNextFrame();
+#ifdef USE_RGB_COLOR
+ // TODO: Allow interpolation for videos where the cursor is drawn, either by
+ // 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);
+ redrawGameScreen();
+ }
+#endif
+}
- // NOTE: Normally this would write a hunk palette at the end of the
- // video bitmap that CelObjMem would read out and submit, but instead
- // we are just submitting it directly here because the decoder exposes
- // this information a little bit differently than the one in SSCI
- const bool dirtyPalette = _decoder->hasDirtyPalette();
- if (dirtyPalette && !_ignorePalettes) {
- Palette palette;
- for (uint16 i = 0; i < _startColor; ++i) {
- palette.colors[i].used = false;
+#ifdef USE_RGB_COLOR
+void VMDPlayer::redrawGameScreen() const {
+ Graphics::Surface *game = g_sci->_gfxFrameout->getCurrentBuffer().convertTo(g_system->getScreenFormat(), g_sci->_gfxPalette32->getHardwarePalette());
+
+ Common::Rect rects[4];
+ int splitCount = splitRects(Common::Rect(game->w, game->h), _drawRect, rects);
+ if (splitCount != -1) {
+ while (splitCount--) {
+ const Common::Rect &drawRect = rects[splitCount];
+ g_system->copyRectToScreen(game->getBasePtr(drawRect.left, drawRect.top), game->pitch, drawRect.left, drawRect.top, drawRect.width(), drawRect.height());
}
- for (uint16 i = _endColor; i < 256; ++i) {
- palette.colors[i].used = false;
+ }
+
+ game->free();
+ delete game;
+}
+#endif
+
+void VMDPlayer::renderOverlay() const {
+ const Graphics::Surface *nextFrame = _decoder->decodeNextFrame();
+
+#ifdef USE_RGB_COLOR
+ if (_usingHighColor) {
+ if (updatePalette()) {
+ redrawGameScreen();
}
-#if SCI_VMD_BLACK_PALETTE
- if (_blackPalette) {
- for (uint16 i = _startColor; i <= _endColor; ++i) {
- palette.colors[i].r = palette.colors[i].g = palette.colors[i].b = 0;
- palette.colors[i].used = true;
+
+ writeFrameToSystem<Graphics::FILTER_BILINEAR>(nextFrame, _decoder, _drawRect);
+ } else {
+#else
+ {
+#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
+ } else {
+ out.copyRectToSurface(nextFrame->getPixels(), nextFrame->pitch, 0, 0, nextFrame->w, nextFrame->h);
+ }
+
+ g_sci->_gfxFrameout->directFrameOut(_drawRect);
+ }
+}
+
+bool VMDPlayer::updatePalette() const {
+ if (_ignorePalettes || !_decoder->hasDirtyPalette()) {
+ return false;
+ }
+
+ Palette palette;
+ for (uint16 i = 0; i < _startColor; ++i) {
+ palette.colors[i].used = false;
+ }
+ for (uint16 i = _endColor + 1; i < ARRAYSIZE(palette.colors); ++i) {
+ palette.colors[i].used = false;
+ }
+#if SCI_VMD_BLACK_PALETTE
+ if (_blackPalette) {
+ for (uint16 i = _startColor; i <= _endColor; ++i) {
+ palette.colors[i].r = palette.colors[i].g = palette.colors[i].b = 0;
+ palette.colors[i].used = true;
+ }
+ } else
#endif
- fillPalette(palette);
+ fillPalette(palette);
- g_sci->_gfxPalette32->submit(palette);
+ if (_isComposited) {
+ SciBitmap *bitmap = _segMan->lookupBitmap(_bitmapId);
+ bitmap->setPalette(palette);
g_sci->_gfxFrameout->updateScreenItem(*_screenItem);
g_sci->_gfxFrameout->frameOut(true);
+ } else {
+ g_sci->_gfxPalette32->submit(palette);
+ g_sci->_gfxPalette32->updateForFrame();
+ g_sci->_gfxPalette32->updateHardware();
+ }
#if SCI_VMD_BLACK_PALETTE
- if (_blackPalette) {
- fillPalette(palette);
- g_sci->_gfxPalette32->submit(palette);
- g_sci->_gfxPalette32->updateForFrame();
- g_sci->_gfxPalette32->updateHardware();
+ if (_blackPalette) {
+ fillPalette(palette);
+ if (_isComposited) {
+ SciBitmap *bitmap = _segMan->lookupBitmap(_bitmapId);
+ bitmap->setPalette(palette);
}
+ g_sci->_gfxPalette32->submit(palette);
+ g_sci->_gfxPalette32->updateForFrame();
+ g_sci->_gfxPalette32->updateHardware();
+ }
#endif
+
+ return true;
+}
+
+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);
+ }
+}
+
+void VMDPlayer::initComposited() {
+ ScaleInfo vmdScaleInfo;
+
+ if (_doublePixels) {
+ vmdScaleInfo.x *= 2;
+ vmdScaleInfo.y *= 2;
+ vmdScaleInfo.signal = kScaleSignalManual;
+ } else if (_stretchVertical) {
+ vmdScaleInfo.y *= 2;
+ vmdScaleInfo.signal = kScaleSignalManual;
+ }
+
+ const uint32 hunkPaletteSize = HunkPalette::calculateHunkPaletteSize(256, false);
+ const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
+
+ SciBitmap &vmdBitmap = *_segMan->allocateBitmap(&_bitmapId, _drawRect.width(), _drawRect.height(), 255, 0, 0, screenWidth, screenHeight, hunkPaletteSize, false, false);
+ vmdBitmap.getBuffer().fillRect(Common::Rect(_drawRect.width(), _drawRect.height()), 0);
+
+ CelInfo32 vmdCelInfo;
+ vmdCelInfo.bitmap = _bitmapId;
+ _decoder->setSurfaceMemory(vmdBitmap.getPixels(), vmdBitmap.getWidth(), vmdBitmap.getHeight(), 1);
+
+ if (_planeIsOwned) {
+ _plane = new Plane(_drawRect, kPlanePicColored);
+ if (_priority) {
+ _plane->_priority = _priority;
+ }
+ g_sci->_gfxFrameout->addPlane(*_plane);
+ _screenItem = new ScreenItem(_plane->_object, vmdCelInfo, Common::Point(), vmdScaleInfo);
+ } else {
+ _screenItem = new ScreenItem(_plane->_object, vmdCelInfo, Common::Point(_drawRect.left, _drawRect.top), vmdScaleInfo);
+ if (_priority) {
+ _screenItem->_priority = _priority;
+ }
+ }
+
+ if (_blackLines) {
+ _screenItem->_drawBlackLines = true;
+ }
+
+ // NOTE: There was code for positioning the screen item using insetRect
+ // here, but none of the game scripts seem to use this functionality.
+
+ g_sci->_gfxFrameout->addScreenItem(*_screenItem);
+}
+
+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);
}
}
+void VMDPlayer::closeComposited() {
+ if (_bitmapId != NULL_REG) {
+ _segMan->freeBitmap(_bitmapId);
+ _bitmapId = NULL_REG;
+ }
+
+ if (!_planeIsOwned && _screenItem != nullptr) {
+ g_sci->_gfxFrameout->deleteScreenItem(*_screenItem);
+ _screenItem = nullptr;
+ } else if (_plane != nullptr) {
+ g_sci->_gfxFrameout->deletePlane(*_plane);
+ _plane = nullptr;
+ }
+
+ if (!_leaveLastFrame && _leaveScreenBlack) {
+ // This call *actually* deletes the plane/screen item
+ g_sci->_gfxFrameout->frameOut(true);
+ }
+}
+
+#pragma mark -
+#pragma mark VMDPlayer - Rendering
+
+void VMDPlayer::renderFrame() const {
+ if (_isComposited) {
+ renderComposited();
+ } else {
+ renderOverlay();
+ }
+}
+
void VMDPlayer::fillPalette(Palette &palette) const {
const byte *vmdPalette = _decoder->getPalette() + _startColor * 3;
for (uint16 i = _startColor; i <= _endColor; ++i) {
- int16 r = *vmdPalette++;
- int16 g = *vmdPalette++;
- int16 b = *vmdPalette++;
+ uint8 r = *vmdPalette++;
+ uint8 g = *vmdPalette++;
+ uint8 b = *vmdPalette++;
if (_boostPercent != 100 && i >= _boostStartColor && i <= _boostEndColor) {
- r = CLIP<int16>(r * _boostPercent / 100, 0, 255);
- g = CLIP<int16>(g * _boostPercent / 100, 0, 255);
- b = CLIP<int16>(b * _boostPercent / 100, 0, 255);
+ r = CLIP(r * _boostPercent / 100, 0, 255);
+ g = CLIP(g * _boostPercent / 100, 0, 255);
+ b = CLIP(b * _boostPercent / 100, 0, 255);
}
palette.colors[i].r = r;
diff --git a/engines/sci/graphics/video32.h b/engines/sci/graphics/video32.h
index a023fd4cd9..0f63996040 100644
--- a/engines/sci/graphics/video32.h
+++ b/engines/sci/graphics/video32.h
@@ -308,6 +308,14 @@ private:
*/
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.).
@@ -330,10 +338,9 @@ public:
private:
/**
- * The location of the VMD plane, in game script
- * coordinates.
+ * The rectangle where the video will be drawn, in screen coordinates.
*/
- int16 _x, _y;
+ Common::Rect _drawRect;
/**
* The plane where the VMD will be drawn.
@@ -399,17 +406,74 @@ private:
bool _ignorePalettes;
/**
+ * Whether or not rendering mode is composited.
+ */
+ 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.
+ */
+ 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;
+#ifdef USE_RGB_COLOR
+ /**
+ * Redraws areas of the screen outside of the video to the system buffer.
+ * This is used when
+ */
+ void redrawGameScreen() const;
+#endif
+
+ /**
+ * Determines whether or not the VMD player should upgrade the renderer to
+ * high color depth when rendering the video.
+ *
+ * @TODO It should be possible in the future to allow high color composited
+ * 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
+ }
+
+ /**
+ * Determines whether or not the video should use the compositing renderer
+ * instead of the overlay renderer.
+ */
+ bool shouldUseCompositing() const {
+#ifdef USE_RGB_COLOR
+ return getSciVersion() == SCI_VERSION_3 && !shouldUseHighColor();
+#else
+ return getSciVersion() == SCI_VERSION_3;
+#endif
+ }
+
#pragma mark -
#pragma mark VMDPlayer - Blackout
public: