scummvm/engines/director/transitions.cpp

1229 lines
38 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/system.h"
#include "graphics/primitives.h"
#include "graphics/macgui/macwindowmanager.h"
#include "director/director.h"
#include "director/movie.h"
#include "director/score.h"
#include "director/window.h"
#include "director/util.h"
#include "director/lingo/lingo.h"
namespace Director {
enum TransitionAlgo {
kTransAlgoBlinds,
kTransAlgoBoxy,
kTransAlgoStrips,
kTransAlgoCenterOut,
kTransAlgoChecker,
kTransAlgoCover,
kTransAlgoDissolve,
kTransAlgoEdgesIn,
kTransAlgoPush,
kTransAlgoRandomLines,
kTransAlgoReveal,
kTransAlgoWipe,
kTransAlgoZoom
};
enum TransitionDirection {
kTransDirNone,
kTransDirHorizontal,
kTransDirVertical,
kTransDirBoth,
kTransDirStepsH,
kTransDirStepsV,
kTransDirCheckers,
kTransDirBlindsV,
kTransDirBlindsH,
kTransDirDissolve
};
enum {
kNumStrips = 16,
kNumChecks = 16,
kNumBlinds = 12
};
#define TRANS(t,a,d) {t,#t,a,d}
// cap transition framerate to 60fps
#define MAX_STEPS(duration) ((duration)*60/1000)
struct {
TransitionType type;
const char *name;
TransitionAlgo algo;
TransitionDirection dir;
} static const transProps[] = {
TRANS(kTransNone, kTransAlgoWipe, kTransDirNone),
TRANS(kTransWipeRight, kTransAlgoWipe, kTransDirHorizontal),
TRANS(kTransWipeLeft, kTransAlgoWipe, kTransDirHorizontal),
TRANS(kTransWipeDown, kTransAlgoWipe, kTransDirVertical),
TRANS(kTransWipeUp, kTransAlgoWipe, kTransDirVertical),
TRANS(kTransCenterOutHorizontal, kTransAlgoCenterOut,kTransDirHorizontal), // 5
TRANS(kTransEdgesInHorizontal, kTransAlgoEdgesIn, kTransDirHorizontal),
TRANS(kTransCenterOutVertical, kTransAlgoCenterOut,kTransDirVertical),
TRANS(kTransEdgesInVertical, kTransAlgoEdgesIn, kTransDirVertical),
TRANS(kTransCenterOutSquare, kTransAlgoCenterOut,kTransDirBoth),
TRANS(kTransEdgesInSquare, kTransAlgoEdgesIn, kTransDirBoth), // 10
TRANS(kTransPushLeft, kTransAlgoPush, kTransDirHorizontal),
TRANS(kTransPushRight, kTransAlgoPush, kTransDirHorizontal),
TRANS(kTransPushDown, kTransAlgoPush, kTransDirVertical),
TRANS(kTransPushUp, kTransAlgoPush, kTransDirVertical),
TRANS(kTransRevealUp, kTransAlgoReveal, kTransDirVertical), // 15
TRANS(kTransRevealUpRight, kTransAlgoReveal, kTransDirBoth),
TRANS(kTransRevealRight, kTransAlgoReveal, kTransDirHorizontal),
TRANS(kTransRevealDownRight, kTransAlgoReveal, kTransDirBoth),
TRANS(kTransRevealDown, kTransAlgoReveal, kTransDirVertical),
TRANS(kTransRevealDownLeft, kTransAlgoReveal, kTransDirBoth), // 20
TRANS(kTransRevealLeft, kTransAlgoReveal, kTransDirHorizontal),
TRANS(kTransRevealUpLeft, kTransAlgoReveal, kTransDirBoth),
TRANS(kTransDissolvePixelsFast, kTransAlgoDissolve, kTransDirDissolve),
TRANS(kTransDissolveBoxyRects, kTransAlgoDissolve, kTransDirDissolve),
TRANS(kTransDissolveBoxySquares, kTransAlgoDissolve, kTransDirDissolve), // 25
TRANS(kTransDissolvePatterns, kTransAlgoDissolve, kTransDirDissolve),
TRANS(kTransRandomRows, kTransAlgoDissolve, kTransDirDissolve),
TRANS(kTransRandomColumns, kTransAlgoDissolve, kTransDirDissolve),
TRANS(kTransCoverDown, kTransAlgoCover, kTransDirVertical),
TRANS(kTransCoverDownLeft, kTransAlgoCover, kTransDirBoth), // 30
TRANS(kTransCoverDownRight, kTransAlgoCover, kTransDirBoth),
TRANS(kTransCoverLeft, kTransAlgoCover, kTransDirHorizontal),
TRANS(kTransCoverRight, kTransAlgoCover, kTransDirHorizontal),
TRANS(kTransCoverUp, kTransAlgoCover, kTransDirVertical),
TRANS(kTransCoverUpLeft, kTransAlgoCover, kTransDirBoth), // 35
TRANS(kTransCoverUpRight, kTransAlgoCover, kTransDirBoth),
TRANS(kTransVenetianBlind, kTransAlgoBlinds, kTransDirBlindsH),
TRANS(kTransCheckerboard, kTransAlgoChecker, kTransDirCheckers),
TRANS(kTransStripsBottomBuildLeft, kTransAlgoStrips, kTransDirStepsV),
TRANS(kTransStripsBottomBuildRight, kTransAlgoStrips, kTransDirStepsV), // 40
TRANS(kTransStripsLeftBuildDown, kTransAlgoStrips, kTransDirStepsH),
TRANS(kTransStripsLeftBuildUp, kTransAlgoStrips, kTransDirStepsH),
TRANS(kTransStripsRightBuildDown, kTransAlgoStrips, kTransDirStepsH),
TRANS(kTransStripsRightBuildUp, kTransAlgoStrips, kTransDirStepsH),
TRANS(kTransStripsTopBuildLeft, kTransAlgoStrips, kTransDirStepsV), // 45
TRANS(kTransStripsTopBuildRight, kTransAlgoStrips, kTransDirStepsV),
TRANS(kTransZoomOpen, kTransAlgoZoom, kTransDirBoth),
TRANS(kTransZoomClose, kTransAlgoZoom, kTransDirBoth),
TRANS(kTransVerticalBinds, kTransAlgoBlinds, kTransDirBlindsV),
TRANS(kTransDissolveBitsFast, kTransAlgoDissolve, kTransDirDissolve), // 50
TRANS(kTransDissolvePixels, kTransAlgoDissolve, kTransDirDissolve),
TRANS(kTransDissolveBits, kTransAlgoDissolve, kTransDirDissolve)
};
void Window::exitTransition(TransParams &t, int step, Graphics::ManagedSurface *nextFrame, Common::Rect clipRect) {
_composeSurface->blitFrom(*nextFrame, clipRect, Common::Point(clipRect.left, clipRect.top));
stepTransition(t, step);
}
void Window::stepTransition(TransParams &t, int step) {
_contentIsDirty = true;
if (t.sourcePal != t.targetPal) {
for (int i = 0; i < 768; i++) {
t.tempPal[i] = lerpByte(
(i < t.sourcePalLength * 3 ? t.sourcePal[i] : 0),
(i < t.targetPalLength * 3 ? t.targetPal[i] : 0),
step, t.steps);
}
g_director->setPalette(t.tempPal, 256);
}
g_director->draw();
}
void Window::playTransition(uint frame, uint16 transDuration, uint8 transArea, uint8 transChunkSize, TransitionType transType, CastMemberID paletteId) {
// Play a transition and return the number of subframes rendered
TransParams t;
t.type = transType;
t.duration = MAX<uint16>(250, transDuration); // When duration is < 1/4s, make it 1/4
t.frame = frame;
t.chunkSize = MAX<uint>(1, transChunkSize);
t.area = MAX<uint>(0, transArea);
// If we requested fast transitions, speed everything up
if (debugChannelSet(-1, kDebugFast))
t.duration = 250;
// Copy palette information
t.sourcePal = g_director->getPalette();
t.sourcePalLength = g_director->getPaletteColorCount();
t.targetPal = g_director->getPalette();
t.targetPalLength = g_director->getPaletteColorCount();
if (!paletteId.isNull()) {
PaletteV4 *target = g_director->getPalette(paletteId);
if (target) {
t.targetPal = target->palette;
t.targetPalLength = target->length;
}
}
// Cache a copy of the frame before the transition.
Graphics::ManagedSurface currentFrame(Graphics::ManagedSurface(_composeSurface->w, _composeSurface->h, g_director->_pixelformat));
currentFrame.copyFrom(*_composeSurface);
// If a transition is being played, render the frame after the transition.
Graphics::ManagedSurface nextFrame(Graphics::ManagedSurface(_composeSurface->w, _composeSurface->h, g_director->_pixelformat));
Common::Rect clipRect;
Score *score = g_director->getCurrentMovie()->getScore();
if (t.area) {
// Changed area transition
score->renderSprites(t.frame);
if (_dirtyRects.size() == 0)
return;
clipRect = *_dirtyRects.begin();
for (Common::List<Common::Rect>::iterator i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i)
clipRect.extend(*i);
// Ensure we redraw any other sprites intersecting the non-clip area.
_dirtyRects.clear();
// Some transitions depend upon an even clipRect size
if (clipRect.width() % 2 == 1)
clipRect.right += 1;
if (clipRect.height() % 2 == 1)
clipRect.bottom += 1;
clipRect.clip(Common::Rect(_innerDims.width(), _innerDims.height()));
_dirtyRects.push_back(clipRect);
render(false, &nextFrame);
} else {
// Full stage transition
score->renderSprites(t.frame, kRenderForceUpdate);
render(true, &nextFrame);
clipRect = _innerDims;
clipRect.moveTo(0, 0);
}
Common::Rect rfrom, rto;
uint32 transStartTime = g_system->getMillis();
debugC(2, kDebugImages, "Window::playTransition(): Playing transition %d", t.type);
initTransParams(t, clipRect);
Graphics::ManagedSurface *blitFrom;
bool fullredraw = false;
switch (transProps[t.type].algo) {
case kTransAlgoDissolve:
if (t.type == kTransDissolvePatterns)
dissolvePatternsTrans(t, clipRect, &nextFrame);
else
dissolveTrans(t, clipRect, &nextFrame);
debugC(2, kDebugImages, "Window::playTransition(): type: %d, duration: %d, chunkSize: %d, steps: %d, stepDuration: %d, xpos: %d, ypos: %d, xStepSize: %d, yStepSize: %d, stripSize: %d", t.type, t.duration, t.chunkSize, t.steps, t.stepDuration, t.xpos, t.ypos, t.xStepSize, t.yStepSize, t.stripSize);
debugC(2, kDebugImages, "Window::playTransition(): Transition %d finished in %d ms", t.type, g_system->getMillis() - transStartTime);
return;
case kTransAlgoChecker:
case kTransAlgoStrips:
case kTransAlgoBlinds:
transMultiPass(t, clipRect, &nextFrame);
debugC(2, kDebugImages, "Window::playTransition(): type: %d, duration: %d, chunkSize: %d, steps: %d, stepDuration: %d, xpos: %d, ypos: %d, xStepSize: %d, yStepSize: %d, stripSize: %d", t.type, t.duration, t.chunkSize, t.steps, t.stepDuration, t.xpos, t.ypos, t.xStepSize, t.yStepSize, t.stripSize);
debugC(2, kDebugImages, "Window::playTransition(): Transition %d finished in %d ms", t.type, g_system->getMillis() - transStartTime);
return;
case kTransAlgoZoom:
transZoom(t, clipRect, &currentFrame, &nextFrame);
debugC(2, kDebugImages, "Window::playTransition(): type: %d, duration: %d, chunkSize: %d, steps: %d, stepDuration: %d, xpos: %d, ypos: %d, xStepSize: %d, yStepSize: %d, stripSize: %d", t.type, t.duration, t.chunkSize, t.steps, t.stepDuration, t.xpos, t.ypos, t.xStepSize, t.yStepSize, t.stripSize);
debugC(2, kDebugImages, "Window::playTransition(): Transition %d finished in %d ms", t.type, g_system->getMillis() - transStartTime);
return;
case kTransAlgoCenterOut:
case kTransAlgoCover:
case kTransAlgoWipe:
blitFrom = &nextFrame;
break;
case kTransAlgoEdgesIn:
case kTransAlgoReveal:
case kTransAlgoPush:
blitFrom = &currentFrame;
fullredraw = true;
break;
default:
blitFrom = &nextFrame;
break;
}
uint w = clipRect.width();
uint h = clipRect.height();
for (uint16 i = 1; i < t.steps + 1; i++) {
uint32 startTime = g_system->getMillis();
bool stop = false;
rto = clipRect;
rfrom = clipRect;
if (transProps[t.type].algo == kTransAlgoReveal ||
transProps[t.type].algo == kTransAlgoEdgesIn) {
_composeSurface->copyRectToSurface(nextFrame, clipRect.left, clipRect.top, clipRect);
}
switch (t.type) {
case kTransWipeRight: // 1
rto.setWidth(t.xStepSize * i);
rfrom = rto;
break;
case kTransWipeLeft: // 2
rto.setWidth(t.xStepSize * i);
rto.translate(w - t.xStepSize * i, 0);
rfrom = rto;
break;
case kTransWipeDown: // 3
rto.setHeight(t.yStepSize * i);
rfrom = rto;
break;
case kTransWipeUp: // 4
rto.setHeight(t.yStepSize * i);
rto.translate(0, h - t.yStepSize * i);
rfrom = rto;
break;
case kTransCenterOutHorizontal: // 5
t.xpos += t.xStepSize;
rto.setWidth(t.xpos * 2);
rto.translate(w / 2 - t.xpos, 0);
rfrom = rto;
break;
case kTransEdgesInHorizontal: // 6
rto.setWidth(w - t.xStepSize * i * 2);
rto.translate(t.xStepSize * i, 0);
rfrom = rto;
break;
case kTransCenterOutVertical: // 7
t.ypos += t.yStepSize;
rto.setHeight(t.ypos * 2);
rto.translate(0, h / 2 - t.ypos);
rfrom = rto;
break;
case kTransEdgesInVertical: // 8
rto.setHeight(h - t.yStepSize * i * 2);
rto.translate(0, t.yStepSize * i);
rfrom = rto;
break;
case kTransCenterOutSquare: // 9
t.ypos += t.yStepSize;
rto.setHeight(t.ypos * 2);
t.xpos += t.xStepSize;
rto.setWidth(t.xpos * 2);
rto.translate(w / 2 - t.xpos, h / 2 - t.ypos);
rfrom = rto;
break;
case kTransEdgesInSquare: // 10
rto.setHeight(h - t.yStepSize * i * 2);
rto.setWidth(w - t.xStepSize * i * 2);
rto.moveTo(t.xStepSize * i, t.yStepSize * i);
rfrom = rto;
break;
case kTransPushLeft: // 11
rto.translate(w - t.xStepSize * i, 0);
rfrom.right -= w - clipRect.findIntersectingRect(rto).width();
rto.clip(clipRect);
_composeSurface->blitFrom(nextFrame, rfrom, Common::Point(rto.left, rto.top));
rfrom.translate(t.xStepSize * i, 0);
rfrom.setWidth(w - t.xStepSize * i);
rto.moveTo(clipRect.left, clipRect.top);
break;
case kTransPushRight: // 12
rfrom.translate(w - t.xStepSize * i, 0);
rfrom.setWidth(t.xStepSize * i);
_composeSurface->blitFrom(nextFrame, rfrom, Common::Point(rto.left, rto.top));
rto.setWidth(w - t.xStepSize * i);
rto.translate(t.xStepSize * i, 0);
rfrom.moveTo(clipRect.left, clipRect.top);
rfrom.setWidth(w - t.xStepSize * i);
break;
case kTransPushDown: // 13
rfrom.translate(0, h - t.yStepSize * i);
rfrom.setHeight(t.yStepSize * i);
_composeSurface->blitFrom(nextFrame, rfrom, Common::Point(rto.left, rto.top));
rto.setHeight(h - t.yStepSize * i);
rto.translate(0, t.yStepSize * i);
rfrom.moveTo(clipRect.left, clipRect.top);
rfrom.setHeight(h - t.yStepSize * i);
break;
case kTransPushUp: // 14
rto.translate(0, h - t.yStepSize * i);
_composeSurface->blitFrom(nextFrame, rfrom, Common::Point(rto.left, rto.top));
rfrom.translate(0, t.yStepSize * i);
rfrom.setHeight(h - t.yStepSize * i);
rto.moveTo(clipRect.left, clipRect.top);
break;
case kTransRevealUp: // 15
rto.translate(0, -t.yStepSize * i);
rfrom.top += h - clipRect.findIntersectingRect(rto).height();
rto.clip(clipRect);
break;
case kTransRevealUpRight: // 16
rto.translate(t.xStepSize * i, -t.yStepSize * i);
rfrom.top += h - clipRect.findIntersectingRect(rto).height();
rfrom.right -= w - clipRect.findIntersectingRect(rto).width();
rto.clip(clipRect);
break;
case kTransRevealRight: // 17
rto.translate(t.xStepSize * i, 0);
rfrom.right -= w - clipRect.findIntersectingRect(rto).width();
rto.clip(clipRect);
break;
case kTransRevealDownRight: // 18
rto.translate(t.xStepSize * i, t.yStepSize * i);
rfrom.bottom -= h - clipRect.findIntersectingRect(rto).height();
rfrom.right -= w - clipRect.findIntersectingRect(rto).width();
rto.clip(clipRect);
break;
case kTransRevealDown: // 19
rto.translate(0, t.yStepSize * i);
rfrom.bottom -= h - clipRect.findIntersectingRect(rto).height();
rto.clip(clipRect);
break;
case kTransRevealDownLeft: // 20
rto.translate(-t.xStepSize * i, t.yStepSize * i);
rfrom.bottom -= h - clipRect.findIntersectingRect(rto).height();
rfrom.left += w - clipRect.findIntersectingRect(rto).width();
rto.clip(clipRect);
break;
case kTransRevealLeft: // 21
rto.translate(-t.xStepSize * i, 0);
rfrom.left += w - clipRect.findIntersectingRect(rto).width();
rto.clip(clipRect);
break;
case kTransRevealUpLeft: // 22
rto.moveTo(-t.xStepSize * i, -t.yStepSize * i);
rfrom.top += h - clipRect.findIntersectingRect(rto).height();
rfrom.left += w - clipRect.findIntersectingRect(rto).width();
rto.clip(clipRect);
break;
case kTransDissolvePixelsFast: // 23
case kTransDissolveBoxyRects: // 24
case kTransDissolveBoxySquares: // 25
case kTransDissolvePatterns: // 26
case kTransRandomRows: // 27
case kTransRandomColumns: // 28
// Dissolve
break;
case kTransCoverDown: // 29
rto.setHeight(h);
rto.translate(0, t.yStepSize * i - h);
rfrom.top += h - clipRect.findIntersectingRect(rto).height();
rto.clip(clipRect);
break;
case kTransCoverDownLeft: // 30
rto.translate(w - t.xStepSize * i, t.yStepSize * i - h);
rfrom.top += h - clipRect.findIntersectingRect(rto).height();
rfrom.right -= w - clipRect.findIntersectingRect(rto).width();
rto.clip(clipRect);
break;
case kTransCoverDownRight: // 31
rto.translate(t.xStepSize * i - w, t.yStepSize * i - h);
rfrom.top += h - clipRect.findIntersectingRect(rto).height();
rfrom.left += w - clipRect.findIntersectingRect(rto).width();
rto.clip(clipRect);
break;
case kTransCoverLeft: // 32
rto.translate(w - t.xStepSize * i, 0);
rfrom.right -= w - clipRect.findIntersectingRect(rto).width();
rto.clip(clipRect);
break;
case kTransCoverRight: // 33
rto.translate(t.xStepSize * i - w, 0);
rfrom.left += w - clipRect.findIntersectingRect(rto).width();
rto.clip(clipRect);
break;
case kTransCoverUp: // 34
rto.translate(0, h - t.yStepSize * i);
rfrom.bottom -= h - clipRect.findIntersectingRect(rto).height();
rto.clip(clipRect);
break;
case kTransCoverUpLeft: // 35
rto.translate(w - t.xStepSize * i, h - t.yStepSize * i);
rfrom.bottom -= h - clipRect.findIntersectingRect(rto).height();
rfrom.right -= w - clipRect.findIntersectingRect(rto).width();
rto.clip(clipRect);
break;
case kTransCoverUpRight: // 36
rto.translate(t.xStepSize * i - w, h - t.yStepSize * i);
rfrom.bottom -= h - clipRect.findIntersectingRect(rto).height();
rfrom.right -= w - clipRect.findIntersectingRect(rto).width();
rto.clip(clipRect);
break;
case kTransVenetianBlind: // 37
case kTransCheckerboard: // 38
case kTransStripsBottomBuildLeft: // 39
case kTransStripsBottomBuildRight: // 40
case kTransStripsLeftBuildDown: // 41
case kTransStripsLeftBuildUp: // 42
case kTransStripsRightBuildDown: // 43
case kTransStripsRightBuildUp: // 44
case kTransStripsTopBuildLeft: // 45
case kTransStripsTopBuildRight: // 46
// Multipass
break;
case kTransZoomOpen: // 47
case kTransZoomClose: // 48
// Zoom
break;
case kTransVerticalBinds: // 49
// Multipass
break;
case kTransDissolveBitsFast: // 50
case kTransDissolvePixels: // 51
case kTransDissolveBits: // 52
// Dissolve
break;
default:
warning("Window::playTransition(): Unhandled transition type %s %d %d", transProps[t.type].name, t.duration, t.chunkSize);
// fallthrough
case kTransNone:
stop = true;
break;
}
if (stop)
break;
_composeSurface->blitFrom(*blitFrom, rfrom, Common::Point(rto.left, rto.top));
if (_vm->processEvents(true)) {
exitTransition(t, i, &nextFrame, clipRect);
break;
}
if (fullredraw) {
stepTransition(t, i);
} else {
rto.clip(clipRect);
if (rto.height() > 0 && rto.width() > 0)
stepTransition(t, i);
}
uint32 endTime = g_system->getMillis();
int diff = (int)t.stepDuration - (int)(endTime - startTime);
g_director->delayMillis(MAX(0, diff));
g_lingo->executePerFrameHook(t.frame, i);
}
// re-render the surface to clean the tracks when of transitions
render(true, _composeSurface);
_contentIsDirty = true;
g_director->draw();
debugC(2, kDebugImages, "Window::playTransition(): type: %d, duration: %d, chunkSize: %d, steps: %d, stepDuration: %d, xpos: %d, ypos: %d, xStepSize: %d, yStepSize: %d, stripSize: %d", t.type, t.duration, t.chunkSize, t.steps, t.stepDuration, t.xpos, t.ypos, t.xStepSize, t.yStepSize, t.stripSize);
debugC(2, kDebugImages, "Window::playTransition(): Transition %d finished in %d ms", t.type, g_system->getMillis() - transStartTime);
}
static int getLog2(int n) {
int res;
for (res = 0; n != 0; res++)
n >>= 1;
return res;
}
static uint32 randomSeed[33] = {
0x00000000UL,
0x00000000UL, 0x00000003UL, 0x00000006UL, 0x0000000cUL,
0x00000014UL, 0x00000030UL, 0x00000060UL, 0x000000b8UL,
0x00000110UL, 0x00000240UL, 0x00000500UL, 0x00000ca0UL,
0x00001b00UL, 0x00003500UL, 0x00006000UL, 0x0000b400UL,
0x00012000UL, 0x00020400UL, 0x00072000UL, 0x00090000UL,
0x00140000UL, 0x00300000UL, 0x00420000UL, 0x00d80000UL,
0x01200000UL, 0x03880000UL, 0x07200000UL, 0x09000000UL,
0x14000000UL, 0x32800000UL, 0x48000000UL, 0xa3000000UL
};
void Window::dissolveTrans(TransParams &t, Common::Rect &clipRect, Graphics::ManagedSurface *nextFrame) {
uint w = clipRect.width();
uint h = clipRect.height();
uint realw = w, realh = h;
byte pixmask[8];
memset(pixmask, 0, 8);
t.xStepSize = 1;
t.yStepSize = 1;
switch (t.type) {
case kTransDissolveBitsFast:
case kTransDissolveBits:
if (t.chunkSize >= 32) {
w = (w + 3) >> 2;
t.xStepSize = 4;
} else if (t.chunkSize >= 16) {
w = (w + 1) >> 1;
t.xStepSize = 2;
} else if (t.chunkSize >= 8) {
t.xStepSize = 1;
} else if (t.chunkSize >= 4) {
w <<= 1;
t.xStepSize = -2;
pixmask[0] = 0x0f;
pixmask[1] = 0xf0;
} else if (t.chunkSize >= 2) {
w <<= 2;
t.xStepSize = -4;
for (int i = 0; i < 4; i++)
pixmask[i] = 0x3 << (i * 2);
} else {
w <<= 3;
t.xStepSize = -8;
for (int i = 0; i < 8; i++)
pixmask[i] = 1 << i;
}
break;
case kTransRandomRows:
t.xStepSize = realw;
t.yStepSize = t.chunkSize;
w = 1;
h = (h + t.chunkSize - 1) / t.chunkSize;
break;
case kTransRandomColumns:
t.xStepSize = t.chunkSize;
t.yStepSize = realh;
w = (w + t.chunkSize - 1) / t.chunkSize;
h = 1;
break;
case kTransDissolveBoxySquares:
t.xStepSize = t.chunkSize;
t.yStepSize = t.chunkSize;
w = (w + t.chunkSize - 1) / t.chunkSize;
h = (h + t.chunkSize - 1) / t.chunkSize;
break;
case kTransDissolveBoxyRects:
if (w > h) {
t.xStepSize = MAX(w * t.chunkSize / h, (uint)1);
t.yStepSize = t.chunkSize;
} else {
t.xStepSize = t.chunkSize;
t.yStepSize = MAX(h * t.chunkSize / w, (uint)1);
}
w = (w + t.xStepSize - 1) / t.xStepSize;
h = (h + t.yStepSize - 1) / t.yStepSize;
break;
default:
break;
}
int vBits = getLog2(w);
int hBits = getLog2(h);
uint32 rnd, seed;
if (hBits <= 0 || vBits <= 0)
return;
rnd = seed = randomSeed[hBits + vBits];
int hMask = (1L << hBits) - 1;
int vShift = hBits;
// Calculate steps
uint32 pixPerStepInit = 1;
int bitSteps = (1 << (hBits + vBits)) - 1;
while (bitSteps > 64) {
pixPerStepInit <<= 1;
bitSteps >>= 1;
}
bitSteps++;
Common::Rect r(MAX(1, t.xStepSize), t.yStepSize);
int bitIndex = -1;
for (int i = 0; i < t.steps; i++) {
uint32 startTime = g_system->getMillis();
int bitEndIndex = (bitSteps - 1) * (i + 1) / t.steps;
while (bitIndex < bitEndIndex) {
bitIndex++;
uint32 pixPerStep = pixPerStepInit;
do {
uint32 x = (rnd - 1) >> vShift;
uint32 y = (rnd - 1) & hMask;
byte mask = 0;
r.setWidth(MAX(1, t.xStepSize));
r.setHeight(t.yStepSize);
if (x < w && y < h) {
if (t.xStepSize >= 1) {
x = x * t.xStepSize;
y = y * t.yStepSize;
if (x < realw && y < realh) {
x += clipRect.left;
y += clipRect.top;
r.moveTo(x, y);
r.clip(clipRect);
if (!r.isEmpty())
_composeSurface->copyRectToSurface(*nextFrame, x, y, r);
}
} else {
mask = pixmask[x % -t.xStepSize];
x = x / -t.xStepSize;
x += clipRect.left;
y += clipRect.top;
byte *dst = (byte *)_composeSurface->getBasePtr(x, y);
byte *src = (byte *)nextFrame->getBasePtr(x, y);
*dst = ((*dst & ~mask) | (*src & mask)) & 0xff;
}
}
rnd = (rnd & 1) ? (rnd >> 1) ^ seed : rnd >> 1;
if (pixPerStep > 0) {
if (--pixPerStep == 0) {
break;
}
}
} while (rnd != seed);
}
stepTransition(t, i);
g_lingo->executePerFrameHook(t.frame, i + 1);
if (_vm->processEvents(true)) {
exitTransition(t, i, nextFrame, clipRect);
break;
}
uint32 endTime = g_system->getMillis();
int diff = (int)t.stepDuration - (int)(endTime - startTime);
g_director->delayMillis(MAX(0, diff));
}
}
static byte dissolvePatterns[][8] = {
{ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x80, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 },
{ 0x88, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 },
{ 0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00 },
{ 0x88, 0x00, 0x20, 0x00, 0x88, 0x00, 0x00, 0x00 },
{ 0x88, 0x00, 0x20, 0x00, 0x88, 0x00, 0x02, 0x00 },
{ 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x02, 0x00 },
{ 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 },
{ 0xa8, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 },
{ 0xa8, 0x00, 0x22, 0x00, 0x8a, 0x00, 0x22, 0x00 },
{ 0xaa, 0x00, 0x22, 0x00, 0x8a, 0x00, 0x22, 0x00 },
{ 0xaa, 0x00, 0x22, 0x00, 0xaa, 0x00, 0x22, 0x00 },
{ 0xaa, 0x00, 0xa2, 0x00, 0xaa, 0x00, 0x22, 0x00 },
{ 0xaa, 0x00, 0xa2, 0x00, 0xaa, 0x00, 0x2a, 0x00 },
{ 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0x2a, 0x00 },
{ 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00 },
{ 0xaa, 0x40, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00 },
{ 0xaa, 0x40, 0xaa, 0x00, 0xaa, 0x04, 0xaa, 0x00 },
{ 0xaa, 0x44, 0xaa, 0x00, 0xaa, 0x04, 0xaa, 0x00 },
{ 0xaa, 0x44, 0xaa, 0x00, 0xaa, 0x44, 0xaa, 0x00 },
{ 0xaa, 0x44, 0xaa, 0x10, 0xaa, 0x44, 0xaa, 0x00 },
{ 0xaa, 0x44, 0xaa, 0x10, 0xaa, 0x44, 0xaa, 0x01 },
{ 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x01 },
{ 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 },
{ 0xaa, 0x54, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 },
{ 0xaa, 0x54, 0xaa, 0x11, 0xaa, 0x45, 0xaa, 0x11 },
{ 0xaa, 0x55, 0xaa, 0x11, 0xaa, 0x45, 0xaa, 0x11 },
{ 0xaa, 0x55, 0xaa, 0x11, 0xaa, 0x55, 0xaa, 0x11 },
{ 0xaa, 0x55, 0xaa, 0x51, 0xaa, 0x55, 0xaa, 0x11 },
{ 0xaa, 0x55, 0xaa, 0x51, 0xaa, 0x55, 0xaa, 0x15 },
{ 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x15 },
{ 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 },
{ 0xea, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 },
{ 0xea, 0x55, 0xaa, 0x55, 0xae, 0x55, 0xaa, 0x55 },
{ 0xee, 0x55, 0xaa, 0x55, 0xae, 0x55, 0xaa, 0x55 },
{ 0xee, 0x55, 0xaa, 0x55, 0xee, 0x55, 0xaa, 0x55 },
{ 0xee, 0x55, 0xba, 0x55, 0xee, 0x55, 0xaa, 0x55 },
{ 0xee, 0x55, 0xba, 0x55, 0xee, 0x55, 0xab, 0x55 },
{ 0xee, 0x55, 0xbb, 0x55, 0xee, 0x55, 0xab, 0x55 },
{ 0xee, 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55 },
{ 0xfe, 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55 },
{ 0xfe, 0x55, 0xbb, 0x55, 0xef, 0x55, 0xbb, 0x55 },
{ 0xff, 0x55, 0xbb, 0x55, 0xef, 0x55, 0xbb, 0x55 },
{ 0xff, 0x55, 0xbb, 0x55, 0xff, 0x55, 0xbb, 0x55 },
{ 0xff, 0x55, 0xfb, 0x55, 0xff, 0x55, 0xbb, 0x55 },
{ 0xff, 0x55, 0xfb, 0x55, 0xff, 0x55, 0xbf, 0x55 },
{ 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xbf, 0x55 },
{ 0xff, 0x55, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55 },
{ 0xff, 0xd5, 0xff, 0x55, 0xff, 0x55, 0xff, 0x55 },
{ 0xff, 0xd5, 0xff, 0x55, 0xff, 0x5d, 0xff, 0x55 },
{ 0xff, 0xdd, 0xff, 0x55, 0xff, 0x5d, 0xff, 0x55 },
{ 0xff, 0xdd, 0xff, 0x55, 0xff, 0xdd, 0xff, 0x55 },
{ 0xff, 0xdd, 0xff, 0x75, 0xff, 0xdd, 0xff, 0x55 },
{ 0xff, 0xdd, 0xff, 0x75, 0xff, 0xdd, 0xff, 0x57 },
{ 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff, 0x57 },
{ 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff, 0x77 },
{ 0xff, 0xfd, 0xff, 0x77, 0xff, 0xdd, 0xff, 0x77 },
{ 0xff, 0xfd, 0xff, 0x77, 0xff, 0xdf, 0xff, 0x77 },
{ 0xff, 0xff, 0xff, 0x77, 0xff, 0xdf, 0xff, 0x77 },
{ 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, 0x77 },
{ 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0x77 },
{ 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0x7f },
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f },
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
};
void Window::dissolvePatternsTrans(TransParams &t, Common::Rect &clipRect, Graphics::ManagedSurface *nextFrame) {
int patternSteps = 64;
for (int i = 0; i < t.steps; i++) {
int patternIndex = (patternSteps - 1) * (i + 1) / t.steps;
uint32 startTime = g_system->getMillis();
for (int y = clipRect.top; y < clipRect.bottom; y++) {
byte pat = dissolvePatterns[patternIndex][y % 8];
byte *dst = (byte *)_composeSurface->getBasePtr(clipRect.left, y);
byte *src = (byte *)nextFrame->getBasePtr(clipRect.left, y);
for (int x = clipRect.left; x < clipRect.right;) {
byte mask = 0x80;
for (int b = 0; b < 8 && x < clipRect.right; b++, x++) {
if (pat & mask)
*dst = *src;
dst++;
src++;
mask >>= 1;
}
}
}
stepTransition(t, i);
g_lingo->executePerFrameHook(t.frame, i + 1);
if (_vm->processEvents(true)) {
exitTransition(t, i, nextFrame, clipRect);
break;
}
uint32 endTime = g_system->getMillis();
int diff = (int)t.stepDuration - (int)(endTime - startTime);
g_director->delayMillis(MAX(0, diff));
}
}
void Window::transMultiPass(TransParams &t, Common::Rect &clipRect, Graphics::ManagedSurface *nextFrame) {
Common::Rect rto;
uint w = clipRect.width();
uint h = clipRect.height();
bool flag = false;
Common::Array<Common::Rect> rects;
rects.reserve(128);
for (uint16 i = 0; i < t.steps; i++) {
uint32 startTime = g_system->getMillis();
bool stop = false;
rto = clipRect;
switch (t.type) {
case kTransVenetianBlind: // 37
rto.setHeight(t.yStepSize * (i + 1));
for (int r = 0; r < kNumBlinds; r++) {
rto.moveTo(0, r * t.stripSize);
rects.push_back(rto);
}
break;
case kTransCheckerboard: // 38
rto.setWidth(t.stripSize);
rto.setHeight((i % ((t.steps + 1) / 2)) * t.chunkSize);
flag = i + i > t.steps;
for (int y = 0; y < t.yStepSize; y++) {
for (int x = 0; x < t.xStepSize; x++) {
if ((x & 2) ^ (y & 2) ^ (int)flag) {
rto.moveTo(x * t.stripSize, y * t.stripSize);
rects.push_back(rto);
}
}
}
break;
case kTransStripsBottomBuildLeft: // 39
for (int r = 0; r < kNumStrips; r++) {
int len = t.yStepSize * i - (kNumStrips - r - 1) * t.stripSize;
if (len > 0) {
rto.setHeight(len);
rto.setWidth(t.xStepSize);
rto.moveTo(t.xStepSize * r, h - len);
rects.push_back(rto);
}
}
break;
case kTransStripsBottomBuildRight: // 40
for (int r = 0; r < kNumStrips; r++) {
int len = t.yStepSize * i - r * t.stripSize;
if (len > 0) {
rto.setHeight(len);
rto.setWidth(t.xStepSize);
rto.moveTo(t.xStepSize * r, h - len);
rects.push_back(rto);
}
}
break;
case kTransStripsLeftBuildDown: // 41
for (int r = 0; r < kNumStrips; r++) {
int len = t.xStepSize * i - r * t.stripSize;
if (len > 0) {
rto.setWidth(len);
rto.setHeight(t.yStepSize);
rto.moveTo(0, t.yStepSize * r);
rects.push_back(rto);
}
}
break;
case kTransStripsLeftBuildUp: // 42
for (int r = 0; r < kNumStrips; r++) {
int len = t.xStepSize * i - (kNumStrips - r - 1) * t.stripSize;
if (len > 0) {
rto.setWidth(len);
rto.setHeight(t.yStepSize);
rto.moveTo(0, t.yStepSize * r);
rects.push_back(rto);
}
}
break;
case kTransStripsRightBuildDown: // 43
for (int r = 0; r < kNumStrips; r++) {
int len = t.xStepSize * i - r * t.stripSize;
if (len > 0) {
rto.setWidth(len);
rto.setHeight(t.yStepSize);
rto.moveTo(w - len, t.yStepSize * r);
rects.push_back(rto);
}
}
break;
case kTransStripsRightBuildUp: // 44
for (int r = 0; r < kNumStrips; r++) {
int len = t.xStepSize * i - (kNumStrips - r - 1) * t.stripSize;
if (len > 0) {
rto.setWidth(len);
rto.setHeight(t.yStepSize);
rto.moveTo(w - len, t.yStepSize * r);
rects.push_back(rto);
}
}
break;
case kTransStripsTopBuildLeft: // 45
for (int r = 0; r < kNumStrips; r++) {
int len = t.yStepSize * i - (kNumStrips - r - 1) * t.stripSize;
if (len > 0) {
rto.setHeight(len);
rto.setWidth(t.xStepSize);
rto.moveTo(t.xStepSize * r, 0);
rects.push_back(rto);
}
}
break;
case kTransStripsTopBuildRight: // 46
for (int r = 0; r < kNumStrips; r++) {
int len = t.yStepSize * i - r * t.stripSize;
if (len > 0) {
rto.setHeight(len);
rto.setWidth(t.xStepSize);
rto.moveTo(t.xStepSize * r, 0);
rects.push_back(rto);
}
}
break;
case kTransVerticalBinds: // 49
rto.setWidth(t.xStepSize * (i + 1));
for (int r = 0; r < kNumBlinds; r++) {
rto.moveTo(r * t.stripSize, 0);
rects.push_back(rto);
}
break;
default:
warning("Window::transMultiPass(): Unhandled transition type %s %d %d", transProps[t.type].name, t.duration, t.chunkSize);
stop = true;
break;
}
if (stop)
break;
for (uint r = 0; r < rects.size(); r++) {
rto = rects[r];
rto.translate(clipRect.left, clipRect.top);
rto.clip(clipRect);
if (rto.height() > 0 && rto.width() > 0) {
_composeSurface->blitFrom(*nextFrame, rto, Common::Point(rto.left, rto.top));
}
}
stepTransition(t, i);
rects.clear();
g_lingo->executePerFrameHook(t.frame, i);
uint32 endTime = g_system->getMillis();
int diff = (int)t.stepDuration - (int)(endTime - startTime);
g_director->delayMillis(MAX(0, diff));
if (_vm->processEvents(true)) {
exitTransition(t, i, nextFrame, clipRect);
break;
}
}
}
void Window::transZoom(TransParams &t, Common::Rect &clipRect, Graphics::ManagedSurface *currentFrame, Graphics::ManagedSurface *nextFrame) {
Common::Rect r = clipRect;
uint w = clipRect.width();
uint h = clipRect.height();
t.steps >>= 1;
t.xStepSize <<= 1;
t.yStepSize <<= 1;
t.steps += 1;
DirectorPlotData pd(g_director, kLineTopBottomSprite, kInkTypeReverse, 0, _wm->_colorWhite, _wm->_colorBlack);
pd.destRect = clipRect;
pd.dst = _composeSurface;
for (uint16 i = 1; i < t.steps; i++) {
uint32 startTime = g_system->getMillis();
// FIXME: figure out the bounding box of the drawn bits
_composeSurface->copyRectToSurface(*currentFrame, clipRect.left, clipRect.top, clipRect);
for (int s = 2; s >= 0; s--) {
if (i - s < 0 || i - s > t.steps - 2)
continue;
if (t.type == kTransZoomOpen) {
r.setHeight(t.yStepSize * (i - s) * 2);
r.setWidth(t.xStepSize * (i - s) * 2);
r.moveTo(w / 2 - t.xStepSize * (i - s), h / 2 - t.yStepSize * (i - s));
} else {
r.setHeight(h - t.yStepSize * (i - s) * 2);
r.setWidth(w - t.xStepSize * (i - s) * 2);
r.moveTo(t.xStepSize * (i - s), t.yStepSize * (i - s));
}
Graphics::drawLine(r.left, r.top, r.right, r.top, _wm->_colorBlack, g_director->getInkDrawPixel(), &pd);
Graphics::drawLine(r.right, r.top, r.right, r.bottom, _wm->_colorBlack, g_director->getInkDrawPixel(), &pd);
Graphics::drawLine(r.left, r.bottom, r.right, r.bottom, _wm->_colorBlack, g_director->getInkDrawPixel(), &pd);
Graphics::drawLine(r.left, r.top, r.left, r.bottom, _wm->_colorBlack, g_director->getInkDrawPixel(), &pd);
}
r.setHeight(t.yStepSize * i * 2);
r.setWidth(t.xStepSize * i * 2);
r.moveTo(w / 2 - t.xStepSize * i, h / 2 - t.yStepSize * i);
if (_vm->processEvents(true)) {
exitTransition(t, i, nextFrame, clipRect);
break;
}
stepTransition(t, i);
uint32 endTime = g_system->getMillis();
int diff = (int)t.stepDuration - (int)(endTime - startTime);
g_director->delayMillis(MAX(0, diff));
g_lingo->executePerFrameHook(t.frame, i);
}
render(true, _composeSurface);
_contentIsDirty = true;
g_director->draw();
}
void Window::initTransParams(TransParams &t, Common::Rect &clipRect) {
int w = clipRect.width();
int h = clipRect.height();
int m = MIN(w, h);
TransitionAlgo a = transProps[t.type].algo;
if (a == kTransAlgoCenterOut || a == kTransAlgoEdgesIn || a == kTransAlgoZoom) {
w = (w + 1) >> 1; // round up
h = (h + 1) >> 1;
}
// If we requested fast transitions, speed everything up
// Ensure the chunksize isn't larger than the amount of pixels.
if (debugChannelSet(-1, kDebugFast))
t.chunkSize = MIN((uint) m, t.chunkSize*16);
uint maxSteps = MAX_STEPS(t.duration);
switch (transProps[t.type].dir) {
case kTransDirHorizontal:
t.steps = MIN(MAX(w / t.chunkSize, (uint)1), maxSteps);
t.xStepSize = w / t.steps;
t.xpos = w % t.steps;
break;
case kTransDirVertical:
t.steps = MIN(MAX(h / t.chunkSize, (uint)1), maxSteps);
t.yStepSize = h / t.steps;
t.ypos = h % t.steps;
break;
case kTransDirBoth:
t.steps = MIN(MAX(m / t.chunkSize, (uint)1), maxSteps);
t.xStepSize = w / t.steps;
t.xpos = w % t.steps;
t.yStepSize = h / t.steps;
t.ypos = h % t.steps;
break;
case kTransDirStepsH:
{
uint minChunkSize = (w - 1)/((maxSteps/2) - 1);
t.xStepSize = MAX(t.chunkSize, minChunkSize);
t.yStepSize = (h + kNumStrips - 1) / kNumStrips;
t.stripSize = (w + kNumStrips - 1) / kNumStrips;
t.steps = ((w + t.xStepSize - 1) / t.xStepSize) * 2;
}
break;
case kTransDirStepsV:
{
uint minChunkSize = (h - 1)/((maxSteps/2) - 1);
t.xStepSize = (w + kNumStrips - 1) / kNumStrips;
t.yStepSize = MAX(t.chunkSize, minChunkSize);
t.stripSize = (h + kNumStrips - 1) / kNumStrips;
t.steps = ((h + t.yStepSize - 1) / t.yStepSize) * 2;
}
break;
case kTransDirCheckers:
if (w > h)
t.stripSize = (w + kNumStrips - 1) / kNumStrips;
else
t.stripSize = (h + kNumStrips - 1) / kNumStrips;
t.steps = ((t.stripSize + t.chunkSize - 1) / t.chunkSize) * 2 + 2;
t.xStepSize = (w + t.stripSize - 1) / t.stripSize; // number of checkers
t.yStepSize = (h + t.stripSize - 1) / t.stripSize; // number of checkers
break;
case kTransDirBlindsV:
t.xStepSize = t.chunkSize;
t.stripSize = (w + kNumBlinds - 1) / kNumBlinds;
t.steps = (t.stripSize + t.xStepSize - 1) / t.xStepSize;
break;
case kTransDirBlindsH:
t.yStepSize = t.chunkSize;
t.stripSize = (h + kNumBlinds - 1) / kNumBlinds;
t.steps = (t.stripSize + t.yStepSize - 1) / t.yStepSize;
break;
case kTransDirDissolve:
if (t.type == kTransDissolvePixelsFast ||
t.type == kTransDissolveBitsFast)
t.duration = 250; // Smallest possible delay
t.steps = MIN<int>(MAX_STEPS(t.duration), 64);
t.stepDuration = t.duration / t.steps;
break;
default:
t.steps = 1;
}
if (debugChannelSet(-1, kDebugFast))
t.steps = 1;
t.stepDuration = t.duration / t.steps;
}
} // End of namespace Director