mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
This allows efficient redraw of surfaces which don't change their position too much.
1152 lines
36 KiB
C++
1152 lines
36 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE // atari-graphics.h's unordered_set
|
|
|
|
#include "backends/graphics/atari/atari-graphics.h"
|
|
|
|
#include <mint/cookie.h>
|
|
#include <mint/falcon.h>
|
|
#include <mint/osbind.h>
|
|
#include <mint/sysvars.h>
|
|
|
|
#include "backends/graphics/atari/atari-graphics-superblitter.h"
|
|
#include "backends/keymapper/action.h"
|
|
#include "backends/keymapper/keymap.h"
|
|
|
|
#include "common/config-manager.h"
|
|
#include "common/str.h"
|
|
#include "common/textconsole.h" // for warning() & error()
|
|
#include "common/translation.h"
|
|
#include "engines/engine.h"
|
|
#include "graphics/blit.h"
|
|
#include "gui/ThemeEngine.h"
|
|
|
|
#define SCREEN_ACTIVE
|
|
|
|
bool g_unalignedPitch = false;
|
|
|
|
// this is how screenptr should have been handled in TOS...
|
|
#undef screenptr
|
|
static volatile uintptr screenptr;
|
|
static void VblHandler() {
|
|
if (screenptr) {
|
|
#ifdef SCREEN_ACTIVE
|
|
if (hasSuperVidel()) {
|
|
// SuperVidel's XBIOS seems to switch to Super mode with BS8C...
|
|
VsetScreen(SCR_NOCHANGE, screenptr, SCR_NOCHANGE, SCR_NOCHANGE);
|
|
} else {
|
|
union { byte c[4]; uintptr p; } sptr;
|
|
sptr.p = screenptr;
|
|
|
|
*((volatile byte *)0xFFFF8201) = sptr.c[1];
|
|
*((volatile byte *)0xFFFF8203) = sptr.c[2];
|
|
*((volatile byte *)0xFFFF820D) = sptr.c[3];
|
|
}
|
|
#endif
|
|
screenptr = 0;
|
|
}
|
|
}
|
|
|
|
static uint32 InstallVblHandler() {
|
|
uint32 installed = 0;
|
|
*vblsem = 0; // lock vbl
|
|
|
|
for (int i = 0; i < *nvbls; ++i) {
|
|
if (!(*_vblqueue)[i]) {
|
|
(*_vblqueue)[i] = VblHandler;
|
|
installed = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*vblsem = 1; // unlock vbl
|
|
return installed;
|
|
}
|
|
|
|
static uint32 UninstallVblHandler() {
|
|
uint32 uninstalled = 0;
|
|
*vblsem = 0; // lock vbl
|
|
|
|
for (int i = 0; i < *nvbls; ++i) {
|
|
if ((*_vblqueue)[i] == VblHandler) {
|
|
(*_vblqueue)[i] = NULL;
|
|
uninstalled = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*vblsem = 1; // unlock vbl
|
|
return uninstalled;
|
|
}
|
|
|
|
static int s_oldRez = -1;
|
|
static int s_oldMode = -1;
|
|
static void *s_oldPhysbase = nullptr;
|
|
|
|
void AtariGraphicsShutdown() {
|
|
Supexec(UninstallVblHandler);
|
|
|
|
if (s_oldRez != -1) {
|
|
Setscreen(SCR_NOCHANGE, s_oldPhysbase, s_oldRez);
|
|
} else if (s_oldMode != -1) {
|
|
// prevent setting video base address just on the VDB line
|
|
Vsync();
|
|
VsetMode(s_oldMode);
|
|
VsetScreen(SCR_NOCHANGE, s_oldPhysbase, SCR_NOCHANGE, SCR_NOCHANGE);
|
|
}
|
|
}
|
|
|
|
AtariGraphicsManager::AtariGraphicsManager() {
|
|
debug("AtariGraphicsManager()");
|
|
|
|
enum {
|
|
VDO_NO_ATARI_HW = 0xffff,
|
|
VDO_ST = 0,
|
|
VDO_STE,
|
|
VDO_TT,
|
|
VDO_FALCON,
|
|
VDO_MILAN
|
|
};
|
|
|
|
long vdo = VDO_NO_ATARI_HW<<16;
|
|
Getcookie(C__VDO, &vdo);
|
|
vdo >>= 16;
|
|
|
|
_tt = (vdo == VDO_TT);
|
|
|
|
if (!_tt)
|
|
_vgaMonitor = VgetMonitor() == MON_VGA;
|
|
|
|
// make the standard GUI renderer default (!DISABLE_FANCY_THEMES implies anti-aliased rendering in ThemeEngine.cpp)
|
|
// (and without DISABLE_FANCY_THEMES we can't use 640x480 themes)
|
|
const char *standardThemeEngineName = GUI::ThemeEngine::findModeConfigName(GUI::ThemeEngine::kGfxStandard);
|
|
if (!ConfMan.hasKey("gui_renderer"))
|
|
ConfMan.set("gui_renderer", standardThemeEngineName);
|
|
|
|
// make the built-in theme default to avoid long loading times
|
|
if (!ConfMan.hasKey("gui_theme"))
|
|
ConfMan.set("gui_theme", "builtin");
|
|
|
|
#ifndef DISABLE_FANCY_THEMES
|
|
// make "themes" the default theme path
|
|
ConfMan.registerDefault("themepath", "themes");
|
|
if (!ConfMan.hasKey("themepath"))
|
|
ConfMan.set("themepath", "themes");
|
|
#endif
|
|
|
|
ConfMan.flushToDisk();
|
|
|
|
// Generate RGB332/RGB121 palette for the overlay
|
|
const Graphics::PixelFormat &format = getOverlayFormat();
|
|
const int paletteSize = getOverlayPaletteSize();
|
|
for (int i = 0; i < paletteSize; i++) {
|
|
if (_tt) {
|
|
// Bits 15-12 Bits 11-8 Bits 7-4 Bits 3-0
|
|
// Reserved Red Green Blue
|
|
_overlayPalette.tt[i] = ((i >> format.rShift) & format.rMax()) << (8 + (format.rLoss - 4));
|
|
_overlayPalette.tt[i] |= ((i >> format.gShift) & format.gMax()) << (4 + (format.gLoss - 4));
|
|
_overlayPalette.tt[i] |= ((i >> format.bShift) & format.bMax()) << (0 + (format.bLoss - 4));
|
|
} else {
|
|
_overlayPalette.falcon[i].red = ((i >> format.rShift) & format.rMax()) << format.rLoss;
|
|
_overlayPalette.falcon[i].green |= ((i >> format.gShift) & format.gMax()) << format.gLoss;
|
|
_overlayPalette.falcon[i].blue |= ((i >> format.bShift) & format.bMax()) << format.bLoss;
|
|
}
|
|
}
|
|
|
|
// although we store/restore video hardware in OSystem_Atari,
|
|
// make sure that internal OS structures are updated correctly, too
|
|
if (_tt) {
|
|
s_oldRez = Getrez();
|
|
} else {
|
|
s_oldMode = VsetMode(VM_INQUIRE);
|
|
}
|
|
s_oldPhysbase = Physbase();
|
|
|
|
if (!Supexec(InstallVblHandler)) {
|
|
error("VBL handler was not installed");
|
|
}
|
|
|
|
g_system->getEventManager()->getEventDispatcher()->registerObserver(this, 10, false);
|
|
}
|
|
|
|
AtariGraphicsManager::~AtariGraphicsManager() {
|
|
debug("~AtariGraphicsManager()");
|
|
|
|
g_system->getEventManager()->getEventDispatcher()->unregisterObserver(this);
|
|
|
|
AtariGraphicsShutdown();
|
|
}
|
|
|
|
bool AtariGraphicsManager::hasFeature(OSystem::Feature f) const {
|
|
switch (f) {
|
|
case OSystem::Feature::kFeatureAspectRatioCorrection:
|
|
//debug("hasFeature(kFeatureAspectRatioCorrection): %d", !_vgaMonitor);
|
|
return !_tt && !_vgaMonitor;
|
|
case OSystem::Feature::kFeatureCursorPalette:
|
|
// FIXME: pretend to have cursor palette at all times, this function
|
|
// can get (and it is) called any time, before and after showOverlay()
|
|
// (overlay cursor uses the cross if kFeatureCursorPalette returns false
|
|
// here too soon)
|
|
//debug("hasFeature(kFeatureCursorPalette): %d", isOverlayVisible());
|
|
//return isOverlayVisible();
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void AtariGraphicsManager::setFeatureState(OSystem::Feature f, bool enable) {
|
|
switch (f) {
|
|
case OSystem::Feature::kFeatureAspectRatioCorrection:
|
|
//debug("setFeatureState(kFeatureAspectRatioCorrection): %d", enable);
|
|
_oldAspectRatioCorrection = _aspectRatioCorrection;
|
|
_aspectRatioCorrection = enable;
|
|
break;
|
|
default:
|
|
[[fallthrough]];
|
|
}
|
|
}
|
|
|
|
bool AtariGraphicsManager::getFeatureState(OSystem::Feature f) const {
|
|
switch (f) {
|
|
case OSystem::Feature::kFeatureAspectRatioCorrection:
|
|
//debug("getFeatureState(kFeatureAspectRatioCorrection): %d", _aspectRatioCorrection);
|
|
return _aspectRatioCorrection;
|
|
case OSystem::Feature::kFeatureCursorPalette:
|
|
//debug("getFeatureState(kFeatureCursorPalette): %d", isOverlayVisible());
|
|
//return isOverlayVisible();
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool AtariGraphicsManager::setGraphicsMode(int mode, uint flags) {
|
|
debug("setGraphicsMode: %d, %d", mode, flags);
|
|
|
|
GraphicsMode graphicsMode = (GraphicsMode)mode;
|
|
|
|
if (graphicsMode >= GraphicsMode::DirectRendering && graphicsMode <= GraphicsMode::TripleBuffering) {
|
|
_pendingState.mode = graphicsMode;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void AtariGraphicsManager::initSize(uint width, uint height, const Graphics::PixelFormat *format) {
|
|
debug("initSize: %d, %d, %d", width, height, format ? format->bytesPerPixel : 1);
|
|
|
|
_pendingState.width = width;
|
|
_pendingState.height = height;
|
|
_pendingState.format = format ? *format : PIXELFORMAT_CLUT8;
|
|
}
|
|
|
|
void AtariGraphicsManager::beginGFXTransaction() {
|
|
debug("beginGFXTransaction");
|
|
}
|
|
|
|
OSystem::TransactionError AtariGraphicsManager::endGFXTransaction() {
|
|
debug("endGFXTransaction");
|
|
|
|
int error = OSystem::TransactionError::kTransactionSuccess;
|
|
|
|
if (_pendingState.format != PIXELFORMAT_CLUT8)
|
|
error |= OSystem::TransactionError::kTransactionFormatNotSupported;
|
|
|
|
if (_pendingState.width > getMaximumScreenWidth() || _pendingState.height > getMaximumScreenHeight())
|
|
error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
|
|
|
|
if (error != OSystem::TransactionError::kTransactionSuccess) {
|
|
warning("endGFXTransaction failed: %02x", (int)error);
|
|
// all our errors are fatal but engine.cpp takes only this one seriously
|
|
error |= OSystem::TransactionError::kTransactionSizeChangeFailed;
|
|
return static_cast<OSystem::TransactionError>(error);
|
|
}
|
|
|
|
_chunkySurface.init(_pendingState.width, _pendingState.height, _pendingState.width,
|
|
_chunkySurface.getPixels(), _pendingState.format);
|
|
|
|
_screen[FRONT_BUFFER]->reset(_pendingState.width, _pendingState.height, 8);
|
|
_screen[BACK_BUFFER1]->reset(_pendingState.width, _pendingState.height, 8);
|
|
_screen[BACK_BUFFER2]->reset(_pendingState.width, _pendingState.height, 8);
|
|
screenptr = 0;
|
|
|
|
_workScreen = _screen[_pendingState.mode <= GraphicsMode::SingleBuffering ? FRONT_BUFFER : BACK_BUFFER1];
|
|
|
|
// in case of resolution change from GUI
|
|
if (_oldWorkScreen)
|
|
_oldWorkScreen = _workScreen;
|
|
|
|
_palette.clear();
|
|
_pendingScreenChange = kPendingScreenChangeMode | kPendingScreenChangeScreen | kPendingScreenChangePalette;
|
|
|
|
static bool firstRun = true;
|
|
if (firstRun) {
|
|
_cursor.setPosition(getOverlayWidth() / 2, getOverlayHeight() / 2);
|
|
_cursor.swap();
|
|
firstRun = false;
|
|
}
|
|
|
|
warpMouse(_pendingState.width / 2, _pendingState.height / 2);
|
|
|
|
_currentState = _pendingState;
|
|
|
|
return OSystem::kTransactionSuccess;
|
|
}
|
|
|
|
void AtariGraphicsManager::setPalette(const byte *colors, uint start, uint num) {
|
|
//debug("setPalette: %d, %d", start, num);
|
|
|
|
if (_tt) {
|
|
uint16 *pal = &_palette.tt[start];
|
|
for (uint i = 0; i < num; ++i) {
|
|
// Bits 15-12 Bits 11-8 Bits 7-4 Bits 3-0
|
|
// Reserved Red Green Blue
|
|
pal[i] = ((colors[i * 3 + 0] >> 4) & 0x0f) << 8;
|
|
pal[i] |= ((colors[i * 3 + 1] >> 4) & 0x0f) << 4;
|
|
pal[i] |= ((colors[i * 3 + 2] >> 4) & 0x0f);
|
|
}
|
|
} else {
|
|
_RGB *pal = &_palette.falcon[start];
|
|
for (uint i = 0; i < num; ++i) {
|
|
pal[i].red = colors[i * 3 + 0];
|
|
pal[i].green = colors[i * 3 + 1];
|
|
pal[i].blue = colors[i * 3 + 2];
|
|
}
|
|
}
|
|
|
|
_pendingScreenChange |= kPendingScreenChangePalette;
|
|
}
|
|
|
|
void AtariGraphicsManager::grabPalette(byte *colors, uint start, uint num) const {
|
|
//debug("grabPalette: %d, %d", start, num);
|
|
|
|
if (_tt) {
|
|
const uint16 *pal = &_palette.tt[start];
|
|
for (uint i = 0; i < num; ++i) {
|
|
// Bits 15-12 Bits 11-8 Bits 7-4 Bits 3-0
|
|
// Reserved Red Green Blue
|
|
*colors++ = ((pal[i] >> 8) & 0x0f) << 4;
|
|
*colors++ = ((pal[i] >> 4) & 0x0f) << 4;
|
|
*colors++ = ((pal[i] ) & 0x0f) << 4;
|
|
}
|
|
} else {
|
|
const _RGB *pal = &_palette.falcon[start];
|
|
for (uint i = 0; i < num; ++i) {
|
|
*colors++ = pal[i].red;
|
|
*colors++ = pal[i].green;
|
|
*colors++ = pal[i].blue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void AtariGraphicsManager::copyRectToScreen(const void *buf, int pitch, int x, int y, int w, int h) {
|
|
//debug("copyRectToScreen: %d, %d, %d(%d), %d", x, y, w, pitch, h);
|
|
|
|
Graphics::Surface &dstSurface = *lockScreen();
|
|
const Common::Rect rect = Common::Rect(x, y, x + w, y + h);
|
|
|
|
dstSurface.copyRectToSurface(buf, pitch, x, y, w, h);
|
|
_workScreen->addDirtyRect(dstSurface, rect);
|
|
|
|
if (_currentState.mode == GraphicsMode::DirectRendering) {
|
|
// TODO: c2p with 16pix align
|
|
updateScreen();
|
|
} else if (_currentState.mode == GraphicsMode::TripleBuffering) {
|
|
_screen[BACK_BUFFER2]->addDirtyRect(dstSurface, rect);
|
|
_screen[FRONT_BUFFER]->addDirtyRect(dstSurface, rect);
|
|
}
|
|
}
|
|
|
|
// this is not really locking anything but it's an useful function
|
|
// to return current rendering surface :)
|
|
Graphics::Surface *AtariGraphicsManager::lockScreen() {
|
|
//debug("lockScreen");
|
|
|
|
if (isOverlayVisible())
|
|
return &_overlaySurface; // used also for cursor clipping
|
|
else if (_currentState.mode != GraphicsMode::DirectRendering)
|
|
return &_chunkySurface;
|
|
else
|
|
return _workScreen->offsettedSurf;
|
|
}
|
|
|
|
void AtariGraphicsManager::unlockScreen() {
|
|
//debug("unlockScreen: %d x %d", _workScreen->surf.w, _workScreen->surf.h);
|
|
|
|
const Graphics::Surface &dstSurface = *lockScreen();
|
|
const Common::Rect rect = Common::Rect(dstSurface.w, dstSurface.h);
|
|
|
|
_workScreen->addDirtyRect(dstSurface, rect);
|
|
|
|
if (_currentState.mode == GraphicsMode::DirectRendering)
|
|
updateScreen();
|
|
else if (_currentState.mode == GraphicsMode::TripleBuffering) {
|
|
_screen[BACK_BUFFER2]->addDirtyRect(dstSurface, rect);
|
|
_screen[FRONT_BUFFER]->addDirtyRect(dstSurface, rect);
|
|
}
|
|
}
|
|
|
|
void AtariGraphicsManager::fillScreen(uint32 col) {
|
|
debug("fillScreen: %d", col);
|
|
|
|
Graphics::Surface *screen = lockScreen();
|
|
screen->fillRect(Common::Rect(screen->w, screen->h), col);
|
|
unlockScreen();
|
|
}
|
|
|
|
void AtariGraphicsManager::updateScreen() {
|
|
//debug("updateScreen");
|
|
|
|
// avoid falling into the debugger (screen may not not initialized yet)
|
|
Common::setErrorHandler(nullptr);
|
|
|
|
if (_checkUnalignedPitch) {
|
|
const Common::ConfigManager::Domain *activeDomain = ConfMan.getActiveDomain();
|
|
if (activeDomain) {
|
|
// FIXME: Some engines are too bound to linear surfaces that it is very
|
|
// hard to repair them. So instead of polluting the engine with
|
|
// Surface::init() & delete[] Surface::getPixels() just use this hack.
|
|
Common::String engineId = activeDomain->getValOrDefault("engineid");
|
|
if (engineId == "parallaction"
|
|
|| engineId == "mohawk"
|
|
|| engineId == "sherlock"
|
|
|| engineId == "tsage") {
|
|
g_unalignedPitch = true;
|
|
}
|
|
}
|
|
|
|
_checkUnalignedPitch = false;
|
|
}
|
|
|
|
// updates outOfScreen OR srcRect/dstRect (only if visible/needed)
|
|
_cursor.update(*lockScreen(), _workScreen->cursorPositionChanged || _workScreen->cursorSurfaceChanged);
|
|
|
|
bool screenUpdated = false;
|
|
|
|
if (isOverlayVisible()) {
|
|
assert(_workScreen == _screen[OVERLAY_BUFFER]);
|
|
screenUpdated = updateScreenInternal(_overlaySurface);
|
|
} else {
|
|
switch (_currentState.mode) {
|
|
case GraphicsMode::DirectRendering:
|
|
assert(_workScreen == _screen[FRONT_BUFFER]);
|
|
screenUpdated = updateScreenInternal(Graphics::Surface());
|
|
break;
|
|
case GraphicsMode::SingleBuffering:
|
|
assert(_workScreen == _screen[FRONT_BUFFER]);
|
|
screenUpdated = updateScreenInternal(_chunkySurface);
|
|
break;
|
|
case GraphicsMode::TripleBuffering:
|
|
assert(_workScreen == _screen[BACK_BUFFER1]);
|
|
screenUpdated = updateScreenInternal(_chunkySurface);
|
|
break;
|
|
}
|
|
}
|
|
|
|
_workScreen->clearDirtyRects();
|
|
|
|
if (_pendingScreenChange & kPendingScreenChangeScreen) {
|
|
// can't call (V)SetScreen without Vsync()
|
|
screenptr = (uintptr)(isOverlayVisible() ? _workScreen->surf.getPixels() : _screen[FRONT_BUFFER]->surf.getPixels());
|
|
} else if (screenUpdated && !isOverlayVisible() && _currentState.mode == GraphicsMode::TripleBuffering) {
|
|
// Triple buffer:
|
|
// - alternate BACK_BUFFER1 and BACK_BUFFER2
|
|
// - check if FRONT_BUFFER has been displayed for at least one frame
|
|
// - display the most recent buffer (BACK_BUFFER2 in our case)
|
|
// - alternate BACK_BUFFER2 and FRONT_BUFFER (only if BACK_BUFFER2
|
|
// has been updated)
|
|
|
|
set_sysvar_to_short(vblsem, 0); // lock vbl
|
|
|
|
static long old_vbclock = get_sysvar(_vbclock);
|
|
long curr_vbclock = get_sysvar(_vbclock);
|
|
|
|
if (old_vbclock != curr_vbclock) {
|
|
// at least one vbl has passed since setting new video base
|
|
// guard BACK_BUFFER2 from overwriting while presented
|
|
Screen *tmp = _screen[BACK_BUFFER2];
|
|
_screen[BACK_BUFFER2] = _screen[FRONT_BUFFER];
|
|
_screen[FRONT_BUFFER] = tmp;
|
|
|
|
old_vbclock = curr_vbclock;
|
|
}
|
|
|
|
// swap back buffers
|
|
Screen *tmp = _screen[BACK_BUFFER1];
|
|
_screen[BACK_BUFFER1] = _screen[BACK_BUFFER2];
|
|
_screen[BACK_BUFFER2] = tmp;
|
|
|
|
// queue BACK_BUFFER2 with the most recent frame content
|
|
screenptr = (uintptr)_screen[BACK_BUFFER2]->surf.getPixels();
|
|
|
|
set_sysvar_to_short(vblsem, 1); // unlock vbl
|
|
|
|
_workScreen = _screen[BACK_BUFFER1];
|
|
// BACK_BUFFER2: now contains finished frame
|
|
// FRONT_BUFFER is displayed and still contains previously finished frame
|
|
}
|
|
|
|
#ifdef SCREEN_ACTIVE
|
|
if (_pendingScreenChange & kPendingScreenChangeMode) {
|
|
// Avoid changing video registers in the middle of rendering...
|
|
Vsync();
|
|
|
|
if (_workScreen->rez != -1) {
|
|
// unfortunately this reinitializes VDI, too
|
|
Setscreen(SCR_NOCHANGE, SCR_NOCHANGE, _workScreen->rez);
|
|
} else if (_workScreen->mode != -1) {
|
|
VsetMode(_workScreen->mode);
|
|
}
|
|
}
|
|
|
|
if (_pendingScreenChange & kPendingScreenChangePalette) {
|
|
if (_tt)
|
|
EsetPalette(0, isOverlayVisible() ? getOverlayPaletteSize() : 256, _workScreen->palette->tt);
|
|
else
|
|
VsetRGB(0, isOverlayVisible() ? getOverlayPaletteSize() : 256, _workScreen->palette->falcon);
|
|
}
|
|
|
|
_pendingScreenChange = kPendingScreenChangeNone;
|
|
|
|
if (_oldAspectRatioCorrection != _aspectRatioCorrection) {
|
|
if (!isOverlayVisible()) {
|
|
if (!_vgaMonitor) {
|
|
short mode = VsetMode(VM_INQUIRE);
|
|
if (_aspectRatioCorrection) {
|
|
// 60 Hz
|
|
mode &= ~PAL;
|
|
mode |= NTSC;
|
|
} else {
|
|
// 50 Hz
|
|
mode &= ~NTSC;
|
|
mode |= PAL;
|
|
}
|
|
VsetMode(mode);
|
|
} else if (hasSuperVidel()) {
|
|
// TODO: reduce to 200 scan lines?
|
|
} else if (!_tt) {
|
|
// TODO: increase vertical frequency?
|
|
} else {
|
|
// TODO: some tricks with TT's 480 lines?
|
|
}
|
|
|
|
_oldAspectRatioCorrection = _aspectRatioCorrection;
|
|
} else {
|
|
// ignore new value in overlay
|
|
_aspectRatioCorrection = _oldAspectRatioCorrection;
|
|
}
|
|
}
|
|
#endif
|
|
//debug("end of updateScreen");
|
|
}
|
|
|
|
void AtariGraphicsManager::setShakePos(int shakeXOffset, int shakeYOffset) {
|
|
debug("setShakePos: %d, %d", shakeXOffset, shakeYOffset);
|
|
}
|
|
|
|
void AtariGraphicsManager::showOverlay(bool inGUI) {
|
|
debug("showOverlay");
|
|
|
|
if (_overlayVisible)
|
|
return;
|
|
|
|
if (_currentState.mode == GraphicsMode::DirectRendering) {
|
|
// make sure that _oldCursorRect is used to restore the original game graphics
|
|
// (but only if resolution hasn't changed, see endGFXTransaction())
|
|
bool wasVisible = showMouse(false);
|
|
|
|
// revert back but don't update screen
|
|
_cursor.visible = wasVisible;
|
|
}
|
|
|
|
_cursor.swap();
|
|
_oldWorkScreen = _workScreen;
|
|
_workScreen = _screen[OVERLAY_BUFFER];
|
|
|
|
// do not cache dirtyRects and oldCursorRect
|
|
int bitsPerPixel = getOverlayFormat().isCLUT8() || getOverlayFormat() == PIXELFORMAT_RGB332 ? 8 : 4;
|
|
_workScreen->reset(getOverlayWidth(), getOverlayHeight(), bitsPerPixel);
|
|
|
|
_pendingScreenChange = kPendingScreenChangeMode | kPendingScreenChangeScreen | kPendingScreenChangePalette;
|
|
|
|
_overlayVisible = true;
|
|
|
|
updateScreen();
|
|
}
|
|
|
|
void AtariGraphicsManager::hideOverlay() {
|
|
debug("hideOverlay");
|
|
|
|
if (!_overlayVisible)
|
|
return;
|
|
|
|
_workScreen = _oldWorkScreen;
|
|
_oldWorkScreen = nullptr;
|
|
_cursor.swap();
|
|
|
|
// FIXME: perhaps there's a better way but this will do for now
|
|
_checkUnalignedPitch = true;
|
|
|
|
_pendingScreenChange = kPendingScreenChangeMode | kPendingScreenChangeScreen | kPendingScreenChangePalette;
|
|
|
|
_overlayVisible = false;
|
|
|
|
updateScreen();
|
|
}
|
|
|
|
void AtariGraphicsManager::clearOverlay() {
|
|
debug("clearOverlay");
|
|
|
|
if (!_overlayVisible)
|
|
return;
|
|
|
|
#ifndef DISABLE_FANCY_THEMES
|
|
const Graphics::Surface &sourceSurface =
|
|
_currentState.mode == GraphicsMode::DirectRendering ? *_screen[FRONT_BUFFER]->offsettedSurf : _chunkySurface;
|
|
|
|
bool upscale = _overlaySurface.w / sourceSurface.w >= 2 && _overlaySurface.h / sourceSurface.h >= 2;
|
|
|
|
int w = upscale ? sourceSurface.w * 2 : sourceSurface.w;
|
|
int h = upscale ? sourceSurface.h * 2 : sourceSurface.h;
|
|
|
|
int hzOffset = (_overlaySurface.w - w) / 2;
|
|
int vOffset = (_overlaySurface.h - h) / 2;
|
|
|
|
int pitch = hzOffset * 2 + (upscale ? _overlaySurface.pitch : 0);
|
|
|
|
// Transpose from game palette to RGB332/RGB121 (overlay palette)
|
|
const byte *src = (const byte*)sourceSurface.getPixels();
|
|
byte *dst = (byte *)_overlaySurface.getBasePtr(hzOffset, vOffset);
|
|
|
|
// for TT: 8/4/0 + (xLoss - 4) + xShift
|
|
static const int rShift = (_tt ? (8 - 4) : 0)
|
|
+ _overlaySurface.format.rLoss - _overlaySurface.format.rShift;
|
|
static const int gShift = (_tt ? (4 - 4) : 0)
|
|
+ _overlaySurface.format.gLoss - _overlaySurface.format.gShift;
|
|
static const int bShift = (_tt ? (0 - 4) : 0)
|
|
+ _overlaySurface.format.bLoss - _overlaySurface.format.bShift;
|
|
|
|
static const int rMask = _overlaySurface.format.rMax() << _overlaySurface.format.rShift;
|
|
static const int gMask = _overlaySurface.format.gMax() << _overlaySurface.format.gShift;
|
|
static const int bMask = _overlaySurface.format.bMax() << _overlaySurface.format.bShift;
|
|
|
|
for (int y = 0; y < sourceSurface.h; y++) {
|
|
for (int x = 0; x < sourceSurface.w; x++) {
|
|
byte pixel;
|
|
|
|
if (_tt) {
|
|
// Bits 15-12 Bits 11-8 Bits 7-4 Bits 3-0
|
|
// Reserved Red Green Blue
|
|
const uint16 &col = _palette.tt[*src++];
|
|
pixel = ((col >> rShift) & rMask)
|
|
| ((col >> gShift) & gMask)
|
|
| ((col >> bShift) & bMask);
|
|
} else {
|
|
const _RGB &col = _palette.falcon[*src++];
|
|
pixel = ((col.red >> rShift) & rMask)
|
|
| ((col.green >> gShift) & gMask)
|
|
| ((col.blue >> bShift) & bMask);
|
|
}
|
|
|
|
if (upscale) {
|
|
*(dst + _overlaySurface.pitch) = pixel;
|
|
*dst++ = pixel;
|
|
*(dst + _overlaySurface.pitch) = pixel;
|
|
}
|
|
*dst++ = pixel;
|
|
}
|
|
|
|
dst += pitch;
|
|
}
|
|
|
|
// top rect
|
|
memset(_overlaySurface.getBasePtr(0, 0), 0, vOffset * _overlaySurface.pitch);
|
|
// bottom rect
|
|
memset(_overlaySurface.getBasePtr(0, _overlaySurface.h - vOffset), 0, vOffset * _overlaySurface.pitch);
|
|
// left rect
|
|
_overlaySurface.fillRect(Common::Rect(0, vOffset, hzOffset, _overlaySurface.h - vOffset), 0);
|
|
// right rect
|
|
_overlaySurface.fillRect(Common::Rect(_overlaySurface.w - hzOffset, vOffset, _overlaySurface.w, _overlaySurface.h - vOffset), 0);
|
|
|
|
_screen[OVERLAY_BUFFER]->addDirtyRect(_overlaySurface, Common::Rect(_overlaySurface.w, _overlaySurface.h));
|
|
#else
|
|
// for cursor background and non-aligned rects
|
|
memset(_overlaySurface.getPixels(), 0, _overlaySurface.h * _overlaySurface.pitch);
|
|
#endif
|
|
}
|
|
|
|
void AtariGraphicsManager::grabOverlay(Graphics::Surface &surface) const {
|
|
debug("grabOverlay: %d(%d), %d", surface.w, surface.pitch, surface.h);
|
|
|
|
assert(surface.w >= _overlaySurface.w);
|
|
assert(surface.h >= _overlaySurface.h);
|
|
assert(surface.format.bytesPerPixel == _overlaySurface.format.bytesPerPixel);
|
|
|
|
#ifndef DISABLE_FANCY_THEMES
|
|
const byte *src = (const byte *)_overlaySurface.getPixels();
|
|
byte *dst = (byte *)surface.getPixels();
|
|
Graphics::copyBlit(dst, src, surface.pitch,
|
|
_overlaySurface.pitch, _overlaySurface.w, _overlaySurface.h, _overlaySurface.format.bytesPerPixel);
|
|
#else
|
|
// assumes that the first dirty rect in copyRectToOverlay is always fullscreen
|
|
memset(surface.getPixels(), 0, surface.h * surface.pitch);
|
|
#endif
|
|
}
|
|
|
|
void AtariGraphicsManager::copyRectToOverlay(const void *buf, int pitch, int x, int y, int w, int h) {
|
|
//debug("copyRectToOverlay: %d, %d, %d(%d), %d", x, y, w, pitch, h);
|
|
|
|
_overlaySurface.copyRectToSurface(buf, pitch, x, y, w, h);
|
|
_screen[OVERLAY_BUFFER]->addDirtyRect(_overlaySurface, Common::Rect(x, y, x + w, y + h));
|
|
}
|
|
|
|
bool AtariGraphicsManager::showMouse(bool visible) {
|
|
//debug("showMouse: %d", visible);
|
|
|
|
if (_cursor.visible == visible) {
|
|
return visible;
|
|
}
|
|
|
|
bool last = _cursor.visible;
|
|
_cursor.visible = visible;
|
|
|
|
cursorVisibilityChanged();
|
|
// don't rely on engines to call it (if they don't it confuses the cursor restore logic)
|
|
updateScreen();
|
|
|
|
return last;
|
|
}
|
|
|
|
void AtariGraphicsManager::warpMouse(int x, int y) {
|
|
//debug("warpMouse: %d, %d", x, y);
|
|
|
|
_cursor.setPosition(x, y);
|
|
cursorPositionChanged();
|
|
}
|
|
|
|
void AtariGraphicsManager::setMouseCursor(const void *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor,
|
|
bool dontScale, const Graphics::PixelFormat *format, const byte *mask) {
|
|
//debug("setMouseCursor: %d, %d, %d, %d, %d, %d", w, h, hotspotX, hotspotY, keycolor, format ? format->bytesPerPixel : 1);
|
|
|
|
if (mask)
|
|
warning("AtariGraphicsManager::setMouseCursor: Masks are not supported");
|
|
|
|
const Graphics::PixelFormat cursorFormat = format ? *format : PIXELFORMAT_CLUT8;
|
|
_cursor.setSurface(buf, (int)w, (int)h, hotspotX, hotspotY, keycolor, cursorFormat);
|
|
cursorSurfaceChanged();
|
|
}
|
|
|
|
void AtariGraphicsManager::setCursorPalette(const byte *colors, uint start, uint num) {
|
|
debug("setCursorPalette: %d, %d", start, num);
|
|
|
|
memcpy(&_cursor.palette[start * 3], colors, num * 3);
|
|
cursorSurfaceChanged();
|
|
}
|
|
|
|
void AtariGraphicsManager::updateMousePosition(int deltaX, int deltaY) {
|
|
_cursor.updatePosition(deltaX, deltaY, *lockScreen());
|
|
cursorPositionChanged();
|
|
}
|
|
|
|
bool AtariGraphicsManager::notifyEvent(const Common::Event &event) {
|
|
if (event.type != Common::EVENT_CUSTOM_BACKEND_ACTION_START) {
|
|
return false;
|
|
}
|
|
|
|
switch ((CustomEventAction) event.customType) {
|
|
case kActionToggleAspectRatioCorrection:
|
|
_aspectRatioCorrection = !_aspectRatioCorrection;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Common::Keymap *AtariGraphicsManager::getKeymap() const {
|
|
Common::Keymap *keymap = new Common::Keymap(Common::Keymap::kKeymapTypeGlobal, "atari-graphics", _("Graphics"));
|
|
Common::Action *act;
|
|
|
|
if (hasFeature(OSystem::kFeatureAspectRatioCorrection)) {
|
|
act = new Common::Action("ASPT", _("Toggle aspect ratio correction"));
|
|
act->addDefaultInputMapping("C+A+a");
|
|
act->setCustomBackendActionEvent(kActionToggleAspectRatioCorrection);
|
|
keymap->addAction(act);
|
|
}
|
|
|
|
return keymap;
|
|
}
|
|
|
|
void AtariGraphicsManager::allocateSurfaces() {
|
|
for (int i : { FRONT_BUFFER, BACK_BUFFER1, BACK_BUFFER2 }) {
|
|
_screen[i] = new Screen(this, getMaximumScreenWidth(), getMaximumScreenHeight(), PIXELFORMAT_CLUT8, &_palette);
|
|
}
|
|
|
|
_screen[OVERLAY_BUFFER] = new Screen(this, getOverlayWidth(), getOverlayHeight(), getOverlayFormat(), &_overlayPalette);
|
|
|
|
_chunkySurface.create(getMaximumScreenWidth(), getMaximumScreenHeight(), PIXELFORMAT_CLUT8);
|
|
_overlaySurface.create(getOverlayWidth(), getOverlayHeight(), getOverlayFormat());
|
|
}
|
|
|
|
void AtariGraphicsManager::freeSurfaces() {
|
|
for (int i : { FRONT_BUFFER, BACK_BUFFER1, BACK_BUFFER2, OVERLAY_BUFFER }) {
|
|
delete _screen[i];
|
|
_screen[i] = nullptr;
|
|
}
|
|
_workScreen = nullptr;
|
|
|
|
_chunkySurface.free();
|
|
_overlaySurface.free();
|
|
}
|
|
|
|
void AtariGraphicsManager::convertRectToSurfaceWithKey(Graphics::Surface &dstSurface, const Graphics::Surface &srcSurface,
|
|
int destX, int destY, const Common::Rect &subRect, uint32 key,
|
|
const byte srcPalette[256*3]) const {
|
|
if (!dstSurface.format.isCLUT8()) {
|
|
assert(srcSurface.format == PIXELFORMAT_CLUT8);
|
|
|
|
static int rShift, gShift, bShift;
|
|
static int rMask, gMask, bMask;
|
|
static Graphics::PixelFormat format;
|
|
|
|
if (dstSurface.format != format) {
|
|
format = dstSurface.format;
|
|
|
|
rShift = format.rLoss - format.rShift;
|
|
gShift = format.gLoss - format.gShift;
|
|
bShift = format.bLoss - format.bShift;
|
|
|
|
rMask = format.rMax() << format.rShift;
|
|
gMask = format.gMax() << format.gShift;
|
|
bMask = format.bMax() << format.bShift;
|
|
}
|
|
|
|
// Convert CLUT8 to RGB332/RGB121 palette and do copyRectToSurfaceWithKey() at the same time
|
|
const byte *src = (const byte*)srcSurface.getBasePtr(subRect.left, subRect.top);
|
|
byte *dst = (byte*)dstSurface.getBasePtr(destX, destY);
|
|
|
|
const int16 w = subRect.width();
|
|
const int16 h = subRect.height();
|
|
|
|
for (int16 y = 0; y < h; ++y) {
|
|
for (int16 x = 0; x < w; ++x) {
|
|
const uint32 color = *src++;
|
|
if (color != key) {
|
|
*dst++ = ((srcPalette[color*3 + 0] >> rShift) & rMask)
|
|
| ((srcPalette[color*3 + 1] >> gShift) & gMask)
|
|
| ((srcPalette[color*3 + 2] >> bShift) & bMask);
|
|
} else {
|
|
dst++;
|
|
}
|
|
}
|
|
|
|
src += (srcSurface.pitch - w);
|
|
dst += (dstSurface.pitch - w);
|
|
}
|
|
} else {
|
|
dstSurface.copyRectToSurfaceWithKey(srcSurface, destX, destY, subRect, key);
|
|
}
|
|
}
|
|
|
|
bool AtariGraphicsManager::updateScreenInternal(const Graphics::Surface &srcSurface) {
|
|
//debug("updateScreenInternal: %d", (int)dirtyRects.size());
|
|
|
|
const DirtyRects &dirtyRects = _workScreen->dirtyRects;
|
|
Graphics::Surface *dstSurface = _workScreen->offsettedSurf;
|
|
bool &cursorPositionChanged = _workScreen->cursorPositionChanged;
|
|
bool &cursorSurfaceChanged = _workScreen->cursorSurfaceChanged;
|
|
bool &cursorVisibilityChanged = _workScreen->cursorVisibilityChanged;
|
|
Common::Rect &oldCursorRect = _workScreen->oldCursorRect;
|
|
const bool &fullRedraw = _workScreen->fullRedraw;
|
|
#ifndef DISABLE_FANCY_THEMES
|
|
const bool directRendering = !isOverlayVisible() && _currentState.mode == GraphicsMode::DirectRendering;
|
|
#else
|
|
// hopefully compiler optimizes all the branching out
|
|
const bool directRendering = false;
|
|
#endif
|
|
|
|
bool updated = false;
|
|
|
|
const bool cursorDrawEnabled = !_cursor.outOfScreen && _cursor.visible;
|
|
bool drawCursor = cursorDrawEnabled
|
|
&& (cursorPositionChanged || cursorSurfaceChanged || cursorVisibilityChanged || fullRedraw);
|
|
|
|
bool restoreCursor = !fullRedraw && !oldCursorRect.isEmpty()
|
|
&& (cursorPositionChanged || cursorSurfaceChanged || (cursorVisibilityChanged && !_cursor.visible));
|
|
|
|
static Graphics::Surface cachedCursorSurface;
|
|
|
|
lockSuperBlitter();
|
|
|
|
for (auto it = dirtyRects.begin(); it != dirtyRects.end(); ++it) {
|
|
if (cursorDrawEnabled && !drawCursor)
|
|
drawCursor = it->intersects(_cursor.dstRect);
|
|
|
|
if (restoreCursor)
|
|
restoreCursor = !it->contains(oldCursorRect);
|
|
|
|
if (!directRendering) {
|
|
copyRectToSurface(*dstSurface, srcSurface, it->left, it->top, *it);
|
|
updated = true;
|
|
} else if (!oldCursorRect.isEmpty()) {
|
|
const Common::Rect intersectingRect = it->findIntersectingRect(oldCursorRect);
|
|
if (!intersectingRect.isEmpty()) {
|
|
// new content has been drawn over stored surface => update
|
|
// TODO: if restoreCursor == true, update only the NON-intersecting part
|
|
const Graphics::Surface intersectingScreenSurface = dstSurface->getSubArea(intersectingRect);
|
|
cachedCursorSurface.copyRectToSurface(
|
|
intersectingScreenSurface,
|
|
intersectingRect.left - oldCursorRect.left,
|
|
intersectingRect.top - oldCursorRect.top,
|
|
Common::Rect(intersectingScreenSurface.w, intersectingScreenSurface.h));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (restoreCursor) {
|
|
//debug("Restore cursor: %d %d %d %d", oldCursorRect.left, oldCursorRect.top, oldCursorRect.width(), oldCursorRect.height());
|
|
|
|
if (!directRendering) {
|
|
// align on 16px (i.e. 16 bytes -> optimize for C2P, MOVE16 or just 16-byte cache lines)
|
|
oldCursorRect.left &= 0xfff0;
|
|
oldCursorRect.right = (oldCursorRect.right + 15) & 0xfff0;
|
|
|
|
copyRectToSurface(
|
|
*dstSurface, srcSurface,
|
|
oldCursorRect.left, oldCursorRect.top,
|
|
oldCursorRect);
|
|
} else {
|
|
copyRectToSurface(
|
|
*dstSurface, cachedCursorSurface,
|
|
oldCursorRect.left, oldCursorRect.top,
|
|
Common::Rect(oldCursorRect.width(), oldCursorRect.height()));
|
|
}
|
|
|
|
oldCursorRect = Common::Rect();
|
|
|
|
updated = true;
|
|
}
|
|
|
|
unlockSuperBlitter();
|
|
|
|
if (drawCursor) {
|
|
//debug("Redraw cursor: %d %d %d %d", _cursor.dstRect.left, _cursor.dstRect.top, _cursor.dstRect.width(), _cursor.dstRect.height());
|
|
|
|
if (directRendering) {
|
|
if (cachedCursorSurface.w != _cursor.dstRect.width() || cachedCursorSurface.h != _cursor.dstRect.height())
|
|
cachedCursorSurface.create(_cursor.dstRect.width(), _cursor.dstRect.height(), _cursor.surface.format);
|
|
|
|
// background has been restored, so it's safe to read from destination surface again
|
|
if (oldCursorRect.isEmpty())
|
|
cachedCursorSurface.copyRectToSurface(*dstSurface, 0, 0, _cursor.dstRect);
|
|
}
|
|
|
|
copyRectToSurfaceWithKey(
|
|
*dstSurface, _cursor.surface,
|
|
_cursor.dstRect.left, _cursor.dstRect.top,
|
|
_cursor.srcRect,
|
|
_cursor.keycolor,
|
|
srcSurface, _cursor.palette);
|
|
|
|
cursorPositionChanged = cursorSurfaceChanged = false;
|
|
oldCursorRect = _cursor.dstRect;
|
|
|
|
updated = true;
|
|
}
|
|
|
|
cursorVisibilityChanged = false;
|
|
|
|
return updated;
|
|
}
|
|
|
|
AtariGraphicsManager::Screen::Screen(AtariGraphicsManager *manager, int width, int height, const Graphics::PixelFormat &format, const Palette *palette_)
|
|
: _manager(manager) {
|
|
const AtariMemAlloc &allocFunc = _manager->getStRamAllocFunc();
|
|
|
|
palette = palette_;
|
|
|
|
surf.init(width, height, width * format.bytesPerPixel, nullptr, format);
|
|
|
|
void *pixelsUnaligned = allocFunc(sizeof(uintptr) + (surf.h * surf.pitch) + ALIGN - 1);
|
|
if (!pixelsUnaligned) {
|
|
error("Failed to allocate memory in ST RAM");
|
|
}
|
|
|
|
surf.setPixels((void *)(((uintptr)pixelsUnaligned + sizeof(uintptr) + ALIGN - 1) & (-ALIGN)));
|
|
|
|
// store the unaligned pointer for later release
|
|
*((uintptr *)surf.getPixels() - 1) = (uintptr)pixelsUnaligned;
|
|
|
|
memset(surf.getPixels(), 0, surf.h * surf.pitch);
|
|
|
|
_offsettedSurf.init(surf.w, surf.h, surf.pitch, surf.getPixels(), surf.format);
|
|
}
|
|
|
|
AtariGraphicsManager::Screen::~Screen() {
|
|
const AtariMemFree &freeFunc = _manager->getStRamFreeFunc();
|
|
|
|
freeFunc((void *)*((uintptr *)surf.getPixels() - 1));
|
|
}
|
|
|
|
void AtariGraphicsManager::Screen::reset(int width, int height, int bitsPerPixel) {
|
|
cursorPositionChanged = true;
|
|
cursorSurfaceChanged = false;
|
|
cursorVisibilityChanged = false;
|
|
clearDirtyRects();
|
|
oldCursorRect = Common::Rect();
|
|
rez = -1;
|
|
mode = -1;
|
|
|
|
// erase old screen
|
|
surf.fillRect(Common::Rect(surf.w, surf.h), 0);
|
|
|
|
if (_manager->_tt) {
|
|
if (width <= 320 && height <= 240) {
|
|
surf.w = 320;
|
|
surf.h = 240;
|
|
surf.pitch = 2*surf.w;
|
|
rez = kRezValueTTLow;
|
|
} else {
|
|
surf.w = 640;
|
|
surf.h = 480;
|
|
surf.pitch = surf.w;
|
|
rez = kRezValueTTMid;
|
|
}
|
|
} else {
|
|
mode = VsetMode(VM_INQUIRE) & PAL;
|
|
|
|
if (_manager->_vgaMonitor) {
|
|
mode |= VGA | (bitsPerPixel == 4 ? BPS4 : (hasSuperVidel() ? BPS8C : BPS8));
|
|
|
|
if (width <= 320 && height <= 240) {
|
|
surf.w = 320;
|
|
surf.h = 240;
|
|
mode |= VERTFLAG | COL40;
|
|
} else {
|
|
surf.w = 640;
|
|
surf.h = 480;
|
|
mode |= COL80;
|
|
}
|
|
} else {
|
|
mode |= TV | BPS8;
|
|
|
|
if (width <= 320 && height <= 200) {
|
|
surf.w = 320;
|
|
surf.h = 200;
|
|
mode |= COL40;
|
|
} else if (width <= 320*1.2 && height <= 200*1.2) {
|
|
surf.w = 320*1.2;
|
|
surf.h = 200*1.2;
|
|
mode |= OVERSCAN | COL40;
|
|
} else if (width <= 640 && height <= 400) {
|
|
surf.w = 640;
|
|
surf.h = 400;
|
|
mode |= VERTFLAG | COL80;
|
|
} else {
|
|
surf.w = 640*1.2;
|
|
surf.h = 400*1.2;
|
|
mode |= VERTFLAG | OVERSCAN | COL80;
|
|
}
|
|
}
|
|
|
|
surf.pitch = surf.w;
|
|
}
|
|
|
|
_offsettedSurf.init(width, height, surf.pitch, surf.getBasePtr((surf.w - width) / 2, (surf.h - height) / 2), surf.format);
|
|
}
|
|
|
|
void AtariGraphicsManager::Screen::addDirtyRect(const Graphics::Surface &srcSurface, Common::Rect rect) {
|
|
if (fullRedraw)
|
|
return;
|
|
|
|
// align on 16px (i.e. 16 bytes -> optimize for C2P, MOVE16 or just 16-byte cache lines)
|
|
rect.left &= 0xfff0;
|
|
rect.right = (rect.right + 15) & 0xfff0;
|
|
|
|
if ((rect.width() == srcSurface.w && rect.height() == srcSurface.h)
|
|
|| dirtyRects.size() == 128) { // 320x200 can hold at most 250 16x16 rectangles
|
|
//debug("addDirtyRect[%d]: purge %d x %d", (int)dirtyRects.size(), srcSurface.w, srcSurface.h);
|
|
|
|
dirtyRects.clear();
|
|
dirtyRects.emplace(srcSurface.w, srcSurface.h);
|
|
|
|
fullRedraw = true;
|
|
return;
|
|
}
|
|
|
|
dirtyRects.emplace(std::move(rect));
|
|
}
|
|
|
|
void AtariGraphicsManager::Cursor::update(const Graphics::Surface &screen, bool isModified) {
|
|
if (!surface.getPixels()) {
|
|
outOfScreen = true;
|
|
return;
|
|
}
|
|
|
|
if (!visible || !isModified)
|
|
return;
|
|
|
|
srcRect = Common::Rect(surface.w, surface.h);
|
|
|
|
dstRect = Common::Rect(
|
|
x - hotspotX, // left
|
|
y - hotspotY, // top
|
|
x - hotspotX + surface.w, // right
|
|
y - hotspotY + surface.h); // bottom
|
|
|
|
outOfScreen = !screen.clip(srcRect, dstRect);
|
|
}
|
|
|
|
void AtariGraphicsManager::Cursor::updatePosition(int deltaX, int deltaY, const Graphics::Surface &screen) {
|
|
x += deltaX;
|
|
y += deltaY;
|
|
|
|
if (x < 0)
|
|
x = 0;
|
|
else if (x >= screen.w)
|
|
x = screen.w - 1;
|
|
|
|
if (y < 0)
|
|
y = 0;
|
|
else if (y >= screen.h)
|
|
y = screen.h - 1;
|
|
}
|
|
|
|
void AtariGraphicsManager::Cursor::setSurface(const void *buf, int w, int h, int hotspotX_, int hotspotY_, uint32 keycolor_,
|
|
const Graphics::PixelFormat &format) {
|
|
if (w == 0 || h == 0 || buf == nullptr) {
|
|
if (surface.getPixels())
|
|
surface.free();
|
|
return;
|
|
}
|
|
|
|
if (surface.w != w || surface.h != h || surface.format != format)
|
|
surface.create(w, h, format);
|
|
|
|
surface.copyRectToSurface(buf, w * surface.format.bytesPerPixel, 0, 0, w, h);
|
|
|
|
hotspotX = hotspotX_;
|
|
hotspotY = hotspotY_;
|
|
keycolor = keycolor_;
|
|
}
|