mirror of https://github.com/stella-emu/stella.git
refactored bezel code
added variable bezel window support
This commit is contained in:
parent
e0374fe681
commit
c31ab36afe
|
@ -24,7 +24,7 @@
|
|||
|
||||
* Added 2nd UI theme and hotkey for toggling UI theme.
|
||||
|
||||
* Adde bezel support.
|
||||
* Added bezel support. (TODO: Doc)
|
||||
|
||||
* Added optional type format detection based on colors used.
|
||||
|
||||
|
|
|
@ -101,6 +101,18 @@ class FBBackendSDL2 : public FBBackend
|
|||
FORCE_INLINE void getRGB(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b) const override
|
||||
{ SDL_GetRGB(pixel, myPixelFormat, r, g, b); }
|
||||
|
||||
/**
|
||||
This method is called to retrieve the R/G/B/A data from the given pixel.
|
||||
|
||||
@param pixel The pixel containing R/G/B data
|
||||
@param r The red component of the color
|
||||
@param g The green component of the color
|
||||
@param b The blue component of the color
|
||||
@param a The alpha component of the color.
|
||||
*/
|
||||
FORCE_INLINE void getRGBA(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b, uInt8* a) const override
|
||||
{ SDL_GetRGBA(pixel, myPixelFormat, r, g, b, a); }
|
||||
|
||||
/**
|
||||
This method is called to map a given R/G/B triple to the screen palette.
|
||||
|
||||
|
@ -111,6 +123,14 @@ class FBBackendSDL2 : public FBBackend
|
|||
inline uInt32 mapRGB(uInt8 r, uInt8 g, uInt8 b) const override
|
||||
{ return SDL_MapRGB(myPixelFormat, r, g, b); }
|
||||
|
||||
/**
|
||||
This method is called to map a given R/G/B/A triple to the screen palette.
|
||||
|
||||
@param r The red component of the color.
|
||||
@param g The green component of the color.
|
||||
@param b The blue component of the color.
|
||||
@param a The alpha component of the color.
|
||||
*/
|
||||
inline uInt32 mapRGBA(uInt8 r, uInt8 g, uInt8 b, uInt8 a) const override
|
||||
{ return SDL_MapRGBA(myPixelFormat, r, g, b, a); }
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
//============================================================================
|
||||
|
||||
#include "Settings.hxx"
|
||||
#include "Bezel.hxx"
|
||||
|
||||
#include "VideoModeHandler.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -33,7 +35,7 @@ void VideoModeHandler::setDisplaySize(const Common::Size& display, Int32 fsIndex
|
|||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const VideoModeHandler::Mode&
|
||||
VideoModeHandler::buildMode(const Settings& settings, bool inTIAMode, bool showBezel)
|
||||
VideoModeHandler::buildMode(const Settings& settings, bool inTIAMode, Bezel::Info bezelInfo)
|
||||
{
|
||||
const bool windowedRequested = myFSIndex == -1;
|
||||
|
||||
|
@ -42,25 +44,24 @@ const VideoModeHandler::Mode&
|
|||
{
|
||||
if(windowedRequested)
|
||||
{
|
||||
const float zoom = settings.getFloat("tia.zoom");
|
||||
const double 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, showBezel);
|
||||
myMode = Mode(myImage.w, myImage.h,
|
||||
Mode::Stretch::Fill, myFSIndex,
|
||||
desc.str(), zoom, bezelInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
const float overscan = 1 - settings.getInt("tia.fs_overscan") / 100.0;
|
||||
const double overscan = 1 - settings.getInt("tia.fs_overscan") / 100.0;
|
||||
|
||||
// First calculate maximum zoom that keeps aspect ratio
|
||||
// Note: We are assuming a 16:9 bezel image here
|
||||
const float bezelScaleW = showBezel ? (16.F / 9.F) / (4.F / 3.F) : 1;
|
||||
const float scaleX = myImage.w / (myDisplay.w / bezelScaleW),
|
||||
scaleY = static_cast<float>(myImage.h) / myDisplay.h;
|
||||
float zoom = 1.F / std::max(scaleX, scaleY);
|
||||
const double scaleX = static_cast<double>(myImage.w) / (myDisplay.w / bezelInfo.ratioW()),
|
||||
scaleY = static_cast<double>(myImage.h) / (myDisplay.h / bezelInfo.ratioH());
|
||||
double zoom = 1. / std::max(scaleX, scaleY);
|
||||
|
||||
// When aspect ratio correction is off, we want pixel-exact images,
|
||||
// so we default to integer zooming
|
||||
|
@ -69,20 +70,19 @@ const VideoModeHandler::Mode&
|
|||
|
||||
if(!settings.getBool("tia.fs_stretch")) // preserve aspect, use all space
|
||||
{
|
||||
myMode = Mode(myImage.w * zoom, myImage.h * zoom,
|
||||
myMode = Mode(myImage.w, myImage.h,
|
||||
myDisplay.w, myDisplay.h,
|
||||
Mode::Stretch::Preserve, myFSIndex,
|
||||
"Fullscreen: Preserve aspect, no stretch",
|
||||
zoom, overscan,
|
||||
showBezel, showBezel ? settings.getInt("bezel.border") : 0);
|
||||
zoom, overscan, bezelInfo);
|
||||
}
|
||||
else // ignore aspect, use all space
|
||||
{
|
||||
myMode = Mode(myImage.w * zoom, myImage.h * zoom,
|
||||
myMode = Mode(myImage.w, myImage.h,
|
||||
myDisplay.w, myDisplay.h,
|
||||
Mode::Stretch::Fill, myFSIndex,
|
||||
"Fullscreen: Ignore aspect, full stretch",
|
||||
zoom, overscan, showBezel);
|
||||
zoom, overscan, bezelInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,54 +94,48 @@ const VideoModeHandler::Mode&
|
|||
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, string_view desc,
|
||||
float zoomLevel,
|
||||
bool showBezel, Int32 bezelBorder)
|
||||
: Mode(iw, ih, iw, ih, smode, fsindex, desc, zoomLevel, 1.F, showBezel, bezelBorder)
|
||||
double zoomLevel, Bezel::Info bezelInfo)
|
||||
: Mode(iw, ih, iw, ih, smode, fsindex, desc, zoomLevel, 1., bezelInfo)
|
||||
{
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh,
|
||||
Stretch smode, Int32 fsindex, string_view desc,
|
||||
float zoomLevel, float overscan,
|
||||
bool showBezel, Int32 bezelBorder)
|
||||
double zoomLevel, double overscan, Bezel::Info bezelInfo)
|
||||
: screenS{sw, sh},
|
||||
stretch{smode},
|
||||
description{desc},
|
||||
zoom{zoomLevel},
|
||||
zoom{zoomLevel}, //hZoom{zoomLevel}, vZoom{zoomLevel},
|
||||
fsIndex{fsindex}
|
||||
{
|
||||
// Note: We are assuming a 16:9 bezel image here
|
||||
const float bezelScaleW = showBezel ? (16.F / 9.F) / (4.F / 3.F) : 1;
|
||||
// Now resize based on windowed/fullscreen mode and stretch factor
|
||||
if(fsIndex != -1) // fullscreen mode
|
||||
{
|
||||
switch(stretch)
|
||||
{
|
||||
case Stretch::Preserve:
|
||||
iw = (iw - bezelBorder * 4.F / 3.F * zoomLevel) * overscan;
|
||||
ih = (ih - bezelBorder * zoomLevel) * overscan;
|
||||
//iw *= overscan;
|
||||
//ih *= overscan;
|
||||
iw = std::round(iw * overscan * zoomLevel);
|
||||
ih = std::round(ih * overscan * zoomLevel);
|
||||
break;
|
||||
|
||||
case Stretch::Fill:
|
||||
{
|
||||
// Scale to all available space
|
||||
iw = screenS.w * (overscan / bezelScaleW);
|
||||
ih = screenS.h * overscan;
|
||||
iw = std::round(screenS.w * overscan / bezelInfo.ratioW());
|
||||
ih = std::round(screenS.h * overscan / bezelInfo.ratioH());
|
||||
break;
|
||||
|
||||
}
|
||||
case Stretch::None: // UI Mode
|
||||
// Don't do any scaling at all
|
||||
iw = std::min(iw, screenS.w) * overscan;
|
||||
ih = std::min(ih, screenS.h) * overscan;
|
||||
iw = std::min(static_cast<uInt32>(iw * zoomLevel), screenS.w) * overscan;
|
||||
ih = std::min(static_cast<uInt32>(ih * zoomLevel), screenS.h) * overscan;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -153,8 +147,10 @@ VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh,
|
|||
{
|
||||
case Stretch::Preserve:
|
||||
case Stretch::Fill:
|
||||
screenS.w = iw * bezelScaleW;
|
||||
screenS.h = ih;
|
||||
iw *= zoomLevel;
|
||||
ih *= zoomLevel;
|
||||
screenS.w = std::round(iw * bezelInfo.ratioW());
|
||||
screenS.h = std::round(ih * bezelInfo.ratioH());
|
||||
break;
|
||||
|
||||
case Stretch::None: // UI Mode
|
||||
|
@ -166,7 +162,17 @@ VideoModeHandler::Mode::Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh,
|
|||
iw = std::min(iw, screenS.w);
|
||||
ih = std::min(ih, screenS.h);
|
||||
|
||||
imageR.moveTo((screenS.w - iw) >> 1, (screenS.h - ih) >> 1);
|
||||
// Allow variable image positions in asymmetric bezels
|
||||
// (works in case of no bezel too)
|
||||
const uInt32 wx = bezelInfo.window().x() * iw / bezelInfo.window().w();
|
||||
const uInt32 wy = bezelInfo.window().y() * ih / bezelInfo.window().h();
|
||||
const uInt32 bezelW = std::min(screenS.w,
|
||||
static_cast<uInt32>(std::round(iw * bezelInfo.ratioW())));
|
||||
const uInt32 bezelH = std::min(screenS.h,
|
||||
static_cast<uInt32>(std::round(ih * bezelInfo.ratioH())));
|
||||
// Center image (no bezel) or move image relative to centered bezel
|
||||
imageR.moveTo(((screenS.w - bezelW) >> 1) + wx, ((screenS.h - bezelH) >> 1) + wy);
|
||||
|
||||
imageR.setWidth(iw);
|
||||
imageR.setHeight(ih);
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ class Settings;
|
|||
|
||||
#include "Rect.hxx"
|
||||
#include "bspf.hxx"
|
||||
#include "Bezel.hxx"
|
||||
|
||||
class VideoModeHandler
|
||||
{
|
||||
|
@ -38,35 +39,23 @@ class VideoModeHandler
|
|||
Fill, // Stretch to fill all available space
|
||||
None // No stretching (1x zoom)
|
||||
};
|
||||
struct BezelInfo
|
||||
{
|
||||
bool enabled{false};
|
||||
bool windowedMode{false};
|
||||
uInt32 topBorder{0};
|
||||
uInt32 bottomBorder{0};
|
||||
|
||||
BezelInfo() = default;
|
||||
BezelInfo(bool _enabled, bool _windowedMode, uInt32 _topBorder, uInt32 _bottomBorder)
|
||||
: enabled{_enabled}, windowedMode{_windowedMode},
|
||||
topBorder{_topBorder}, bottomBorder(_bottomBorder) { }
|
||||
};
|
||||
|
||||
Common::Rect imageR;
|
||||
Common::Rect screenR;
|
||||
Common::Size screenS;
|
||||
Stretch stretch{Mode::Stretch::None};
|
||||
string description;
|
||||
float zoom{1.F};
|
||||
double zoom{1.};
|
||||
Int32 fsIndex{-1}; // -1 indicates windowed mode
|
||||
|
||||
Mode() = default;
|
||||
Mode(uInt32 iw, uInt32 ih, uInt32 sw, uInt32 sh, Stretch smode,
|
||||
Int32 fsindex = -1, string_view desc = "",
|
||||
float zoomLevel = 1.F, float overscan = 1.F,
|
||||
bool showBezel = false, Int32 bezelBorder = 0);
|
||||
double zoomLevel = 1., double overscan = 1.,
|
||||
Bezel::Info bezelInfo = Bezel::Info());
|
||||
Mode(uInt32 iw, uInt32 ih, Stretch smode, Int32 fsindex = -1,
|
||||
string_view desc = "", float zoomLevel = 1.F,
|
||||
bool showBezel = false, Int32 bezelBorder = 0);
|
||||
string_view desc = "", double zoomLevel = 1.,
|
||||
Bezel::Info bezelInfo = Bezel::Info());
|
||||
|
||||
friend ostream& operator<<(ostream& os, const Mode& vm)
|
||||
{
|
||||
|
@ -108,8 +97,8 @@ class VideoModeHandler
|
|||
|
||||
@return A video mode based on the given criteria
|
||||
*/
|
||||
const VideoModeHandler::Mode& buildMode(const Settings& settings,
|
||||
bool inTIAMode, bool showBezel);
|
||||
const VideoModeHandler::Mode& buildMode(const Settings& settings, bool inTIAMode,
|
||||
Bezel::Info bezelInfo = Bezel::Info());
|
||||
|
||||
private:
|
||||
Common::Size myImage, myDisplay;
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
//============================================================================
|
||||
//
|
||||
// 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-2023 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 "OSystem.hxx"
|
||||
#include "Console.hxx"
|
||||
#include "EventHandler.hxx"
|
||||
#include "FBSurface.hxx"
|
||||
#include "PNGLibrary.hxx"
|
||||
|
||||
#include "Bezel.hxx"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Bezel::Bezel(OSystem& osystem)
|
||||
: myOSystem{osystem},
|
||||
myFB{osystem.frameBuffer()}
|
||||
{
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const string Bezel::getName(int& index) const
|
||||
{
|
||||
if(++index == 1)
|
||||
return myOSystem.console().properties().get(PropType::Bezel_Name);
|
||||
|
||||
// Try to generate bezel name from cart name
|
||||
const string& cartName = myOSystem.console().properties().get(PropType::Cart_Name);
|
||||
const size_t pos = cartName.find_first_of("(");
|
||||
if(index < 10 && pos != std::string::npos && pos > 0)
|
||||
{
|
||||
// The following suffixes are from "The Official No-Intro Convention",
|
||||
// covering all used combinations by "The Bezel Project" (except single ones)
|
||||
// (Unl) = unlicensed (Homebrews)
|
||||
const std::array<string, 8> suffixes = {
|
||||
" (USA)", " (USA) (Proto)", " (USA) (Unl)", " (USA) (Hack)",
|
||||
" (Europe)", " (Germany)", " (France) (Unl)", " (Australia)"
|
||||
};
|
||||
return cartName.substr(0, pos - 1) + suffixes[index - 2];
|
||||
}
|
||||
|
||||
if(index == 10)
|
||||
{
|
||||
index = -1;
|
||||
return "default";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 Bezel::borderSize(uInt32 x, uInt32 y, uInt32 size, Int32 step) const
|
||||
{
|
||||
uInt32 *pixels{nullptr}, pitch;
|
||||
uInt32 i;
|
||||
|
||||
mySurface->basePtr(pixels, pitch);
|
||||
pixels += x + y * pitch;
|
||||
|
||||
for(i = 0; i < size; ++i, pixels += step)
|
||||
{
|
||||
uInt8 r, g, b, a;
|
||||
|
||||
myFB.getRGBA(*pixels, &r, &g, &b, &a);
|
||||
if(a < 255)
|
||||
return i;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Bezel::load()
|
||||
{
|
||||
bool isValid = false;
|
||||
|
||||
#ifdef IMAGE_SUPPORT
|
||||
const bool isShown = myOSystem.eventHandler().inTIAMode() &&
|
||||
myOSystem.settings().getBool("bezel.show") &&
|
||||
(myFB.fullScreen() || myOSystem.settings().getBool("bezel.windowed"));
|
||||
|
||||
if(mySurface)
|
||||
myFB.deallocateSurface(mySurface);
|
||||
mySurface = nullptr;
|
||||
|
||||
if(isShown)
|
||||
{
|
||||
double aspectRatio = 1;
|
||||
|
||||
mySurface = myFB.allocateSurface(1, 1); // dummy size
|
||||
try
|
||||
{
|
||||
const string& path = myOSystem.bezelDir().getPath();
|
||||
string imageName;
|
||||
VariantList metaData;
|
||||
int index = 0;
|
||||
|
||||
do
|
||||
{
|
||||
const string& name = getName(index);
|
||||
if(name != EmptyString)
|
||||
{
|
||||
imageName = path + name + ".png";
|
||||
FSNode node(imageName);
|
||||
if(node.exists())
|
||||
{
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(index != -1);
|
||||
if(isValid)
|
||||
myOSystem.png().loadImage(imageName, *mySurface, &aspectRatio, metaData);
|
||||
}
|
||||
catch(const runtime_error&)
|
||||
{
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if(isValid)
|
||||
{
|
||||
const Settings& settings = myOSystem.settings();
|
||||
const Int32 w = mySurface->width();
|
||||
const Int32 h = mySurface->height();
|
||||
uInt32 top, bottom, left, right;
|
||||
|
||||
if(settings.getBool("bezel.autoborders"))
|
||||
{
|
||||
// Determine transparent window inside bezel image
|
||||
top = borderSize(w >> 1, 0, h, w);
|
||||
bottom = h - 1 - borderSize(w >> 1, h - 1, h, -w);
|
||||
left = borderSize(0, (bottom + top) >> 1, w, 1);
|
||||
right = w - 1 - borderSize(w - 1, (bottom + top) >> 1, w, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
left = std::min(w, settings.getInt("bezel.leftborder"));
|
||||
right = w - 1 - std::min(w, settings.getInt("bezel.rightborder"));
|
||||
top = std::min(h, settings.getInt("bezel.topborder"));
|
||||
bottom = h - 1 - std::min(h, settings.getInt("bezel.bottomborder"));
|
||||
}
|
||||
//cerr << right - left + 1 << " x " << bottom - top + 1 << " = "
|
||||
// << double(right - left + 1) / double(bottom - top + 1);
|
||||
myInfo = Info(Common::Size(w, h), Common::Rect(left, top, right, bottom));
|
||||
}
|
||||
else
|
||||
myInfo = Info();
|
||||
|
||||
return isValid;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Bezel::apply()
|
||||
{
|
||||
if(isShown())
|
||||
{
|
||||
const uInt32 bezelW =
|
||||
std::min(myFB.screenSize().w,
|
||||
static_cast<uInt32>(std::round(myFB.imageRect().w() * myInfo.ratioW())));
|
||||
const uInt32 bezelH =
|
||||
std::min(myFB.screenSize().h,
|
||||
static_cast<uInt32>(std::round(myFB.imageRect().h() * myInfo.ratioH())));
|
||||
|
||||
// Position and scale bezel
|
||||
mySurface->setDstSize(bezelW, bezelH);
|
||||
mySurface->setDstPos((myFB.screenSize().w - bezelW) / 2, // center
|
||||
(myFB.screenSize().h - bezelH) / 2);
|
||||
mySurface->setScalingInterpolation(ScalingInterpolation::sharp);
|
||||
// Note: Variable bezel window positions are handled in VideoModeHandler::Mode
|
||||
|
||||
// Enable blending to allow overlaying the bezel over the TIA output
|
||||
mySurface->attributes().blending = true;
|
||||
mySurface->attributes().blendalpha = 100;
|
||||
mySurface->applyAttributes();
|
||||
mySurface->setVisible(true);
|
||||
}
|
||||
else
|
||||
if(mySurface)
|
||||
mySurface->setVisible(false);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Bezel::render()
|
||||
{
|
||||
if(mySurface)
|
||||
mySurface->render();
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
//============================================================================
|
||||
//
|
||||
// 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-2023 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 BEZEL_HXX
|
||||
#define BEZEL_HXX
|
||||
|
||||
class OSystem;
|
||||
class FBSurface;
|
||||
class FrameBuffer;
|
||||
|
||||
#include "Rect.hxx"
|
||||
|
||||
/**
|
||||
This class handles the bezels.
|
||||
|
||||
Bezels are loaded using a file name which is either a bezel name property or
|
||||
is autogenerated from the cart name property. The bezels can be any size and
|
||||
their transparent emulation window can be at any position. The position of
|
||||
the window can be determined automatically.
|
||||
|
||||
+--------------------------------------+
|
||||
| | display.h
|
||||
+--------------------------------------+
|
||||
| |
|
||||
| +---------------+ |
|
||||
| | window | |
|
||||
| | | |
|
||||
| | tia.h * zoom | |
|
||||
| | | | bezel.h * zoom
|
||||
| | | |
|
||||
| +---------------+ |
|
||||
| |
|
||||
+--------------------------------------+ size
|
||||
| |
|
||||
+--------------------------------------+
|
||||
|
||||
The bezel and window sizes and their ratios are used for correct scaling.
|
||||
|
||||
@author Thomas Jentzsch
|
||||
*/
|
||||
|
||||
class Bezel
|
||||
{
|
||||
public:
|
||||
explicit Bezel(OSystem& osystem);
|
||||
~Bezel() = default;
|
||||
|
||||
struct Info
|
||||
{
|
||||
private:
|
||||
bool _isShown{false}; // Is bezel shown?
|
||||
Common::Size _size{1, 1}; // Bezel size
|
||||
Common::Rect _window{1, 1}; // Area of transparent TIA window inside bezel
|
||||
|
||||
public:
|
||||
explicit Info() = default;
|
||||
explicit Info(Common::Size size, Common::Rect window)
|
||||
: _isShown{true}, _size{size}, _window{window} { }
|
||||
|
||||
bool isShown() const { return _isShown; }
|
||||
Common::Size size() const { return _size; }
|
||||
Common::Rect window() const { return _window; }
|
||||
|
||||
// Ratios between bezel sizes and TIA window sizes
|
||||
double ratioW() const { return static_cast<double>(size().w) / window().w(); }
|
||||
double ratioH() const { return static_cast<double>(size().h) / window().h(); }
|
||||
};
|
||||
|
||||
// Structure access methods
|
||||
const Info& info() const { return myInfo; }
|
||||
bool isShown() const { return myInfo.isShown(); }
|
||||
Common::Size size() const { return myInfo.size(); }
|
||||
Common::Rect window() const { return myInfo.window(); }
|
||||
// Ratio between bezel size and TIA window size
|
||||
double ratioW() const { return myInfo.ratioW(); }
|
||||
double ratioH() const { return myInfo.ratioH(); }
|
||||
|
||||
/*
|
||||
Calculate size of a bezel border.
|
||||
*/
|
||||
uInt32 borderSize(uInt32 x, uInt32 y, uInt32 size, Int32 step) const;
|
||||
|
||||
/*
|
||||
Load the bezel.
|
||||
*/
|
||||
bool load();
|
||||
|
||||
/*
|
||||
Display scaled bezel.
|
||||
*/
|
||||
void apply();
|
||||
|
||||
/*
|
||||
Render bezel surface
|
||||
*/
|
||||
void render();
|
||||
|
||||
private:
|
||||
/*
|
||||
Generate bezel file name.
|
||||
*/
|
||||
const string getName(int& index) const;
|
||||
|
||||
private:
|
||||
// The parent system for the bezel
|
||||
OSystem& myOSystem;
|
||||
|
||||
// Pointer to the FrameBuffer object
|
||||
FrameBuffer& myFB;
|
||||
|
||||
// The bezel surface which blends over the TIA surface
|
||||
shared_ptr<FBSurface> mySurface;
|
||||
|
||||
// Bezel info structure
|
||||
Info myInfo;
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
Bezel() = delete;
|
||||
Bezel(const Bezel&) = delete;
|
||||
Bezel(Bezel&&) = delete;
|
||||
Bezel& operator=(const Bezel&) = delete;
|
||||
Bezel& operator=(Bezel&&) = delete;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -128,6 +128,17 @@ class FBBackend
|
|||
*/
|
||||
virtual void getRGB(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b) const = 0;
|
||||
|
||||
/**
|
||||
This method is called to retrieve the R/G/B/A data from the given pixel.
|
||||
|
||||
@param pixel The pixel containing R/G/B data
|
||||
@param r The red component of the color
|
||||
@param g The green component of the color
|
||||
@param b The blue component of the color
|
||||
@param a The alpha component of the color.
|
||||
*/
|
||||
virtual void getRGBA(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b, uInt8* a) const = 0;
|
||||
|
||||
/**
|
||||
This method is called to map a given R/G/B triple to the screen palette.
|
||||
|
||||
|
@ -143,6 +154,7 @@ class FBBackend
|
|||
@param r The red component of the color.
|
||||
@param g The green component of the color.
|
||||
@param b The blue component of the color.
|
||||
@param a The alpha component of the color.
|
||||
*/
|
||||
virtual uInt32 mapRGBA(uInt8 r, uInt8 g, uInt8 b, uInt8 a) const = 0;
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
#include "FBSurface.hxx"
|
||||
#include "TIASurface.hxx"
|
||||
#include "Bezel.hxx"
|
||||
#include "FrameBuffer.hxx"
|
||||
#include "PaletteHandler.hxx"
|
||||
#include "StateManager.hxx"
|
||||
|
@ -126,6 +127,8 @@ void FrameBuffer::initialize()
|
|||
|
||||
// Create a TIA surface; we need it for rendering TIA images
|
||||
myTIASurface = make_unique<TIASurface>(myOSystem);
|
||||
// Create a bezel surface for TIA overlays
|
||||
myBezel = make_unique<Bezel>(myOSystem);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -278,8 +281,10 @@ FBInitStatus FrameBuffer::createDisplay(string_view title, BufferType type,
|
|||
|
||||
if(myBufferType == BufferType::Emulator)
|
||||
{
|
||||
myBezel->load();
|
||||
|
||||
// Determine possible TIA windowed zoom levels
|
||||
const float currentTIAZoom = myOSystem.settings().getFloat("tia.zoom");
|
||||
const double currentTIAZoom = myOSystem.settings().getFloat("tia.zoom");
|
||||
myOSystem.settings().setValue("tia.zoom",
|
||||
BSPF::clampw(currentTIAZoom, supportedTIAMinZoom(), supportedTIAMaxZoom()));
|
||||
}
|
||||
|
@ -960,8 +965,8 @@ void FrameBuffer::renderTIA(bool shade, bool doClear)
|
|||
clear(); // TODO - test this: it may cause slowdowns on older systems
|
||||
|
||||
myTIASurface->render(shade);
|
||||
if(myBezelSurface)
|
||||
myBezelSurface->render();
|
||||
if(myBezel)
|
||||
myBezel->render();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -1146,7 +1151,7 @@ void FrameBuffer::toggleFullscreen(bool toggle)
|
|||
msg << "enabled (" << myBackend->refreshRate() << " Hz, ";
|
||||
else
|
||||
msg << "disabled (";
|
||||
msg << "Zoom " << myActiveVidMode.zoom * 100 << "%)";
|
||||
msg << "Zoom " << round(myActiveVidMode.zoom * 100) << "%)";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1228,7 +1233,7 @@ void FrameBuffer::switchVideoMode(int direction)
|
|||
if(!fullScreen())
|
||||
{
|
||||
// Windowed TIA modes support variable zoom levels
|
||||
float zoom = myOSystem.settings().getFloat("tia.zoom");
|
||||
double zoom = myOSystem.settings().getFloat("tia.zoom");
|
||||
if(direction == +1) zoom += ZOOM_STEPS;
|
||||
else if(direction == -1) zoom -= ZOOM_STEPS;
|
||||
|
||||
|
@ -1271,17 +1276,10 @@ FBInitStatus FrameBuffer::applyVideoMode()
|
|||
myVidModeHandler.setDisplaySize(myAbsDesktopSize[display]);
|
||||
|
||||
const bool inTIAMode = myOSystem.eventHandler().inTIAMode();
|
||||
#ifdef IMAGE_SUPPORT
|
||||
const bool showBezel = inTIAMode &&
|
||||
myOSystem.settings().getBool("bezel.show") &&
|
||||
(fullScreen() || myOSystem.settings().getBool("bezel.windowed")) &&
|
||||
checkBezel();
|
||||
#else
|
||||
const bool showBezel = false;
|
||||
#endif
|
||||
|
||||
// Build the new mode based on current settings
|
||||
const VideoModeHandler::Mode& mode = myVidModeHandler.buildMode(s, inTIAMode, showBezel);
|
||||
const VideoModeHandler::Mode& mode
|
||||
= myVidModeHandler.buildMode(s, inTIAMode, myBezel->info());
|
||||
if(mode.imageR.size() > mode.screenS)
|
||||
return FBInitStatus::FailTooLarge;
|
||||
|
||||
|
@ -1306,11 +1304,7 @@ FBInitStatus FrameBuffer::applyVideoMode()
|
|||
if(inTIAMode)
|
||||
{
|
||||
#ifdef IMAGE_SUPPORT
|
||||
if(myBezelSurface)
|
||||
deallocateSurface(myBezelSurface);
|
||||
myBezelSurface = nullptr;
|
||||
if(showBezel)
|
||||
loadBezel();
|
||||
myBezel->apply();
|
||||
#endif
|
||||
|
||||
myTIASurface->initialize(myOSystem.console(), myActiveVidMode);
|
||||
|
@ -1334,147 +1328,18 @@ FBInitStatus FrameBuffer::applyVideoMode()
|
|||
return status;
|
||||
}
|
||||
|
||||
#ifdef IMAGE_SUPPORT
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const string FrameBuffer::getBezelName(int& index) const
|
||||
{
|
||||
if(++index == 1)
|
||||
return myOSystem.console().properties().get(PropType::Bezel_Name);
|
||||
|
||||
// Try to generate bezel name from cart name
|
||||
const string& cartName = myOSystem.console().properties().get(PropType::Cart_Name);
|
||||
const size_t pos = cartName.find_first_of("(");
|
||||
if(index < 10 && pos != std::string::npos && pos > 0)
|
||||
{
|
||||
// The following suffixes are from "The Official No-Intro Convention",
|
||||
// covering all used combinations by "The Bezel Project" (except single ones)
|
||||
// (Unl) = unlicensed (Homebrews)
|
||||
const std::array<string, 8> suffixes = {
|
||||
" (USA)", " (USA) (Proto)", " (USA) (Unl)", " (USA) (Hack)",
|
||||
" (Europe)", " (Germany)", " (France) (Unl)", " (Australia)"
|
||||
};
|
||||
return cartName.substr(0, pos - 1) + suffixes[index - 2];
|
||||
}
|
||||
|
||||
if(index == 10)
|
||||
{
|
||||
index = -1;
|
||||
return "default";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool FrameBuffer::checkBezel()
|
||||
{
|
||||
const string& path = myOSystem.bezelDir().getPath();
|
||||
int index = 0;
|
||||
|
||||
do
|
||||
{
|
||||
const string& name = getBezelName(index);
|
||||
|
||||
if(name != EmptyString)
|
||||
{
|
||||
FSNode node(path + name + ".png");
|
||||
if(node.exists())
|
||||
return true;
|
||||
}
|
||||
} while (index != -1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool FrameBuffer::loadBezel()
|
||||
{
|
||||
bool isValid = false;
|
||||
double aspectRatio = 1;
|
||||
|
||||
myBezelSurface = allocateSurface(myActiveVidMode.screenS.w, myActiveVidMode.screenS.h);
|
||||
try
|
||||
{
|
||||
const string& path = myOSystem.bezelDir().getPath();
|
||||
string imageName;
|
||||
VariantList metaData;
|
||||
int index = 0;
|
||||
|
||||
do
|
||||
{
|
||||
const string& name = getBezelName(index);
|
||||
if(name != EmptyString)
|
||||
{
|
||||
imageName = path + name + ".png";
|
||||
FSNode node(imageName);
|
||||
if(node.exists())
|
||||
{
|
||||
isValid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (index != -1);
|
||||
if(isValid)
|
||||
myOSystem.png().loadImage(imageName, *myBezelSurface, &aspectRatio, metaData);
|
||||
}
|
||||
catch(const runtime_error&)
|
||||
{
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if(isValid)
|
||||
{
|
||||
const float overscan = 1 - myOSystem.settings().getInt("tia.fs_overscan") / 100.F;
|
||||
|
||||
uInt32 imageW, imageH;
|
||||
if(fullScreen())
|
||||
{
|
||||
const float bezelBorder = myOSystem.settings().getInt("bezel.border") * overscan * myActiveVidMode.zoom;
|
||||
imageW = (myActiveVidMode.imageR.w() + static_cast<int>(bezelBorder * 4.F / 3.F)) * (16.F / 9.F) / (4.F / 3.F);
|
||||
imageH = myActiveVidMode.imageR.h() + static_cast<int>(bezelBorder);
|
||||
}
|
||||
else
|
||||
{
|
||||
imageW = static_cast<uInt32>(myActiveVidMode.imageR.w() * (16.F / 9.F) / (4.F / 3.F));
|
||||
imageH = myActiveVidMode.imageR.h();
|
||||
}
|
||||
|
||||
// Scale bezel to fullscreen (preserve or stretch) or window size
|
||||
const uInt32 bezelW = std::min(
|
||||
myActiveVidMode.screenS.w, imageW);
|
||||
//static_cast<uInt32>(myActiveVidMode.imageR.w() * (16.F / 9.F) / (4.F / 3.F)) + static_cast<int>(40 * myActiveVidMode.zoom));
|
||||
const uInt32 bezelH = std::min(
|
||||
myActiveVidMode.screenS.h, imageH);
|
||||
//myActiveVidMode.imageR.h() + static_cast<int>(30 * myActiveVidMode.zoom));
|
||||
//cerr << bezelW << " x " << bezelH << endl;
|
||||
myBezelSurface->setDstSize(bezelW, bezelH);
|
||||
myBezelSurface->setDstPos((myActiveVidMode.screenS.w - bezelW) / 2,
|
||||
(myActiveVidMode.screenS.h - bezelH) / 2); // center
|
||||
myBezelSurface->setScalingInterpolation(ScalingInterpolation::sharp);
|
||||
|
||||
// Enable blending to allow overlaying the bezel over the TIA output
|
||||
myBezelSurface->attributes().blending = true;
|
||||
myBezelSurface->attributes().blendalpha = 100;
|
||||
myBezelSurface->applyAttributes();
|
||||
}
|
||||
if(myBezelSurface)
|
||||
myBezelSurface->setVisible(isValid);
|
||||
return isValid;
|
||||
}
|
||||
#endif
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
float FrameBuffer::maxWindowZoom() const
|
||||
double FrameBuffer::maxWindowZoom() const
|
||||
{
|
||||
const int display = displayId(BufferType::Emulator);
|
||||
float multiplier = 1;
|
||||
|
||||
const bool showBezel = myOSystem.settings().getBool("bezel.show");
|
||||
const float scaleW = showBezel ? (16.F / 9.F) / (4.F / 3.F) : 1.F; // = 1.333
|
||||
double multiplier = 1;
|
||||
|
||||
for(;;)
|
||||
{
|
||||
// Figure out the zoomed size of the window
|
||||
const uInt32 width = TIAConstants::viewableWidth * multiplier * scaleW;
|
||||
const uInt32 height = TIAConstants::viewableHeight * multiplier;
|
||||
// Figure out the zoomed size of the window (incl. the bezel)
|
||||
const uInt32 width = static_cast<double>(TIAConstants::viewableWidth) * myBezel->ratioW() * multiplier;
|
||||
const uInt32 height = static_cast<double>(TIAConstants::viewableHeight) * myBezel->ratioH() * multiplier;
|
||||
|
||||
if((width > myAbsDesktopSize[display].w) ||
|
||||
(height > myAbsDesktopSize[display].h))
|
||||
|
|
|
@ -25,6 +25,7 @@ class Console;
|
|||
class Settings;
|
||||
class FBSurface;
|
||||
class TIASurface;
|
||||
class Bezel;
|
||||
|
||||
#ifdef GUI_SUPPORT
|
||||
#include "Font.hxx"
|
||||
|
@ -53,7 +54,7 @@ class FrameBuffer
|
|||
{
|
||||
public:
|
||||
// Zoom level step interval
|
||||
static constexpr float ZOOM_STEPS = 0.25;
|
||||
static constexpr double ZOOM_STEPS = 0.25;
|
||||
|
||||
enum UpdateMode {
|
||||
NONE = 0,
|
||||
|
@ -219,8 +220,8 @@ class FrameBuffer
|
|||
Get the minimum/maximum supported TIA zoom level (windowed mode)
|
||||
for the framebuffer.
|
||||
*/
|
||||
float supportedTIAMinZoom() const { return myTIAMinZoom * hidpiScaleFactor(); }
|
||||
float supportedTIAMaxZoom() const { return maxWindowZoom(); }
|
||||
double supportedTIAMinZoom() const { return myTIAMinZoom * hidpiScaleFactor(); }
|
||||
double supportedTIAMaxZoom() const { return maxWindowZoom(); }
|
||||
|
||||
/**
|
||||
Get the TIA surface associated with the framebuffer.
|
||||
|
@ -349,6 +350,19 @@ class FrameBuffer
|
|||
myBackend->getRGB(pixel, r, g, b);
|
||||
}
|
||||
|
||||
/**
|
||||
This method is called to retrieve the R/G/B/A data from the given pixel.
|
||||
|
||||
@param pixel The pixel containing R/G/B data
|
||||
@param r The red component of the color
|
||||
@param g The green component of the color
|
||||
@param b The blue component of the color
|
||||
@param a The alpha component of the color.
|
||||
*/
|
||||
void getRGBA(uInt32 pixel, uInt8* r, uInt8* g, uInt8* b, uInt8* a) const {
|
||||
myBackend->getRGBA(pixel, r, g, b, a);
|
||||
}
|
||||
|
||||
/**
|
||||
This method is called to map a given R/G/B triple to the screen palette.
|
||||
|
||||
|
@ -361,11 +375,12 @@ class FrameBuffer
|
|||
}
|
||||
|
||||
/**
|
||||
This method is called to map a given R/G/B triple to the screen palette.
|
||||
This method is called to map a given R/G/B/A triple to the screen palette.
|
||||
|
||||
@param r The red component of the color.
|
||||
@param g The green component of the color.
|
||||
@param b The blue component of the color.
|
||||
@param a The alpha component of the color.
|
||||
*/
|
||||
uInt32 mapRGBA(uInt8 r, uInt8 g, uInt8 b, uInt8 a) const {
|
||||
return myBackend->mapRGBA(r, g, b, a);
|
||||
|
@ -462,37 +477,11 @@ class FrameBuffer
|
|||
*/
|
||||
FBInitStatus applyVideoMode();
|
||||
|
||||
#ifdef IMAGE_SUPPORT
|
||||
/**
|
||||
Return bezel names, which are either read from the properties
|
||||
or generated from the cart name.
|
||||
|
||||
@param index The index of the returned bezel name
|
||||
|
||||
@return The bezel name for the given index
|
||||
*/
|
||||
const string getBezelName(int& index) const;
|
||||
|
||||
/**
|
||||
Check if a bezel for the current ROM name exists.
|
||||
|
||||
@return Whether the bezel was found or not
|
||||
*/
|
||||
bool checkBezel();
|
||||
|
||||
/**
|
||||
Load the bezel for the current ROM.
|
||||
|
||||
@return Whether the bezel was loaded or not
|
||||
*/
|
||||
bool loadBezel();
|
||||
#endif
|
||||
|
||||
/**
|
||||
Calculate the maximum level by which the base window can be zoomed and
|
||||
still fit in the desktop screen.
|
||||
*/
|
||||
float maxWindowZoom() const;
|
||||
double maxWindowZoom() const;
|
||||
|
||||
/**
|
||||
Enables/disables fullscreen mode.
|
||||
|
@ -567,7 +556,7 @@ class FrameBuffer
|
|||
shared_ptr<TIASurface> myTIASurface;
|
||||
|
||||
// The BezelSurface which blends over the TIA surface
|
||||
shared_ptr<FBSurface> myBezelSurface;
|
||||
unique_ptr<Bezel> myBezel;
|
||||
|
||||
// Used for onscreen messages and frame statistics
|
||||
// (scanline count and framerate)
|
||||
|
@ -594,7 +583,7 @@ class FrameBuffer
|
|||
vector<bool> myHiDPIEnabled;
|
||||
|
||||
// Minimum TIA zoom level that can be used for this framebuffer
|
||||
float myTIAMinZoom{2.F};
|
||||
double myTIAMinZoom{2.F};
|
||||
|
||||
// Holds a reference to all the surfaces that have been created
|
||||
std::list<shared_ptr<FBSurface>> mySurfaceList;
|
||||
|
|
|
@ -64,7 +64,11 @@ Settings::Settings()
|
|||
setPermanent("pausedim", "true");
|
||||
setPermanent("bezel.show", "true");
|
||||
setPermanent("bezel.windowed", "false");
|
||||
setPermanent("bezel.border", "30");
|
||||
setPermanent("bezel.autoborders", "true");
|
||||
setPermanent("bezel.leftborder", "0");
|
||||
setPermanent("bezel.rightborder", "0");
|
||||
setPermanent("bezel.topborder", "0");
|
||||
setPermanent("bezel.bottomborder", "0");
|
||||
// TIA specific options
|
||||
setPermanent("tia.inter", "false");
|
||||
setPermanent("tia.zoom", "3");
|
||||
|
@ -542,8 +546,13 @@ void Settings::usage()
|
|||
<< " -turbo <1|0> Enable 'Turbo' mode for maximum emulation speed\n"
|
||||
<< " -uimessages <1|0> Show onscreen UI messages for different events\n"
|
||||
<< " -pausedim <1|0> Enable emulation dimming in pause mode\n"
|
||||
<< " -bezel.show <1|0> Show bezel left and right of emulation\n"
|
||||
<< " -bezel.windowed <1|0> Show bezel in windowed modes\n"
|
||||
<< " -bezel.show <1|0> Show bezel left and right of emulation\n"
|
||||
<< " -bezel.windowed <1|0> Show bezel in windowed modes\n"
|
||||
<< " -bezel.autoborders <1|0> Automatically set bezel window borders\n"
|
||||
<< " -bezel.leftborder <number> Set left bezel window border\n"
|
||||
<< " -bezel.rightborder <number> Set right bezel window border\n"
|
||||
<< " -bezel.topborder <number> Set top bezel window border\n"
|
||||
<< " -bezel.bottomborder <number> Set bottom bezel window border\n"
|
||||
<< endl
|
||||
#ifdef SOUND_SUPPORT
|
||||
<< " -audio.enabled <1|0> Enable audio\n"
|
||||
|
|
|
@ -2,6 +2,7 @@ MODULE := src/emucore
|
|||
|
||||
MODULE_OBJS := \
|
||||
src/emucore/AtariVox.o \
|
||||
src/emucore/Bezel.o
|
||||
src/emucore/Bankswitch.o \
|
||||
src/emucore/Booster.o \
|
||||
src/emucore/Cart.o \
|
||||
|
|
|
@ -1410,7 +1410,7 @@ void DeveloperDialog::handleDebugColours(int idx, int color)
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void DeveloperDialog::handleDebugColours(string_view colors)
|
||||
{
|
||||
for(int i = 0; i < DEBUG_COLORS; ++i)
|
||||
for(int i = 0; i < DEBUG_COLORS && i < colors.length(); ++i)
|
||||
{
|
||||
switch(colors[i])
|
||||
{
|
||||
|
|
|
@ -478,21 +478,47 @@ void VideoAudioDialog::addBezelTab()
|
|||
ypos += lineHeight + VGAP * 3;
|
||||
myBezelShowWindowed = new CheckboxWidget(myTab, _font, xpos, ypos,
|
||||
"Show in windowed modes");
|
||||
//myBezelEnableCheckbox->setToolTip(Event::BezelToggle);
|
||||
//myBezelShowWindowed->setToolTip(Event::BezelToggle);
|
||||
wid.push_back(myBezelShowWindowed);
|
||||
|
||||
// Disable auto borders
|
||||
ypos += lineHeight + VGAP * 1;
|
||||
myTopBorderSlider = new SliderWidget(myTab, _font, xpos, ypos,
|
||||
"Top border ", 0, 0, 6 * fontWidth, "px");
|
||||
myTopBorderSlider->setMinValue(0); myTopBorderSlider->setMaxValue(50);
|
||||
myManualBorders = new CheckboxWidget(myTab, _font, xpos, ypos,
|
||||
"Manual borders", kAutoBordersChanged);
|
||||
myManualBorders->setToolTip("Enable if automatic border detection fails.");
|
||||
wid.push_back(myManualBorders);
|
||||
xpos += INDENT;
|
||||
|
||||
const int lWidth = _font.getStringWidth("Bottom ");
|
||||
const int sWidth = myBezelPath->getRight() - xpos - lWidth - 5.5 * fontWidth; // _w - HBORDER - xpos - lwidth;
|
||||
ypos += lineHeight + VGAP * 1;
|
||||
myLeftBorderSlider = new SliderWidget(myTab, _font, xpos, ypos, sWidth, lineHeight,
|
||||
"Left ", 0, 0, 5 * fontWidth, "px");
|
||||
myLeftBorderSlider->setMinValue(0); myLeftBorderSlider->setMaxValue(500);
|
||||
myLeftBorderSlider->setTickmarkIntervals(10);
|
||||
//myLeftBorderSlider->setToolTip(Event::VolumeDecrease, Event::VolumeIncrease);
|
||||
wid.push_back(myLeftBorderSlider);
|
||||
|
||||
ypos += lineHeight + VGAP * 1;
|
||||
myRightBorderSlider = new SliderWidget(myTab, _font, xpos, ypos, sWidth, lineHeight,
|
||||
"Right ", 0, 0, 5 * fontWidth, "px");
|
||||
myRightBorderSlider->setMinValue(0); myRightBorderSlider->setMaxValue(500);
|
||||
myRightBorderSlider->setTickmarkIntervals(10);
|
||||
//myRightBorderSlider->setToolTip(Event::VolumeDecrease, Event::VolumeIncrease);
|
||||
wid.push_back(myRightBorderSlider);
|
||||
|
||||
ypos += lineHeight + VGAP * 1;
|
||||
myTopBorderSlider = new SliderWidget(myTab, _font, xpos, ypos, sWidth, lineHeight,
|
||||
"Top ", 0, 0, 5 * fontWidth, "px");
|
||||
myTopBorderSlider->setMinValue(0); myTopBorderSlider->setMaxValue(250);
|
||||
myTopBorderSlider->setTickmarkIntervals(5);
|
||||
//myTopBorderSlider->setToolTip(Event::VolumeDecrease, Event::VolumeIncrease);
|
||||
wid.push_back(myTopBorderSlider);
|
||||
|
||||
ypos += lineHeight + VGAP;
|
||||
myBtmBorderSlider = new SliderWidget(myTab, _font, xpos, ypos,
|
||||
"Bottom border ", 0, 0, 6 * fontWidth, "px");
|
||||
myBtmBorderSlider->setMinValue(0); myBtmBorderSlider->setMaxValue(50);
|
||||
myBtmBorderSlider = new SliderWidget(myTab, _font, xpos, ypos, sWidth, lineHeight,
|
||||
"Bottom ", 0, 0, 5 * fontWidth, "px");
|
||||
myBtmBorderSlider->setMinValue(0); myBtmBorderSlider->setMaxValue(250);
|
||||
myBtmBorderSlider->setTickmarkIntervals(5);
|
||||
//myBtmBorderSlider->setToolTip(Event::VolumeDecrease, Event::VolumeIncrease);
|
||||
wid.push_back(myBtmBorderSlider);
|
||||
|
@ -748,6 +774,9 @@ void VideoAudioDialog::loadConfig()
|
|||
myBezelEnableCheckbox->setState(settings.getBool("bezel.show"));
|
||||
myBezelPath->setText(settings.getString("bezel.dir"));
|
||||
myBezelShowWindowed->setState(settings.getBool("bezel.windowed"));
|
||||
myManualBorders->setState(!settings.getBool("bezel.autoborders"));
|
||||
myLeftBorderSlider->setValue(settings.getInt("bezel.leftborder"));
|
||||
myRightBorderSlider->setValue(settings.getInt("bezel.rightborder"));
|
||||
myTopBorderSlider->setValue(settings.getInt("bezel.topborder"));
|
||||
myBtmBorderSlider->setValue(settings.getInt("bezel.bottomborder"));
|
||||
handleBezelChange();
|
||||
|
@ -880,9 +909,11 @@ void VideoAudioDialog::saveConfig()
|
|||
settings.setValue("bezel.show", myBezelEnableCheckbox->getState());
|
||||
settings.setValue("bezel.dir", myBezelPath->getText());
|
||||
settings.setValue("bezel.windowed", myBezelShowWindowed->getState());
|
||||
settings.setValue("bezel.autoborders", !myManualBorders->getState());
|
||||
settings.setValue("bezel.leftborder", myLeftBorderSlider->getValueLabel());
|
||||
settings.setValue("bezel.rightborder", myRightBorderSlider->getValueLabel());
|
||||
settings.setValue("bezel.topborder", myTopBorderSlider->getValueLabel());
|
||||
settings.setValue("bezel.bottomborder", myBtmBorderSlider->getValueLabel());
|
||||
cerr << myTopBorderSlider << endl;
|
||||
|
||||
// Note: The following has to happen after all video related setting have been saved
|
||||
if(instance().hasConsole())
|
||||
|
@ -1022,8 +1053,7 @@ void VideoAudioDialog::setDefaults()
|
|||
myBezelEnableCheckbox->setState(true);
|
||||
myBezelPath->setText(instance().userDir().getShortPath());
|
||||
myBezelShowWindowed->setState(false);
|
||||
myTopBorderSlider->setValue(0);
|
||||
myBtmBorderSlider->setValue(0);
|
||||
myManualBorders->setState(false);
|
||||
handleBezelChange();
|
||||
break;
|
||||
|
||||
|
@ -1193,12 +1223,15 @@ void VideoAudioDialog::handlePhosphorChange()
|
|||
void VideoAudioDialog::handleBezelChange()
|
||||
{
|
||||
const bool enable = myBezelEnableCheckbox->getState();
|
||||
const bool nonAuto = myManualBorders->getState();
|
||||
|
||||
myOpenBrowserButton->setEnabled(enable);
|
||||
myBezelPath->setEnabled(enable);
|
||||
myBezelShowWindowed->setEnabled(enable);
|
||||
myTopBorderSlider->setEnabled(enable);
|
||||
myBtmBorderSlider->setEnabled(enable);
|
||||
myLeftBorderSlider->setEnabled(enable && nonAuto);
|
||||
myRightBorderSlider->setEnabled(enable && nonAuto);
|
||||
myTopBorderSlider->setEnabled(enable && nonAuto);
|
||||
myBtmBorderSlider->setEnabled(enable && nonAuto);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -1317,6 +1350,7 @@ void VideoAudioDialog::handleCommand(CommandSender* sender, int cmd,
|
|||
break;
|
||||
|
||||
case kBezelEnableChanged:
|
||||
case kAutoBordersChanged:
|
||||
handleBezelChange();
|
||||
break;
|
||||
|
||||
|
|
|
@ -135,6 +135,9 @@ class VideoAudioDialog : public Dialog
|
|||
ButtonWidget* myOpenBrowserButton{nullptr};
|
||||
EditTextWidget* myBezelPath{nullptr};
|
||||
CheckboxWidget* myBezelShowWindowed{nullptr};
|
||||
CheckboxWidget* myManualBorders{nullptr};
|
||||
SliderWidget* myLeftBorderSlider{nullptr};
|
||||
SliderWidget* myRightBorderSlider{nullptr};
|
||||
SliderWidget* myTopBorderSlider{nullptr};
|
||||
SliderWidget* myBtmBorderSlider{nullptr};
|
||||
|
||||
|
@ -180,6 +183,7 @@ class VideoAudioDialog : public Dialog
|
|||
|
||||
kBezelEnableChanged = 'BZen',
|
||||
kChooseBezelDirCmd = 'BZsl',
|
||||
kAutoBordersChanged = 'BZab',
|
||||
|
||||
kSoundEnableChanged = 'ADse',
|
||||
kDeviceChanged = 'ADdc',
|
||||
|
|
|
@ -993,6 +993,7 @@
|
|||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-NoDebugger|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\emucore\Bankswitch.cxx" />
|
||||
<ClCompile Include="..\..\emucore\Bezel.cxx" />
|
||||
<ClCompile Include="..\..\emucore\Cart03E0.cxx" />
|
||||
<ClCompile Include="..\..\emucore\Cart3EPlus.cxx" />
|
||||
<ClCompile Include="..\..\emucore\Cart3EX.cxx" />
|
||||
|
@ -2320,6 +2321,7 @@
|
|||
<ClInclude Include="..\..\emucore\AmigaMouse.hxx" />
|
||||
<ClInclude Include="..\..\emucore\AtariMouse.hxx" />
|
||||
<ClInclude Include="..\..\emucore\Bankswitch.hxx" />
|
||||
<ClInclude Include="..\..\emucore\Bezel.hxx" />
|
||||
<ClInclude Include="..\..\emucore\Cart03E0.hxx" />
|
||||
<ClInclude Include="..\..\emucore\Cart3EPlus.hxx" />
|
||||
<ClInclude Include="..\..\emucore\Cart3EX.hxx" />
|
||||
|
|
|
@ -1209,6 +1209,9 @@
|
|||
<ClCompile Include="..\..\debugger\gui\Cart03E0Widget.cxx">
|
||||
<Filter>Source Files\debugger\gui</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\emucore\Bezel.cxx">
|
||||
<Filter>Source Files\emucore</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\common\bspf.hxx">
|
||||
|
@ -2465,6 +2468,9 @@
|
|||
<ClInclude Include="..\..\debugger\gui\Cart03E0Widget.hxx">
|
||||
<Filter>Header Files\debugger\gui</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\emucore\Bezel.hxx">
|
||||
<Filter>Header Files\emucore</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="stella.ico">
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 994 KiB After Width: | Height: | Size: 549 KiB |
Loading…
Reference in New Issue