diff options
author | Martin Kiewitz | 2016-02-27 21:44:21 +0100 |
---|---|---|
committer | Martin Kiewitz | 2016-02-27 21:44:21 +0100 |
commit | e1c36a52b561463217e22687605d2f4f1dc33be6 (patch) | |
tree | 467a36e258d105164bc4e1883f15af66bcc4693f | |
parent | 9059189e35b37f7c4bec2714cf866e67d80c140d (diff) | |
download | scummvm-rg350-e1c36a52b561463217e22687605d2f4f1dc33be6.tar.gz scummvm-rg350-e1c36a52b561463217e22687605d2f4f1dc33be6.tar.bz2 scummvm-rg350-e1c36a52b561463217e22687605d2f4f1dc33be6.zip |
AGI: Add support for upscaling and Hercules hires font
- User option to force Hercules hires font for any rendering mode
- Also change mouse cursor hotspots from 1,1 to 0,0
- Fix inaccuracy in mouse controlled game menu
- Change render_Block(), drawBox(), drawDisplayRect() to use
upper Y instead of lower Y. Original AGI uses lower Y, but
upper Y makes upscaling way easier.
-rw-r--r-- | engines/agi/agi.cpp | 17 | ||||
-rw-r--r-- | engines/agi/agi.h | 2 | ||||
-rw-r--r-- | engines/agi/detection.cpp | 10 | ||||
-rw-r--r-- | engines/agi/detection_tables.h | 9 | ||||
-rw-r--r-- | engines/agi/font.cpp | 183 | ||||
-rw-r--r-- | engines/agi/font.h | 3 | ||||
-rw-r--r-- | engines/agi/graphics.cpp | 595 | ||||
-rw-r--r-- | engines/agi/graphics.h | 91 | ||||
-rw-r--r-- | engines/agi/keyboard.cpp | 11 | ||||
-rw-r--r-- | engines/agi/menu.cpp | 30 | ||||
-rw-r--r-- | engines/agi/menu.h | 4 | ||||
-rw-r--r-- | engines/agi/op_cmd.cpp | 2 | ||||
-rw-r--r-- | engines/agi/picture.cpp | 11 | ||||
-rw-r--r-- | engines/agi/preagi.cpp | 4 | ||||
-rw-r--r-- | engines/agi/sprite.cpp | 3 | ||||
-rw-r--r-- | engines/agi/systemui.cpp | 101 | ||||
-rw-r--r-- | engines/agi/systemui.h | 5 | ||||
-rw-r--r-- | engines/agi/text.cpp | 30 | ||||
-rw-r--r-- | engines/agi/text.h | 2 |
19 files changed, 836 insertions, 277 deletions
diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp index ec10344cfb..6773398271 100644 --- a/engines/agi/agi.cpp +++ b/engines/agi/agi.cpp @@ -466,7 +466,7 @@ void AgiEngine::initialize() { _console = new Console(this); _words = new Words(this); _font = new GfxFont(this); - _gfx = new GfxMgr(this); + _gfx = new GfxMgr(this, _font); _sound = new SoundMgr(this, _mixer); _picture = new PictureMgr(this, _gfx); _sprites = new SpritesMgr(this, _gfx); @@ -474,9 +474,9 @@ void AgiEngine::initialize() { _systemUI = new SystemUI(this, _gfx, _text); _inventory = new InventoryMgr(this, _gfx, _text, _systemUI); + _font->init(); _gfx->initVideo(); - _font->init(); _text->init(_systemUI); _game.gameFlags = 0; @@ -512,19 +512,6 @@ void AgiEngine::redrawScreen() { _text->promptRedraw(); } -// Adjust a given coordinate to the local game screen -// Used on mouse cursor coordinates before passing them to scripts -void AgiEngine::adjustPosToGameScreen(int16 &x, int16 &y) { - x = x / 2; // 320 -> 160 - y = y - _gfx->getRenderStartOffsetY(); // remove status bar line - if (y < 0) { - y = 0; - } - if (y >= SCRIPT_HEIGHT) { - y = SCRIPT_HEIGHT + 1; // 1 beyond - } -} - AgiEngine::~AgiEngine() { agiDeinit(); delete _loader; diff --git a/engines/agi/agi.h b/engines/agi/agi.h index 98d6bcff8a..6534331f6e 100644 --- a/engines/agi/agi.h +++ b/engines/agi/agi.h @@ -739,8 +739,6 @@ public: Common::Error loadGameState(int slot); Common::Error saveGameState(int slot, const Common::String &description); - void adjustPosToGameScreen(int16 &x, int16 &y); - private: int _keyQueue[KEY_QUEUE_SIZE]; int _keyQueueStart; diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp index 3efd6bde65..308489ad0b 100644 --- a/engines/agi/detection.cpp +++ b/engines/agi/detection.cpp @@ -171,6 +171,16 @@ static const ADExtraGuiOptionsMap optionsList[] = { } }, + { + GAMEOPTION_USE_HERCULES_FONT, + { + _s("Use Hercules hires font"), + _s("Uses Hercules hires font, when font file is available."), + "herculesfont", + false + } + }, + AD_EXTRA_GUI_OPTIONS_TERMINATOR }; diff --git a/engines/agi/detection_tables.h b/engines/agi/detection_tables.h index 5c3e814e74..87da0c2c01 100644 --- a/engines/agi/detection_tables.h +++ b/engines/agi/detection_tables.h @@ -24,12 +24,13 @@ namespace Agi { #define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1 #define GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE GUIO_GAMEOPTIONS2 -#define GAMEOPTION_DISABLE_MOUSE GUIO_GAMEOPTIONS3 +#define GAMEOPTION_DISABLE_MOUSE GUIO_GAMEOPTIONS3 +#define GAMEOPTION_USE_HERCULES_FONT GUIO_GAMEOPTIONS4 // TODO: properly implement GAMEOPTIONs -#define GAMEOPTIONS_DEFAULT GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_DISABLE_MOUSE) -#define GAMEOPTIONS_AMIGA GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE) -#define GAMEOPTIONS_FANMADE_MOUSE GUIO1(GAMEOPTION_ORIGINAL_SAVELOAD) +#define GAMEOPTIONS_DEFAULT GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_DISABLE_MOUSE,GAMEOPTION_USE_HERCULES_FONT) +#define GAMEOPTIONS_AMIGA GUIO3(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_AMIGA_ALTERNATIVE_PALETTE,GAMEOPTION_USE_HERCULES_FONT) +#define GAMEOPTIONS_FANMADE_MOUSE GUIO2(GAMEOPTION_ORIGINAL_SAVELOAD,GAMEOPTION_USE_HERCULES_FONT) #define GAME_LVFPN(id,extra,fname,md5,size,lang,ver,features,gid,platform,interp,guioptions) { \ { \ diff --git a/engines/agi/font.cpp b/engines/agi/font.cpp index 896befca04..c453ee5aa1 100644 --- a/engines/agi/font.cpp +++ b/engines/agi/font.cpp @@ -20,6 +20,7 @@ * */ +#include "common/config-manager.h" #include "agi/agi.h" #include "agi/font.h" #include "agi/text.h" @@ -31,6 +32,7 @@ GfxFont::GfxFont(AgiBase *vm) { _fontData = nullptr; _fontDataAllocated = nullptr; + _fontIsHires = false; } GfxFont::~GfxFont() { @@ -619,52 +621,59 @@ static const uint8 fontData_ExtendedRussian[] = { }; void GfxFont::init() { - switch (_vm->_renderMode) { - case Common::kRenderAmiga: - // Try user-file first, if that fails use our internal inaccurate topaz font - loadFontScummVMFile("agi-font-amiga.bin"); - if (!_fontData) { - loadFontAmigaPseudoTopaz(); - } - break; - case Common::kRenderApple2GS: - // Special font, stored in file AGIFONT - loadFontAppleIIgs(); - break; - case Common::kRenderAtariST: - // TODO: Atari ST uses another font - // Seems to be the standard Atari ST 8x8 system font - loadFontScummVMFile("agi-font-atarist.bin"); - if (!_fontData) { - loadFontAtariST("agi-font-atarist-system.fnt"); + if (ConfMan.getBool("herculesfont")) { + // User wants, that we use Hercules hires font, try to load it + loadFontHercules(); + } + + if (!_fontData) { + switch (_vm->_renderMode) { + case Common::kRenderAmiga: + // Try user-file first, if that fails use our internal inaccurate topaz font + loadFontScummVMFile("agi-font-amiga.bin"); if (!_fontData) { - // TODO: in case we find a recreation of the font, add it in here + loadFontAmigaPseudoTopaz(); } - } - break; - case Common::kRenderCGA: - case Common::kRenderEGA: - case Common::kRenderVGA: - switch (_vm->getGameID()) { - case GID_MICKEY: - // load mickey mouse font from interpreter file - loadFontMickey(); break; + case Common::kRenderApple2GS: + // Special font, stored in file AGIFONT + loadFontAppleIIgs(); + break; + case Common::kRenderAtariST: + // TODO: Atari ST uses another font + // Seems to be the standard Atari ST 8x8 system font + loadFontScummVMFile("agi-font-atarist.bin"); + if (!_fontData) { + loadFontAtariST("agi-font-atarist-system.fnt"); + if (!_fontData) { + // TODO: in case we find a recreation of the font, add it in here + } + } + break; + case Common::kRenderCGA: + case Common::kRenderEGA: + case Common::kRenderVGA: + switch (_vm->getGameID()) { + case GID_MICKEY: + // load mickey mouse font from interpreter file + loadFontMickey(); + break; + default: + loadFontScummVMFile("agi-font-dos.bin"); + break; + } + break; + default: - loadFontScummVMFile("agi-font-dos.bin"); break; } - break; - - default: - break; - } - if (!_fontData) { - // no font assigned? - // use regular PC-BIOS font (taken from Dos-Box with a few modifications) - _fontData = fontData_PCBIOS; - debug("AGI: Using PC-BIOS font"); + if (!_fontData) { + // no font assigned? + // use regular PC-BIOS font (taken from Dos-Box with a few modifications) + _fontData = fontData_PCBIOS; + debug("AGI: Using PC-BIOS font"); + } } if (_vm->getLanguage() == Common::RU_RUS) { @@ -678,6 +687,10 @@ const byte *GfxFont::getFontData() { return _fontData; } +bool GfxFont::isFontHires() { + return _fontIsHires; +} + void GfxFont::overwriteSaveRestoreDialogCharacter() { // overwrite character 0x1A with the standard Sierra arrow to the right character // required for the original save/restore dialogs @@ -1165,4 +1178,96 @@ void GfxFont::loadFontAtariST(Common::String fontFilename) { debug("AGI: Using Atari ST 8x8 system font"); } +// Loads a Sierra Hercules font file +void GfxFont::loadFontHercules() { + Common::File fontFile; + int32 fontFileSize = 0; + byte *fontData = nullptr; + byte *rawData = nullptr; + + uint16 rawDataPos = 0; + uint16 curCharNr = 0; + uint16 curCharLine = 0; + + if (fontFile.open("hgc_font")) { + // hgc_font file found, this is interleaved font data 16x12, should be 3072 bytes + // 24 bytes per character, 128 characters + fontFileSize = fontFile.size(); + if (fontFileSize == (128 * 24)) { + // size seems to be fine + fontData = (uint8 *)calloc(256, 32); + _fontDataAllocated = fontData; + + rawData = (byte *)calloc(128, 24); + fontFile.read(rawData, 128 * 24); + + // convert interleaved 16x12 -> non-interleaved 16x16 + for (curCharNr = 0; curCharNr < 128; curCharNr++) { + fontData += 4; // skip the first 2 lines + for (curCharLine = 0; curCharLine < 6; curCharLine++) { + fontData[0] = rawData[rawDataPos + 2 + 0]; + fontData[1] = rawData[rawDataPos + 2 + 1]; + fontData[2] = rawData[rawDataPos + 0 + 0]; + fontData[3] = rawData[rawDataPos + 0 + 1]; + rawDataPos += 4; + fontData += 4; + } + fontData += 4; // skip the last 2 lines + } + + } else { + warning("Fontfile 'hgc_font': unexpected file size"); + } + fontFile.close(); + + } + + if (!_fontDataAllocated) { + if (fontFile.open("hgc_graf.ovl")) { + // hgc_graf.ovl file found, this is font data + code. non-interleaved font data, should be 3075 bytes + // 16 bytes per character, 128 characters, 2048 bytes of font data, starting offset 21 + fontFileSize = fontFile.size(); + if (fontFileSize == 3075) { + // size seems to be fine + fontData = (uint8 *)calloc(256, 32); + _fontDataAllocated = fontData; + + fontFile.seek(21); + rawData = (byte *)calloc(128, 16); + fontFile.read(rawData, 128 * 16); + + // repeat every line 2 times to get 16x16 pixels + for (curCharNr = 0; curCharNr < 128; curCharNr++) { + for (curCharLine = 0; curCharLine < 8; curCharLine++) { + fontData[0] = rawData[rawDataPos + 0]; + fontData[1] = rawData[rawDataPos + 1]; + fontData[2] = rawData[rawDataPos + 0]; + fontData[3] = rawData[rawDataPos + 1]; + rawDataPos += 2; + fontData += 4; + } + } + + free(rawData); + + } else { + warning("Fontfile 'hgc_graf.ovl': unexpected file size"); + } + fontFile.close(); + } + } + + if (_fontDataAllocated) { + // font loaded + _fontData = _fontDataAllocated; + _fontIsHires = true; + + debug("AGI: Using Hercules hires font"); + + } else { + // Continue, if no file was not found + warning("Could not open/use file 'hgc_font' or 'hgc_graf.ovl' for Hercules hires font"); + } +} + } // End of namespace Agi diff --git a/engines/agi/font.h b/engines/agi/font.h index 0bb1bbb18d..485b139858 100644 --- a/engines/agi/font.h +++ b/engines/agi/font.h @@ -36,6 +36,7 @@ private: public: void init(); const byte *getFontData(); + bool isFontHires(); private: void overwriteSaveRestoreDialogCharacter(); @@ -46,9 +47,11 @@ private: void loadFontAmigaPseudoTopaz(); void loadFontAppleIIgs(); void loadFontAtariST(Common::String fontFilename); + void loadFontHercules(); const uint8 *_fontData; // pointer to the currently used font uint8 *_fontDataAllocated; + bool _fontIsHires; }; } // End of namespace Agi diff --git a/engines/agi/graphics.cpp b/engines/agi/graphics.cpp index 6f4b272e2b..5fa652c5ff 100644 --- a/engines/agi/graphics.cpp +++ b/engines/agi/graphics.cpp @@ -39,7 +39,7 @@ namespace Agi { #include "agi/font.h" -GfxMgr::GfxMgr(AgiBase *vm) : _vm(vm) { +GfxMgr::GfxMgr(AgiBase *vm, GfxFont *font) : _vm(vm), _font(font) { _agipalFileNum = 0; memset(&_paletteGfxMode, 0, sizeof(_paletteGfxMode)); @@ -50,7 +50,17 @@ GfxMgr::GfxMgr(AgiBase *vm) : _vm(vm) { initPriorityTable(); - _renderStartOffsetY = 0; + _renderStartVisualOffsetY = 0; + _renderStartDisplayOffsetY = 0; + + _upscaledHires = DISPLAY_UPSCALED_DISABLED; + _displayScreenWidth = DISPLAY_DEFAULT_WIDTH; + _displayScreenHeight = DISPLAY_DEFAULT_HEIGHT; + _displayFontWidth = 8; + _displayFontHeight = 8; + + _displayWidthMulAdjust = 0; // visualPos * (2+0) = displayPos + _displayHeightMulAdjust = 0; // visualPos * (1+0) = displayPos } /** @@ -125,31 +135,45 @@ int GfxMgr::initVideo() { break; } + //bool forcedUpscale = true; + + if (_font->isFontHires()) { + // Upscaling enable + _upscaledHires = DISPLAY_UPSCALED_640x400; + _displayScreenWidth = 640; + _displayScreenHeight = 400; + _displayFontWidth = 16; + _displayFontHeight = 16; + + _displayWidthMulAdjust = 2; + _displayHeightMulAdjust = 1; + } + // set up mouse cursors switch (_vm->_renderMode) { case Common::kRenderEGA: case Common::kRenderCGA: case Common::kRenderVGA: - initMouseCursor(&_mouseCursor, MOUSECURSOR_SCI, 11, 16, 1, 1); + initMouseCursor(&_mouseCursor, MOUSECURSOR_SCI, 11, 16, 0, 0); initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_SCI_BUSY, 15, 16, 7, 8); break; case Common::kRenderAmiga: - initMouseCursor(&_mouseCursor, MOUSECURSOR_AMIGA, 8, 11, 1, 1); + initMouseCursor(&_mouseCursor, MOUSECURSOR_AMIGA, 8, 11, 0, 0); initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_AMIGA_BUSY, 13, 16, 7, 8); break; case Common::kRenderApple2GS: // had no special busy mouse cursor - initMouseCursor(&_mouseCursor, MOUSECURSOR_APPLE_II_GS, 9, 11, 1, 1); + initMouseCursor(&_mouseCursor, MOUSECURSOR_APPLE_II_GS, 9, 11, 0, 0); initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_SCI_BUSY, 15, 16, 7, 8); break; case Common::kRenderAtariST: - initMouseCursor(&_mouseCursor, MOUSECURSOR_ATARI_ST, 11, 16, 1, 1); + initMouseCursor(&_mouseCursor, MOUSECURSOR_ATARI_ST, 11, 16, 0, 0); initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_SCI_BUSY, 15, 16, 7, 8); break; case Common::kRenderMacintosh: // It looks like Atari ST + Macintosh used the same standard mouse cursor // TODO: Verify by checking actual hardware - initMouseCursor(&_mouseCursor, MOUSECURSOR_ATARI_ST, 11, 16, 1, 1); + initMouseCursor(&_mouseCursor, MOUSECURSOR_ATARI_ST, 11, 16, 0, 0); initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_MACINTOSH_BUSY, 10, 14, 7, 8); break; default: @@ -158,15 +182,15 @@ int GfxMgr::initVideo() { } _pixels = SCRIPT_WIDTH * SCRIPT_HEIGHT; - _visualScreen = (byte *)calloc(_pixels, 1); + _gameScreen = (byte *)calloc(_pixels, 1); _priorityScreen = (byte *)calloc(_pixels, 1); - _activeScreen = _visualScreen; + _activeScreen = _gameScreen; //_activeScreen = _priorityScreen; - _displayPixels = DISPLAY_WIDTH * DISPLAY_HEIGHT; + _displayPixels = _displayScreenWidth * _displayScreenHeight; _displayScreen = (byte *)calloc(_displayPixels, 1); - initGraphics(DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_WIDTH > 320); + initGraphics(_displayScreenWidth, _displayScreenHeight, _displayScreenWidth > 320); setPalette(true); // set gfx-mode palette @@ -183,27 +207,151 @@ int GfxMgr::initVideo() { * @see init_video() */ int GfxMgr::deinitVideo() { + // Free mouse cursors in case they were allocated + if (_mouseCursor.bitmapDataAllocated) + free(_mouseCursor.bitmapDataAllocated); + if (_mouseCursorBusy.bitmapDataAllocated) + free(_mouseCursorBusy.bitmapDataAllocated); + free(_displayScreen); - free(_visualScreen); + free(_gameScreen); free(_priorityScreen); return errOK; } void GfxMgr::setRenderStartOffset(uint16 offsetY) { - if (offsetY >= (DISPLAY_HEIGHT - SCRIPT_HEIGHT)) + if (offsetY >= (VISUAL_HEIGHT - SCRIPT_HEIGHT)) error("invalid render start offset"); - _renderStartOffsetY = offsetY; + _renderStartVisualOffsetY = offsetY; + _renderStartDisplayOffsetY = offsetY * (1 + _displayHeightMulAdjust); +} +uint16 GfxMgr::getRenderStartDisplayOffsetY() { + return _renderStartDisplayOffsetY; +} + +// Translates a game screen coordinate to a display screen coordinate +// Game screen to 320x200 -> x * 2, y + renderStart +// Game screen to 640x400 -> x * 4, (y * 2) + renderStart +void GfxMgr::translateGamePosToDisplayScreen(int16 &x, int16 &y) { + x = x * (2 + _displayWidthMulAdjust); + y = y * (1 + _displayHeightMulAdjust) + _renderStartDisplayOffsetY; } -uint16 GfxMgr::getRenderStartOffsetY() { - return _renderStartOffsetY; + +// Translates a visual coordinate to a display screen coordinate +// Visual to 320x200 -> x * 2, y +// Visual to 640x400 -> x * 4, y * 2 +void GfxMgr::translateVisualPosToDisplayScreen(int16 &x, int16 &y) { + x = x * (2 + _displayWidthMulAdjust); + y = y * (1 + _displayHeightMulAdjust); +} + +// Translates a display screen coordinate to a game screen coordinate +// Display screen to 320x200 -> x / 2, y - renderStart +// Display screen to 640x400 -> x / 4, (y / 2) - renderStart +void GfxMgr::translateDisplayPosToGameScreen(int16 &x, int16 &y) { + y -= _renderStartDisplayOffsetY; // remove status bar line + x = x / (2 + _displayWidthMulAdjust); + y = y / (1 + _displayHeightMulAdjust); + if (y < 0) + y = 0; + if (y >= SCRIPT_HEIGHT) + y = SCRIPT_HEIGHT + 1; // 1 beyond +} + +// Translates dimension from visual screen to display screen +void GfxMgr::translateVisualDimensionToDisplayScreen(int16 &width, int16 &height) { + width = width * (2 + _displayWidthMulAdjust); + height = height * (1 + _displayHeightMulAdjust); +} + +// Translates dimension from display screen to visual screen +void GfxMgr::translateDisplayDimensionToVisualScreen(int16 &width, int16 &height) { + width = width / (2 + _displayWidthMulAdjust); + height = height / (1 + _displayHeightMulAdjust); +} + +// Translates a rect from game screen to display screen +void GfxMgr::translateGameRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height) { + translateGamePosToDisplayScreen(x, y); + translateVisualDimensionToDisplayScreen(width, height); +} + +// Translates a rect from visual screen to display screen +void GfxMgr::translateVisualRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height) { + translateVisualPosToDisplayScreen(x, y); + translateVisualDimensionToDisplayScreen(width, height); +} + +uint32 GfxMgr::getDisplayOffsetToGameScreenPos(int16 x, int16 y) { + translateGamePosToDisplayScreen(x, y); + return (y * _displayScreenWidth) + x; +} + +uint32 GfxMgr::getDisplayOffsetToVisualScreenPos(int16 x, int16 y) { + translateVisualPosToDisplayScreen(x, y); + return (y * _displayScreenWidth) + x; +} + +// Attention: uses display screen coordinates! +void GfxMgr::copyDisplayRectToScreen(int16 x, int16 y, int16 width, int16 height) { + g_system->copyRectToScreen(_displayScreen + y * _displayScreenWidth + x, _displayScreenWidth, x, y, width, height); +} +void GfxMgr::copyDisplayRectToScreen(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight) { + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + break; + case DISPLAY_UPSCALED_640x400: + adjX *= 2; adjY *= 2; + adjWidth *= 2; adjHeight *= 2; + break; + default: + assert(0); + break; + } + x += adjX; y += adjY; + width += adjWidth; height += adjHeight; + g_system->copyRectToScreen(_displayScreen + y * _displayScreenWidth + x, _displayScreenWidth, x, y, width, height); +} +void GfxMgr::copyDisplayRectToScreenUsingGamePos(int16 x, int16 y, int16 width, int16 height) { + translateGameRectToDisplayScreen(x, y, width, height); + g_system->copyRectToScreen(_displayScreen + (y * _displayScreenWidth) + x, _displayScreenWidth, x, y, width, height); +} +void GfxMgr::copyDisplayRectToScreenUsingVisualPos(int16 x, int16 y, int16 width, int16 height) { + translateVisualRectToDisplayScreen(x, y, width, height); + g_system->copyRectToScreen(_displayScreen + (y * _displayScreenWidth) + x, _displayScreenWidth, x, y, width, height); +} +void GfxMgr::copyDisplayToScreen() { + g_system->copyRectToScreen(_displayScreen, _displayScreenWidth, 0, 0, _displayScreenWidth, _displayScreenHeight); +} + +void GfxMgr::translateFontPosToDisplayScreen(int16 &x, int16 &y) { + x *= _displayFontWidth; + y *= _displayFontHeight; +} +void GfxMgr::translateDisplayPosToFontScreen(int16 &x, int16 &y) { + x /= _displayFontWidth; + y /= _displayFontHeight; +} +void GfxMgr::translateFontDimensionToDisplayScreen(int16 &width, int16 &height) { + width *= _displayFontWidth; + height *= _displayFontHeight; +} +void GfxMgr::translateFontRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height) { + translateFontPosToDisplayScreen(x, y); + translateFontDimensionToDisplayScreen(width, height); +} +Common::Rect GfxMgr::getFontRectForDisplayScreen(int16 column, int16 row, int16 width, int16 height) { + Common::Rect displayRect(width * _displayFontWidth, height * _displayFontHeight); + displayRect.moveTo(column * _displayFontWidth, row * _displayFontHeight); + return displayRect; } void GfxMgr::debugShowMap(int mapNr) { switch (mapNr) { case 0: - _activeScreen = _visualScreen; + _activeScreen = _gameScreen; break; case 1: _activeScreen = _priorityScreen; @@ -212,11 +360,11 @@ void GfxMgr::debugShowMap(int mapNr) { break; } - render_Block(0, 167, SCRIPT_WIDTH, SCRIPT_HEIGHT); + render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT); } void GfxMgr::clear(byte color, byte priority) { - memset(_visualScreen, color, _pixels); + memset(_gameScreen, color, _pixels); memset(_priorityScreen, priority, _pixels); } @@ -224,7 +372,7 @@ void GfxMgr::clearDisplay(byte color, bool copyToScreen) { memset(_displayScreen, color, _displayPixels); if (copyToScreen) { - g_system->copyRectToScreen(_displayScreen, DISPLAY_WIDTH, 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); + copyDisplayToScreen(); } } @@ -232,7 +380,7 @@ void GfxMgr::putPixel(int16 x, int16 y, byte drawMask, byte color, byte priority int offset = y * SCRIPT_WIDTH + x; if (drawMask & GFX_SCREEN_MASK_VISUAL) { - _visualScreen[offset] = color; + _gameScreen[offset] = color; } if (drawMask & GFX_SCREEN_MASK_PRIORITY) { _priorityScreen[offset] = priority; @@ -240,15 +388,72 @@ void GfxMgr::putPixel(int16 x, int16 y, byte drawMask, byte color, byte priority } void GfxMgr::putPixelOnDisplay(int16 x, int16 y, byte color) { - int offset = y * DISPLAY_WIDTH + x; + uint32 offset = 0; + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + offset = y * _displayScreenWidth + x; + + _displayScreen[offset] = color; + break; + case DISPLAY_UPSCALED_640x400: + offset = (y * _displayScreenWidth) + x; + + _displayScreen[offset + 0] = color; + _displayScreen[offset + 1] = color; + _displayScreen[offset + _displayScreenWidth + 0] = color; + _displayScreen[offset + _displayScreenWidth + 1] = color; + break; + default: + break; + } +} - _displayScreen[offset] = color; +void GfxMgr::putPixelOnDisplay(int16 x, int16 adjX, int16 y, int16 adjY, byte color) { + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + break; + case DISPLAY_UPSCALED_640x400: + adjX *= 2; adjY *= 2; + break; + default: + assert(0); + break; + } + x += adjX; + y += adjY; + putPixelOnDisplay(x, y, color); +} + +void GfxMgr::putFontPixelOnDisplay(int16 baseX, int16 baseY, int16 addX, int16 addY, byte color, bool isHires) { + uint32 offset = 0; + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + offset = ((baseY + addY) * _displayScreenWidth) + (baseX + addX); + _displayScreen[offset] = color; + break; + case DISPLAY_UPSCALED_640x400: + if (isHires) { + offset = ((baseY + addY) * _displayScreenWidth) + (baseX + addX); + _displayScreen[offset] = color; + } else { + offset = ((baseY + addY * 2) * _displayScreenWidth) + (baseX + addX * 2); + _displayScreen[offset + 0] = color; + _displayScreen[offset + 1] = color; + _displayScreen[offset + _displayScreenWidth + 0] = color; + _displayScreen[offset + _displayScreenWidth + 1] = color; + } + break; + default: + break; + } } byte GfxMgr::getColor(int16 x, int16 y) { int offset = y * SCRIPT_WIDTH + x; - return _visualScreen[offset]; + return _gameScreen[offset]; } byte GfxMgr::getPriority(int16 x, int16 y) { @@ -288,7 +493,8 @@ byte GfxMgr::getCGAMixtureColor(byte color) { return CGA_MixtureColorTable[color & 0x0F]; } -// Attention: y-coordinate points to the LOWER left! +// Attention: in our implementation, y-coordinate is upper left. +// Sierra passed the lower left instead. We changed it to make upscaling easier. void GfxMgr::render_Block(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) { if (!render_Clip(x, y, width, height)) return; @@ -304,17 +510,26 @@ void GfxMgr::render_Block(int16 x, int16 y, int16 width, int16 height, bool copy } if (copyToScreen) { - int16 upperY = y - height + 1 + _renderStartOffsetY; - g_system->copyRectToScreen(_displayScreen + upperY * DISPLAY_WIDTH + x * 2, DISPLAY_WIDTH, x * 2, upperY, width * 2, height); + copyDisplayRectToScreenUsingGamePos(x, y, width, height); } } bool GfxMgr::render_Clip(int16 &x, int16 &y, int16 &width, int16 &height, int16 clipAgainstWidth, int16 clipAgainstHeight) { if ((x >= clipAgainstWidth) || ((x + width - 1) < 0) || - (y < 0) || ((y - (height - 1)) >= clipAgainstHeight)) { + (y < 0) || ((y + (height - 1)) >= clipAgainstHeight)) { return false; } + if (y < 0) { + height += y; + y = 0; + } + + if ((y + height - 1) >= clipAgainstHeight) { + height = clipAgainstHeight - y; + } + +#if 0 if ((y - height + 1) < 0) height = y + 1; @@ -322,6 +537,7 @@ bool GfxMgr::render_Clip(int16 &x, int16 &y, int16 &width, int16 &height, int16 height -= y - (clipAgainstHeight - 1); y = clipAgainstHeight - 1; } +#endif if (x < 0) { width += x; @@ -335,44 +551,104 @@ bool GfxMgr::render_Clip(int16 &x, int16 &y, int16 &width, int16 &height, int16 } void GfxMgr::render_BlockEGA(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) { - int offsetVisual = SCRIPT_WIDTH * y + x; - int offsetDisplay = (DISPLAY_WIDTH * (y + _renderStartOffsetY)) + x * 2; + uint32 offsetVisual = SCRIPT_WIDTH * y + x; + uint32 offsetDisplay = getDisplayOffsetToGameScreenPos(x, y); int16 remainingWidth = width; int16 remainingHeight = height; byte curColor = 0; + int16 displayWidth = width * (2 + _displayWidthMulAdjust); while (remainingHeight) { remainingWidth = width; - while (remainingWidth) { - curColor = _activeScreen[offsetVisual++]; - _displayScreen[offsetDisplay++] = curColor; - _displayScreen[offsetDisplay++] = curColor; - remainingWidth--; + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + while (remainingWidth) { + curColor = _activeScreen[offsetVisual++]; + _displayScreen[offsetDisplay++] = curColor; + _displayScreen[offsetDisplay++] = curColor; + remainingWidth--; + } + break; + case DISPLAY_UPSCALED_640x400: + while (remainingWidth) { + curColor = _activeScreen[offsetVisual++]; + memset(&_displayScreen[offsetDisplay], curColor, 4); + memset(&_displayScreen[offsetDisplay + _displayScreenWidth], curColor, 4); + offsetDisplay += 4; + remainingWidth--; + } + break; + default: + assert(0); + break; + } + + offsetVisual += SCRIPT_WIDTH - width; + offsetDisplay += _displayScreenWidth - displayWidth; + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_640x400: + offsetDisplay += _displayScreenWidth;; + break; + default: + break; } - offsetVisual -= SCRIPT_WIDTH + width; - offsetDisplay -= DISPLAY_WIDTH + width * 2; remainingHeight--; } } void GfxMgr::render_BlockCGA(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) { - int offsetVisual = SCRIPT_WIDTH * y + x; - int offsetDisplay = (DISPLAY_WIDTH * (y + _renderStartOffsetY)) + x * 2; + uint32 offsetVisual = SCRIPT_WIDTH * y + x; + uint32 offsetDisplay = getDisplayOffsetToGameScreenPos(x, y); int16 remainingWidth = width; int16 remainingHeight = height; byte curColor = 0; + int16 displayWidth = width * (2 + _displayWidthMulAdjust); while (remainingHeight) { remainingWidth = width; - while (remainingWidth) { - curColor = _activeScreen[offsetVisual++]; - _displayScreen[offsetDisplay++] = curColor & 0x03; // we process CGA mixture - _displayScreen[offsetDisplay++] = curColor >> 2; - remainingWidth--; + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + while (remainingWidth) { + curColor = _activeScreen[offsetVisual++]; + _displayScreen[offsetDisplay++] = curColor & 0x03; // we process CGA mixture + _displayScreen[offsetDisplay++] = curColor >> 2; + remainingWidth--; + } + break; + case DISPLAY_UPSCALED_640x400: + while (remainingWidth) { + curColor = _activeScreen[offsetVisual++]; + _displayScreen[offsetDisplay + 0] = curColor & 0x03; // we process CGA mixture + _displayScreen[offsetDisplay + 1] = curColor >> 2; + _displayScreen[offsetDisplay + 2] = curColor & 0x03; + _displayScreen[offsetDisplay + 3] = curColor >> 2; + _displayScreen[offsetDisplay + _displayScreenWidth + 0] = curColor & 0x03; + _displayScreen[offsetDisplay + _displayScreenWidth + 1] = curColor >> 2; + _displayScreen[offsetDisplay + _displayScreenWidth + 2] = curColor & 0x03; + _displayScreen[offsetDisplay + _displayScreenWidth + 3] = curColor >> 2; + offsetDisplay += 4; + remainingWidth--; + } + break; + default: + assert(0); + break; + } + + offsetVisual += SCRIPT_WIDTH - width; + offsetDisplay += _displayScreenWidth - displayWidth; + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_640x400: + offsetDisplay += _displayScreenWidth;; + break; + default: + break; } - offsetVisual -= SCRIPT_WIDTH + width; - offsetDisplay -= DISPLAY_WIDTH + width * 2; remainingHeight--; } @@ -380,7 +656,7 @@ void GfxMgr::render_BlockCGA(int16 x, int16 y, int16 width, int16 height, bool c void GfxMgr::transition_Amiga() { uint16 screenPos = 1; - uint16 screenStepPos = 1; + uint32 screenStepPos = 1; int16 posY = 0, posX = 0; int16 stepCount = 0; @@ -402,15 +678,29 @@ void GfxMgr::transition_Amiga() { posY = screenStepPos / SCRIPT_WIDTH; posX = screenStepPos - (posY * SCRIPT_WIDTH); - posY += _renderStartOffsetY; // adjust to only update the main area, not the status bar - posX *= 2; // adjust for display screen - - screenStepPos = (screenStepPos * 2) + (_renderStartOffsetY * DISPLAY_WIDTH); // adjust here too for display screen - for (int16 multiPixel = 0; multiPixel < 4; multiPixel++) { - g_system->copyRectToScreen(_displayScreen + screenStepPos, DISPLAY_WIDTH, posX, posY, 2, 1); - screenStepPos += (0x1A40 * 2); // 6720d - posY += 42; + // Adjust to only update the game screen, not the status bar + translateGamePosToDisplayScreen(posX, posY); + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + for (int16 multiPixel = 0; multiPixel < 4; multiPixel++) { + screenStepPos = (posY * _displayScreenWidth) + posX; + g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 2, 1); + posY += 42; + } + break; + case DISPLAY_UPSCALED_640x400: + for (int16 multiPixel = 0; multiPixel < 4; multiPixel++) { + screenStepPos = (posY * _displayScreenWidth) + posX; + g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 4, 2); + posY += 42 * 2; + } + break; + default: + assert(0); + break; } + stepCount++; if (stepCount == 220) { // 30 times for the whole transition, so should take around 0.5 seconds @@ -433,7 +723,7 @@ void GfxMgr::transition_Amiga() { // Atari ST definitely had a hi-res transition using the full resolution unlike the Amiga transition. void GfxMgr::transition_AtariSt() { uint16 screenPos = 1; - uint16 screenStepPos = 1; + uint32 screenStepPos = 1; int16 posY = 0, posX = 0; int16 stepCount = 0; @@ -452,17 +742,31 @@ void GfxMgr::transition_AtariSt() { if ((screenPos < 13440) && (screenPos & 1)) { screenStepPos = screenPos >> 1; - posY = screenStepPos / DISPLAY_WIDTH; - posX = screenStepPos - (posY * DISPLAY_WIDTH); - - posY += _renderStartOffsetY; // adjust to only update the main area, not the status bar - - screenStepPos = screenStepPos + (_renderStartOffsetY * DISPLAY_WIDTH); // adjust here too for display screen - for (int16 multiPixel = 0; multiPixel < 8; multiPixel++) { - g_system->copyRectToScreen(_displayScreen + screenStepPos, DISPLAY_WIDTH, posX, posY, 1, 1); - screenStepPos += 0x1a40; // 6720d - posY += 21; + posY = screenStepPos / DISPLAY_DEFAULT_WIDTH; + posX = screenStepPos - (posY * DISPLAY_DEFAULT_WIDTH); + + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + posY += _renderStartDisplayOffsetY; // adjust to only update the main area, not the status bar + for (int16 multiPixel = 0; multiPixel < 8; multiPixel++) { + screenStepPos = (posY * _displayScreenWidth) + posX; + g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 1, 1); + posY += 21; + } + break; + case DISPLAY_UPSCALED_640x400: + posX *= 2; posY *= 2; + posY += _renderStartDisplayOffsetY; // adjust to only update the main area, not the status bar + for (int16 multiPixel = 0; multiPixel < 8; multiPixel++) { + screenStepPos = (posY * _displayScreenWidth) + posX; + g_system->copyRectToScreen(_displayScreen + screenStepPos, _displayScreenWidth, posX, posY, 2, 2); + posY += 21 * 2; + } + break; + default: + break; } + stepCount++; if (stepCount == 168) { // 40 times for the whole transition, so should take around 0.7 seconds @@ -493,7 +797,7 @@ void GfxMgr::block_save(int16 x, int16 y, int16 width, int16 height, byte *buffe //warning("block_save: %d, %d -> %d, %d", x, y, width, height); while (remainingHeight) { - memcpy(curBufferPtr, _visualScreen + offset, width); + memcpy(curBufferPtr, _gameScreen + offset, width); offset += SCRIPT_WIDTH; curBufferPtr += width; remainingHeight--; @@ -519,7 +823,7 @@ void GfxMgr::block_restore(int16 x, int16 y, int16 width, int16 height, byte *bu //warning("block_restore: %d, %d -> %d, %d", x, y, width, height); while (remainingHeight) { - memcpy(_visualScreen + offset, curBufferPtr, width); + memcpy(_gameScreen + offset, curBufferPtr, width); offset += SCRIPT_WIDTH; curBufferPtr += width; remainingHeight--; @@ -535,15 +839,8 @@ void GfxMgr::block_restore(int16 x, int16 y, int16 width, int16 height, byte *bu } } -// Attention: uses visual screen coordinates! -void GfxMgr::copyDisplayRectToScreen(int16 x, int16 y, int16 width, int16 height) { - g_system->copyRectToScreen(_displayScreen + y * DISPLAY_WIDTH + x, DISPLAY_WIDTH, x, y, width, height); -} -void GfxMgr::copyDisplayToScreen() { - g_system->copyRectToScreen(_displayScreen, DISPLAY_WIDTH, 0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); -} - // coordinates are for visual screen, but are supposed to point somewhere inside the playscreen +// x, y is the upper left. Sierra passed them as lower left. We change that to make upscaling easier. // attention: Clipping is done here against 160x200 instead of 160x168 // Original interpreter didn't do any clipping, we do it for security. // Clipping against the regular script width/height must not be done, @@ -551,13 +848,13 @@ void GfxMgr::copyDisplayToScreen() { // Going beyond 160x168 will result in messageboxes not getting fully removed // In KQ4's case, the scripts clear the screen that's why it works. void GfxMgr::drawBox(int16 x, int16 y, int16 width, int16 height, byte backgroundColor, byte lineColor) { - if (!render_Clip(x, y, width, height, SCRIPT_WIDTH, DISPLAY_HEIGHT - _renderStartOffsetY)) + if (!render_Clip(x, y, width, height, VISUAL_WIDTH, VISUAL_HEIGHT - _renderStartVisualOffsetY)) return; // coordinate translation: visual-screen -> display-screen - x = x * 2; - y = y + _renderStartOffsetY; // drawDisplayRect paints anywhere on the whole screen, our coordinate is within playscreen - width = width * 2; // width was given as visual width, we need display width + translateVisualRectToDisplayScreen(x, y, width, height); + + y = y + _renderStartDisplayOffsetY; // drawDisplayRect paints anywhere on the whole screen, our coordinate is within playscreen // draw box background drawDisplayRect(x, y, width, height, backgroundColor); @@ -567,27 +864,27 @@ void GfxMgr::drawBox(int16 x, int16 y, int16 width, int16 height, byte backgroun case Common::kRenderApple2GS: case Common::kRenderAmiga: // Slightly different window frame, and actually using 1-pixel width, which is "hi-res" - drawDisplayRect(x + 2, y - 2, width - 4, 1, lineColor); - drawDisplayRect(x + width - 3, y - 2, 1, height - 4, lineColor); - drawDisplayRect(x + 2, y - height + 3, width - 4, 1, lineColor); - drawDisplayRect(x + 2, y - 2, 1, height - 4, lineColor); + drawDisplayRect(x, +2, y, +2, width, -4, 0, 1, lineColor); + drawDisplayRect(x + width, -3, y, +2, 0, 1, height, -4, lineColor); + drawDisplayRect(x, +2, y + height, -3, width, -4, 0, 1, lineColor); + drawDisplayRect(x, +2, y, +2, 0, 1, height, -4, lineColor); break; case Common::kRenderMacintosh: // 1 pixel between box and frame lines. Frame lines were black - drawDisplayRect(x + 1, y - 1, width - 2, 1, 0); - drawDisplayRect(x + width - 2, y - 1, 1, height - 2, 0); - drawDisplayRect(x + 1, y - height + 2, width - 2, 1, 0); - drawDisplayRect(x + 1, y - 1, 1, height - 2, 0); + drawDisplayRect(x, +1, y, +1, width, -2, 0, 1, 0); + drawDisplayRect(x + width, -2, y, +1, 0, 1, height, -2, 0); + drawDisplayRect(x, +1, y + height, -2, width, -2, 0, 1, 0); + drawDisplayRect(x, +1, y, +1, 0, 1, height, -2, 0); break; case Common::kRenderCGA: case Common::kRenderEGA: case Common::kRenderVGA: case Common::kRenderAtariST: default: - drawDisplayRect(x + 2, y - 1, width - 4, 1, lineColor); - drawDisplayRect(x + width - 4, y - 2, 2, height - 4, lineColor); - drawDisplayRect(x + 2, y - height + 2, width - 4, 1, lineColor); - drawDisplayRect(x + 2, y - 2, 2, height - 4, lineColor); + drawDisplayRect(x, +2, y, +1, width, -4, 0, 1, lineColor); + drawDisplayRect(x + width, -4, y, +2, 0, 2, height, -4, lineColor); + drawDisplayRect(x, +2, y + height, -2, width, -4, 0, 1, lineColor); + drawDisplayRect(x, +2, y, +2, 0, 2, height, -4, lineColor); break; } } @@ -604,25 +901,41 @@ void GfxMgr::drawDisplayRect(int16 x, int16 y, int16 width, int16 height, byte c break; } if (copyToScreen) { - int16 upperY = y - height + 1; - g_system->copyRectToScreen(_displayScreen + upperY * DISPLAY_WIDTH + x, DISPLAY_WIDTH, x, upperY, width, height); + copyDisplayRectToScreen(x, y, width, height); + } +} + +void GfxMgr::drawDisplayRect(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight, byte color, bool copyToScreen) { + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + x += adjX; y += adjY; + width += adjWidth; height += adjHeight; + break; + case DISPLAY_UPSCALED_640x400: + x += adjX * 2; y += adjY * 2; + width += adjWidth * 2; height += adjHeight * 2; + break; + default: + assert(0); + break; } + drawDisplayRect(x, y, width, height, color, copyToScreen); } void GfxMgr::drawDisplayRectEGA(int16 x, int16 y, int16 width, int16 height, byte color) { - int offsetDisplay = (DISPLAY_WIDTH * y) + x; + uint32 offsetDisplay = (y * _displayScreenWidth) + x; int16 remainingHeight = height; while (remainingHeight) { memset(_displayScreen + offsetDisplay, color, width); - offsetDisplay -= DISPLAY_WIDTH; + offsetDisplay += _displayScreenWidth; remainingHeight--; } } void GfxMgr::drawDisplayRectCGA(int16 x, int16 y, int16 width, int16 height, byte color) { - int offsetDisplay = (DISPLAY_WIDTH * y) + x; + uint32 offsetDisplay = (y * _displayScreenWidth) + x; int16 remainingHeight = height; int16 remainingWidth = width; byte CGAMixtureColor = getCGAMixtureColor(color); @@ -643,18 +956,20 @@ void GfxMgr::drawDisplayRectCGA(int16 x, int16 y, int16 width, int16 height, byt remainingWidth -= 2; } - offsetDisplay -= DISPLAY_WIDTH; + offsetDisplay += _displayScreenWidth; remainingHeight--; } } // row + column are text-coordinates void GfxMgr::drawCharacter(int16 row, int16 column, byte character, byte foreground, byte background, bool disabledLook) { - int16 x = column * FONT_DISPLAY_WIDTH; - int16 y = row * FONT_DISPLAY_HEIGHT; + int16 x = column; + int16 y = row; byte transformXOR = 0; byte transformOR = 0; + translateFontPosToDisplayScreen(x, y); + // Now figure out, if special handling needs to be done if (_vm->_game.gfxMode) { if (background & 0x08) { @@ -675,22 +990,43 @@ void GfxMgr::drawStringOnDisplay(int16 x, int16 y, const char *text, byte foregr while (*text) { drawCharacterOnDisplay(x, y, *text, foregroundColor, backgroundColor); text++; - x += FONT_DISPLAY_WIDTH; + x += _displayFontWidth; } } +void GfxMgr::drawStringOnDisplay(int16 x, int16 adjX, int16 y, int16 adjY, const char *text, byte foregroundColor, byte backgroundColor) { + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + x += adjX; + y += adjY; + break; + case DISPLAY_UPSCALED_640x400: + x += adjX * 2; + y += adjY * 2; + break; + default: + assert(0); + break; + } + drawStringOnDisplay(x, y, text, foregroundColor, backgroundColor); +} + void GfxMgr::drawCharacterOnDisplay(int16 x, int16 y, const byte character, byte foreground, byte background, byte transformXOR, byte transformOR) { int16 curX, curY; const byte *fontData; + bool fontIsHires = _font->isFontHires(); + int16 fontHeight = fontIsHires ? 16 : FONT_DISPLAY_HEIGHT; + int16 fontWidth = fontIsHires ? 16 : FONT_DISPLAY_WIDTH; + int16 fontBytesPerCharacter = fontIsHires ? 32 : FONT_BYTES_PER_CHARACTER; byte curByte = 0; uint16 curBit; // get font data of specified character - fontData = _vm->getFontData() + character * FONT_BYTES_PER_CHARACTER; + fontData = _font->getFontData() + character * fontBytesPerCharacter; curBit = 0; - for (curY = 0; curY < FONT_DISPLAY_HEIGHT; curY++) { - for (curX = 0; curX < FONT_DISPLAY_WIDTH; curX++) { + for (curY = 0; curY < fontHeight; curY++) { + for (curX = 0; curX < fontWidth; curX++) { if (!curBit) { curByte = *fontData; // do transformations in case they are needed (invert/disabled look) @@ -700,9 +1036,9 @@ void GfxMgr::drawCharacterOnDisplay(int16 x, int16 y, const byte character, byte curBit = 0x80; } if (curByte & curBit) { - putPixelOnDisplay(x + curX, y + curY, foreground); + putFontPixelOnDisplay(x, y, curX, curY, foreground, fontIsHires); } else { - putPixelOnDisplay(x + curX, y + curY, background); + putFontPixelOnDisplay(x, y, curX, curY, background, fontIsHires); } curBit = curBit >> 1; } @@ -710,18 +1046,20 @@ void GfxMgr::drawCharacterOnDisplay(int16 x, int16 y, const byte character, byte transformOR ^= 0xFF; } - copyDisplayRectToScreen(x, y, FONT_DISPLAY_WIDTH, FONT_DISPLAY_HEIGHT); + copyDisplayRectToScreen(x, y, _displayFontWidth, _displayFontHeight); } #define SHAKE_VERTICAL_PIXELS 4 -#define SHAKE_HORIZONTAL_PIXELS 8 +#define SHAKE_HORIZONTAL_PIXELS 4 // Sierra used some EGA port trickery to do it, we have to do it by copying pixels around void GfxMgr::shakeScreen(int16 repeatCount) { int shakeNr, shakeCount; uint8 *blackSpace; + int16 shakeHorizontalPixels = SHAKE_HORIZONTAL_PIXELS * (2 + _displayWidthMulAdjust); + int16 shakeVerticalPixels = SHAKE_VERTICAL_PIXELS * (1 + _displayHeightMulAdjust); - if ((blackSpace = (uint8 *)calloc(SHAKE_HORIZONTAL_PIXELS * DISPLAY_WIDTH, 1)) == NULL) + if ((blackSpace = (uint8 *)calloc(shakeVerticalPixels * _displayScreenWidth, 1)) == NULL) return; shakeCount = repeatCount * 8; // effectively 4 shakes per repeat @@ -733,10 +1071,10 @@ void GfxMgr::shakeScreen(int16 repeatCount) { // move back copyDisplayToScreen(); } else { - g_system->copyRectToScreen(_displayScreen, DISPLAY_WIDTH, SHAKE_HORIZONTAL_PIXELS, SHAKE_VERTICAL_PIXELS, DISPLAY_WIDTH - SHAKE_HORIZONTAL_PIXELS, DISPLAY_HEIGHT - SHAKE_VERTICAL_PIXELS); + g_system->copyRectToScreen(_displayScreen, _displayScreenWidth, shakeHorizontalPixels, shakeVerticalPixels, _displayScreenWidth - shakeHorizontalPixels, _displayScreenHeight - shakeVerticalPixels); // additionally fill the remaining space with black - g_system->copyRectToScreen(blackSpace, DISPLAY_WIDTH, 0, 0, DISPLAY_WIDTH, SHAKE_VERTICAL_PIXELS); - g_system->copyRectToScreen(blackSpace, SHAKE_HORIZONTAL_PIXELS, 0, 0, SHAKE_HORIZONTAL_PIXELS, DISPLAY_HEIGHT); + g_system->copyRectToScreen(blackSpace, _displayScreenWidth, 0, 0, _displayScreenWidth, shakeVerticalPixels); + g_system->copyRectToScreen(blackSpace, shakeHorizontalPixels, 0, 0, shakeHorizontalPixels, _displayScreenHeight); } g_system->updateScreen(); g_system->delayMillis(66); // Sierra waited for 4 V'Syncs, which is around 66 milliseconds @@ -956,7 +1294,38 @@ int GfxMgr::getAGIPalFileNum() { } void GfxMgr::initMouseCursor(MouseCursorData *mouseCursor, const byte *bitmapData, uint16 width, uint16 height, int hotspotX, int hotspotY) { - mouseCursor->bitmapData = bitmapData; + switch (_upscaledHires) { + case DISPLAY_UPSCALED_DISABLED: + mouseCursor->bitmapData = bitmapData; + break; + case DISPLAY_UPSCALED_640x400: { + mouseCursor->bitmapDataAllocated = (byte *)malloc(width * height * 4); + mouseCursor->bitmapData = mouseCursor->bitmapDataAllocated; + + // Upscale mouse cursor + byte *upscaledData = mouseCursor->bitmapDataAllocated; + + for (uint16 y = 0; y < height; y++) { + for (uint16 x = 0; x < width; x++) { + byte curColor = *bitmapData++; + upscaledData[x * 2 + 0] = curColor; + upscaledData[x * 2 + 1] = curColor; + upscaledData[x * 2 + (width * 2) + 0] = curColor; + upscaledData[x * 2 + (width * 2) + 1] = curColor; + } + upscaledData += width * 2 * 2; + } + + width *= 2; + height *= 2; + hotspotX *= 2; + hotspotY *= 2; + break; + } + default: + assert(0); + break; + } mouseCursor->width = width; mouseCursor->height = height; mouseCursor->hotspotX = hotspotX; diff --git a/engines/agi/graphics.h b/engines/agi/graphics.h index 3f94b3e950..c523b61a8f 100644 --- a/engines/agi/graphics.h +++ b/engines/agi/graphics.h @@ -29,8 +29,15 @@ namespace Agi { #define SCRIPT_WIDTH 160 #define SCRIPT_HEIGHT 168 -#define DISPLAY_WIDTH 320 -#define DISPLAY_HEIGHT 200 +#define VISUAL_WIDTH 160 +#define VISUAL_HEIGHT 200 +#define DISPLAY_DEFAULT_WIDTH 320 +#define DISPLAY_DEFAULT_HEIGHT 200 + +enum GfxScreenUpscaledMode { + DISPLAY_UPSCALED_DISABLED = 0, + DISPLAY_UPSCALED_640x400 = 1 +}; class AgiEngine; @@ -42,6 +49,7 @@ enum GfxScreenMasks { struct MouseCursorData { const byte *bitmapData; + byte *bitmapDataAllocated; uint16 width; uint16 height; int hotspotX; @@ -51,6 +59,7 @@ struct MouseCursorData { class GfxMgr { private: AgiBase *_vm; + GfxFont *_font; uint8 _paletteGfxMode[256 * 3]; uint8 _paletteTextMode[256 * 3]; @@ -59,7 +68,7 @@ private: int _agipalFileNum; public: - GfxMgr(AgiBase *vm); + GfxMgr(AgiBase *vm, GfxFont *font); int initVideo(); int deinitVideo(); @@ -73,18 +82,58 @@ public: void setMouseCursor(bool busy = false); void setRenderStartOffset(uint16 offsetY); - uint16 getRenderStartOffsetY(); + uint16 getRenderStartDisplayOffsetY(); + + void translateGamePosToDisplayScreen(int16 &x, int16 &y); + void translateVisualPosToDisplayScreen(int16 &x, int16 &y); + void translateDisplayPosToGameScreen(int16 &x, int16 &y); + + void translateVisualDimensionToDisplayScreen(int16 &width, int16 &height); + void translateDisplayDimensionToVisualScreen(int16 &width, int16 &height); + + void translateGameRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height); + void translateVisualRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height); + void translateDisplayRectToVisualScreen(int16 &x, int16 &y, int16 &width, int16 &height); + + uint32 getDisplayOffsetToGameScreenPos(int16 x, int16 y); + uint32 getDisplayOffsetToVisualScreenPos(int16 x, int16 y); + + void copyDisplayRectToScreen(int16 x, int16 y, int16 width, int16 height); + void copyDisplayRectToScreen(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight); + void copyDisplayRectToScreenUsingGamePos(int16 x, int16 y, int16 width, int16 height); + void copyDisplayRectToScreenUsingVisualPos(int16 x, int16 y, int16 width, int16 height); + void copyDisplayToScreen(); + + void translateFontPosToDisplayScreen(int16 &x, int16 &y); + void translateDisplayPosToFontScreen(int16 &x, int16 &y); + void translateFontDimensionToDisplayScreen(int16 &width, int16 &height); + void translateFontRectToDisplayScreen(int16 &x, int16 &y, int16 &width, int16 &height); + Common::Rect getFontRectForDisplayScreen(int16 column, int16 row, int16 width, int16 height); private: uint _pixels; - //uint16 _displayWidth; - //uint16 _displayHeight; uint _displayPixels; byte *_activeScreen; - byte *_visualScreen; // 160x168 - byte *_priorityScreen; // 160x168 - byte *_displayScreen; // 320x200 + byte *_gameScreen; // 160x168 - screen, where the actual game content is drawn to (actual graphics, not including status line, prompt, etc.) + byte *_priorityScreen; // 160x168 - screen contains priority information of the game screen + // the term "visual screen" is effectively the display screen, but at 160x200 resolution. Used for coordinate translation + byte *_displayScreen; // 320x200 or 640x400 - screen, that the game is rendered to and which is then copied to framebuffer + + uint16 _displayScreenWidth; + uint16 _displayScreenHeight; + + uint16 _displayFontWidth; + uint16 _displayFontHeight; + + uint16 _displayWidthMulAdjust; + uint16 _displayHeightMulAdjust; + + /** + * This variable defines, if upscaled hires is active and what upscaled mode + * is used. + */ + GfxScreenUpscaledMode _upscaledHires; bool _priorityTableSet; uint8 _priorityTable[SCRIPT_HEIGHT]; /**< priority table */ @@ -92,15 +141,32 @@ private: MouseCursorData _mouseCursor; MouseCursorData _mouseCursorBusy; - uint16 _renderStartOffsetY; + uint16 _renderStartVisualOffsetY; + uint16 _renderStartDisplayOffsetY; public: + uint16 getDisplayScreenWidth() { + return _displayScreenWidth; + } + uint16 getDisplayFontWidth() { + return _displayFontWidth; + } + uint16 getDisplayFontHeight() { + return _displayFontHeight; + } + + GfxScreenUpscaledMode getUpscaledHires() { + return _upscaledHires; + } + void debugShowMap(int mapNr); void clear(byte color, byte priority); void clearDisplay(byte color, bool copyToScreen = true); void putPixel(int16 x, int16 y, byte drawMask, byte color, byte priority); void putPixelOnDisplay(int16 x, int16 y, byte color); + void putPixelOnDisplay(int16 x, int16 adjX, int16 y, int16 adjY, byte color); + void putFontPixelOnDisplay(int16 baseX, int16 baseY, int16 addX, int16 addY, byte color, bool isHires); byte getColor(int16 x, int16 y); byte getPriority(int16 x, int16 y); @@ -122,11 +188,9 @@ public: void block_save(int16 x, int16 y, int16 width, int16 height, byte *bufferPtr); void block_restore(int16 x, int16 y, int16 width, int16 height, byte *bufferPtr); - void copyDisplayRectToScreen(int16 x, int16 y, int16 width, int16 height); - void copyDisplayToScreen(); - void drawBox(int16 x, int16 y, int16 width, int16 height, byte backgroundColor, byte lineColor); void drawDisplayRect(int16 x, int16 y, int16 width, int16 height, byte color, bool copyToScreen = true); + void drawDisplayRect(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight, byte color, bool copyToScreen = true); private: void drawDisplayRectEGA(int16 x, int16 y, int16 width, int16 height, byte color); void drawDisplayRectCGA(int16 x, int16 y, int16 width, int16 height, byte color); @@ -134,6 +198,7 @@ private: public: void drawCharacter(int16 row, int16 column, byte character, byte foreground, byte background, bool disabledLook); void drawStringOnDisplay(int16 x, int16 y, const char *text, byte foreground, byte background); + void drawStringOnDisplay(int16 x, int16 adjX, int16 y, int16 adjY, const char *text, byte foregroundColor, byte backgroundColor); void drawCharacterOnDisplay(int16 x, int16 y, byte character, byte foreground, byte background, byte transformXOR = 0, byte transformOR = 0); void shakeScreen(int16 repeatCount); diff --git a/engines/agi/keyboard.cpp b/engines/agi/keyboard.cpp index ba1b2f5729..3bc45af5d4 100644 --- a/engines/agi/keyboard.cpp +++ b/engines/agi/keyboard.cpp @@ -313,7 +313,8 @@ bool AgiEngine::handleMouseClicks(uint16 &key) { if (!cycleInnerLoopIsActive()) { // Only do this, when no inner loop is currently active - Common::Rect displayLineRect(DISPLAY_WIDTH, FONT_DISPLAY_HEIGHT); + 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 @@ -328,7 +329,7 @@ bool AgiEngine::handleMouseClicks(uint16 &key) { // Prompt is currently enabled int16 promptRow = _text->promptRow_Get(); - displayLineRect.moveTo(0, promptRow * FONT_DISPLAY_HEIGHT); + displayLineRect.moveTo(0, promptRow * _gfx->getDisplayFontHeight()); if (displayLineRect.contains(_mouse.pos)) { // and user clicked within the line of the prompt @@ -351,9 +352,7 @@ bool AgiEngine::handleMouseClicks(uint16 &key) { _text->stringPos_Get(stringRow, stringColumn); stringMaxLen = _text->stringGetMaxLen(); - Common::Rect displayRect(stringMaxLen * FONT_DISPLAY_WIDTH, FONT_DISPLAY_HEIGHT); - displayRect.moveTo(stringColumn * FONT_DISPLAY_WIDTH, stringRow * FONT_DISPLAY_HEIGHT); - + Common::Rect displayRect = _gfx->getFontRectForDisplayScreen(stringColumn, stringRow, stringMaxLen, 1); if (displayRect.contains(_mouse.pos)) { // user clicked inside the input space showPredictiveDialog(); @@ -493,7 +492,7 @@ bool AgiEngine::handleController(uint16 key) { // in case you walked to the log by using the mouse, so don't!!! int16 egoDestinationX = _mouse.pos.x; int16 egoDestinationY = _mouse.pos.y; - adjustPosToGameScreen(egoDestinationX, egoDestinationY); + _gfx->translateDisplayPosToGameScreen(egoDestinationX, egoDestinationY); screenObjEgo->motionType = kMotionEgo; if (egoDestinationX < (screenObjEgo->xSize / 2)) { diff --git a/engines/agi/menu.cpp b/engines/agi/menu.cpp index cef60ca161..49c2d0eeab 100644 --- a/engines/agi/menu.cpp +++ b/engines/agi/menu.cpp @@ -49,8 +49,8 @@ GfxMenu::GfxMenu(AgiEngine *vm, GfxMgr *gfx, PictureMgr *picture, TextMgr *text) _drawnMenuNr = -1; _drawnMenuHeight = 0; _drawnMenuWidth = 0; - _drawnMenuRow = 0; - _drawnMenuColumn = 0; + _drawnMenuY = 0; + _drawnMenuX = 0; } GfxMenu::~GfxMenu() { @@ -323,8 +323,9 @@ void GfxMenu::execute() { // Unless we are in "via mouse" mode. In that case check current mouse position if (viaMouse) { - int16 mouseRow = _vm->_mouse.pos.y / FONT_DISPLAY_HEIGHT; - int16 mouseColumn = _vm->_mouse.pos.x / FONT_DISPLAY_WIDTH; + int16 mouseRow = _vm->_mouse.pos.y; + int16 mouseColumn = _vm->_mouse.pos.x; + _gfx->translateDisplayPosToFontScreen(mouseColumn, mouseRow); mouseFindMenuSelection(mouseRow, mouseColumn, _drawnMenuNr, _mouseModeItemNr); } @@ -368,7 +369,7 @@ void GfxMenu::execute() { // WORKAROUND: Playarea starts right at the stop, so instead of clearing that part, render it from playarea // Required for at least Donald Duck // This was not done by original AGI, which means the upper pixel line were cleared in this case. - _gfx->render_Block(0, (1 * FONT_VISUAL_HEIGHT) - 1, SCRIPT_WIDTH, FONT_VISUAL_HEIGHT); + _gfx->render_Block(0, 0, SCRIPT_WIDTH, FONT_VISUAL_HEIGHT); } else { _text->clearLine(0, 0); } @@ -427,10 +428,11 @@ void GfxMenu::drawMenu(int16 selectedMenuNr, int16 selectedMenuItemNr) { // calculate active menu dimensions _drawnMenuHeight = (menuEntry->itemCount + 2) * FONT_VISUAL_HEIGHT; _drawnMenuWidth = (menuEntry->maxItemTextLen * FONT_VISUAL_WIDTH) + 8; - _drawnMenuRow = (menuEntry->itemCount + 3 - _text->getWindowRowMin()) * FONT_VISUAL_HEIGHT - 1; - _drawnMenuColumn = (itemEntry->column - 1) * FONT_VISUAL_WIDTH; + _drawnMenuY = (1 - _text->getWindowRowMin()) * FONT_VISUAL_HEIGHT; + //(menuEntry->itemCount + 3 - _text->getWindowRowMin()) * FONT_VISUAL_HEIGHT - 1; + _drawnMenuX = (itemEntry->column - 1) * FONT_VISUAL_WIDTH; - _gfx->drawBox(_drawnMenuColumn, _drawnMenuRow, _drawnMenuWidth, _drawnMenuHeight, 15, 0); + _gfx->drawBox(_drawnMenuX, _drawnMenuY, _drawnMenuWidth, _drawnMenuHeight, 15, 0); while (itemCount) { if (itemNr == selectedMenuItemNr) { @@ -448,7 +450,7 @@ void GfxMenu::removeActiveMenu(int16 selectedMenuNr) { drawMenuName(selectedMenuNr, false); // overwrite actual menu items by rendering play screen - _gfx->render_Block(_drawnMenuColumn, _drawnMenuRow, _drawnMenuWidth, _drawnMenuHeight); + _gfx->render_Block(_drawnMenuX, _drawnMenuY, _drawnMenuWidth, _drawnMenuHeight); } void GfxMenu::keyPress(uint16 newKey) { @@ -549,8 +551,10 @@ void GfxMenu::keyPress(uint16 newKey) { // In "via mouse" mode, we check if user let go of the left mouse button and then select the item that way void GfxMenu::mouseEvent(uint16 newKey) { // Find out, where current mouse cursor actually is - int16 mouseRow = _vm->_mouse.pos.y / FONT_DISPLAY_HEIGHT; - int16 mouseColumn = _vm->_mouse.pos.x / FONT_DISPLAY_WIDTH; + int16 mouseRow = _vm->_mouse.pos.y; + int16 mouseColumn = _vm->_mouse.pos.x; + + _gfx->translateDisplayPosToFontScreen(mouseColumn, mouseRow); int16 activeMenuNr, activeItemNr; mouseFindMenuSelection(mouseRow, mouseColumn, activeMenuNr, activeItemNr); @@ -638,7 +642,7 @@ void GfxMenu::mouseFindMenuSelection(int16 mouseRow, int16 mouseColumn, int16 &a if (mouseRow == menuEntry->row) { // line match - if ((mouseColumn >= menuEntry->column) && (mouseColumn <= (menuEntry->column + menuEntry->textLen))) { + if ((mouseColumn >= menuEntry->column) && (mouseColumn < (menuEntry->column + menuEntry->textLen))) { // full match activeMenuNr = menuNr; activeMenuItemNr = -1; // no item selected @@ -660,7 +664,7 @@ void GfxMenu::mouseFindMenuSelection(int16 mouseRow, int16 mouseColumn, int16 &a if (mouseRow == itemEntry->row) { // line match - if ((mouseColumn >= itemEntry->column) && (mouseColumn <= (itemEntry->column + itemEntry->textLen))) { + if ((mouseColumn >= itemEntry->column) && (mouseColumn < (itemEntry->column + itemEntry->textLen))) { // full match if (itemEntry->enabled) { // Only see it, when it's currently enabled diff --git a/engines/agi/menu.h b/engines/agi/menu.h index a621d7f0f2..b47289180b 100644 --- a/engines/agi/menu.h +++ b/engines/agi/menu.h @@ -111,8 +111,8 @@ private: uint16 _drawnMenuHeight; uint16 _drawnMenuWidth; - int16 _drawnMenuRow; - int16 _drawnMenuColumn; + int16 _drawnMenuY; + int16 _drawnMenuX; // Following variables are used in "via mouse" mode int16 _mouseModeItemNr; diff --git a/engines/agi/op_cmd.cpp b/engines/agi/op_cmd.cpp index 0e0f49b542..fed07ea986 100644 --- a/engines/agi/op_cmd.cpp +++ b/engines/agi/op_cmd.cpp @@ -2227,7 +2227,7 @@ void cmdMousePosn(AgiGame *state, AgiEngine *vm, uint8 *parameter) { int16 mouseX = vm->_mouse.pos.x; int16 mouseY = vm->_mouse.pos.y; - state->_vm->adjustPosToGameScreen(mouseX, mouseY); + vm->_gfx->translateDisplayPosToGameScreen(mouseX, mouseY); vm->setVar(destVarNr1, mouseX); vm->setVar(destVarNr2, mouseY); diff --git a/engines/agi/picture.cpp b/engines/agi/picture.cpp index 0be2de7089..a80e811f44 100644 --- a/engines/agi/picture.cpp +++ b/engines/agi/picture.cpp @@ -1001,7 +1001,7 @@ void PictureMgr::clear() { void PictureMgr::showPic() { debugC(8, kDebugLevelMain, "Show picture!"); - _gfx->render_Block(0, 167, SCRIPT_WIDTH, SCRIPT_HEIGHT); + _gfx->render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT); } /** @@ -1014,8 +1014,7 @@ void PictureMgr::showPic(int16 x, int16 y, int16 pic_width, int16 pic_height) { debugC(8, kDebugLevelMain, "Show picture!"); - // render block requires lower left coordinate! - _gfx->render_Block(x, pic_height + y - 1, pic_width, pic_height); + _gfx->render_Block(x, y, pic_width, pic_height); } void PictureMgr::showPicWithTransition() { @@ -1038,13 +1037,13 @@ void PictureMgr::showPicWithTransition() { case Common::kRenderAmiga: case Common::kRenderApple2GS: // Platform Amiga/Apple II GS -> render and do Amiga transition - _gfx->render_Block(0, 167, SCRIPT_WIDTH, SCRIPT_HEIGHT, false); + _gfx->render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT, false); _gfx->transition_Amiga(); return; break; case Common::kRenderAtariST: // Platform Atari ST used a different transition, looks "high-res" (full 320x168) - _gfx->render_Block(0, 167, SCRIPT_WIDTH, SCRIPT_HEIGHT, false); + _gfx->render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT, false); _gfx->transition_AtariSt(); return; default: @@ -1054,7 +1053,7 @@ void PictureMgr::showPicWithTransition() { } } - _gfx->render_Block(0, 167, SCRIPT_WIDTH, SCRIPT_HEIGHT); + _gfx->render_Block(0, 0, SCRIPT_WIDTH, SCRIPT_HEIGHT); } // preagi needed functions (for plotPattern) diff --git a/engines/agi/preagi.cpp b/engines/agi/preagi.cpp index cbd15f2666..bb5d3b8896 100644 --- a/engines/agi/preagi.cpp +++ b/engines/agi/preagi.cpp @@ -58,7 +58,7 @@ void PreAgiEngine::initialize() { initRenderMode(); _font = new GfxFont(this); - _gfx = new GfxMgr(this); + _gfx = new GfxMgr(this, _font); _picture = new PictureMgr(this, _gfx); _font->init(); @@ -112,7 +112,7 @@ void PreAgiEngine::clearScreen(int attr, bool overrideDefault) { } void PreAgiEngine::clearGfxScreen(int attr) { - _gfx->drawDisplayRect(0, 0, DISPLAY_WIDTH - 1, IDI_MAX_ROW_PIC * 8 - 1, (attr & 0xF0) / 0x10); + _gfx->drawDisplayRect(0, 0, DISPLAY_DEFAULT_WIDTH - 1, IDI_MAX_ROW_PIC * 8 - 1, (attr & 0xF0) / 0x10); } // String functions diff --git a/engines/agi/sprite.cpp b/engines/agi/sprite.cpp index 434cf1b30e..8263ea12ac 100644 --- a/engines/agi/sprite.cpp +++ b/engines/agi/sprite.cpp @@ -354,7 +354,8 @@ void SpritesMgr::showSprite(ScreenObjEntry *screenObj) { } // render this block - _gfx->render_Block(x, y, width, height); + int16 upperY = y - height + 1; + _gfx->render_Block(x, upperY, width, height); } void SpritesMgr::showSprites(SpriteList &spriteList) { diff --git a/engines/agi/systemui.cpp b/engines/agi/systemui.cpp index f618459823..7aa26131c9 100644 --- a/engines/agi/systemui.cpp +++ b/engines/agi/systemui.cpp @@ -751,21 +751,23 @@ bool SystemUI::askForVerification(const char *verifyText, const char *button1Tex // Buttons enabled, calculate button coordinates int16 msgBoxX = 0, msgBoxY = 0, msgBoxLowerY = 0; int16 msgBoxWidth = 0, msgBoxHeight = 0; + int16 fontHeight = _gfx->getDisplayFontHeight(); + int16 fontWidth = _gfx->getDisplayFontWidth(); _text->getMessageBoxInnerDisplayDimensions(msgBoxX, msgBoxY, msgBoxWidth, msgBoxHeight); - // Adjust Y coordinate to lower edge + // Calculate lower Y msgBoxLowerY = msgBoxY + (msgBoxHeight - 1); buttonEntry.active = false; if (button1Text) { buttonEntry.text = button1Text; - buttonEntry.textWidth = strlen(button1Text) * FONT_DISPLAY_WIDTH; + buttonEntry.textWidth = strlen(button1Text) * _gfx->getDisplayFontWidth(); buttonEntry.isDefault = true; _buttonArray.push_back(buttonEntry); } if (button2Text) { buttonEntry.text = button2Text; - buttonEntry.textWidth = strlen(button2Text) * FONT_DISPLAY_WIDTH; + buttonEntry.textWidth = strlen(button2Text) * _gfx->getDisplayFontWidth(); buttonEntry.isDefault = false; _buttonArray.push_back(buttonEntry); } @@ -773,37 +775,30 @@ bool SystemUI::askForVerification(const char *verifyText, const char *button1Tex // Render-Mode specific calculations switch (_vm->_renderMode) { case Common::kRenderApple2GS: - _buttonArray[0].rect = Common::Rect(14 + _buttonArray[0].textWidth, FONT_DISPLAY_HEIGHT + 6); - _buttonArray[0].rect.moveTo(msgBoxX + 2, msgBoxLowerY - (8 + FONT_DISPLAY_HEIGHT + 2)); - + _buttonArray[0].rect = createRect(msgBoxX, +2, msgBoxLowerY - fontHeight, -(8 + 2), _buttonArray[0].textWidth, +14, fontHeight, +6); + if (_buttonArray.size() > 1) { - int16 adjustedX = msgBoxX + msgBoxWidth - 10; - _buttonArray[1].rect = Common::Rect(14 + _buttonArray[1].textWidth, FONT_DISPLAY_HEIGHT + 6); - adjustedX -= _buttonArray[1].rect.width(); - _buttonArray[1].rect.moveTo(adjustedX, msgBoxLowerY - (8 + FONT_DISPLAY_HEIGHT + 2)); + int16 adjustedX = msgBoxX + msgBoxWidth - _buttonArray[1].textWidth; // - 10; + _buttonArray[1].rect = createRect(adjustedX, -(14 + 10), _buttonArray[0].rect.top, 0, _buttonArray[1].textWidth, +14, fontHeight, +6); } break; - case Common::kRenderAmiga: - _buttonArray[0].rect = Common::Rect(4 + _buttonArray[0].textWidth + 4, 2 + FONT_DISPLAY_HEIGHT + 2); - _buttonArray[0].rect.moveTo(msgBoxX, msgBoxLowerY - _buttonArray[0].rect.height()); + case Common::kRenderAmiga: { + _buttonArray[0].rect = createRect(msgBoxX, 0, msgBoxLowerY - fontHeight, -(2 + 2), _buttonArray[0].textWidth, +(4 + 4), fontHeight, +(2 + 2)); if (_buttonArray.size() > 1) { - int16 adjustedX = msgBoxX + msgBoxWidth; - _buttonArray[1].rect = Common::Rect(4 + _buttonArray[1].textWidth + 4, 2 + FONT_DISPLAY_HEIGHT + 2); - adjustedX -= _buttonArray[1].rect.width(); - _buttonArray[1].rect.moveTo(adjustedX, msgBoxLowerY - _buttonArray[1].rect.height()); + int16 adjustedX = msgBoxX + msgBoxWidth - _buttonArray[1].textWidth; + _buttonArray[1].rect = createRect(adjustedX, -(4 + 4), _buttonArray[0].rect.top, 0, _buttonArray[1].textWidth, +(4 + 4), fontHeight, +(2 + 2)); } break; + } case Common::kRenderAtariST: - _buttonArray[0].rect = Common::Rect(_buttonArray[0].textWidth, FONT_DISPLAY_HEIGHT); - _buttonArray[0].rect.moveTo(msgBoxX + (5 * FONT_DISPLAY_WIDTH), msgBoxLowerY - FONT_DISPLAY_HEIGHT); + _buttonArray[0].rect = createRect(msgBoxX + (5 * fontWidth), 0, msgBoxLowerY - fontHeight, 0, _buttonArray[0].textWidth, 0, fontHeight, 0); + if (_buttonArray.size() > 1) { - int16 adjustedX = msgBoxX + msgBoxWidth - (5 * FONT_DISPLAY_WIDTH); - _buttonArray[1].rect = Common::Rect(_buttonArray[1].textWidth, FONT_DISPLAY_HEIGHT); - adjustedX -= _buttonArray[1].rect.width(); - _buttonArray[1].rect.moveTo(adjustedX, msgBoxLowerY - _buttonArray[1].rect.height()); + int16 adjustedX = msgBoxX + msgBoxWidth - (5 * fontWidth + _buttonArray[1].textWidth); + _buttonArray[1].rect = createRect(adjustedX, 0, _buttonArray[0].rect.top, 0, _buttonArray[1].textWidth, 0, fontHeight, 0); } break; @@ -951,6 +946,25 @@ void SystemUI::askForVerificationKeyPress(uint16 newKey) { } } +Common::Rect SystemUI::createRect(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight) { + switch (_gfx->getUpscaledHires()) { + case DISPLAY_UPSCALED_DISABLED: + break; + case DISPLAY_UPSCALED_640x400: + adjX *= 2; adjY *= 2; + adjWidth *= 2; adjHeight *= 2; + break; + default: + assert(0); + break; + } + x += adjX; y += adjY; + width += adjWidth; height += adjHeight; + Common::Rect newRect(width, height); + newRect.moveTo(x, y); + return newRect; +} + #define SYSTEMUI_BUTTONEDGE_APPLEIIGS_WIDTH 8 #define SYSTEMUI_BUTTONEDGE_APPLEIIGS_HEIGHT 5 @@ -998,20 +1012,20 @@ void SystemUI::drawButtonAppleIIgs(SystemUIButtonEntry *button) { } // draw base box for it - _gfx->drawDisplayRect(button->rect.left, button->rect.bottom - 1, button->rect.width(), button->rect.height(), backgroundColor, false); + _gfx->drawDisplayRect(button->rect.left, button->rect.top, button->rect.width(), button->rect.height(), backgroundColor, false); // draw inner lines - _gfx->drawDisplayRect(button->rect.left + 1, button->rect.top - 1, button->rect.width() - 2, 1, 0, false); // upper horizontal - _gfx->drawDisplayRect(button->rect.left - 2, button->rect.bottom - 2, 2, button->rect.height() - 2, 0, false); // left vertical - _gfx->drawDisplayRect(button->rect.right, button->rect.bottom - 2, 2, button->rect.height() - 2, 0, false); // right vertical - _gfx->drawDisplayRect(button->rect.left + 1, button->rect.bottom, button->rect.width() - 2, 1, 0, false); // lower horizontal + _gfx->drawDisplayRect(button->rect.left, +1, button->rect.top, -1, button->rect.width(), -2, 0, 1, 0, false); // lower horizontal + _gfx->drawDisplayRect(button->rect.left, -2, button->rect.top, +1, 0, 2, button->rect.height(), -2, 0, false); // left vertical + _gfx->drawDisplayRect(button->rect.right, 0, button->rect.top, +1, 0, 2, button->rect.height(), -2, 0, false); // right vertical + _gfx->drawDisplayRect(button->rect.left, +1, button->rect.bottom, 0, button->rect.width(), -2, 0, 1, 0, false); // upper horizontal if (button->isDefault) { // draw outer lines - _gfx->drawDisplayRect(button->rect.left, button->rect.top - 3, button->rect.width(), 1, 0, false); // upper horizontal - _gfx->drawDisplayRect(button->rect.left - 5, button->rect.bottom - 2, 2, button->rect.height() - 2, 0, false); // left vertical - _gfx->drawDisplayRect(button->rect.right + 3, button->rect.bottom - 2, 2, button->rect.height() - 2, 0, false); // right vertical - _gfx->drawDisplayRect(button->rect.left, button->rect.bottom + 2, button->rect.width(), 1, 0, false); // lower horizontal + _gfx->drawDisplayRect(button->rect.left, 0, button->rect.top, -3, button->rect.width(), 0, 0, 1, 0, false); // upper horizontal + _gfx->drawDisplayRect(button->rect.left, -5, button->rect.top, +2, 0, 2, button->rect.height(), -2, 0, false); // left vertical + _gfx->drawDisplayRect(button->rect.right, +3, button->rect.top, +2, 0, 2, button->rect.height(), -2, 0, false); // right vertical + _gfx->drawDisplayRect(button->rect.left, 0, button->rect.bottom, +2, button->rect.width(), 0, 0, 1, 0, false); // lower horizontal if (button->active) edgeBitmap = buttonEdgeAppleIIgsDefaultActive; @@ -1026,18 +1040,18 @@ void SystemUI::drawButtonAppleIIgs(SystemUIButtonEntry *button) { } // draw edge graphics - drawButtonAppleIIgsEdgePixels(button->rect.left - 5, button->rect.top - 3, edgeBitmap, false, false); - drawButtonAppleIIgsEdgePixels(button->rect.right + 4, button->rect.top - 3, edgeBitmap, true, false); - drawButtonAppleIIgsEdgePixels(button->rect.left - 5, button->rect.bottom + 2, edgeBitmap, false, true); - drawButtonAppleIIgsEdgePixels(button->rect.right + 4, button->rect.bottom + 2, edgeBitmap, true, true); + drawButtonAppleIIgsEdgePixels(button->rect.left, -5, button->rect.top, -3, edgeBitmap, false, false); + drawButtonAppleIIgsEdgePixels(button->rect.right, +4, button->rect.top, -3, edgeBitmap, true, false); + drawButtonAppleIIgsEdgePixels(button->rect.left, -5, button->rect.bottom, +2, edgeBitmap, false, true); + drawButtonAppleIIgsEdgePixels(button->rect.right, +4, button->rect.bottom, +2, edgeBitmap, true, true); // Button text - _gfx->drawStringOnDisplay(button->rect.left + 7, button->rect.top + 3, button->text, foregroundColor, backgroundColor); + _gfx->drawStringOnDisplay(button->rect.left, +7, button->rect.top, +3, button->text, foregroundColor, backgroundColor); - _gfx->copyDisplayRectToScreen(button->rect.left - 5, button->rect.top - 3, button->rect.width() + 10, button->rect.height() + 6); + _gfx->copyDisplayRectToScreen(button->rect.left, -5, button->rect.top, -3, button->rect.width(), +10, button->rect.height(), +6); } -void SystemUI::drawButtonAppleIIgsEdgePixels(int16 x, int16 y, byte *edgeBitmap, bool mirrored, bool upsideDown) { +void SystemUI::drawButtonAppleIIgsEdgePixels(int16 x, int16 adjX, int16 y, int16 adjY, byte *edgeBitmap, bool mirrored, bool upsideDown) { int8 directionY = upsideDown ? -1 : +1; int8 directionX = mirrored ? -1 : +1; int8 curY = 0; @@ -1055,9 +1069,9 @@ void SystemUI::drawButtonAppleIIgsEdgePixels(int16 x, int16 y, byte *edgeBitmap, while (widthLeft) { if (curBitmapByte & curBitmapBit) { - _gfx->putPixelOnDisplay(x + curX, y + curY, 0); + _gfx->putPixelOnDisplay(x, adjX + curX, y, adjY + curY, 0); } else { - _gfx->putPixelOnDisplay(x + curX, y + curY, 15); + _gfx->putPixelOnDisplay(x, adjX + curX, y, adjY + curY, 15); } curBitmapBit = curBitmapBit >> 1; @@ -1092,12 +1106,11 @@ void SystemUI::drawButtonAmiga(SystemUIButtonEntry *button) { } // draw base box for it - _gfx->drawDisplayRect(button->rect.left, button->rect.bottom - 1, button->rect.width(), button->rect.height(), backgroundColor, false); + _gfx->drawDisplayRect(button->rect.left, button->rect.top, button->rect.width(), button->rect.height(), backgroundColor, false); // Button text - _gfx->drawStringOnDisplay(button->rect.left + 4, button->rect.top + 2, button->text, foregroundColor, backgroundColor); + _gfx->drawStringOnDisplay(button->rect.left, +4, button->rect.top, +2, button->text, foregroundColor, backgroundColor); - // draw base box for it _gfx->copyDisplayRectToScreen(button->rect.left, button->rect.top, button->rect.width(), button->rect.height()); } diff --git a/engines/agi/systemui.h b/engines/agi/systemui.h index ceb78935eb..ffb1238ebe 100644 --- a/engines/agi/systemui.h +++ b/engines/agi/systemui.h @@ -107,9 +107,12 @@ private: private: SystemUIButtonArray _buttonArray; + Common::Rect createRect(int16 x, int16 adjX, int16 y, int16 adjY, int16 width, int16 adjWidth, int16 height, int16 adjHeight); + //void moveRect(int16 x, int16 adjX, int16 y, int16 adjY); + void drawButton(SystemUIButtonEntry *button); void drawButtonAppleIIgs(SystemUIButtonEntry *buttonEntry); - void drawButtonAppleIIgsEdgePixels(int16 x, int16 y, byte *edgeBitmap, bool mirrored, bool upsideDown); + void drawButtonAppleIIgsEdgePixels(int16 x, int16 adjX, int16 y, int16 adjY, byte *edgeBitmap, bool mirrored, bool upsideDown); void drawButtonAmiga(SystemUIButtonEntry *buttonEntry); void drawButtonAtariST(SystemUIButtonEntry *buttonEntry); diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp index 611bd135eb..31f364d856 100644 --- a/engines/agi/text.cpp +++ b/engines/agi/text.cpp @@ -88,7 +88,7 @@ void TextMgr::configureScreen(uint16 row_Min) { _window_Row_Max = row_Min + 21; // forward data to GfxMgr as well - _gfx->setRenderStartOffset(row_Min * FONT_DISPLAY_HEIGHT); + _gfx->setRenderStartOffset(row_Min * FONT_VISUAL_HEIGHT); } uint16 TextMgr::getWindowRowMin() { return _window_Row_Min; @@ -466,7 +466,8 @@ void TextMgr::drawMessageBox(const char *textPtr, int16 forcedHeight, int16 want _messageState.backgroundSize_Width = (_messageState.textSize_Width * FONT_VISUAL_WIDTH) + 10; _messageState.backgroundSize_Height = (_messageState.textSize_Height * FONT_VISUAL_HEIGHT) + 10; _messageState.backgroundPos_x = (_messageState.textPos.column * FONT_VISUAL_WIDTH) - 5; - _messageState.backgroundPos_y = (_messageState.textPos_Edge.row - _window_Row_Min + 1) * FONT_VISUAL_HEIGHT + 4; + _messageState.backgroundPos_y = (startingRow * FONT_VISUAL_HEIGHT) - 5; + // original AGI used lowerY here, calculated using (_messageState.textPos_Edge.row - _window_Row_Min + 1) * FONT_VISUAL_HEIGHT + 4; // Hardcoded colors: white background and red lines _gfx->drawBox(_messageState.backgroundPos_x, _messageState.backgroundPos_y, _messageState.backgroundSize_Width, _messageState.backgroundSize_Height, 15, 4); @@ -487,10 +488,11 @@ void TextMgr::getMessageBoxInnerDisplayDimensions(int16 &x, int16 &y, int16 &wid if (!_messageState.window_Active) return; - y = _messageState.textPos.row * FONT_DISPLAY_HEIGHT; - x = _messageState.textPos.column * FONT_DISPLAY_WIDTH; - width = _messageState.textSize_Width * FONT_DISPLAY_WIDTH; - height = _messageState.textSize_Height * FONT_DISPLAY_HEIGHT; + y = _messageState.textPos.row; + x = _messageState.textPos.column; + width = _messageState.textSize_Width; + height = _messageState.textSize_Height; + _gfx->translateFontRectToDisplayScreen(x, y, width, height); } bool TextMgr::isMouseWithinMessageBox() { @@ -499,10 +501,10 @@ bool TextMgr::isMouseWithinMessageBox() { int16 mouseX = _vm->_mouse.pos.x; if (_messageState.window_Active) { - _vm->adjustPosToGameScreen(mouseX, mouseY); + _gfx->translateDisplayPosToGameScreen(mouseX, mouseY); - if ((mouseX >= _messageState.backgroundPos_x) && (mouseX <= (_messageState.backgroundPos_x + _messageState.backgroundSize_Width))) { - if ((mouseY >= _messageState.backgroundPos_y - _messageState.backgroundSize_Height) && (mouseY <= (_messageState.backgroundPos_y))) { + if ((mouseX >= _messageState.backgroundPos_x) && (mouseX < (_messageState.backgroundPos_x + _messageState.backgroundSize_Width))) { + if ((mouseY >= _messageState.backgroundPos_y) && (mouseY < (_messageState.backgroundPos_y + _messageState.backgroundSize_Height))) { return true; } } @@ -581,12 +583,12 @@ void TextMgr::clearBlock(int16 row_Upper, int16 column_Upper, int16 row_Lower, i charPos_Clip(row_Upper, column_Upper); charPos_Clip(row_Lower, column_Lower); - int16 x = column_Upper * FONT_DISPLAY_WIDTH; - int16 y = row_Upper * FONT_DISPLAY_HEIGHT; - int16 width = (column_Lower + 1 - column_Upper) * FONT_DISPLAY_WIDTH; - int16 height = (row_Lower + 1 - row_Upper) * FONT_DISPLAY_HEIGHT; + int16 x = column_Upper; + int16 y = row_Upper; + int16 width = (column_Lower + 1 - column_Upper); + int16 height = (row_Lower + 1 - row_Upper); + _gfx->translateFontRectToDisplayScreen(x, y, width, height); - y = y + height - 1; // drawDisplayRect wants lower Y-coordinate _gfx->drawDisplayRect(x, y, width, height, color); } diff --git a/engines/agi/text.h b/engines/agi/text.h index 72d012b917..7e9228e30a 100644 --- a/engines/agi/text.h +++ b/engines/agi/text.h @@ -55,7 +55,7 @@ struct MessageState_Struct { uint16 printed_Height; int16 backgroundPos_x; - int16 backgroundPos_y; + int16 backgroundPos_y; // original AGI used lowerY here, we use upperY so that upscaling is easier int16 backgroundSize_Width; int16 backgroundSize_Height; }; |