TestExitStatus EventTests::mouseEvents() { Testsuite::clearScreen(); Common::String info = "Testing Mouse events.\n " "Any movement/click generated by L/R/M mouse buttons or the mouse wheel should be detected.\n" "Press X to exit"; if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) { Testsuite::logPrintf("Info! Skipping test : keyboard events\n"); return kTestSkipped; } Common::EventManager *eventMan = g_system->getEventManager(); Common::Point pt(0, 30); Common::Rect rectInfo = Testsuite::writeOnScreen("Generate mouse events make L/R/M button clicks, move wheel", pt); pt.y += 15; Testsuite::writeOnScreen("Press X to exit", pt); pt.y = 70; Common::Rect rectLB = Testsuite::writeOnScreen("Left-button click : Not tested", pt); pt.y += 15; Common::Rect rectRB = Testsuite::writeOnScreen("Right-button click : Not tested", pt); pt.y += 15; Common::Rect rectMB = Testsuite::writeOnScreen("Middle-button click : Not tested", pt); pt.y += 15; Common::Rect rectWheel = Testsuite::writeOnScreen("Wheel Movements : Not tested", pt); // Init Mouse Palette GFXtests::initMousePalette(); Common::Rect finishZone = drawFinishZone(); bool quitLoop = false; TestExitStatus passed = kTestPassed; // handle all mouse events Common::Event event; while (!quitLoop) { // Show mouse CursorMan.showMouse(true); g_system->updateScreen(); while (eventMan->pollEvent(event)) { // Quit if explicitly requested if (Engine::shouldQuit()) { return passed; } switch (event.type) { case Common::EVENT_MOUSEMOVE: // Movements havee already been tested in GFX break; case Common::EVENT_LBUTTONDOWN: Testsuite::clearScreen(rectInfo); Testsuite::writeOnScreen("Mouse left-button pressed", Common::Point(rectInfo.left, rectInfo.top)); break; case Common::EVENT_RBUTTONDOWN: Testsuite::clearScreen(rectInfo); Testsuite::writeOnScreen("Mouse right-button pressed", Common::Point(rectInfo.left, rectInfo.top)); break; case Common::EVENT_WHEELDOWN: Testsuite::clearScreen(rectInfo); Testsuite::writeOnScreen("Mouse wheel moved down", Common::Point(rectInfo.left, rectInfo.top)); Testsuite::writeOnScreen("Wheel Movements : Done!", Common::Point(rectWheel.left, rectWheel.top)); break; case Common::EVENT_MBUTTONDOWN: Testsuite::clearScreen(rectInfo); Testsuite::writeOnScreen("Mouse middle-button pressed ", Common::Point(rectInfo.left, rectInfo.top)); break; case Common::EVENT_LBUTTONUP: Testsuite::clearScreen(rectInfo); if (finishZone.contains(eventMan->getMousePos())) { quitLoop = true; } Testsuite::writeOnScreen("Mouse left-button released", Common::Point(rectInfo.left, rectInfo.top)); Testsuite::writeOnScreen("Left-button clicks : Done!", Common::Point(rectLB.left, rectLB.top)); break; case Common::EVENT_RBUTTONUP: Testsuite::clearScreen(rectInfo); Testsuite::writeOnScreen("Mouse right-button released", Common::Point(rectInfo.left, rectInfo.top)); Testsuite::writeOnScreen("Right-button clicks : Done!", Common::Point(rectRB.left, rectRB.top)); break; case Common::EVENT_WHEELUP: Testsuite::clearScreen(rectInfo); Testsuite::writeOnScreen("Mouse wheel moved up", Common::Point(rectInfo.left, rectInfo.top)); Testsuite::writeOnScreen("Wheel Movements : Done!", Common::Point(rectWheel.left, rectWheel.top)); break; case Common::EVENT_MBUTTONUP: Testsuite::clearScreen(rectInfo); Testsuite::writeOnScreen("Mouse middle-button released ", Common::Point(rectInfo.left, rectInfo.top)); Testsuite::writeOnScreen("Middle-button clicks : Done!", Common::Point(rectMB.left, rectMB.top)); break; case Common::EVENT_KEYDOWN: if (event.kbd.keycode == Common::KEYCODE_x) { Testsuite::clearScreen(rectInfo); Testsuite::writeOnScreen("Exit requested", Common::Point(rectInfo.left, rectInfo.top)); quitLoop = true; } break; default: break; } } } CursorMan.showMouse(false); // Verify results now! if (Testsuite::handleInteractiveInput("Were mouse clicks (L/R/M buttons) and wheel movements identfied ?", "Yes", "No", kOptionRight)) { Testsuite::logDetailedPrintf("Mouse clicks (L/R/M buttons) and wheel movements failed"); passed = kTestFailed; } return passed; }
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; } }
void UISlots::draw(bool updateFlag, bool delFlag) { Scene &scene = _vm->_game->_scene; UserInterface &userInterface = scene._userInterface; DirtyArea *dirtyAreaPtr = nullptr; // Loop through setting up the dirty areas for (uint idx = 0; idx < size(); ++idx) { DirtyArea &dirtyArea = userInterface._dirtyAreas[idx]; UISlot &slot = (*this)[idx]; if (slot._flags >= IMG_STATIC) { dirtyArea._active = false; } else { dirtyArea.setUISlot(&slot); dirtyArea._textActive = true; if (slot._segmentId == IMG_SPINNING_OBJECT && slot._flags == IMG_FULL_UPDATE) { dirtyArea._active = false; dirtyAreaPtr = &dirtyArea; } } } userInterface._dirtyAreas.merge(1, userInterface._uiSlots.size()); if (dirtyAreaPtr) dirtyAreaPtr->_active = true; // Copy parts of the user interface background that need to be erased for (uint idx = 0; idx < size(); ++idx) { DirtyArea &dirtyArea = userInterface._dirtyAreas[idx]; UISlot &slot = (*this)[idx]; if (dirtyArea._active && dirtyArea._bounds.width() > 0 && dirtyArea._bounds.height() > 0 && slot._flags > -20) { if (slot._flags >= IMG_ERASE) { // Merge area userInterface.mergeFrom(&userInterface._surface, dirtyArea._bounds, Common::Point(dirtyArea._bounds.left, dirtyArea._bounds.top)); } else { // Copy area userInterface.blitFrom(userInterface._surface, dirtyArea._bounds, Common::Point(dirtyArea._bounds.left, dirtyArea._bounds.top)); } } } for (uint idx = 0; idx < size(); ++idx) { DirtyArea &dirtyArea = userInterface._dirtyAreas[idx]; UISlot &slot = (*this)[idx]; int slotType = slot._flags; if (slotType >= IMG_STATIC) { dirtyArea.setUISlot(&slot); if (!updateFlag) slotType &= ~0x40; dirtyArea._textActive = slotType > 0; slot._flags &= 0x40; } } userInterface._dirtyAreas.merge(1, userInterface._uiSlots.size()); for (uint idx = 0; idx < size(); ++idx) { DirtyArea *dirtyArea = &userInterface._dirtyAreas[idx]; UISlot &slot = (*this)[idx]; if (slot._flags >= IMG_STATIC && !(slot._flags & 0x40)) { if (!dirtyArea->_active) { do { dirtyArea = dirtyArea->_mergedArea; } while (!dirtyArea->_active); } if (dirtyArea->_textActive) { SpriteAsset *asset = scene._sprites[slot._spritesIndex]; // Get the frame details int frameNumber = ABS(slot._frameNumber); bool flipped = slot._frameNumber < 0; if (slot._segmentId == IMG_SPINNING_OBJECT) { MSprite *sprite = asset->getFrame(frameNumber - 1); userInterface.transBlitFrom(*sprite, slot._position, sprite->getTransparencyIndex()); } else { MSprite *sprite = asset->getFrame(frameNumber - 1); if (flipped) { MSurface *spr = sprite->flipHorizontal(); userInterface.mergeFrom(spr, spr->getBounds(), slot._position, sprite->getTransparencyIndex()); spr->free(); delete spr; } else { userInterface.mergeFrom(sprite, sprite->getBounds(), slot._position, sprite->getTransparencyIndex()); } } } } } // Mark areas of the screen surface for updating if (updateFlag) { for (uint idx = 0; idx < size(); ++idx) { DirtyArea &dirtyArea = userInterface._dirtyAreas[idx]; if (dirtyArea._active && dirtyArea._textActive && dirtyArea._bounds.width() > 0 && dirtyArea._bounds.height() > 0) { // Flag area of screen as needing update Common::Rect r = dirtyArea._bounds; r.translate(0, scene._interfaceY); //_vm->_screen->copyRectToScreen(r); } } } // Post-processing to remove slots no longer needed for (int idx = (int)size() - 1; idx >= 0; --idx) { UISlot &slot = (*this)[idx]; if (slot._flags < IMG_STATIC) { if (delFlag || updateFlag) remove_at(idx); else if (slot._flags > -20) slot._flags -= 20; } else { if (updateFlag) slot._flags &= ~0x40; else slot._flags |= 0x40; } } }
void GfxControls32::kernelTexteditChange(reg_t controlObject) { SciEvent curEvent; uint16 maxChars = 40; //readSelectorValue(_segMan, controlObject, SELECTOR(max)); // TODO reg_t textReference = readSelector(_segMan, controlObject, SELECTOR(text)); GfxFont *font = _cache->getFont(readSelectorValue(_segMan, controlObject, SELECTOR(font))); Common::String text; uint16 textSize; bool textChanged = false; bool textAddChar = false; Common::Rect rect; if (textReference.isNull()) error("kEditControl called on object that doesnt have a text reference"); text = _segMan->getString(textReference); // TODO: Finish this warning("kEditText ('%s')", text.c_str()); return; uint16 cursorPos = 0; //uint16 oldCursorPos = cursorPos; bool captureEvents = true; EventManager* eventMan = g_sci->getEventManager(); while (captureEvents) { curEvent = g_sci->getEventManager()->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_PEEK); if (curEvent.type == SCI_EVENT_NONE) { eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event } else { textSize = text.size(); switch (curEvent.type) { case SCI_EVENT_MOUSE_PRESS: // TODO: Implement mouse support for cursor change break; case SCI_EVENT_KEYBOARD: switch (curEvent.character) { case SCI_KEY_BACKSPACE: if (cursorPos > 0) { cursorPos--; text.deleteChar(cursorPos); textChanged = true; } eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; case SCI_KEY_DELETE: if (cursorPos < textSize) { text.deleteChar(cursorPos); textChanged = true; } eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; case SCI_KEY_HOME: // HOME cursorPos = 0; textChanged = true; eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; case SCI_KEY_END: // END cursorPos = textSize; textChanged = true; eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; case SCI_KEY_LEFT: // LEFT if (cursorPos > 0) { cursorPos--; textChanged = true; } eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; case SCI_KEY_RIGHT: // RIGHT if (cursorPos + 1 <= textSize) { cursorPos++; textChanged = true; } eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; case 3: // returned in SCI1 late and newer when Control - C is pressed if (curEvent.modifiers & SCI_KEYMOD_CTRL) { // Control-C erases the whole line cursorPos = 0; text.clear(); textChanged = true; } eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; case SCI_KEY_UP: case SCI_KEY_DOWN: case SCI_KEY_ENTER: case SCI_KEY_ESC: case SCI_KEY_TAB: case SCI_KEY_SHIFT_TAB: captureEvents = false; break; default: if ((curEvent.modifiers & SCI_KEYMOD_CTRL) && curEvent.character == 'c') { // Control-C in earlier SCI games (SCI0 - SCI1 middle) // Control-C erases the whole line cursorPos = 0; text.clear(); textChanged = true; } else if (curEvent.character > 31 && curEvent.character < 256 && textSize < maxChars) { // insert pressed character textAddChar = true; textChanged = true; } eventMan->getSciEvent(SCI_EVENT_KEYBOARD); // consume the event break; } break; } } if (textChanged) { rect = g_sci->_gfxCompare->getNSRect(controlObject); if (textAddChar) { const char *textPtr = text.c_str(); // We check if we are really able to add the new char uint16 textWidth = 0; while (*textPtr) textWidth += font->getCharWidth((byte)*textPtr++); textWidth += font->getCharWidth(curEvent.character); // Does it fit? if (textWidth >= rect.width()) { return; } text.insertChar(curEvent.character, cursorPos++); // Note: the following checkAltInput call might make the text // too wide to fit, but SSCI fails to check that too. } reg_t hunkId = readSelector(_segMan, controlObject, SELECTOR(bitmap)); Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(controlObject); //texteditCursorErase(); // TODO: Cursor // Write back string _segMan->strcpy(textReference, text.c_str()); // Modify the buffer and show it _text->createTextBitmap(controlObject, 0, 0, hunkId); _text->drawTextBitmap(0, 0, nsRect, controlObject); //texteditCursorDraw(rect, text.c_str(), cursorPos); // TODO: Cursor g_system->updateScreen(); } else { // TODO: Cursor /* if (g_system->getMillis() >= _texteditBlinkTime) { _paint16->invertRect(_texteditCursorRect); _paint16->bitsShow(_texteditCursorRect); _texteditCursorVisible = !_texteditCursorVisible; texteditSetBlinkTime(); } */ } textAddChar = false; textChanged = false; g_sci->sleep(10); } // while }
// 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); } }
void ShaderRenderer::draw2DText(const Common::String &text, const Common::Point &position) { OpenGLTexture *glFont = static_cast<OpenGLTexture *>(_font); // The font only has uppercase letters Common::String textToDraw = text; textToDraw.toUppercase(); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); if (_prevText != textToDraw || _prevTextPosition != position) { _prevText = textToDraw; _prevTextPosition = position; float x = position.x / _currentViewport.getWidth(); float y = position.y / _currentViewport.getHeight(); float *bufData = new float[16 * textToDraw.size()]; float *cur = bufData; for (uint i = 0; i < textToDraw.size(); i++) { Common::Rect textureRect = getFontCharacterRect(textToDraw[i]); float w = textureRect.width() / _currentViewport.getWidth(); float h = textureRect.height() / _currentViewport.getHeight(); float cw = textureRect.width() / (float)glFont->internalWidth; float ch = textureRect.height() / (float)glFont->internalHeight; float cx = textureRect.left / (float)glFont->internalWidth; float cy = textureRect.top / (float)glFont->internalHeight; const float charData[] = { cx, cy + ch, x, y, cx + cw, cy + ch, x + w, y, cx + cw, cy, x + w, y + h, cx, cy, x, y + h, }; memcpy(cur, charData, 16 * sizeof(float)); cur += 16; x += (textureRect.width() - 3) / _currentViewport.getWidth(); } glBindBuffer(GL_ARRAY_BUFFER, _textVBO); glBufferSubData(GL_ARRAY_BUFFER, 0, textToDraw.size() * 16 * sizeof(float), bufData); delete[] bufData; } _textShader->use(); glBindTexture(GL_TEXTURE_2D, glFont->id); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _quadEBO); glDrawElements(GL_TRIANGLES, 6 * textToDraw.size(), GL_UNSIGNED_SHORT, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDisable(GL_BLEND); glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); }
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 // this code. if (getSciVersion() >= SCI_VERSION_1_MIDDLE) { int16 leftPos = rect.right <= _screen->getWidth() ? 0 : _screen->getWidth() - rect.right; int16 topPos = rect.bottom <= _screen->getHeight() ? 0 : _screen->getHeight() - rect.bottom; _ports->move(leftPos, topPos); rect.moveTo(_ports->getPort()->curLeft, _ports->getPort()->curTop); } if (doSaveUnder) result = bitsSave(rect, GFX_SCREEN_MASK_VISUAL); if (colorBack != -1) fillRect(rect, GFX_SCREEN_MASK_VISUAL, colorBack, 0, 0); _text16->Box(text, false, rect, alignment, -1); if (_screen->_picNotValid == 0 && bRedraw) bitsShow(rect); // restoring port and cursor pos Port *currport = _ports->getPort(); uint16 tTop = currport->curTop; uint16 tLeft = currport->curLeft; if (!g_sci->_features->usesOldGfxFunctions()) { // Restore port settings for some SCI0LATE and SCI01+ only. // // The change actually happened inbetween .530 (hoyle1) and .566 (heros // quest). We don't have any detection for that currently, so we are // using oldGfxFunctions (.502). The only games that could get // regressions because of this are hoyle1, kq4 and funseeker. If there // are regressions, we should use interpreter version (which would // require exe version detection). // // If we restore the port for whole SCI0LATE, at least sq3old will get // an issue - font 0 will get used when scanning for planets instead of // font 600 - a setfont parameter is missing in one of the kDisplay // calls in script 19. I assume this is a script bug, because it was // added in sq3new. *currport = oldPort; } currport->curTop = tTop; currport->curLeft = tLeft; return result; }
void ThemeEngine::restoreBackground(Common::Rect r) { r.clip(_screen.w, _screen.h); _vectorRenderer->blitSurface(&_backBuffer, r); }
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; } }
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; } int16 oldtop = pwnd->dims.top; int16 oldleft = pwnd->dims.left; if (wmprect.top > pwnd->dims.top) pwnd->dims.moveTo(pwnd->dims.left, wmprect.top); if (wmprect.bottom < pwnd->dims.bottom) pwnd->dims.moveTo(pwnd->dims.left, wmprect.bottom - pwnd->dims.bottom + pwnd->dims.top); if (wmprect.right < pwnd->dims.right) pwnd->dims.moveTo(wmprect.right + pwnd->dims.left - pwnd->dims.right, pwnd->dims.top); if (wmprect.left > pwnd->dims.left) pwnd->dims.moveTo(wmprect.left, pwnd->dims.top); pwnd->rect.moveTo(pwnd->rect.left + pwnd->dims.left - oldleft, pwnd->rect.top + pwnd->dims.top - oldtop); if (restoreRect == 0) pwnd->restoreRect = pwnd->dims; if (pwnd->restoreRect.top < 0 && g_sci->getPlatform() == Common::kPlatformMacintosh && (style & SCI_WINDOWMGR_STYLE_USER) && _wmgrPort->top + pwnd->restoreRect.top >= 0) { // Special case for Dr. Brain 1 Mac (check above), applied to the // restore rectangle. pwnd->restoreRect.moveTo(pwnd->restoreRect.left, wmprect.top); } if (draw) drawWindow(pwnd); setPort((Port *)pwnd); // All SCI0 games till kq4 .502 (not including) did not adjust against _wmgrPort, we set _wmgrPort->top to 0 in that case setOrigin(pwnd->rect.left, pwnd->rect.top + _wmgrPort->top); pwnd->rect.moveTo(0, 0); return pwnd; }
void GfxPorts::drawWindow(Window *pWnd) { if (pWnd->bDrawn) return; int16 wndStyle = pWnd->wndStyle; pWnd->bDrawn = true; Port *oldport = setPort(_wmgrPort); penColor(0); if ((wndStyle & SCI_WINDOWMGR_STYLE_TRANSPARENT) == 0) { pWnd->hSaved1 = _paint16->bitsSave(pWnd->restoreRect, GFX_SCREEN_MASK_VISUAL); if (pWnd->saveScreenMask & GFX_SCREEN_MASK_PRIORITY) { pWnd->hSaved2 = _paint16->bitsSave(pWnd->restoreRect, GFX_SCREEN_MASK_PRIORITY); if ((wndStyle & SCI_WINDOWMGR_STYLE_USER) == 0) _paint16->fillRect(pWnd->restoreRect, GFX_SCREEN_MASK_PRIORITY, 0, 15); } } // drawing frame,shadow and title if ((getSciVersion() >= SCI_VERSION_1_LATE) ? !(wndStyle & _styleUser) : wndStyle != _styleUser) { Common::Rect r = pWnd->dims; if (!(wndStyle & SCI_WINDOWMGR_STYLE_NOFRAME)) { r.top++; r.left++; _paint16->frameRect(r);// draw shadow r.translate(-1, -1); _paint16->frameRect(r);// draw actual window frame if (wndStyle & SCI_WINDOWMGR_STYLE_TITLE) { if (getSciVersion() <= SCI_VERSION_0_LATE) { // draw a black line between titlebar and actual window content for SCI0 r.bottom = r.top + 10; _paint16->frameRect(r); } r.grow(-1); if (getSciVersion() <= SCI_VERSION_0_LATE) _paint16->fillRect(r, GFX_SCREEN_MASK_VISUAL, 8); // grey titlebar for SCI0 else _paint16->fillRect(r, GFX_SCREEN_MASK_VISUAL, 0); // black titlebar for SCI01+ if (!pWnd->title.empty()) { int16 oldcolor = getPort()->penClr; penColor(_screen->getColorWhite()); _text16->Box(pWnd->title.c_str(), true, r, SCI_TEXT16_ALIGNMENT_CENTER, 0); penColor(oldcolor); } r.grow(+1); r.bottom = pWnd->dims.bottom - 1; r.top += 9; } r.grow(-1); } if (!(wndStyle & SCI_WINDOWMGR_STYLE_TRANSPARENT)) _paint16->fillRect(r, GFX_SCREEN_MASK_VISUAL, pWnd->backClr); _paint16->bitsShow(pWnd->dims); } setPort(oldport); }
void PressureDoor::openInteraction() { if (_isUpperDoor) { _levelsMovie.initFromMovieFile("Images/Norad Alpha/Upper Levels Movie"); _levelsMovie.moveElementTo(kNoradUpperLevelsLeft, kNoradUpperLevelsTop); } else { _levelsMovie.initFromMovieFile("Images/Norad Alpha/Lower Levels Movie"); _levelsMovie.moveElementTo(kNoradLowerLevelsLeft, kNoradLowerLevelsTop); } _levelsScale = _levelsMovie.getScale(); _levelsMovie.setDisplayOrder(kPressureLevelsOrder); _levelsMovie.startDisplaying(); _levelsMovie.setSegment(kLevelsSplashStart * _levelsScale, kLevelsSplashStop * _levelsScale); _levelsMovie.setTime(kLevelsSplashStart * _levelsScale); _levelsMovie.redrawMovieWorld(); _levelsMovie.show(); _pressureCallBack.setNotification(&_pressureNotification); _pressureCallBack.initCallBack(&_levelsMovie, kCallBackAtExtremes); _pressureCallBack.setCallBackFlag(kSplashFinished); _pressureCallBack.scheduleCallBack(kTriggerAtStop, 0, 0); _pressureNotification.notifyMe(this, kPressureNotificationFlags, kPressureNotificationFlags); if (_isUpperDoor) { _typeMovie.initFromMovieFile("Images/Norad Alpha/Upper Type Movie"); _typeMovie.moveElementTo(kNoradUpperTypeLeft, kNoradUpperTypeTop); } else { _typeMovie.initFromMovieFile("Images/Norad Alpha/Lower Type Movie"); _typeMovie.moveElementTo(kNoradLowerTypeLeft, kNoradLowerTypeTop); } _typeScale = _typeMovie.getScale(); _typeMovie.setDisplayOrder(kPressureTypeOrder); _typeMovie.startDisplaying(); _typeMovie.setTime(kDoorSealedTime * _typeScale); _typeMovie.redrawMovieWorld(); SpriteFrame *frame = new SpriteFrame(); if (_isUpperDoor) frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureUpOffPICTID); else frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureUpOffPICTID); _upButton.addFrame(frame, 0, 0); frame = new SpriteFrame(); if (_isUpperDoor) frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureUpOnPICTID); else frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureUpOnPICTID); _upButton.addFrame(frame, 0, 0); _upButton.setCurrentFrameIndex(0); _upButton.setDisplayOrder(kPressureUpOrder); Common::Rect r; frame->getSurfaceBounds(r); if (_isUpperDoor) r.moveTo(kNoradUpperUpLeft, kNoradUpperUpTop); else r.moveTo(kNoradLowerUpLeft, kNoradLowerUpTop); _upButton.setBounds(r); _upButton.startDisplaying(); _upButton.show(); frame = new SpriteFrame(); if (_isUpperDoor) frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureDownOffPICTID); else frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureDownOffPICTID); _downButton.addFrame(frame, 0, 0); frame = new SpriteFrame(); if (_isUpperDoor) frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kLowerPressureDownOnPICTID); else frame->initFromPICTResource(((PegasusEngine *)g_engine)->_resFork, kUpperPressureDownOnPICTID); _downButton.addFrame(frame, 0, 0); _downButton.setCurrentFrameIndex(0); _downButton.setDisplayOrder(kPressureDownOrder); frame->getSurfaceBounds(r); if (_isUpperDoor) r.moveTo(kNoradUpperDownLeft, kNoradUpperDownTop); else r.moveTo(kNoradLowerDownLeft, kNoradLowerDownTop); _downButton.setBounds(r); _downButton.startDisplaying(); _downButton.show(); _utilityCallBack.setNotification(&_utilityNotification); _utilityCallBack.initCallBack(&_utilityTimer, kCallBackAtTime); _utilityNotification.notifyMe(this, kUtilityNotificationFlags, kUtilityNotificationFlags); _utilityTimer.setMasterTimeBase(getOwner()->getNavMovie()); if (_playingAgainstRobot) _neighborhoodNotification->notifyMe(this, kExtraCompletedFlag | kDelayCompletedFlag | kSpotSoundCompletedFlag, kExtraCompletedFlag | kDelayCompletedFlag | kSpotSoundCompletedFlag); else _neighborhoodNotification->notifyMe(this, kDelayCompletedFlag | kSpotSoundCompletedFlag, kDelayCompletedFlag | kSpotSoundCompletedFlag); _gameState = kPlayingSplash; }
reg_t GfxText32::createTextBitmap(reg_t textObject, uint16 maxWidth, uint16 maxHeight) { reg_t stringObject = readSelector(_segMan, textObject, SELECTOR(text)); // The object in the text selector of the item can be either a raw string // or a Str object. In the latter case, we need to access the object's data // selector to get the raw string. if (_segMan->isHeapObject(stringObject)) stringObject = readSelector(_segMan, stringObject, SELECTOR(data)); Common::String text = _segMan->getString(stringObject); // HACK: The character offsets of the up and down arrow buttons are off by one // in GK1, for some unknown reason. Fix them here. if (text.size() == 1 && (text[0] == 29 || text[0] == 30)) { text.setChar(text[0] + 1, 0); } GuiResourceId fontId = readSelectorValue(_segMan, textObject, SELECTOR(font)); GfxFont *font = _cache->getFont(fontId); bool dimmed = readSelectorValue(_segMan, textObject, SELECTOR(dimmed)); int16 alignment = readSelectorValue(_segMan, textObject, SELECTOR(mode)); uint16 foreColor = readSelectorValue(_segMan, textObject, SELECTOR(fore)); uint16 backColor = readSelectorValue(_segMan, textObject, SELECTOR(back)); Common::Rect nsRect = g_sci->_gfxCompare->getNSRect(textObject); uint16 width = nsRect.width() + 1; uint16 height = nsRect.height() + 1; // Limit rectangle dimensions, if requested if (maxWidth > 0) width = maxWidth; if (maxHeight > 0) height = maxHeight; // Upscale the coordinates/width if the fonts are already upscaled if (_screen->fontIsUpscaled()) { width = width * _screen->getDisplayWidth() / _screen->getWidth(); height = height * _screen->getDisplayHeight() / _screen->getHeight(); } int entrySize = width * height + BITMAP_HEADER_SIZE; reg_t memoryId = _segMan->allocateHunkEntry("TextBitmap()", entrySize); writeSelector(_segMan, textObject, SELECTOR(bitmap), memoryId); byte *memoryPtr = _segMan->getHunkPointer(memoryId); memset(memoryPtr, backColor, entrySize); byte *bitmap = memoryPtr + BITMAP_HEADER_SIZE; // Save totalWidth, totalHeight WRITE_LE_UINT16(memoryPtr, width); WRITE_LE_UINT16(memoryPtr + 2, height); int16 charCount = 0; uint16 curX = 0, curY = 0; const char *txt = text.c_str(); int16 textWidth, textHeight, totalHeight = 0, offsetX = 0, offsetY = 0; uint16 start = 0; // Calculate total text height while (*txt) { charCount = GetLongest(txt, width, font); if (charCount == 0) break; Width(txt, 0, (int16)strlen(txt), fontId, textWidth, textHeight, true); totalHeight += textHeight; txt += charCount; while (*txt == ' ') txt++; // skip over breaking spaces } txt = text.c_str(); // Draw text in buffer while (*txt) { charCount = GetLongest(txt, width, font); if (charCount == 0) break; Width(txt, start, charCount, fontId, textWidth, textHeight, true); switch (alignment) { case SCI_TEXT32_ALIGNMENT_RIGHT: offsetX = width - textWidth; break; case SCI_TEXT32_ALIGNMENT_CENTER: // Center text both horizontally and vertically offsetX = (width - textWidth) / 2; offsetY = (height - totalHeight) / 2; break; case SCI_TEXT32_ALIGNMENT_LEFT: offsetX = 0; break; default: warning("Invalid alignment %d used in TextBox()", alignment); } for (int i = 0; i < charCount; i++) { unsigned char curChar = txt[i]; font->drawToBuffer(curChar, curY + offsetY, curX + offsetX, foreColor, dimmed, bitmap, width, height); curX += font->getCharWidth(curChar); } curX = 0; curY += font->getHeight(); txt += charCount; while (*txt == ' ') txt++; // skip over breaking spaces } return memoryId; }
bool RenderObject::getObjectIntersection(RenderObjectPtr<RenderObject> pObject, Common::Rect &result) { result = pObject->getBbox(); result.clip(_bbox); return result.isValidRect(); }
void RenderManager::prepareBackground() { _backgroundDirtyRect.clip(_backgroundWidth, _backgroundHeight); RenderTable::RenderState state = _renderTable.getRenderState(); if (state == RenderTable::PANORAMA) { // Calculate the visible portion of the background Common::Rect viewPort(_workingWidth, _workingHeight); viewPort.translate(-(_screenCenterX - _backgroundOffset), 0); Common::Rect drawRect = _backgroundDirtyRect; drawRect.clip(viewPort); // Render the visible portion if (!drawRect.isEmpty()) { blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, _screenCenterX - _backgroundOffset + drawRect.left, drawRect.top); } // Mark the dirty portion of the surface _backgroundSurfaceDirtyRect = _backgroundDirtyRect; _backgroundSurfaceDirtyRect.translate(_screenCenterX - _backgroundOffset, 0); // Panorama mode allows the user to spin in circles. Therefore, we need to render // the portion of the image that wrapped to the other side of the screen if (_backgroundOffset < _screenCenterX) { viewPort.moveTo(-(_screenCenterX - (_backgroundOffset + _backgroundWidth)), 0); drawRect = _backgroundDirtyRect; drawRect.clip(viewPort); if (!drawRect.isEmpty()) blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, _screenCenterX - (_backgroundOffset + _backgroundWidth) + drawRect.left, drawRect.top); Common::Rect tmp = _backgroundDirtyRect; tmp.translate(_screenCenterX - (_backgroundOffset + _backgroundWidth), 0); if (!tmp.isEmpty()) _backgroundSurfaceDirtyRect.extend(tmp); } else if (_backgroundWidth - _backgroundOffset < _screenCenterX) { viewPort.moveTo(-(_screenCenterX + _backgroundWidth - _backgroundOffset), 0); drawRect = _backgroundDirtyRect; drawRect.clip(viewPort); if (!drawRect.isEmpty()) blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, _screenCenterX + _backgroundWidth - _backgroundOffset + drawRect.left, drawRect.top); Common::Rect tmp = _backgroundDirtyRect; tmp.translate(_screenCenterX + _backgroundWidth - _backgroundOffset, 0); if (!tmp.isEmpty()) _backgroundSurfaceDirtyRect.extend(tmp); } } else if (state == RenderTable::TILT) { // Tilt doesn't allow wrapping, so we just do a simple clip Common::Rect viewPort(_workingWidth, _workingHeight); viewPort.translate(0, -(_screenCenterY - _backgroundOffset)); Common::Rect drawRect = _backgroundDirtyRect; drawRect.clip(viewPort); if (!drawRect.isEmpty()) blitSurfaceToSurface(_currentBackgroundImage, drawRect, _backgroundSurface, drawRect.left, _screenCenterY - _backgroundOffset + drawRect.top); // Mark the dirty portion of the surface _backgroundSurfaceDirtyRect = _backgroundDirtyRect; _backgroundSurfaceDirtyRect.translate(0, _screenCenterY - _backgroundOffset); } else { if (!_backgroundDirtyRect.isEmpty()) blitSurfaceToSurface(_currentBackgroundImage, _backgroundDirtyRect, _backgroundSurface, _backgroundDirtyRect.left, _backgroundDirtyRect.top); _backgroundSurfaceDirtyRect = _backgroundDirtyRect; } // Clear the dirty rect since everything is clean now _backgroundDirtyRect = Common::Rect(); _backgroundSurfaceDirtyRect.clip(_workingWidth, _workingHeight); }
/** * 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()); }
void ShaderRenderer::setupCameraPerspective(float pitch, float heading, float fov) { Renderer::setupCameraPerspective(pitch, heading, fov); Common::Rect frame = frameViewport(); glViewport(frame.left, frame.top, frame.width(), frame.height()); }
void GfxScreen::copyRectToScreen(const Common::Rect &rect, int16 x, int16 y) { if (!_upscaledHires) { g_system->copyRectToScreen(_activeScreen + rect.top * _displayWidth + rect.left, _displayWidth, x, y, rect.width(), rect.height()); } else { int rectHeight = _upscaledMapping[rect.bottom] - _upscaledMapping[rect.top]; g_system->copyRectToScreen(_activeScreen + _upscaledMapping[rect.top] * _displayWidth + rect.left * 2, _displayWidth, x * 2, _upscaledMapping[y], rect.width() * 2, rectHeight); } }
void Slide::drawSlideElement(const Common::Rect &drawRect, const Common::Rect &oldBounds, DisplayElement *picture) { if (picture && drawRect.intersects(oldBounds)) { picture->moveElementTo(oldBounds.left, oldBounds.top); picture->draw(drawRect.findIntersectingRect(oldBounds)); } }
bool BaseSurfaceOSystem::drawSprite(int x, int y, Rect32 *rect, float zoomX, float zoomY, uint32 alpha, bool alphaDisable, TSpriteBlendMode blendMode, bool mirrorX, bool mirrorY, int offsetX, int offsetY) { BaseRenderOSystem *renderer = static_cast<BaseRenderOSystem *>(_gameRef->_renderer); if (!_loaded) { finishLoad(); } if (renderer->_forceAlphaColor != 0) { alpha = renderer->_forceAlphaColor; } byte r = RGBCOLGetR(alpha); byte g = RGBCOLGetG(alpha); byte b = RGBCOLGetB(alpha); byte a = RGBCOLGetA(alpha); renderer->setAlphaMod(a); renderer->setColorMod(r, g, b); #if 0 // These are kept for reference if BlendMode is reimplemented at some point. if (alphaDisable) { SDL_SetTextureBlendMode(_texture, SDL_BLENDMODE_NONE); } else { SDL_SetTextureBlendMode(_texture, SDL_BLENDMODE_BLEND); } #endif // TODO: This _might_ miss the intended behaviour by 1 in each direction // But I think it fits the model used in Wintermute. Common::Rect srcRect; srcRect.left = rect->left; srcRect.top = rect->top; srcRect.setWidth(rect->right - rect->left); srcRect.setHeight(rect->bottom - rect->top); Common::Rect position; position.left = x + offsetX; position.top = y + offsetY; // Crop off-by-ones: if (position.left == -1) { position.left = 0; // TODO: Something is wrong } if (position.top == -1) { position.top = 0; // TODO: Something is wrong } position.setWidth((int16)((float)srcRect.width() * zoomX / 100.f)); position.setHeight((int16)((float)srcRect.height() * zoomX / 100.f)); renderer->modTargetRect(&position); /* position.left += offsetX; position.top += offsetY;*/ // TODO: This actually requires us to have the SAME source-offsets every time, // But no checking is in place for that yet. // TODO: Optimize by not doing alpha-blits if we lack or disable alpha bool hasAlpha; if (_hasAlpha && !alphaDisable) { hasAlpha = true; } else { hasAlpha = false; } if (alphaDisable) { warning("BaseSurfaceOSystem::drawSprite - AlphaDisable ignored"); } renderer->drawSurface(this, _surface, &srcRect, &position, mirrorX, mirrorY, !hasAlpha); return STATUS_OK; }
void Menu::renderSubmenu(MenuItem *menu) { Common::Rect *r = &menu->subbbox; if (r->width() == 0 || r->height() == 0) return; Design::drawFilledRect(&_gui->_screen, *r, kColorWhite, _gui->_patterns, kPatternSolid); Design::drawRect(&_gui->_screen, *r, 1, kColorBlack, _gui->_patterns, kPatternSolid); Design::drawVLine(&_gui->_screen, r->right + 1, r->top + 3, r->bottom + 1, 1, kColorBlack, _gui->_patterns, kPatternSolid); Design::drawHLine(&_gui->_screen, r->left + 3, r->right + 1, r->bottom + 1, 1, kColorBlack, _gui->_patterns, kPatternSolid); int x = r->left + kMenuDropdownPadding; int y = r->top + 1; for (uint i = 0; i < menu->subitems.size(); i++) { Common::String text(menu->subitems[i]->text); Common::String acceleratorText(getAcceleratorString(menu->subitems[i], "")); int accelX = r->right - 25; int color = kColorBlack; if (i == (uint)_activeSubItem && !text.empty() && menu->subitems[i]->enabled) { color = kColorWhite; Common::Rect trect(r->left, y - (_gui->_builtInFonts ? 1 : 0), r->right, y + _font->getFontHeight()); Design::drawFilledRect(&_gui->_screen, trect, kColorBlack, _gui->_patterns, kPatternSolid); } if (!text.empty()) { Graphics::ManagedSurface *s = &_gui->_screen; int tx = x, ty = y; if (!menu->subitems[i]->enabled) { s = &_tempSurface; tx = 0; ty = 0; accelX -= x; _tempSurface.clear(kColorGreen); } _font->drawString(s, text, tx, ty, r->width(), color); if (!acceleratorText.empty()) _font->drawString(s, acceleratorText, accelX, ty, r->width(), color); if (!menu->subitems[i]->enabled) { // I am lazy to extend drawString() with plotProc as a parameter, so // fake it here for (int ii = 0; ii < _tempSurface.h; ii++) { const byte *src = (const byte *)_tempSurface.getBasePtr(0, ii); byte *dst = (byte *)_gui->_screen.getBasePtr(x, y+ii); byte pat = _gui->_patterns[kPatternCheckers2 - 1][ii % 8]; for (int j = 0; j < r->width(); j++) { if (*src != kColorGreen && (pat & (1 << (7 - (x + j) % 8)))) *dst = *src; src++; dst++; } } } } else { // Delimiter Design::drawHLine(&_gui->_screen, r->left + 1, r->right - 1, y + kMenuDropdownItemHeight / 2, 1, kColorBlack, _gui->_patterns, kPatternStripes); } y += kMenuDropdownItemHeight; } g_system->copyRectToScreen(_gui->_screen.getBasePtr(r->left, r->top), _gui->_screen.pitch, r->left, r->top, r->width() + 3, r->height() + 3); }
void GfxFrameout::kernelFrameout() { if (g_sci->_robotDecoder->isVideoLoaded()) { bool skipVideo = false; RobotDecoder *videoDecoder = g_sci->_robotDecoder; uint16 x = videoDecoder->getPos().x; uint16 y = videoDecoder->getPos().y; if (videoDecoder->hasDirtyPalette()) videoDecoder->setSystemPalette(); while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) { if (videoDecoder->needsUpdate()) { const Graphics::Surface *frame = videoDecoder->decodeNextFrame(); if (frame) { g_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); if (videoDecoder->hasDirtyPalette()) videoDecoder->setSystemPalette(); g_system->updateScreen(); } } Common::Event event; while (g_system->getEventManager()->pollEvent(event)) { if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) skipVideo = true; } g_system->delayMillis(10); } return; } _palette->palVaryUpdate(); for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) { reg_t planeObject = it->object; uint16 planeLastPriority = it->lastPriority; // Update priority here, sq6 sets it w/o UpdatePlane uint16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority)); it->lastPriority = planePriority; if (planePriority == 0xffff) { // Plane currently not meant to be shown // If plane was shown before, delete plane rect if (planePriority != planeLastPriority) _paint32->fillRect(it->planeRect, 0); continue; } if (it->planeBack) _paint32->fillRect(it->planeRect, it->planeBack); GuiResourceId planeMainPictureId = it->pictureId; _coordAdjuster->pictureSetDisplayArea(it->planeRect); _palette->drewPicture(planeMainPictureId); FrameoutList itemList; // Copy screen items of the current frame to the list of items to be drawn for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) { reg_t itemPlane = readSelector(_segMan, (*listIterator)->object, SELECTOR(plane)); if (planeObject == itemPlane) { kernelUpdateScreenItem((*listIterator)->object); // TODO: Why is this necessary? itemList.push_back(*listIterator); } } for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) { if (pictureIt->object == planeObject) { GfxPicture *planePicture = pictureIt->picture; // Allocate memory for picture cels pictureIt->pictureCels = new FrameoutEntry[planePicture->getSci32celCount()]; // Add following cels to the itemlist FrameoutEntry *picEntry = pictureIt->pictureCels; int planePictureCels = planePicture->getSci32celCount(); for (int pictureCelNr = 0; pictureCelNr < planePictureCels; pictureCelNr++) { picEntry->celNo = pictureCelNr; picEntry->object = NULL_REG; picEntry->picture = planePicture; picEntry->y = planePicture->getSci32celY(pictureCelNr); picEntry->x = planePicture->getSci32celX(pictureCelNr); picEntry->picStartX = pictureIt->startX; picEntry->priority = planePicture->getSci32celPriority(pictureCelNr); itemList.push_back(picEntry); picEntry++; } } } // Now sort our itemlist Common::sort(itemList.begin(), itemList.end(), sortHelper); // warning("Plane %s", _segMan->getObjectName(planeObject)); for (FrameoutList::iterator listIterator = itemList.begin(); listIterator != itemList.end(); listIterator++) { FrameoutEntry *itemEntry = *listIterator; if (itemEntry->object.isNull()) { // Picture cel data itemEntry->y = ((itemEntry->y * _screen->getHeight()) / scriptsRunningHeight); itemEntry->x = ((itemEntry->x * _screen->getWidth()) / scriptsRunningWidth); itemEntry->picStartX = ((itemEntry->picStartX * _screen->getWidth()) / scriptsRunningWidth); // Out of view int16 pictureCelStartX = itemEntry->picStartX + itemEntry->x; int16 pictureCelEndX = pictureCelStartX + itemEntry->picture->getSci32celWidth(itemEntry->celNo); int16 planeStartX = it->planeOffsetX; int16 planeEndX = planeStartX + it->planeRect.width(); if (pictureCelEndX < planeStartX) continue; if (pictureCelStartX > planeEndX) continue; int16 pictureOffsetX = it->planeOffsetX; int16 pictureX = itemEntry->x; if ((it->planeOffsetX) || (itemEntry->picStartX)) { if (it->planeOffsetX <= itemEntry->picStartX) { pictureX += itemEntry->picStartX - it->planeOffsetX; pictureOffsetX = 0; } else { pictureOffsetX = it->planeOffsetX - itemEntry->picStartX; } } itemEntry->picture->drawSci32Vga(itemEntry->celNo, pictureX, itemEntry->y, pictureOffsetX, it->planePictureMirrored); // warning("picture cel %d %d", itemEntry->celNo, itemEntry->priority); } else if (itemEntry->viewId != 0xFFFF) { GfxView *view = _cache->getView(itemEntry->viewId); // warning("view %s %04x:%04x", _segMan->getObjectName(itemEntry->object), PRINT_REG(itemEntry->object)); if (view->isSci2Hires()) { int16 dummyX = 0; _screen->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x); _screen->adjustToUpscaledCoordinates(itemEntry->z, dummyX); } else if (getSciVersion() == SCI_VERSION_2_1) { itemEntry->y = (itemEntry->y * _screen->getHeight()) / scriptsRunningHeight; itemEntry->x = (itemEntry->x * _screen->getWidth()) / scriptsRunningWidth; itemEntry->z = (itemEntry->z * _screen->getHeight()) / scriptsRunningHeight; } // Adjust according to current scroll position itemEntry->x -= it->planeOffsetX; uint16 useInsetRect = readSelectorValue(_segMan, itemEntry->object, SELECTOR(useInsetRect)); if (useInsetRect) { itemEntry->celRect.top = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inTop)); itemEntry->celRect.left = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inLeft)); itemEntry->celRect.bottom = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inBottom)) + 1; itemEntry->celRect.right = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inRight)) + 1; if (view->isSci2Hires()) { _screen->adjustToUpscaledCoordinates(itemEntry->celRect.top, itemEntry->celRect.left); _screen->adjustToUpscaledCoordinates(itemEntry->celRect.bottom, itemEntry->celRect.right); } itemEntry->celRect.translate(itemEntry->x, itemEntry->y); // TODO: maybe we should clip the cels rect with this, i'm not sure // the only currently known usage is game menu of gk1 } else { if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) view->getCelRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect); else view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo, itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX, itemEntry->scaleY, itemEntry->celRect); Common::Rect nsRect = itemEntry->celRect; // Translate back to actual coordinate within scrollable plane nsRect.translate(it->planeOffsetX, 0); if (view->isSci2Hires()) { _screen->adjustBackUpscaledCoordinates(nsRect.top, nsRect.left); _screen->adjustBackUpscaledCoordinates(nsRect.bottom, nsRect.right); } else if (getSciVersion() == SCI_VERSION_2_1) { nsRect.top = (nsRect.top * scriptsRunningHeight) / _screen->getHeight(); nsRect.left = (nsRect.left * scriptsRunningWidth) / _screen->getWidth(); nsRect.bottom = (nsRect.bottom * scriptsRunningHeight) / _screen->getHeight(); nsRect.right = (nsRect.right * scriptsRunningWidth) / _screen->getWidth(); } writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsLeft), nsRect.left); writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsTop), nsRect.top); writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsRight), nsRect.right); writeSelectorValue(_segMan, itemEntry->object, SELECTOR(nsBottom), nsRect.bottom); } int16 screenHeight = _screen->getHeight(); int16 screenWidth = _screen->getWidth(); if (view->isSci2Hires()) { screenHeight = _screen->getDisplayHeight(); screenWidth = _screen->getDisplayWidth(); } if (itemEntry->celRect.bottom < 0 || itemEntry->celRect.top >= screenHeight) continue; if (itemEntry->celRect.right < 0 || itemEntry->celRect.left >= screenWidth) continue; Common::Rect clipRect, translatedClipRect; clipRect = itemEntry->celRect; if (view->isSci2Hires()) { clipRect.clip(it->upscaledPlaneClipRect); translatedClipRect = clipRect; translatedClipRect.translate(it->upscaledPlaneRect.left, it->upscaledPlaneRect.top); } else { clipRect.clip(it->planeClipRect); translatedClipRect = clipRect; translatedClipRect.translate(it->planeRect.left, it->planeRect.top); } if (!clipRect.isEmpty()) { if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128)) view->draw(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, 0, view->isSci2Hires()); else view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect, itemEntry->loopNo, itemEntry->celNo, 255, itemEntry->scaleX, itemEntry->scaleY); } } else { // Most likely a text entry // This draws text the "SCI0-SCI11" way. In SCI2, text is prerendered in kCreateTextBitmap // TODO: rewrite this the "SCI2" way (i.e. implement the text buffer to draw inside kCreateTextBitmap) if (lookupSelector(_segMan, itemEntry->object, SELECTOR(text), NULL, NULL) == kSelectorVariable) { reg_t stringObject = readSelector(_segMan, itemEntry->object, SELECTOR(text)); // The object in the text selector of the item can be either a raw string // or a Str object. In the latter case, we need to access the object's data // selector to get the raw string. if (_segMan->isHeapObject(stringObject)) stringObject = readSelector(_segMan, stringObject, SELECTOR(data)); Common::String text = _segMan->getString(stringObject); GfxFont *font = _cache->getFont(readSelectorValue(_segMan, itemEntry->object, SELECTOR(font))); bool dimmed = readSelectorValue(_segMan, itemEntry->object, SELECTOR(dimmed)); uint16 foreColor = readSelectorValue(_segMan, itemEntry->object, SELECTOR(fore)); itemEntry->y = ((itemEntry->y * _screen->getHeight()) / scriptsRunningHeight); itemEntry->x = ((itemEntry->x * _screen->getWidth()) / scriptsRunningWidth); uint16 startX = itemEntry->x + it->planeRect.left; uint16 curY = itemEntry->y + it->planeRect.top; const char *txt = text.c_str(); // HACK. The plane sometimes doesn't contain the correct width. This // hack breaks the dialog options when speaking with Grace, but it's // the best we got up to now. This happens because of the unimplemented // kTextWidth function in SCI32. // TODO: Remove this once kTextWidth has been implemented. uint16 w = it->planeRect.width() >= 20 ? it->planeRect.width() : _screen->getWidth() - 10; int16 charCount; // Upscale the coordinates/width if the fonts are already upscaled if (_screen->fontIsUpscaled()) { startX = startX * _screen->getDisplayWidth() / _screen->getWidth(); curY = curY * _screen->getDisplayHeight() / _screen->getHeight(); w = w * _screen->getDisplayWidth() / _screen->getWidth(); } while (*txt) { charCount = GetLongest(txt, w, font); if (charCount == 0) break; uint16 curX = startX; for (int i = 0; i < charCount; i++) { unsigned char curChar = txt[i]; font->draw(curChar, curY, curX, foreColor, dimmed); curX += font->getCharWidth(curChar); } curY += font->getHeight(); txt += charCount; while (*txt == ' ') txt++; // skip over breaking spaces } } } } for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) { if (pictureIt->object == planeObject) { delete[] pictureIt->pictureCels; pictureIt->pictureCels = 0; } } } _screen->copyToScreen(); g_sci->getEngineState()->_throttleTrigger = true; }
bool VideoManager::drawNextFrame(VideoEntryPtr videoEntry) { Video::VideoDecoder *video = videoEntry->_video; const Graphics::Surface *frame = video->decodeNextFrame(); if (!frame || !videoEntry->isEnabled()) { return false; } Graphics::Surface *convertedFrame = nullptr; Graphics::PixelFormat pixelFormat = _vm->_system->getScreenFormat(); if (frame->format != pixelFormat) { // We don't support downconverting to 8bpp without having // support in the codec. Set _enableDither if shows up. if (pixelFormat.bytesPerPixel == 1) { warning("Cannot convert high color video frame to 8bpp"); return false; } // Convert to the current screen format convertedFrame = frame->convertTo(pixelFormat, video->getPalette()); frame = convertedFrame; } else if (pixelFormat.bytesPerPixel == 1 && video->hasDirtyPalette()) { // Set the palette when running in 8bpp mode only // Don't do this for Myst, which has its own per-stack handling if (_vm->getGameType() != GType_MYST) _vm->_system->getPaletteManager()->setPalette(video->getPalette(), 0, 256); } // Clip the video to make sure it stays on the screen (Myst does this a few times) Common::Rect targetRect = Common::Rect(video->getWidth(), video->getHeight()); targetRect.translate(videoEntry->getX(), videoEntry->getY()); Common::Rect frameRect = Common::Rect(video->getWidth(), video->getHeight()); if (targetRect.left < 0) { frameRect.left -= targetRect.left; targetRect.left = 0; } if (targetRect.top < 0) { frameRect.top -= targetRect.top; targetRect.top = 0; } if (targetRect.right > _vm->_system->getWidth()) { frameRect.right -= targetRect.right - _vm->_system->getWidth(); targetRect.right = _vm->_system->getWidth(); } if (targetRect.bottom > _vm->_system->getHeight()) { frameRect.bottom -= targetRect.bottom - _vm->_system->getHeight(); targetRect.bottom = _vm->_system->getHeight(); } _vm->_system->copyRectToScreen(frame->getBasePtr(frameRect.left, frameRect.top), frame->pitch, targetRect.left, targetRect.top, targetRect.width(), targetRect.height()); // Delete 8bpp conversion surface if (convertedFrame) { convertedFrame->free(); delete convertedFrame; } // We've drawn something to the screen, make sure we update it return true; }
bool AgiEngine::handleMouseClicks(uint16 &key) { // No mouse click? -> exit if (key != AGI_MOUSE_BUTTON_LEFT) return false; if (!cycleInnerLoopIsActive()) { // Only do this, when no inner loop is currently active Common::Rect displayLineRect = _gfx->getFontRectForDisplayScreen(0, 0, FONT_COLUMN_CHARACTERS, 1); // Common::Rect displayLineRect(_gfx->getDisplayScreenWidth(), _gfx->getDisplayFontHeight()); if (displayLineRect.contains(_mouse.pos)) { // Mouse is inside first line of the screen if (getFlag(VM_FLAG_MENUS_ACCESSIBLE) && _menu->isAvailable()) { _menu->delayedExecuteViaMouse(); key = 0; // eat event return true; } } if (_text->promptIsEnabled()) { // Prompt is currently enabled int16 promptRow = _text->promptRow_Get(); displayLineRect.moveTo(0, promptRow * _gfx->getDisplayFontHeight()); if (displayLineRect.contains(_mouse.pos)) { // and user clicked within the line of the prompt showPredictiveDialog(); key = 0; // eat event return true; } } } if (cycleInnerLoopIsActive()) { // inner loop active, check what kind of loop it is. Then process / forward it switch (_game.cycleInnerLoopType) { case CYCLE_INNERLOOP_GETSTRING: case CYCLE_INNERLOOP_GETNUMBER: { // process in here int16 stringRow, stringColumn, stringMaxLen; _text->stringPos_Get(stringRow, stringColumn); stringMaxLen = _text->stringGetMaxLen(); Common::Rect displayRect = _gfx->getFontRectForDisplayScreen(stringColumn, stringRow, stringMaxLen, 1); if (displayRect.contains(_mouse.pos)) { // user clicked inside the input space showPredictiveDialog(); key = 0; // eat event return true; } break; } case CYCLE_INNERLOOP_INVENTORY: // TODO: forward break; case CYCLE_INNERLOOP_MENU_VIA_KEYBOARD: _menu->mouseEvent(key); key = 0; // eat event break; case CYCLE_INNERLOOP_SYSTEMUI_SELECTSAVEDGAMESLOT: // TODO: forward break; default: break; } } return false; }
void CUP_Player::copyRectToScreen(const Common::Rect &r) { const uint8 *src = _offscreenBuffer + r.top * _width + r.left; _system->copyRectToScreen(src, _width, r.left, r.top, r.width() + 1, r.height() + 1); }
uint16 PopupMenu::Show(int numEntries, const char *actions[]) { if (numEntries == 0) return 0xffff; LureEngine &engine = LureEngine::getReference(); Events &e = Events::getReference(); Mouse &mouse = Mouse::getReference(); OSystem &system = *g_system; Screen &screen = Screen::getReference(); Common::Rect r; bool isEGA = LureEngine::getReference().isEGA(); byte bgColor = isEGA ? EGA_DIALOG_BG_COLOR : 0; byte textColor = isEGA ? EGA_DIALOG_TEXT_COLOR : VGA_DIALOG_TEXT_COLOR; byte whiteColor = isEGA ? EGA_DIALOG_WHITE_COLOR : VGA_DIALOG_WHITE_COLOR; const uint16 yMiddle = FULL_SCREEN_HEIGHT / 2; #ifndef LURE_CLICKABLE_MENUS uint16 oldX = mouse.x(); uint16 oldY = mouse.y(); mouse.cursorOff(); mouse.setPosition(FULL_SCREEN_WIDTH / 2, yMiddle); // Round up number of lines in dialog to next odd number uint16 numLines = (numEntries / 2) * 2 + 1; if (numLines > 5) numLines = 5; #else mouse.pushCursorNum(CURSOR_ARROW); // In WinCE, the whole menu is shown and the items are click-selectable uint16 numLines = numEntries; #endif // Figure out the character width uint16 numCols = 0; for (int ctr = 0; ctr < numEntries; ++ctr) { int len = strlen(actions[ctr]); if (len > numCols) numCols = len; } // Create the dialog surface Common::Point size; Surface::getDialogBounds(size, numCols, numLines, false); Surface *s = new Surface(size.x, size.y); s->createDialog(true); int selectedIndex = 0; bool refreshFlag = true; r.left = Surface::textX(); r.right = s->width() - Surface::textX() + 1; r.top = Surface::textY(); r.bottom = s->height() - Surface::textY() + 1; bool bailOut = false; while (!bailOut) { if (refreshFlag) { // Set up the contents of the menu s->fillRect(r, bgColor); for (int index = 0; index < numLines; ++index) { #ifndef LURE_CLICKABLE_MENUS int actionIndex = selectedIndex - (numLines / 2) + index; #else int actionIndex = index; #endif if ((actionIndex >= 0) && (actionIndex < numEntries)) { s->writeString(Surface::textX(), Surface::textY() + index * FONT_HEIGHT, actions[actionIndex], true, #ifndef LURE_CLICKABLE_MENUS (index == (numLines / 2)) ? whiteColor : textColor, #else (index == selectedIndex) ? whiteColor : textColor, #endif false); } } s->copyToScreen(0, yMiddle-(s->height() / 2)); system.updateScreen(); refreshFlag = false; } while (e.pollEvent()) { if (engine.shouldQuit()) { selectedIndex = 0xffff; bailOut = true; break; } else if (e.type() == Common::EVENT_WHEELUP) { // Scroll upwards if (selectedIndex > 0) { --selectedIndex; refreshFlag = true; } } else if (e.type() == Common::EVENT_WHEELDOWN) { // Scroll downwards if (selectedIndex < numEntries - 1) { ++selectedIndex; refreshFlag = true; } } else if (e.type() == Common::EVENT_KEYDOWN) { uint16 keycode = e.event().kbd.keycode; if (((keycode == Common::KEYCODE_KP8) || (keycode == Common::KEYCODE_UP)) && (selectedIndex > 0)) { --selectedIndex; refreshFlag = true; } else if (((keycode == Common::KEYCODE_KP2) || (keycode == Common::KEYCODE_DOWN)) && (selectedIndex < numEntries-1)) { ++selectedIndex; refreshFlag = true; } else if ((keycode == Common::KEYCODE_RETURN) || (keycode == Common::KEYCODE_KP_ENTER)) { bailOut = true; break; } else if (keycode == Common::KEYCODE_ESCAPE) { selectedIndex = 0xffff; bailOut = true; break; } #ifdef LURE_CLICKABLE_MENUS } else if (e.type() == Common::EVENT_LBUTTONDOWN || e.type() == Common::EVENT_MOUSEMOVE) { int16 x = mouse.x(); int16 y = mouse.y() - yMiddle + (s->height() / 2); refreshFlag = true; if (r.contains(x, y)) { selectedIndex = (y - r.top) / FONT_HEIGHT; if (e.type() == Common::EVENT_LBUTTONDOWN) bailOut = true; break; } #else } else if ((e.type() == Common::EVENT_LBUTTONDOWN) || (e.type() == Common::EVENT_MBUTTONDOWN)) { //mouse.waitForRelease(); bailOut = true; break; #endif } else if (e.type() == Common::EVENT_RBUTTONDOWN) { mouse.waitForRelease(); selectedIndex = 0xffff; bailOut = true; break; } } if (!bailOut) { #ifndef LURE_CLICKABLE_MENUS // Warping the mouse to "neutral" even if the top/bottom menu // entry has been reached has both pros and cons. It makes the // menu behave a bit more sensibly, but it also makes it harder // to move the mouse pointer out of the ScummVM window. if (mouse.y() < yMiddle - POPMENU_CHANGE_SENSITIVITY) { if (selectedIndex > 0) { --selectedIndex; refreshFlag = true; } mouse.setPosition(FULL_SCREEN_WIDTH / 2, yMiddle); } else if (mouse.y() > yMiddle + POPMENU_CHANGE_SENSITIVITY) { if (selectedIndex < numEntries - 1) { ++selectedIndex; refreshFlag = true; } mouse.setPosition(FULL_SCREEN_WIDTH / 2, yMiddle); } #endif system.delayMillis(20); } } // bailOut delete s; #ifndef LURE_CLICKABLE_MENUS mouse.setPosition(oldX, oldY); mouse.cursorOn(); #else mouse.popCursor(); #endif screen.update(); return selectedIndex; }
void Surface::fillRect(const Common::Rect &r, uint8 color) { for (int yp = r.top; yp <= r.bottom; ++yp) { byte *const addr = _data->data() + (yp * _width) + r.left; memset(addr, color, r.width()); } }
void RenderManager::blitSurfaceToSurface(const Graphics::Surface &src, const Common::Rect &_srcRect , Graphics::Surface &dst, int _x, int _y, uint32 colorkey) { Common::Rect srcRect = _srcRect; if (srcRect.isEmpty()) srcRect = Common::Rect(src.w, src.h); srcRect.clip(src.w, src.h); Common::Rect dstRect = Common::Rect(-_x + srcRect.left , -_y + srcRect.top, -_x + srcRect.left + dst.w, -_y + srcRect.top + dst.h); srcRect.clip(dstRect); if (srcRect.isEmpty() || !srcRect.isValidRect()) return; Graphics::Surface *srcAdapted = src.convertTo(dst.format); uint32 keycolor = colorkey & ((1 << (src.format.bytesPerPixel << 3)) - 1); // Copy srcRect from src surface to dst surface const byte *srcBuffer = (const byte *)srcAdapted->getBasePtr(srcRect.left, srcRect.top); int xx = _x; int yy = _y; if (xx < 0) xx = 0; if (yy < 0) yy = 0; if (_x >= dst.w || _y >= dst.h) { srcAdapted->free(); delete srcAdapted; return; } byte *dstBuffer = (byte *)dst.getBasePtr(xx, yy); int32 w = srcRect.width(); int32 h = srcRect.height(); for (int32 y = 0; y < h; y++) { switch (srcAdapted->format.bytesPerPixel) { case 1: { const uint *srcTemp = (const uint *)srcBuffer; uint *dstTemp = (uint *)dstBuffer; for (int32 x = 0; x < w; x++) { if (*srcTemp != keycolor) *dstTemp = *srcTemp; srcTemp++; dstTemp++; } } break; case 2: { const uint16 *srcTemp = (const uint16 *)srcBuffer; uint16 *dstTemp = (uint16 *)dstBuffer; for (int32 x = 0; x < w; x++) { if (*srcTemp != keycolor) *dstTemp = *srcTemp; srcTemp++; dstTemp++; } } break; case 4: { const uint32 *srcTemp = (const uint32 *)srcBuffer; uint32 *dstTemp = (uint32 *)dstBuffer; for (int32 x = 0; x < w; x++) { if (*srcTemp != keycolor) *dstTemp = *srcTemp; srcTemp++; dstTemp++; } } break; default: break; } srcBuffer += srcAdapted->pitch; dstBuffer += dst.pitch; } srcAdapted->free(); delete srcAdapted; }
bool UserInterface::getBounds(ScrCategory category, int v, Common::Rect &bounds) { int heightMultiplier, widthMultiplier; int leftStart, yOffset, widthAmt; switch (category) { case CAT_COMMAND: heightMultiplier = v % 5; widthMultiplier = v / 5; leftStart = 2; yOffset = 3; widthAmt = 32; break; case CAT_INV_LIST: if (v < _inventoryTopIndex || v >= (_inventoryTopIndex + 5)) return false; heightMultiplier = v - _inventoryTopIndex; widthMultiplier = 0; leftStart = 90; yOffset = 3; widthAmt = 69; break; case CAT_TALK_ENTRY: heightMultiplier = v; widthMultiplier = 0; leftStart = 2; yOffset = 3; widthAmt = 310; break; case CAT_INV_SCROLLER: heightMultiplier = 0; widthMultiplier = 0; yOffset = 0; widthAmt = 9; leftStart = (v != 73) ? 73 : 75; break; default: heightMultiplier = v; widthMultiplier = 0; leftStart = 240; yOffset = 3; widthAmt = 80; break; } bounds.left = (widthMultiplier > 0) ? widthMultiplier * widthAmt + leftStart : leftStart; bounds.setWidth(widthAmt); bounds.top = heightMultiplier * 8 + yOffset; bounds.setHeight(8); if (category == CAT_INV_SCROLLER) { switch (v) { case SCROLLBAR_UP: // Arrow up bounds.top = 4; bounds.setHeight(7); break; case SCROLLBAR_DOWN: // Arrow down bounds.top = 35; bounds.setHeight(7); break; case SCROLLBAR_ELEVATOR: // Scroller bounds.top = 12; bounds.setHeight(22); break; case SCROLLBAR_THUMB: // Thumb bounds.top = _scrollbarElevator + 14; bounds.setHeight(1); break; default: break; } } return true; }
void Screen::setDisplayBounds(const Common::Rect &r) { _backBuffer.create(_backBuffer1, r); assert(_backBuffer.width() == r.width()); assert(_backBuffer.height() == r.height()); }