Started cleanup of FrameBuffer class.

- Moved Video mode stuff into separate class
- Fix bug with aspect correction in fullscreen mode still giving graphical artifacts
This commit is contained in:
Stephen Anthony 2020-10-13 10:47:37 -02:30
parent 7eece4e994
commit e15d27dc96
14 changed files with 439 additions and 462 deletions

View File

@ -225,7 +225,8 @@ Int32 FrameBufferSDL2::getCurrentDisplayIndex() const
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBufferSDL2::setVideoMode(const string& title, const VideoMode& mode)
bool FrameBufferSDL2::activateVideoMode(const string& title,
const VideoModeHandler::Mode& mode)
{
ASSERT_MAIN_THREAD;

View File

@ -179,7 +179,8 @@ class FrameBufferSDL2 : public FrameBuffer
@return False on any errors, else true
*/
bool setVideoMode(const string& title, const VideoMode& mode) override;
bool activateVideoMode(const string& title,
const VideoModeHandler::Mode& mode) override;
/**
Checks if the display refresh rate should be adapted to game refresh rate in (real) fullscreen mode

View File

@ -18,7 +18,7 @@
#ifndef VERSION_HXX
#define VERSION_HXX
#define STELLA_VERSION "6.3"
#define STELLA_VERSION "6.4_pre"
#define STELLA_BUILD "6180"
#endif

View File

@ -0,0 +1,164 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#include "Settings.hxx"
#include "VideoModeHandler.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void VideoModeHandler::setImageSize(const Common::Size& image)
{
myImage = image;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void VideoModeHandler::setDisplaySize(const Common::Size& display, Int32 fsIndex)
{
myDisplay = display;
myFSIndex = fsIndex;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const VideoModeHandler::Mode&
VideoModeHandler::buildMode(const Settings& settings, bool inTIAMode)
{
const bool windowedRequested = myFSIndex == -1;
// TIA mode allows zooming at non-integral factors in most cases
if(inTIAMode)
{
if(windowedRequested)
{
const float zoom = settings.getFloat("tia.zoom");
ostringstream desc;
desc << (zoom * 100) << "%";
// Image and screen (aka window) dimensions are the same
// Overscan is not applicable in this mode
myMode = Mode(myImage.w * zoom, myImage.h * zoom, Mode::Stretch::Fill,
myFSIndex, desc.str(), zoom);
}
else
{
const float overscan = 1 - settings.getInt("tia.fs_overscan") / 100.0;
// First calculate maximum zoom that keeps aspect ratio
const float scaleX = float(myImage.w) / myDisplay.w,
scaleY = float(myImage.h) / myDisplay.h;
float zoom = 1.F / std::max(scaleX, scaleY);
// When aspect ratio correction is off, we want pixel-exact images,
// so we default to integer zooming
if(!settings.getBool("tia.correct_aspect"))
zoom = static_cast<uInt32>(zoom);
if(!settings.getBool("tia.fs_stretch")) // preserve aspect, use all space
{
myMode = Mode(myImage.w * zoom, myImage.h * zoom,
myDisplay.w, myDisplay.h,
Mode::Stretch::Preserve, myFSIndex,
"Fullscreen: Preserve aspect, no stretch", zoom, overscan);
}
else // ignore aspect, use all space
{
myMode = Mode(myImage.w * zoom, myImage.h * zoom,
myDisplay.w, myDisplay.h,
Mode::Stretch::Fill, myFSIndex,
"Fullscreen: Ignore aspect, full stretch", zoom, overscan);
}
}
}
else // UI mode (no zooming)
{
if(windowedRequested)
myMode = Mode(myImage.w, myImage.h, Mode::Stretch::None);
else
myMode = Mode(myImage.w, myImage.h, myDisplay.w, myDisplay.h,
Mode::Stretch::None, myFSIndex);
}
return myMode;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, Stretch smode,
Int32 fsindex, const string& desc,
float zoomLevel)
: Mode(iw, ih, iw, ih, smode, fsindex, desc, zoomLevel)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh,
Stretch smode, Int32 fsindex, const string& desc,
float zoomLevel, float overscan)
: stretch(smode),
description(desc),
zoom(zoomLevel),
fsIndex(fsindex)
{
// First set default size and positioning
screen = Common::Size(sw, sh);
// Now resize based on windowed/fullscreen mode and stretch factor
if(fsIndex != -1) // fullscreen mode
{
switch(stretch)
{
case Stretch::Preserve:
{
iw *= overscan;
ih *= overscan;
break;
}
case Stretch::Fill:
// Scale to all available space
iw = screen.w * overscan;
ih = screen.h * overscan;
break;
case Stretch::None:
// Don't do any scaling at all
iw = std::min(iw, screen.w) * overscan;
ih = std::min(ih, screen.h) * overscan;
break;
}
}
else
{
// In windowed mode, currently the size is scaled to the screen
// TODO - this may be updated if/when we allow variable-sized windows
switch(stretch)
{
case Stretch::Preserve:
case Stretch::Fill:
screen.w = iw;
screen.h = ih;
break;
case Stretch::None:
break; // Do not change image or screen rects whatsoever
}
}
// Now re-calculate the dimensions
iw = std::min(iw, screen.w);
ih = std::min(ih, screen.h);
image.moveTo((screen.w - iw) >> 1, (screen.h - ih) >> 1);
image.setWidth(iw);
image.setHeight(ih);
}

View File

@ -0,0 +1,111 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2020 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#ifndef VIDEO_MODE_HANDLER_HXX
#define VIDEO_MODE_HANDLER_HXX
class Settings;
#include "Rect.hxx"
#include "bspf.hxx"
class VideoModeHandler
{
public:
// Contains all relevant info for the dimensions of a video screen
// Also takes care of the case when the image should be 'centered'
// within the given screen:
// 'image' is the image dimensions into the screen
// 'screen' are the dimensions of the screen itself
struct Mode
{
enum class Stretch {
Preserve, // Stretch to fill all available space; preserve aspect ratio
Fill, // Stretch to fill all available space
None // No stretching (1x zoom)
};
Common::Rect image;
Common::Size screen;
Stretch stretch{Mode::Stretch::None};
string description;
float zoom{1.F};
Int32 fsIndex{-1}; // -1 indicates windowed mode
Mode() = default;
Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh, Stretch smode,
Int32 fsindex = -1, const string& desc = "",
float zoomLevel = 1.F, float overscan = 1.F);
Mode(uInt32 iw, uInt32 ih, Stretch smode, Int32 fsindex = -1,
const string& desc = "", float zoomLevel = 1.F);
friend ostream& operator<<(ostream& os, const Mode& vm)
{
os << "image=" << vm.image << " screen=" << vm.screen
<< " stretch=" << (vm.stretch == Stretch::Preserve ? "preserve" :
vm.stretch == Stretch::Fill ? "fill" : "none")
<< " desc=" << vm.description << " zoom=" << vm.zoom
<< " fsIndex= " << vm.fsIndex;
return os;
}
};
public:
VideoModeHandler() = default;
/**
Set the base size of the image. Scaling can be applied to this,
which will change the effective size.
@param image The base dimensions of the image
*/
void setImageSize(const Common::Size& image);
/**
Set the size of the display. This could be either the desktop size,
or the size of the monitor currently active.
@param display The dimensions of the enclosing display
@param fsIndex Fullscreen mode in use (-1 indicates windowed mode)
*/
void setDisplaySize(const Common::Size& display, Int32 fsIndex = -1);
/**
Build a video mode based on the given parameters, assuming that
setImageSize and setDisplaySize have been previously called.
@param settings Used to query various options that affect video mode
@param inTIAMode Whether the video mode is being used for TIA emulation
@return A video mode based on the given criteria
*/
const VideoModeHandler::Mode& buildMode(const Settings& settings, bool inTIAMode);
private:
Common::Size myImage, myDisplay;
Int32 myFSIndex{-1};
Mode myMode;
private:
VideoModeHandler(const VideoModeHandler&) = delete;
VideoModeHandler(VideoModeHandler&&) = delete;
VideoModeHandler& operator=(const VideoModeHandler&) = delete;
VideoModeHandler& operator=(const VideoModeHandler&&) = delete;
};
#endif // VIDEO_MODE_HANDLER_HXX

View File

@ -1,9 +1,12 @@
MODULE := src/common
MODULE_OBJS := \
src/common/AudioQueue.o \
src/common/AudioSettings.o \
src/common/Base.o \
src/common/EventHandlerSDL2.o \
src/common/FBSurfaceSDL2.o \
src/common/FpsMeter.o \
src/common/FrameBufferSDL2.o \
src/common/FSNodeZIP.o \
src/common/JoyMap.o \
@ -19,14 +22,12 @@ MODULE_OBJS := \
src/common/PNGLibrary.o \
src/common/RewindManager.o \
src/common/SoundSDL2.o \
src/common/StateManager.o \
src/common/TimerManager.o \
src/common/ZipHandler.o \
src/common/AudioQueue.o \
src/common/AudioSettings.o \
src/common/FpsMeter.o \
src/common/ThreadDebugging.o \
src/common/StaggeredLogger.o \
src/common/StateManager.o \
src/common/ThreadDebugging.o \
src/common/TimerManager.o \
src/common/VideoModeHandler.o \
src/common/ZipHandler.o \
src/common/repository/KeyValueRepositoryConfigfile.o \
src/common/sdl_blitter/BilinearBlitter.o \
src/common/sdl_blitter/QisBlitter.o \

View File

@ -622,6 +622,10 @@ FBInitStatus Console::initializeVideo(bool full)
if(full)
{
auto size = myOSystem.settings().getBool("tia.correct_aspect") ?
Common::Size(TIAConstants::viewableWidth, TIAConstants::viewableHeight) :
Common::Size(2 * myTIA->width(), myTIA->height());
uInt32 width, height;
if (!myOSystem.settings().getBool("tia.correct_aspect")) {
width = 2 * myTIA->width();

View File

@ -411,7 +411,7 @@ AdjustFunction EventHandler::getAdjustSetting(AdjustSetting setting)
{
// Audio & Video settings
std::bind(&Sound::adjustVolume, &myOSystem.sound(), _1),
std::bind(&FrameBuffer::selectVidMode, &myOSystem.frameBuffer(), _1),
std::bind(&FrameBuffer::switchVideoMode, &myOSystem.frameBuffer(), _1),
std::bind(&FrameBuffer::toggleFullscreen, &myOSystem.frameBuffer(), _1),
#ifdef ADAPTABLE_REFRESH_SUPPORT
std::bind(&FrameBuffer::toggleAdaptRefresh, &myOSystem.frameBuffer(), _1),
@ -693,7 +693,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated)
case Event::VidmodeDecrease:
if(pressed)
{
myOSystem.frameBuffer().selectVidMode(-1);
myOSystem.frameBuffer().switchVideoMode(-1);
myAdjustSetting = AdjustSetting::ZOOM;
myAdjustActive = true;
}
@ -702,7 +702,7 @@ void EventHandler::handleEvent(Event::Type event, Int32 value, bool repeated)
case Event::VidmodeIncrease:
if(pressed)
{
myOSystem.frameBuffer().selectVidMode(+1);
myOSystem.frameBuffer().switchVideoMode(+1);
myAdjustSetting = AdjustSetting::ZOOM;
myAdjustActive = true;
}

View File

@ -104,9 +104,11 @@ bool FrameBuffer::initialize()
#endif
// Determine possible TIA windowed zoom levels
myTIAMaxZoom = maxZoomForScreen(
TIAConstants::viewableWidth, TIAConstants::viewableHeight,
myAbsDesktopSize.w, myAbsDesktopSize.h);
myTIAMaxZoom = maxWindowZoom(TIAConstants::viewableWidth,
TIAConstants::viewableHeight);
float currentTIAZoom = myOSystem.settings().getFloat("tia.zoom");
myOSystem.settings().setValue("tia.zoom",
BSPF::clampw(currentTIAZoom, supportedTIAMinZoom(), myTIAMaxZoom));
setUIPalette();
@ -173,13 +175,13 @@ void FrameBuffer::setupFonts()
// However, we have to make sure all Dialogs are sized using the fontsize.
int zoom_h = (fd.height * 4 * 2) / GUI::stellaMediumDesc.height;
int zoom_w = (fd.maxwidth * 4 * 2) / GUI::stellaMediumDesc.maxwidth;
myTIAMinZoom = std::max(std::max(zoom_w, zoom_h) / 4.F, 2.F); // round to 25% steps, >= 200%
// round to 25% steps, >= 200%
myTIAMinZoom = std::max(std::max(zoom_w, zoom_h) / 4.F, 2.F);
}
// The font used by the ROM launcher
const string& lf = myOSystem.settings().getString("launcherfont");
myLauncherFont = make_unique<GUI::Font>(getFontDesc(lf)); // 8x13
}
@ -215,7 +217,7 @@ FBInitStatus FrameBuffer::createDisplay(const string& title, BufferType type,
++myInitializedCount;
myScreenTitle = title;
// In HiDPI mode, all created displays must be scaled by 2x
// In HiDPI mode, all created displays must be scaled appropriately
if(honourHiDPI && hidpiEnabled())
{
width *= hidpiScaleFactor();
@ -232,7 +234,6 @@ FBInitStatus FrameBuffer::createDisplay(const string& title, BufferType type,
// If the WINDOWED_SUPPORT macro is defined, we treat the system as the
// former type; if not, as the latter type
bool useFullscreen = false;
#ifdef WINDOWED_SUPPORT
// We assume that a desktop of at least minimum acceptable size means that
// we're running on a 'large' system, and the window size requirements
@ -241,40 +242,37 @@ FBInitStatus FrameBuffer::createDisplay(const string& title, BufferType type,
if(myDesktopSize.w < FBMinimum::Width && myDesktopSize.h < FBMinimum::Height &&
(myDesktopSize.w < width || myDesktopSize.h < height))
return FBInitStatus::FailTooLarge;
useFullscreen = myOSystem.settings().getBool("fullscreen");
#else
// Make sure this mode is even possible
// We only really need to worry about it in non-windowed environments,
// where requesting a window that's too large will probably cause a crash
if(myDesktopSize.w < width || myDesktopSize.h < height)
return FBInitStatus::FailTooLarge;
useFullscreen = true;
#endif
// Set the available video modes for this framebuffer
setAvailableVidModes(width, height);
// Initialize video mode handler, so it can know what video modes are
// appropriate for this framebuffer
myVidModeHandler.setImageSize(Common::Size(width, height));
// Initialize video subsystem (make sure we get a valid mode)
string pre_about = about();
const FrameBuffer::VideoMode& mode = getSavedVidMode(useFullscreen);
if(width <= mode.screen.w && height <= mode.screen.h)
myActiveVidMode = buildVideoMode();
if(width <= myActiveVidMode.screen.w && height <= myActiveVidMode.screen.h)
{
// Changing the video mode can take some time, during which the last
// sound played may get 'stuck'
// So we mute the sound until the operation completes
bool oldMuteState = myOSystem.sound().mute(true);
if(setVideoMode(myScreenTitle, mode))
if(activateVideoMode(myScreenTitle, myActiveVidMode))
{
myImageRect = mode.image;
myScreenSize = mode.screen;
myScreenRect = Common::Rect(mode.screen);
myImageRect = myActiveVidMode.image;
myScreenSize = myActiveVidMode.screen;
myScreenRect = Common::Rect(myActiveVidMode.screen);
// Inform TIA surface about new mode
if(myOSystem.eventHandler().state() != EventHandlerState::LAUNCHER &&
myOSystem.eventHandler().state() != EventHandlerState::DEBUGGER)
myTIASurface->initialize(myOSystem.console(), mode);
myTIASurface->initialize(myOSystem.console(), myActiveVidMode);
// Did we get the requested fullscreen state?
myOSystem.settings().setValue("fullscreen", fullScreen());
@ -835,8 +833,9 @@ void FrameBuffer::setPauseDelay()
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
shared_ptr<FBSurface> FrameBuffer::allocateSurface(int w, int h, ScalingInterpolation interpolation,
const uInt32* data)
shared_ptr<FBSurface> FrameBuffer::allocateSurface(
int w, int h, ScalingInterpolation interpolation, const uInt32* data
)
{
// Add new surface to the list
mySurfaceList.push_back(createSurface(w, h, interpolation, data));
@ -1011,17 +1010,18 @@ void FrameBuffer::setFullscreen(bool enable)
// So we mute the sound until the operation completes
bool oldMuteState = myOSystem.sound().mute(true);
const VideoMode& mode = getSavedVidMode(enable);
if(setVideoMode(myScreenTitle, mode))
myOSystem.settings().setValue("fullscreen", enable);
myActiveVidMode = buildVideoMode();
if(activateVideoMode(myScreenTitle, myActiveVidMode))
{
myImageRect = mode.image;
myScreenSize = mode.screen;
myScreenRect = Common::Rect(mode.screen);
myImageRect = myActiveVidMode.image;
myScreenSize = myActiveVidMode.screen;
myScreenRect = Common::Rect(myActiveVidMode.screen);
// Inform TIA surface about new mode
if(myOSystem.eventHandler().state() != EventHandlerState::LAUNCHER &&
myOSystem.eventHandler().state() != EventHandlerState::DEBUGGER)
myTIASurface->initialize(myOSystem.console(), mode);
myTIASurface->initialize(myOSystem.console(), myActiveVidMode);
// Did we get the requested fullscreen state?
myOSystem.settings().setValue("fullscreen", fullScreen());
@ -1035,7 +1035,7 @@ void FrameBuffer::setFullscreen(bool enable)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::toggleFullscreen(bool toggle)
{
switch (myOSystem.eventHandler().state())
switch(myOSystem.eventHandler().state())
{
case EventHandlerState::LAUNCHER:
case EventHandlerState::EMULATION:
@ -1043,20 +1043,17 @@ void FrameBuffer::toggleFullscreen(bool toggle)
case EventHandlerState::DEBUGGER:
{
const bool isFullscreen = toggle ? !fullScreen() : fullScreen();
setFullscreen(isFullscreen);
if (myBufferType != BufferType::Launcher)
if(myBufferType != BufferType::Launcher)
{
ostringstream msg;
const VideoMode& mode = getSavedVidMode(isFullscreen);
msg << "Fullscreen ";
if(isFullscreen)
msg << "enabled (" << refreshRate() << " Hz, ";
else
msg << "disabled (";
msg << "Zoom " << mode.zoom * 100 << "%)";
msg << "Zoom " << myActiveVidMode.zoom * 100 << "%)";
showMessage(msg.str());
}
@ -1122,7 +1119,7 @@ void FrameBuffer::changeOverscan(int direction)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::selectVidMode(int direction)
void FrameBuffer::switchVideoMode(int direction)
{
EventHandlerState state = myOSystem.eventHandler().state();
bool tiaMode = (state != EventHandlerState::DEBUGGER &&
@ -1132,10 +1129,27 @@ void FrameBuffer::selectVidMode(int direction)
if(!tiaMode)
return;
if(direction == +1)
myCurrentModeList->next();
else if(direction == -1)
myCurrentModeList->previous();
if(!fullScreen())
{
// Windowed TIA modes support variable zoom levels
float zoom = myOSystem.settings().getFloat("tia.zoom");
if(direction == +1) zoom += ZOOM_STEPS;
else if(direction == -1) zoom -= ZOOM_STEPS;
// Make sure the level is within the allowable desktop size
zoom = BSPF::clampw(zoom, supportedTIAMinZoom(), myTIAMaxZoom);
myOSystem.settings().setValue("tia.zoom", zoom);
}
else
{
// In fullscreen mode, there are only two modes, so direction
// is irrelevant
if(direction == +1 || direction == -1)
{
bool stretch = myOSystem.settings().getBool("tia.fs_stretch");
myOSystem.settings().setValue("tia.fs_stretch", !stretch);
}
}
saveCurrentWindowPosition();
@ -1144,34 +1158,75 @@ void FrameBuffer::selectVidMode(int direction)
// So we mute the sound until the operation completes
bool oldMuteState = myOSystem.sound().mute(true);
const VideoMode& mode = myCurrentModeList->current();
if(setVideoMode(myScreenTitle, mode))
myActiveVidMode = buildVideoMode();
if(activateVideoMode(myScreenTitle, myActiveVidMode))
{
myImageRect = mode.image;
myScreenSize = mode.screen;
myScreenRect = Common::Rect(mode.screen);
myImageRect = myActiveVidMode.image;
myScreenSize = myActiveVidMode.screen;
myScreenRect = Common::Rect(myActiveVidMode.screen);
// Inform TIA surface about new mode
myTIASurface->initialize(myOSystem.console(), mode);
myTIASurface->initialize(myOSystem.console(), myActiveVidMode);
resetSurfaces();
if(fullScreen())
showMessage(mode.description);
showMessage(myActiveVidMode.description);
else
showMessage("Zoom", mode.description, mode.zoom, supportedTIAMinZoom(), myTIAMaxZoom);
showMessage("Zoom", myActiveVidMode.description, myActiveVidMode.zoom,
supportedTIAMinZoom(), myTIAMaxZoom);
myOSystem.sound().mute(oldMuteState);
// Error check: were the settings applied as requested?
if(fullScreen())
myOSystem.settings().setValue("tia.fs_stretch",
mode.stretch == VideoMode::Stretch::Fill);
myActiveVidMode.stretch == VideoModeHandler::Mode::Stretch::Fill);
else
myOSystem.settings().setValue("tia.zoom", mode.zoom);
myOSystem.settings().setValue("tia.zoom", myActiveVidMode.zoom);
return;
}
myOSystem.sound().mute(oldMuteState);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const VideoModeHandler::Mode& FrameBuffer::buildVideoMode()
{
// Update display size, in case windowed/fullscreen mode has changed
const Settings& s = myOSystem.settings();
if(s.getBool("fullscreen"))
{
Int32 fsIndex = std::max(getCurrentDisplayIndex(), 0);
myVidModeHandler.setDisplaySize(myFullscreenDisplays[fsIndex], fsIndex);
}
else
myVidModeHandler.setDisplaySize(myAbsDesktopSize);
// And now build the new mode based on current settings
const bool tiaMode =
myOSystem.eventHandler().state() != EventHandlerState::DEBUGGER &&
myOSystem.eventHandler().state() != EventHandlerState::LAUNCHER;
return myVidModeHandler.buildMode(s, tiaMode);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
float FrameBuffer::maxWindowZoom(uInt32 baseWidth, uInt32 baseHeight) const
{
float multiplier = 1;
for(;;)
{
// Figure out the zoomed size of the window
uInt32 width = baseWidth * multiplier;
uInt32 height = baseHeight * multiplier;
if((width > myAbsDesktopSize.w) || (height > myAbsDesktopSize.h))
break;
multiplier += ZOOM_STEPS;
}
return multiplier > 1 ? multiplier - ZOOM_STEPS : 1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::setCursorState()
{
@ -1234,296 +1289,6 @@ void FrameBuffer::toggleGrabMouse()
: "Grab mouse not allowed while cursor shown");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
float FrameBuffer::maxZoomForScreen(uInt32 baseWidth, uInt32 baseHeight,
uInt32 screenWidth, uInt32 screenHeight) const
{
float multiplier = 1;
for(;;)
{
// Figure out the zoomed size of the window
uInt32 width = baseWidth * multiplier;
uInt32 height = baseHeight * multiplier;
if((width > screenWidth) || (height > screenHeight))
break;
multiplier += ZOOM_STEPS;
}
return multiplier > 1 ? multiplier - ZOOM_STEPS : 1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::setAvailableVidModes(uInt32 baseWidth, uInt32 baseHeight)
{
myWindowedModeList.clear();
for(auto& mode: myFullscreenModeLists)
mode.clear();
for(size_t i = myFullscreenModeLists.size(); i < myFullscreenDisplays.size(); ++i)
myFullscreenModeLists.emplace_back(VideoModeList());
// Check if zooming is allowed for this state (currently only allowed
// for TIA screens)
EventHandlerState state = myOSystem.eventHandler().state();
bool tiaMode = (state != EventHandlerState::DEBUGGER &&
state != EventHandlerState::LAUNCHER);
float overscan = 1 - myOSystem.settings().getInt("tia.fs_overscan") / 100.0;
// TIA mode allows zooming at integral factors in windowed modes,
// and also non-integral factors in fullscreen mode
if(tiaMode)
{
// TIA windowed modes
float minZoom = supportedTIAMinZoom();
myTIAMaxZoom = maxZoomForScreen(baseWidth, baseHeight,
myAbsDesktopSize.w, myAbsDesktopSize.h);
// Determine all zoom levels
for(float zoom = minZoom; zoom <= myTIAMaxZoom; zoom += ZOOM_STEPS)
{
ostringstream desc;
desc << (zoom * 100) << "%";
VideoMode mode(baseWidth*zoom, baseHeight*zoom, baseWidth*zoom, baseHeight*zoom,
VideoMode::Stretch::Fill, 1.0, desc.str(), zoom);
myWindowedModeList.add(mode);
}
// TIA fullscreen mode
for(uInt32 i = 0; i < myFullscreenDisplays.size(); ++i)
{
myTIAMaxZoom = maxZoomForScreen(baseWidth, baseHeight,
myFullscreenDisplays[i].w * overscan,
myFullscreenDisplays[i].h * overscan);
// Add both normal aspect and filled modes
// It's easier to define them both now, and simply switch between
// them when necessary
VideoMode mode1(baseWidth * myTIAMaxZoom, baseHeight * myTIAMaxZoom,
myFullscreenDisplays[i].w, myFullscreenDisplays[i].h,
VideoMode::Stretch::Preserve, overscan,
"Fullscreen: Preserve aspect, no stretch", myTIAMaxZoom, i);
myFullscreenModeLists[i].add(mode1);
VideoMode mode2(baseWidth * myTIAMaxZoom, baseHeight * myTIAMaxZoom,
myFullscreenDisplays[i].w, myFullscreenDisplays[i].h,
VideoMode::Stretch::Fill, overscan,
"Fullscreen: Ignore aspect, full stretch", myTIAMaxZoom, i);
myFullscreenModeLists[i].add(mode2);
}
}
else // UI mode
{
// Windowed and fullscreen mode differ only in screen size
myWindowedModeList.add(
VideoMode(baseWidth, baseHeight, baseWidth, baseHeight,
VideoMode::Stretch::None)
);
for(uInt32 i = 0; i < myFullscreenDisplays.size(); ++i)
{
myFullscreenModeLists[i].add(
VideoMode(baseWidth, baseHeight,
myFullscreenDisplays[i].w, myFullscreenDisplays[i].h,
VideoMode::Stretch::None, 1.0, "", 1, i)
);
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const FrameBuffer::VideoMode& FrameBuffer::getSavedVidMode(bool fullscreen)
{
if(fullscreen)
{
Int32 i = getCurrentDisplayIndex();
if(i < 0)
{
// default to the first display
i = 0;
}
myCurrentModeList = &myFullscreenModeLists[i];
}
else
myCurrentModeList = &myWindowedModeList;
// Now select the best resolution depending on the state
// UI modes (launcher and debugger) have only one supported resolution
// so the 'current' one is the only valid one
EventHandlerState state = myOSystem.eventHandler().state();
if(state == EventHandlerState::DEBUGGER || state == EventHandlerState::LAUNCHER)
myCurrentModeList->setByZoom(1);
else // TIA mode
{
if(fullscreen)
myCurrentModeList->setByStretch(myOSystem.settings().getBool("tia.fs_stretch")
? VideoMode::Stretch::Fill : VideoMode::Stretch::Preserve);
else
myCurrentModeList->setByZoom(myOSystem.settings().getFloat("tia.zoom"));
}
return myCurrentModeList->current();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
// VideoMode implementation
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FrameBuffer::VideoMode::VideoMode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh,
Stretch smode, float overscan, const string& desc,
float zoomLevel, Int32 fsindex)
: stretch(smode),
description(desc),
zoom(zoomLevel),
fsIndex(fsindex)
{
// First set default size and positioning
sw = std::max(sw, TIAConstants::viewableWidth);
sh = std::max(sh, TIAConstants::viewableHeight);
iw = std::min(iw, sw);
ih = std::min(ih, sh);
int ix = (sw - iw) >> 1;
int iy = (sh - ih) >> 1;
image = Common::Rect(ix, iy, ix+iw, iy+ih);
screen = Common::Size(sw, sh);
// Now resize based on windowed/fullscreen mode and stretch factor
iw = image.w();
ih = image.h();
if(fsIndex != -1)
{
switch(stretch)
{
case Stretch::Preserve:
{
float stretchFactor = 1.0;
float scaleX = float(iw) / screen.w;
float scaleY = float(ih) / screen.h;
// Scale to all available space, keep aspect correct
if(scaleX > scaleY)
stretchFactor = float(screen.w) / iw;
else
stretchFactor = float(screen.h) / ih;
iw = uInt32(stretchFactor * iw) * overscan;
ih = uInt32(stretchFactor * ih) * overscan;
break;
}
case Stretch::Fill:
// Scale to all available space
iw = screen.w * overscan;
ih = screen.h * overscan;
break;
case Stretch::None:
// Don't do any scaling at all, but obey overscan
iw = std::min(iw, screen.w) * overscan;
ih = std::min(ih, screen.h) * overscan;
break;
}
}
else
{
// In windowed mode, currently the size is scaled to the screen
// TODO - this may be updated if/when we allow variable-sized windows
switch(stretch)
{
case Stretch::Preserve:
case Stretch::Fill:
screen.w = iw;
screen.h = ih;
break;
case Stretch::None:
break; // Do not change image or screen rects whatsoever
}
}
// Now re-calculate the dimensions
iw = std::min(iw, screen.w);
ih = std::min(ih, screen.h);
image.moveTo((screen.w - iw) >> 1, (screen.h - ih) >> 1);
image.setWidth(iw);
image.setHeight(ih);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
// VideoModeList implementation
//
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::VideoModeList::add(const VideoMode& mode)
{
myModeList.emplace_back(mode);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::VideoModeList::clear()
{
myModeList.clear();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool FrameBuffer::VideoModeList::empty() const
{
return myModeList.empty();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 FrameBuffer::VideoModeList::size() const
{
return uInt32(myModeList.size());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::VideoModeList::previous()
{
--myIdx;
if(myIdx < 0) myIdx = int(myModeList.size()) - 1;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const FrameBuffer::VideoMode& FrameBuffer::VideoModeList::current() const
{
return myModeList[myIdx];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::VideoModeList::next()
{
myIdx = (myIdx + 1) % myModeList.size();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::VideoModeList::setByZoom(float zoom)
{
for(uInt32 i = 0; i < myModeList.size(); ++i)
{
if(myModeList[i].zoom == zoom)
{
myIdx = i;
return;
}
}
myIdx = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void FrameBuffer::VideoModeList::setByStretch(FrameBuffer::VideoMode::Stretch stretch)
{
for(uInt32 i = 0; i < myModeList.size(); ++i)
{
if(myModeList[i].stretch == stretch)
{
myIdx = i;
return;
}
}
myIdx = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/*
Palette is defined as follows:

View File

@ -35,6 +35,7 @@ class TIASurface;
#include "TIAConstants.hxx"
#include "FrameBufferConstants.hxx"
#include "EventHandlerConstants.hxx"
#include "VideoModeHandler.hxx"
#include "bspf.hxx"
/**
@ -50,38 +51,7 @@ class TIASurface;
class FrameBuffer
{
public:
// Contains all relevant info for the dimensions of a video screen
// Also takes care of the case when the image should be 'centered'
// within the given screen:
// 'image' is the image dimensions into the screen
// 'screen' are the dimensions of the screen itself
struct VideoMode
{
enum class Stretch { Preserve, Fill, None };
Common::Rect image;
Common::Size screen;
Stretch stretch{VideoMode::Stretch::None};
string description;
float zoom{1.F};
Int32 fsIndex{-1};
VideoMode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh,
Stretch smode, float overscan = 1.F,
const string& desc = "", float zoomLevel = 1, Int32 fsindex = -1);
friend ostream& operator<<(ostream& os, const VideoMode& vm)
{
os << "image=" << vm.image << " screen=" << vm.screen
<< " stretch=" << (vm.stretch == Stretch::Preserve ? "preserve" :
vm.stretch == Stretch::Fill ? "fill" : "none")
<< " desc=" << vm.description << " zoom=" << vm.zoom
<< " fsIndex= " << vm.fsIndex;
return os;
}
};
struct DisplayMode
struct DisplayMode // FIXME - is this needed?
{
uInt32 display;
Common::Size size;
@ -259,11 +229,6 @@ class FrameBuffer
*/
TIASurface& tiaSurface() const { return *myTIASurface; }
/**
Enables/disables fullscreen mode.
*/
void setFullscreen(bool enable);
/**
Toggles between fullscreen and window mode.
*/
@ -285,15 +250,15 @@ class FrameBuffer
/**
This method is called when the user wants to switch to the next
available video mode. In windowed mode, this typically means going to
the next/previous zoom level. In fullscreen mode, this typically means
switching between normal aspect and fully filling the screen.
available TIA video mode. In windowed mode, this typically means going
to the next/previous zoom level. In fullscreen mode, this typically
means switching between normal aspect and fully filling the screen.
direction = -1 means go to the next lower video mode
direction = +1 means go to the next higher video mode
@param direction +1 indicates increase, -1 indicates decrease.
*/
void selectVidMode(int direction = +1);
void switchVideoMode(int direction = +1);
/**
Sets the state of the cursor (hidden or grabbed) based on the
@ -453,7 +418,6 @@ class FrameBuffer
virtual int scaleY(int y) const { return y; }
protected:
/**
This method is called to query and initialize the video hardware
for desktop and fullscreen resolution information. Since several
@ -475,8 +439,8 @@ class FrameBuffer
@return False on any errors, else true
*/
virtual bool setVideoMode(const string& title,
const FrameBuffer::VideoMode& mode) = 0;
virtual bool activateVideoMode(const string& title,
const VideoModeHandler::Mode& mode) = 0;
/**
This method is called to create a surface with the given attributes.
@ -547,6 +511,28 @@ class FrameBuffer
*/
bool drawMessage();
// Draws the frame stats overlay
void drawFrameStats(float framesPerSecond);
/**
Build an applicable video mode based on the current settings in
effect, whether TIA mode is active, etc.
Note that this only creates the video mode definition itself;
to apply it, we need to call 'activateVideoMode()'.
*/
const VideoModeHandler::Mode& buildVideoMode();
/**
Calculate the maximum level by which the base window can be zoomed and
still fit in the desktop screen.
*/
float maxWindowZoom(uInt32 baseWidth, uInt32 baseHeight) const;
/**
Enables/disables fullscreen mode.
*/
void setFullscreen(bool enable);
/**
Frees and reloads all surfaces that the framebuffer knows about.
*/
@ -559,60 +545,6 @@ class FrameBuffer
void setupFonts();
#endif
/**
Calculate the maximum level by which the base window can be zoomed and
still fit in the given screen dimensions.
*/
float maxZoomForScreen(uInt32 baseWidth, uInt32 baseHeight,
uInt32 screenWidth, uInt32 screenHeight) const;
/**
Set all possible video modes (both windowed and fullscreen) available for
this framebuffer based on given image dimensions and maximum window size.
*/
void setAvailableVidModes(uInt32 basewidth, uInt32 baseheight);
/**
Returns an appropriate video mode based on the current eventhandler
state, taking into account the maximum size of the window.
@param fullscreen Whether to use a windowed or fullscreen mode
@return A valid VideoMode for this framebuffer
*/
const FrameBuffer::VideoMode& getSavedVidMode(bool fullscreen);
private:
/**
This class implements an iterator around an array of VideoMode objects.
*/
class VideoModeList
{
public:
void add(const FrameBuffer::VideoMode& mode);
void clear();
bool empty() const;
uInt32 size() const;
void previous();
const FrameBuffer::VideoMode& current() const;
void next();
void setByZoom(float zoom);
void setByStretch(FrameBuffer::VideoMode::Stretch stretch);
friend ostream& operator<<(ostream& os, const VideoModeList& l)
{
for(const auto& vm: l.myModeList)
os << "-----\n" << vm << endl << "-----\n";
return os;
}
private:
vector<FrameBuffer::VideoMode> myModeList;
int myIdx{-1};
};
protected:
// Title of the main window/screen
string myScreenTitle;
@ -629,9 +561,6 @@ class FrameBuffer
vector<Common::Size> myFullscreenDisplays;
private:
// Draws the frame stats overlay
void drawFrameStats(float framesPerSecond);
// Indicates the number of times the framebuffer was initialized
uInt32 myInitializedCount{0};
@ -659,6 +588,10 @@ class FrameBuffer
// Supported renderers
VariantList myRenderers;
// The VideoModeHandler class takes responsibility for all video mode functionality
VideoModeHandler myVidModeHandler;
VideoModeHandler::Mode myActiveVidMode;
#ifdef GUI_SUPPORT
// The font object to use for the normal in-game GUI
unique_ptr<GUI::Font> myFont;
@ -699,11 +632,6 @@ class FrameBuffer
bool myHiDPIAllowed{false};
bool myHiDPIEnabled{false};
// The list of all available video modes for this framebuffer
VideoModeList* myCurrentModeList{nullptr};
VideoModeList myWindowedModeList;
vector<VideoModeList> myFullscreenModeLists;
// Minimum TIA zoom level that can be used for this framebuffer
float myTIAMinZoom{2.F};
// Maximum TIA zoom level that can be used for this framebuffer
@ -714,7 +642,7 @@ class FrameBuffer
FullPaletteArray myFullPalette;
// Holds UI palette data (for each variation)
static UIPaletteArray ourStandardUIPalette, ourClassicUIPalette,
static UIPaletteArray ourStandardUIPalette, ourClassicUIPalette,
ourLightUIPalette, ourDarkUIPalette;
private:

View File

@ -91,7 +91,7 @@ TIASurface::~TIASurface()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIASurface::initialize(const Console& console,
const FrameBuffer::VideoMode& mode)
const VideoModeHandler::Mode& mode)
{
myTIA = &(console.tia());

View File

@ -54,7 +54,7 @@ class TIASurface
/**
Set the TIA object, which is needed for actually rendering the TIA image.
*/
void initialize(const Console& console, const FrameBuffer::VideoMode& mode);
void initialize(const Console& console, const VideoModeHandler::Mode& mode);
/**
Set the palette for TIA rendering. This currently consists of two

View File

@ -147,7 +147,8 @@ class FrameBufferLIBRETRO : public FrameBuffer
@return False on any errors, else true
*/
bool setVideoMode(const string& title, const VideoMode& mode) override { return true; }
bool activateVideoMode(const string& title,
const VideoModeHandler::Mode& mode) override { return true; }
/**
This method is called to create a surface with the given attributes.

View File

@ -33,6 +33,7 @@ SOURCES_CXX := \
$(CORE_DIR)/common/StaggeredLogger.cxx \
$(CORE_DIR)/common/StateManager.cxx \
$(CORE_DIR)/common/TimerManager.cxx \
$(CORE_DIR)/common/VideoModeHandler.cxx \
$(CORE_DIR)/common/tv_filters/AtariNTSC.cxx \
$(CORE_DIR)/common/tv_filters/NTSCFilter.cxx \
$(CORE_DIR)/emucore/AtariVox.cxx \