PEGASUS: Implement screen fading

This does linear instead of gamma for speed and complexity reasons.
This commit is contained in:
Matthew Hoops 2012-07-05 20:36:34 -04:00
parent 91efe792d5
commit 3860f34136
7 changed files with 112 additions and 40 deletions

View File

@ -30,6 +30,7 @@
#include "pegasus/elements.h"
#include "pegasus/graphics.h"
#include "pegasus/transition.h"
namespace Pegasus {
@ -46,10 +47,13 @@ GraphicsManager::GraphicsManager(PegasusEngine *vm) : _vm(vm) {
_modifiedScreen = false;
_curSurface = &_workArea;
_erase = false;
_updatesEnabled = true;
_screenFader = new ScreenFader();
}
GraphicsManager::~GraphicsManager() {
_workArea.free();
delete _screenFader;
}
void GraphicsManager::invalRect(const Common::Rect &rect) {
@ -175,7 +179,7 @@ void GraphicsManager::updateDisplay() {
_dirtyRect = Common::Rect();
}
if (screenDirty || _modifiedScreen)
if (_updatesEnabled && (screenDirty || _modifiedScreen))
g_system->updateScreen();
_modifiedScreen = false;
@ -200,19 +204,14 @@ DisplayElement *GraphicsManager::findDisplayElement(const DisplayElementID id) {
return 0;
}
void GraphicsManager::doFadeOutSync(const TimeValue, const TimeValue, uint32 color) {
if (color == 0)
color = g_system->getScreenFormat().RGBToColor(0, 0, 0);
// HACK: Until fading out is done, white-out the screen here
Graphics::Surface *screen = g_system->lockScreen();
screen->fillRect(Common::Rect(0, 0, 640, 480), color);
g_system->unlockScreen();
g_system->updateScreen();
void GraphicsManager::doFadeOutSync(const TimeValue time, const TimeScale scale, uint32 color) {
_updatesEnabled = false;
_screenFader->doFadeOutSync(time, scale, color == 0);
}
void GraphicsManager::doFadeInSync(const TimeValue, const TimeValue, uint32) {
// TODO
void GraphicsManager::doFadeInSync(const TimeValue time, const TimeScale scale, uint32 color) {
_screenFader->doFadeInSync(time, scale, color == 0);
_updatesEnabled = true;
}
void GraphicsManager::markCursorAsDirty() {
@ -333,5 +332,15 @@ void GraphicsManager::enableErase() {
void GraphicsManager::disableErase() {
_erase = false;
}
void GraphicsManager::enableUpdates() {
_updatesEnabled = true;
_screenFader->setFaderValue(100);
}
void GraphicsManager::disableUpdates() {
_updatesEnabled = false;
_screenFader->setFaderValue(0);
}
} // End of namespace Pegasus

View File

@ -40,6 +40,7 @@ namespace Pegasus {
class Cursor;
class DisplayElement;
class PegasusEngine;
class ScreenFader;
class GraphicsManager {
friend class Cursor;
@ -61,6 +62,8 @@ public:
void shakeTheWorld(TimeValue time, TimeScale scale);
void enableErase();
void disableErase();
void enableUpdates();
void disableUpdates();
// These default to black
void doFadeOutSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, uint32 color = 0);
@ -82,6 +85,9 @@ private:
static const int kMaxShakeOffsets = 17;
Common::Point _shakeOffsets[kMaxShakeOffsets];
void newShakePoint(int32 index1, int32 index2, int32 maxRadius);
bool _updatesEnabled;
ScreenFader *_screenFader;
};
} // End of namespace Pegasus

View File

@ -237,10 +237,7 @@ void MainMenu::startMainMenuLoop() {
_menuLoop.loopSound();
spec.makeTwoKnotFaderSpec(30, 0, 0, 30, 255);
// FIXME: Should be sync, but it's a pain to use the main menu right now
// with this one.
_menuFader.startFader(spec);
_menuFader.startFaderSync(spec);
}
void MainMenu::stopMainMenuLoop() {

View File

@ -204,17 +204,24 @@ void Caldoria::start() {
if (!pullbackMovie->loadFile("Images/Caldoria/Pullback.movie"))
error("Could not load pullback movie");
bool skipped = false;
Input input;
// Draw the first frame so we can fade to it
pullbackMovie->pauseVideo(true);
const Graphics::Surface *frame = pullbackMovie->decodeNextFrame();
assert(frame);
assert(frame->format == g_system->getScreenFormat());
g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 64, 112, frame->w, frame->h);
_vm->_gfx->doFadeInSync(kTwoSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond);
pullbackMovie->pauseVideo(false);
bool saveAllowed = _vm->swapSaveAllowed(false);
bool openAllowed = _vm->swapLoadAllowed(false);
bool skipped = false;
Input input;
while (!_vm->shouldQuit() && !pullbackMovie->endOfVideo()) {
if (pullbackMovie->needsUpdate()) {
const Graphics::Surface *frame = pullbackMovie->decodeNextFrame();
frame = pullbackMovie->decodeNextFrame();
if (frame) {
g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, 64, 112, frame->w, frame->h);
@ -233,6 +240,9 @@ void Caldoria::start() {
delete pullbackMovie;
if (_vm->shouldQuit())
return;
_vm->swapSaveAllowed(saveAllowed);
_vm->swapLoadAllowed(openAllowed);
@ -240,11 +250,8 @@ void Caldoria::start() {
if (!skipped) {
uint32 white = g_system->getScreenFormat().RGBToColor(0xff, 0xff, 0xff);
_vm->_gfx->doFadeOutSync(kThreeSeconds * kFifteenTicksPerSecond, kFifteenTicksPerSecond, white);
g_system->delayMillis(3 * 1000 / 2);
getExtraEntry(kCaldoria00WakeUp1, entry);
_navMovie.setTime(entry.movieStart);
_navMovie.redrawMovieWorld();

View File

@ -1479,6 +1479,7 @@ void PegasusEngine::startNewGame() {
_gfx->doFadeOutSync();
useMenu(0);
_gfx->updateDisplay();
_gfx->enableUpdates();
createInterface();

View File

@ -24,43 +24,92 @@
*/
#include "common/system.h"
#include "graphics/surface.h"
#include "pegasus/transition.h"
namespace Pegasus {
ScreenFader::ScreenFader() {
_fadeTarget = getBlack();
_isBlack = true;
// Initially, assume screens are on at full brightness.
Fader::setFaderValue(100);
_screen = new Graphics::Surface();
}
void ScreenFader::doFadeOutSync(const TimeValue duration, const TimeValue scale, const uint32 fadeTarget) {
_fadeTarget = fadeTarget;
ScreenFader::~ScreenFader() {
_screen->free();
delete _screen;
}
void ScreenFader::doFadeOutSync(const TimeValue duration, const TimeValue scale, bool isBlack) {
_isBlack = isBlack;
_screen->copyFrom(*g_system->lockScreen());
g_system->unlockScreen();
FaderMoveSpec spec;
spec.makeTwoKnotFaderSpec(scale, 0, getFaderValue(), duration, 0);
startFaderSync(spec);
_screen->free();
}
void ScreenFader::doFadeInSync(const TimeValue duration, const TimeValue scale, const uint32 fadeTarget) {
_fadeTarget = fadeTarget;
void ScreenFader::doFadeInSync(const TimeValue duration, const TimeValue scale, bool isBlack) {
_isBlack = isBlack;
_screen->copyFrom(*g_system->lockScreen());
g_system->unlockScreen();
FaderMoveSpec spec;
spec.makeTwoKnotFaderSpec(scale, 0, getFaderValue(), duration, 100);
startFaderSync(spec);
_screen->free();
}
void ScreenFader::setFaderValue(const int32 value) {
if (value != getFaderValue()) {
Fader::setFaderValue(value);
// TODO: Gamma fading
if (_screen->pixels) {
// The original game does a gamma fade here using the Mac API. In order to do
// that, it would require an immense amount of CPU processing. This does a
// linear fade instead, which looks fairly well, IMO.
Graphics::Surface *screen = g_system->lockScreen();
for (uint y = 0; y < _screen->h; y++) {
for (uint x = 0; x < _screen->w; x++) {
if (_screen->format.bytesPerPixel == 2)
WRITE_UINT16(screen->getBasePtr(x, y), fadePixel(READ_UINT16(_screen->getBasePtr(x, y)), value));
else
WRITE_UINT32(screen->getBasePtr(x, y), fadePixel(READ_UINT32(_screen->getBasePtr(x, y)), value));
}
}
g_system->unlockScreen();
g_system->updateScreen();
}
}
}
uint32 ScreenFader::getBlack() {
return g_system->getScreenFormat().RGBToColor(0, 0, 0);
static inline byte fadeComponent(byte comp, int32 percent) {
return comp * percent / 100;
}
uint32 ScreenFader::fadePixel(uint32 color, int32 percent) const {
byte r, g, b;
g_system->getScreenFormat().colorToRGB(color, r, g, b);
if (_isBlack) {
r = fadeComponent(r, percent);
g = fadeComponent(g, percent);
b = fadeComponent(b, percent);
} else {
r = 0xFF - fadeComponent(0xFF - r, percent);
g = 0xFF - fadeComponent(0xFF - g, percent);
b = 0xFF - fadeComponent(0xFF - b, percent);
}
return g_system->getScreenFormat().RGBToColor(r, g, b);
}
Transition::Transition(const DisplayElementID id) : FaderAnimation(id) {

View File

@ -28,23 +28,26 @@
#include "pegasus/fader.h"
namespace Graphics {
struct Surface;
}
namespace Pegasus {
class ScreenFader : public Fader {
public:
ScreenFader();
virtual ~ScreenFader() {}
virtual ~ScreenFader();
void doFadeOutSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, const uint32 = getBlack());
void doFadeInSync(const TimeValue = kHalfSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, const uint32 = getBlack());
void doFadeOutSync(const TimeValue = kOneSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, bool isBlack = true);
void doFadeInSync(const TimeValue = kHalfSecondPerThirtyTicks, const TimeScale = kThirtyTicksPerSecond, bool isBlack = true);
void setFaderValue(const int32);
protected:
uint32 _fadeTarget;
void setFaderValue(const int32);
private:
static uint32 getBlack();
bool _isBlack;
uint32 fadePixel(uint32 color, int32 percent) const;
Graphics::Surface *_screen;
};
// Transitions are faders that range over [0,1000], which makes their