diff options
author | sluicebox | 2019-12-04 09:06:35 -0800 |
---|---|---|
committer | sluicebox | 2019-12-04 09:11:21 -0800 |
commit | 05b21ace680d65336219a74cd87696eb157f7e84 (patch) | |
tree | b9dfd3ee22c83ee9fd6211a058a05b5c4dd3dafc /engines/sci/graphics | |
parent | da9c7d3d5bb5b64f026d20eabfa8f3cb4297867b (diff) | |
download | scummvm-rg350-05b21ace680d65336219a74cd87696eb157f7e84.tar.gz scummvm-rg350-05b21ace680d65336219a74cd87696eb157f7e84.tar.bz2 scummvm-rg350-05b21ace680d65336219a74cd87696eb157f7e84.zip |
SCI32: Implement VMD Censorship Blobs
Phantasmagoria 1's censorship mode is now supported
Trac #11229
Diffstat (limited to 'engines/sci/graphics')
-rw-r--r-- | engines/sci/graphics/video32.cpp | 80 | ||||
-rw-r--r-- | engines/sci/graphics/video32.h | 47 |
2 files changed, 123 insertions, 4 deletions
diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp index 7133be5bf4..567660d97a 100644 --- a/engines/sci/graphics/video32.cpp +++ b/engines/sci/graphics/video32.cpp @@ -126,7 +126,9 @@ VideoPlayer::EventFlags VideoPlayer::playUntilEvent(const EventFlags flags, cons EventFlags stopFlag = kEventFlagNone; for (;;) { - g_sci->sleep(MIN(_decoder->getTimeToNextFrame(), maxSleepMs)); + if (!_needsUpdate) { + 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 @@ -140,9 +142,20 @@ VideoPlayer::EventFlags VideoPlayer::playUntilEvent(const EventFlags flags, cons } // Some frames may contain only audio and/or palette data; this occurs - // with Duck videos and is not an error + // with Duck videos and is not an error. + // If _needsUpdate has been set but it's not time to render the next frame + // then the current frame is rendered again. This reduces the delay between + // a script adding or removing censorship blobs and the screen reflecting + // this upon resuming playback. if (nextFrame) { renderFrame(*nextFrame); + _currentFrame = nextFrame; + _needsUpdate = false; + } else if (_needsUpdate) { + if (_currentFrame) { + renderFrame(*_currentFrame); + } + _needsUpdate = false; } // Event checks must only happen *after* the decoder is updated (1) and @@ -647,6 +660,9 @@ VMDPlayer::IOStatus VMDPlayer::close() { _planeIsOwned = true; _priority = 0; _drawRect = Common::Rect(); + _blobs.clear(); + _needsUpdate = false; + _currentFrame = nullptr; return kIOSuccess; } @@ -756,6 +772,43 @@ VMDPlayer::EventFlags VMDPlayer::checkForEvent(const EventFlags flags) { return kEventFlagNone; } +int16 VMDPlayer::addBlob(int16 blockSize, int16 top, int16 left, int16 bottom, int16 right) { + if (_blobs.size() >= kMaxBlobs) { + return -1; + } + + int16 blobNumber = 0; + Common::List<Blob>::iterator prevBlobIterator = _blobs.begin(); + for (; prevBlobIterator != _blobs.end(); ++prevBlobIterator, ++blobNumber) { + if (blobNumber < prevBlobIterator->blobNumber) { + break; + } + } + + Blob blob = { blobNumber, blockSize, top, left, bottom, right }; + _blobs.insert(prevBlobIterator, blob); + + _needsUpdate = true; + return blobNumber; +} + +void VMDPlayer::deleteBlobs() { + if (!_blobs.empty()) { + _blobs.clear(); + _needsUpdate = true; + } +} + +void VMDPlayer::deleteBlob(int16 blobNumber) { + for (Common::List<Blob>::iterator b = _blobs.begin(); b != _blobs.end(); ++b) { + if (b->blobNumber == blobNumber) { + _blobs.erase(b); + _needsUpdate = true; + break; + } + } +} + void VMDPlayer::initOverlay() { // Composited videos forced through the overlay renderer (due to HQ video // mode) still need to occlude whatever is behind them in the renderer (as @@ -977,7 +1030,28 @@ void VMDPlayer::renderFrame(const Graphics::Surface &nextFrame) const { if (_isComposited) { renderComposited(); } else { - renderOverlay(nextFrame); + if (_blobs.empty()) { + renderOverlay(nextFrame); + } else { + Graphics::Surface censoredFrame; + censoredFrame.create(nextFrame.w, nextFrame.h, nextFrame.format); + censoredFrame.copyFrom(nextFrame); + drawBlobs(censoredFrame); + renderOverlay(censoredFrame); + censoredFrame.free(); + } + } +} + +void VMDPlayer::drawBlobs(Graphics::Surface& frame) const { + for (Common::List<Blob>::const_iterator blob = _blobs.begin(); blob != _blobs.end(); ++blob) { + for (int16 blockLeft = blob->left; blockLeft < blob->right; blockLeft += blob->blockSize) { + for (int16 blockTop = blob->top; blockTop < blob->bottom; blockTop += blob->blockSize) { + byte color = *(byte *)frame.getBasePtr(blockLeft, blockTop); + Common::Rect block(blockLeft, blockTop, blockLeft + blob->blockSize, blockTop + blob->blockSize); + frame.fillRect(block, color); + } + } } } diff --git a/engines/sci/graphics/video32.h b/engines/sci/graphics/video32.h index cbfbe7dfa7..1fad807ac2 100644 --- a/engines/sci/graphics/video32.h +++ b/engines/sci/graphics/video32.h @@ -69,7 +69,9 @@ public: VideoPlayer(EventManager *eventMan, Video::VideoDecoder *decoder = nullptr) : _eventMan(eventMan), - _decoder(decoder) + _decoder(decoder), + _needsUpdate(false), + _currentFrame(nullptr) #ifdef USE_RGB_COLOR , _hqVideoMode(false) @@ -171,6 +173,18 @@ protected: */ Common::Rect _drawRect; + /** + * If true, playUntilEvent() will immediately render a frame. + * Used by VMDPlayer when censorship blobs are added or removed in Phant1 + * in order to immediately update the screen upon resuming playback. + */ + bool _needsUpdate; + + /** + * Current frame rendered by playUntilEvent() + */ + const Graphics::Surface* _currentFrame; + #ifdef USE_RGB_COLOR /** * Whether or not the player is currently in high-quality video rendering @@ -614,6 +628,37 @@ private: * Whether or not the mouse cursor should be shown during playback. */ bool _showCursor; + +#pragma mark - +#pragma mark VMDPlayer - Censorship blobs +public: + /** + * Censorship blobs are pixelated rectangles which are added and removed by + * game scripts. Phant1 is the only game known to use this and always sets a + * blockSize of 10. Each block's color comes from the pixel in the upper left + * corner of the block's location. + */ + int16 addBlob(int16 blockSize, int16 top, int16 left, int16 bottom, int16 right); + void deleteBlobs(); + void deleteBlob(int16 blobNumber); + +private: + enum { + kMaxBlobs = 10 + }; + + struct Blob { + int16 blobNumber; + int16 blockSize; + int16 top; + int16 left; + int16 bottom; + int16 right; + }; + + Common::List<Blob> _blobs; + + void drawBlobs(Graphics::Surface& frame) const; }; #pragma mark - |