void ZVision::playVideo(Video::VideoDecoder &videoDecoder, const Common::Rect &destRect, bool skippable) {
byte bytesPerPixel = videoDecoder.getPixelFormat().bytesPerPixel;
uint16 origWidth = videoDecoder.getWidth();
uint16 origHeight = videoDecoder.getHeight();
uint scale = 1;
// If destRect is empty, no specific scaling was requested. However, we may choose to do scaling anyway
if (destRect.isEmpty()) {
// Most videos are very small. Therefore we do a simple 2x scale
if (origWidth * 2 <= 640 && origHeight * 2 <= 480) {
scale = 2;
}
} else {
// Assume bilinear scaling. AKA calculate the scale from just the width.
// Also assume that the scaling is in integral intervals. AKA no 1.5x scaling
// TODO: Test ^these^ assumptions
scale = destRect.width() / origWidth;
// TODO: Test if we need to support downscale.
}
uint16 pitch = origWidth * bytesPerPixel;
uint16 finalWidth = origWidth * scale;
uint16 finalHeight = origHeight * scale;
byte *scaledVideoFrameBuffer;
if (scale != 1) {
scaledVideoFrameBuffer = new byte[finalWidth * finalHeight * bytesPerPixel];
}
uint16 x = ((WINDOW_WIDTH - finalWidth) / 2) + destRect.left;
uint16 y = ((WINDOW_HEIGHT - finalHeight) / 2) + destRect.top;
_clock.stop();
videoDecoder.start();
// Only continue while the video is still playing
while (!shouldQuit() && !videoDecoder.endOfVideo() && videoDecoder.isPlaying()) {
// Check for engine quit and video stop key presses
while (!videoDecoder.endOfVideo() && videoDecoder.isPlaying() && _eventMan->pollEvent(_event)) {
switch (_event.type) {
case Common::EVENT_KEYDOWN:
switch (_event.kbd.keycode) {
case Common::KEYCODE_q:
if (_event.kbd.hasFlags(Common::KBD_CTRL))
quitGame();
break;
case Common::KEYCODE_SPACE:
if (skippable) {
videoDecoder.stop();
}
break;
default:
break;
}
default:
break;
}
}
if (videoDecoder.needsUpdate()) {
const Graphics::Surface *frame = videoDecoder.decodeNextFrame();
if (frame) {
if (scale != 1) {
scaleBuffer((const byte *)frame->getPixels(), scaledVideoFrameBuffer, origWidth, origHeight, bytesPerPixel, scale);
_system->copyRectToScreen(scaledVideoFrameBuffer, pitch * 2, x, y, finalWidth, finalHeight);
} else {
_system->copyRectToScreen((const byte *)frame->getPixels(), pitch, x, y, finalWidth, finalHeight);
}
}
}
// Always update the screen so the mouse continues to render
_system->updateScreen();
_system->delayMillis(videoDecoder.getTimeToNextFrame());
}
_clock.start();
if (scale != 1) {
delete[] scaledVideoFrameBuffer;
}
}
// Draws a text in rect.
void GfxText16::Box(const char *text, uint16 languageSplitter, bool show, const Common::Rect &rect, TextAlignment alignment, GuiResourceId fontId) {
int16 textWidth, maxTextWidth, textHeight, charCount;
int16 offset = 0;
int16 hline = 0;
GuiResourceId previousFontId = GetFontId();
int16 previousPenColor = _ports->_curPort->penClr;
bool doubleByteMode = false;
const char *curTextPos = text;
const char *curTextLine = text;
if (fontId != -1)
SetFont(fontId);
else
fontId = previousFontId;
// Reset reference code rects
_codeRefRects.clear();
_codeRefTempRect.left = _codeRefTempRect.top = -1;
maxTextWidth = 0;
while (*curTextPos) {
// We need to check for Shift-JIS every line
// Police Quest 2 PC-9801 often draws English + Japanese text during the same call
if (g_sci->getLanguage() == Common::JA_JPN) {
if (SwitchToFont900OnSjis(curTextPos, languageSplitter))
doubleByteMode = true;
}
charCount = GetLongest(curTextPos, rect.width(), fontId);
if (charCount == 0)
break;
Width(curTextLine, 0, charCount, fontId, textWidth, textHeight, true);
maxTextWidth = MAX<int16>(maxTextWidth, textWidth);
switch (alignment) {
case SCI_TEXT16_ALIGNMENT_RIGHT:
offset = rect.width() - textWidth;
break;
case SCI_TEXT16_ALIGNMENT_CENTER:
offset = (rect.width() - textWidth) / 2;
break;
case SCI_TEXT16_ALIGNMENT_LEFT:
offset = 0;
break;
default:
warning("Invalid alignment %d used in TextBox()", alignment);
}
_ports->moveTo(rect.left + offset, rect.top + hline);
if (show) {
Show(curTextLine, 0, charCount, fontId, previousPenColor);
} else {
Draw(curTextLine, 0, charCount, fontId, previousPenColor);
}
hline += textHeight;
curTextLine = curTextPos;
}
SetFont(previousFontId);
_ports->penColor(previousPenColor);
if (doubleByteMode) {
// Kanji is written by pc98 rom to screen directly. Because of
// GetLongest() behavior (not cutting off the last char, that causes a
// new line), results in the script thinking that the text would need
// less space. The coordinate adjustment in fontsjis.cpp handles the
// incorrect centering because of that and this code actually shows all
// of the chars - if we don't do this, the scripts will only show most
// of the chars, but the last few pixels won't get shown most of the
// time.
Common::Rect kanjiRect = rect;
_ports->offsetRect(kanjiRect);
kanjiRect.left &= 0xFFC;
kanjiRect.right = kanjiRect.left + maxTextWidth;
kanjiRect.bottom = kanjiRect.top + hline;
kanjiRect.left *= 2; kanjiRect.right *= 2;
kanjiRect.top *= 2; kanjiRect.bottom *= 2;
_screen->copyDisplayRectToScreen(kanjiRect);
}
}
reg_t GfxPaint16::kernelDisplay(const char *text, int argc, reg_t *argv) {
reg_t displayArg;
TextAlignment alignment = SCI_TEXT16_ALIGNMENT_LEFT;
int16 colorPen = -1, colorBack = -1, width = -1, bRedraw = 1;
bool doSaveUnder = false;
Common::Rect rect;
reg_t result = NULL_REG;
// Make a "backup" of the port settings (required for some SCI0LATE and
// SCI01+ only)
Port oldPort = *_ports->getPort();
// setting defaults
_ports->penMode(0);
_ports->penColor(0);
_ports->textGreyedOutput(false);
// processing codes in argv
while (argc > 0) {
displayArg = argv[0];
if (displayArg.getSegment())
displayArg.setOffset(0xFFFF);
argc--; argv++;
switch (displayArg.getOffset()) {
case SCI_DISPLAY_MOVEPEN:
_ports->moveTo(argv[0].toUint16(), argv[1].toUint16());
argc -= 2; argv += 2;
break;
case SCI_DISPLAY_SETALIGNMENT:
alignment = argv[0].toSint16();
argc--; argv++;
break;
case SCI_DISPLAY_SETPENCOLOR:
colorPen = argv[0].toUint16();
_ports->penColor(colorPen);
argc--; argv++;
break;
case SCI_DISPLAY_SETBACKGROUNDCOLOR:
colorBack = argv[0].toUint16();
argc--; argv++;
break;
case SCI_DISPLAY_SETGREYEDOUTPUT:
_ports->textGreyedOutput(!argv[0].isNull());
argc--; argv++;
break;
case SCI_DISPLAY_SETFONT:
_text16->SetFont(argv[0].toUint16());
argc--; argv++;
break;
case SCI_DISPLAY_WIDTH:
width = argv[0].toUint16();
argc--; argv++;
break;
case SCI_DISPLAY_SAVEUNDER:
doSaveUnder = true;
break;
case SCI_DISPLAY_RESTOREUNDER:
bitsGetRect(argv[0], &rect);
rect.translate(-_ports->getPort()->left, -_ports->getPort()->top);
bitsRestore(argv[0]);
kernelGraphRedrawBox(rect);
// finishing loop
argc = 0;
break;
case SCI_DISPLAY_DONTSHOWBITS:
bRedraw = 0;
break;
// The following three dummy calls are not supported by the Sierra SCI
// interpreter, but are erroneously called in some game scripts.
case SCI_DISPLAY_DUMMY1: // Longbow demo (all rooms) and QFG1 EGA demo (room 11)
case SCI_DISPLAY_DUMMY2: // Longbow demo (all rooms)
case SCI_DISPLAY_DUMMY3: // QFG1 EGA demo (room 11) and PQ2 (room 23)
if (!(g_sci->getGameId() == GID_LONGBOW && g_sci->isDemo()) &&
!(g_sci->getGameId() == GID_QFG1 && g_sci->isDemo()) &&
!(g_sci->getGameId() == GID_PQ2))
error("Unknown kDisplay argument %d", displayArg.getOffset());
if (displayArg.getOffset() == SCI_DISPLAY_DUMMY2) {
if (!argc)
error("No parameter left for kDisplay(115)");
argc--; argv++;
}
break;
default:
SciTrackOriginReply originReply;
SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kDisplay_workarounds, &originReply);
if (solution.type == WORKAROUND_NONE)
error("Unknown kDisplay argument (%04x:%04x) from method %s::%s (script %d, localCall %x)",
PRINT_REG(displayArg), originReply.objectName.c_str(), originReply.methodName.c_str(),
originReply.scriptNr, originReply.localCallOffset);
assert(solution.type == WORKAROUND_IGNORE);
break;
}
}
// now drawing the text
_text16->Size(rect, text, -1, width);
rect.moveTo(_ports->getPort()->curLeft, _ports->getPort()->curTop);
// Note: This code has been found in SCI1 middle and newer games. It was
// previously only for SCI1 late and newer, but the LSL1 interpreter contains
//.........这里部分代码省略.........
void DeathMenu::drawAllScores() {
Surface numbers;
numbers.getImageFromPICTFile("Images/Death Screens/Numbers.pict");
Common::Rect scoreBounds;
GameScoreType caldoriaTotal = 0;
switch (_deathReason) {
case kDeathCardBomb:
case kDeathShotBySinclair:
case kDeathSinclairShotDelegate:
case kDeathNuclearExplosion:
case kDeathGassedInNorad:
case kDeathWokeUpNorad:
case kDeathArrestedInNorad:
case kDeathSubDestroyed:
case kDeathRobotThroughNoradDoor:
case kDeathRobotSubControlRoom:
case kDeathWrongShuttleLock:
case kDeathArrestedInMars:
case kDeathRunOverByPod:
case kDeathDidntGetOutOfWay:
case kDeathReactorBurn:
case kDeathDidntFindMarsBomb:
case kDeathDidntDisarmMarsBomb:
case kDeathNoMaskInMaze:
case kDeathNoAirInMaze:
case kDeathGroundByMazebot:
case kDeathMissedOreBucket:
case kDeathDidntLeaveBucket:
case kDeathRanIntoCanyonWall:
case kDeathRanIntoSpaceJunk:
case kDeathDidntStopPoison:
case kDeathArrestedInWSC:
case kDeathHitByPlasma:
case kDeathShotOnCatwalk:
case kPlayerWonGame:
caldoriaTotal += kMaxCaldoriaTSAScoreAfter;
scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 5,
kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 5 + kDeathScreenScoreHeight);
drawScore(GameState.getGandhiScore(), kMaxGandhiScore, scoreBounds, &numbers);
scoreBounds.translate(0, kDeathScreenScoreSkipVert);
drawScore(GameState.getWSCScore(), kMaxWSCScore, scoreBounds, &numbers);
scoreBounds.translate(0, kDeathScreenScoreSkipVert);
drawScore(GameState.getNoradScore(), kMaxNoradScore, scoreBounds, &numbers);
scoreBounds.translate(0, kDeathScreenScoreSkipVert);
drawScore(GameState.getMarsScore(), kMaxMarsScore, scoreBounds, &numbers);
// fall through
case kDeathFallOffCliff:
case kDeathEatenByDinosaur:
case kDeathStranded:
case kDeathShotByTSARobots:
scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert,
kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert + kDeathScreenScoreHeight);
drawScore(GameState.getPrehistoricScore(), kMaxPrehistoricScore, scoreBounds, &numbers);
// fall through
case kDeathUncreatedInCaldoria:
case kDeathUncreatedInTSA:
scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop, kDeathScreenScoreLeft + kDeathScreenScoreWidth,
kDeathScreenScoreTop + kDeathScreenScoreHeight);
caldoriaTotal += kMaxCaldoriaTSAScoreBefore;
drawScore(GameState.getCaldoriaTSAScore(), caldoriaTotal, scoreBounds, &numbers);
scoreBounds = Common::Rect(kDeathScreenScoreLeft, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 6,
kDeathScreenScoreLeft + kDeathScreenScoreWidth, kDeathScreenScoreTop - kDeathScreenScoreSkipVert * 6 + kDeathScreenScoreHeight);
drawScore(GameState.getTotalScore(), kMaxTotalScore, scoreBounds, &numbers);
break;
}
}
开发者ID:Fyre91,项目名称:scummvm,代码行数:73,代码来源:menu.cpp
示例9: Window
Window *GfxPorts::addWindow(const Common::Rect &dims, const Common::Rect *restoreRect, const char *title, uint16 style, int16 priority, bool draw) {
// Find an unused window/port id
uint id = PORTS_FIRSTWINDOWID;
while (id < _windowsById.size() && _windowsById[id]) {
if (_windowsById[id]->counterTillFree) {
// port that is already disposed, but not freed yet
freeWindow((Window *)_windowsById[id]);
_freeCounter--;
break; // reuse the handle
// we do this especially for sq4cd. it creates and disposes the
// inventory window all the time, but reuses old handles as well
// this worked somewhat under the original interpreter, because
// it put the new window where the old was.
}
++id;
}
if (id == _windowsById.size())
_windowsById.push_back(0);
assert(0 < id && id < 0xFFFF);
Window *pwnd = new Window(id);
Common::Rect r;
if (!pwnd) {
error("Can't open window");
return 0;
}
_windowsById[id] = pwnd;
// KQ1sci, KQ4, iceman, QfG2 always add windows to the back of the list.
// KQ5CD checks style.
// Hoyle3-demo also always adds to the back (#3036763).
bool forceToBack = (getSciVersion() <= SCI_VERSION_1_EGA_ONLY) ||
(g_sci->getGameId() == GID_HOYLE3 && g_sci->isDemo());
if (!forceToBack && (style & SCI_WINDOWMGR_STYLE_TOPMOST))
_windowList.push_front(pwnd);
else
_windowList.push_back(pwnd);
openPort(pwnd);
r = dims;
// This looks fishy, but it's exactly what Sierra did. They removed last
// bit of the left dimension in their interpreter. It seems Sierra did it
// for EGA byte alignment (EGA uses 1 byte for 2 pixels) and left it in
// their interpreter even in the newer VGA games.
r.left = r.left & 0xFFFE;
if (r.width() > _screen->getWidth()) {
// We get invalid dimensions at least at the end of sq3 (script bug!).
// Same happens very often in lsl5, sierra sci didnt fix it but it looked awful.
// Also happens frequently in the demo of GK1.
warning("Fixing too large window, left: %d, right: %d", dims.left, dims.right);
r.left = 0;
r.right = _screen->getWidth() - 1;
if ((style != _styleUser) && !(style & SCI_WINDOWMGR_STYLE_NOFRAME))
r.right--;
}
pwnd->rect = r;
if (restoreRect)
pwnd->restoreRect = *restoreRect;
pwnd->wndStyle = style;
pwnd->hSaved1 = pwnd->hSaved2 = NULL_REG;
pwnd->bDrawn = false;
if ((style & SCI_WINDOWMGR_STYLE_TRANSPARENT) == 0)
pwnd->saveScreenMask = (priority == -1 ? GFX_SCREEN_MASK_VISUAL : GFX_SCREEN_MASK_VISUAL | GFX_SCREEN_MASK_PRIORITY);
if (title && (style & SCI_WINDOWMGR_STYLE_TITLE)) {
pwnd->title = title;
}
r = pwnd->rect;
if ((style != _styleUser) && !(style & SCI_WINDOWMGR_STYLE_NOFRAME)) {
r.grow(1);
if (style & SCI_WINDOWMGR_STYLE_TITLE) {
r.top -= 10;
r.bottom++;
}
}
pwnd->dims = r;
// Clip window, if needed
Common::Rect wmprect = _wmgrPort->rect;
// Handle a special case for Dr. Brain 1 Mac. When hovering the mouse cursor
// over the status line on top, the game scripts try to draw the game's icon
// bar above the current port, by specifying a negative window top, so that
// the end result will be drawn 10 pixels above the current port. This is a
// hack by Sierra, and is only limited to user style windows. Normally, we
// should not clip, same as what Sierra does. However, this will result in
// having invalid rectangles with negative coordinates. For this reason, we
// adjust the containing rectangle instead.
if (pwnd->dims.top < 0 && g_sci->getPlatform() == Common::kPlatformMacintosh &&
(style & SCI_WINDOWMGR_STYLE_USER) && _wmgrPort->top + pwnd->dims.top >= 0) {
// Offset the final rect top by the requested pixels
wmprect.top += pwnd->dims.top;
}
//.........这里部分代码省略.........
/**
* This copies a rect to screen w/o scaling adjustment and is only meant to be
* used on hires graphics used in upscaled hires mode.
*/
void GfxScreen::copyDisplayRectToScreen(const Common::Rect &rect) {
if (!_upscaledHires)
error("copyDisplayRectToScreen: not in upscaled hires mode");
g_system->copyRectToScreen(_activeScreen + rect.top * _displayWidth + rect.left, _displayWidth, rect.left, rect.top, rect.width(), rect.height());
}
请发表评论