mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-22 22:13:35 +02:00
Merge pull request #4704 from IvanSavenko/video_autodetect
Enable autodetection of video scaling parameters
This commit is contained in:
commit
3113b7f60a
@ -250,8 +250,8 @@ void CGuiHandler::setStatusbar(std::shared_ptr<IStatusBar> newStatusBar)
|
||||
void CGuiHandler::onScreenResize(bool resolutionChanged)
|
||||
{
|
||||
if(resolutionChanged)
|
||||
{
|
||||
screenHandler().onScreenResize();
|
||||
}
|
||||
|
||||
windows().onScreenResize();
|
||||
CCS->curh->onScreenResize();
|
||||
}
|
||||
|
@ -312,3 +312,8 @@ void CursorHandler::changeCursor(Cursor::ShowType newShowType)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CursorHandler::onScreenResize()
|
||||
{
|
||||
cursor->setImage(getCurrentImage(), getPivotOffset());
|
||||
}
|
||||
|
@ -182,6 +182,7 @@ public:
|
||||
|
||||
void hide();
|
||||
void show();
|
||||
void onScreenResize();
|
||||
|
||||
/// change cursor's positions to (x, y)
|
||||
void cursorMove(const int & x, const int & y);
|
||||
|
@ -44,6 +44,8 @@ public:
|
||||
/// Dimensions of logical output. Can be different if scaling is used
|
||||
virtual Point getLogicalResolution() const = 0;
|
||||
|
||||
virtual int getInterfaceScalingPercentage() const = 0;
|
||||
|
||||
virtual int getScalingFactor() const = 0;
|
||||
|
||||
/// Window has focus
|
||||
|
@ -11,11 +11,14 @@
|
||||
#include "StdInc.h"
|
||||
#include "CursorHardware.h"
|
||||
|
||||
#include "SDL_Extensions.h"
|
||||
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../render/IScreenHandler.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../render/IImage.h"
|
||||
#include "SDL_Extensions.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
|
||||
#include <SDL_render.h>
|
||||
#include <SDL_events.h>
|
||||
@ -45,19 +48,28 @@ void CursorHardware::setVisible(bool on)
|
||||
|
||||
void CursorHardware::setImage(std::shared_ptr<IImage> image, const Point & pivotOffset)
|
||||
{
|
||||
auto cursorSurface = CSDL_Ext::newSurface(image->dimensions() * GH.screenHandler().getScalingFactor());
|
||||
int videoScalingSettings = GH.screenHandler().getInterfaceScalingPercentage();
|
||||
float cursorScalingSettings = settings["video"]["cursorScalingFactor"].Float();
|
||||
int cursorScalingPercent = videoScalingSettings * cursorScalingSettings;
|
||||
Point cursorDimensions = image->dimensions() * GH.screenHandler().getScalingFactor();
|
||||
Point cursorDimensionsScaled = image->dimensions() * cursorScalingPercent / 100;
|
||||
Point pivotOffsetScaled = pivotOffset * cursorScalingPercent / 100 / GH.screenHandler().getScalingFactor();
|
||||
|
||||
auto cursorSurface = CSDL_Ext::newSurface(cursorDimensions);
|
||||
|
||||
CSDL_Ext::fillSurface(cursorSurface, CSDL_Ext::toSDL(Colors::TRANSPARENCY));
|
||||
|
||||
image->draw(cursorSurface, Point(0,0));
|
||||
auto cursorSurfaceScaled = CSDL_Ext::scaleSurface(cursorSurface, cursorDimensionsScaled.x, cursorDimensionsScaled.y );
|
||||
|
||||
auto oldCursor = cursor;
|
||||
cursor = SDL_CreateColorCursor(cursorSurface, pivotOffset.x, pivotOffset.y);
|
||||
cursor = SDL_CreateColorCursor(cursorSurfaceScaled, pivotOffsetScaled.x, pivotOffsetScaled.y);
|
||||
|
||||
if (!cursor)
|
||||
logGlobal->error("Failed to set cursor! SDL says %s", SDL_GetError());
|
||||
|
||||
SDL_FreeSurface(cursorSurface);
|
||||
SDL_FreeSurface(cursorSurfaceScaled);
|
||||
|
||||
GH.dispatchMainThread([this, oldCursor](){
|
||||
SDL_SetCursor(cursor);
|
||||
|
@ -84,19 +84,39 @@ Rect ScreenHandler::convertLogicalPointsToWindow(const Rect & input) const
|
||||
return result;
|
||||
}
|
||||
|
||||
int ScreenHandler::getInterfaceScalingPercentage() const
|
||||
{
|
||||
auto [minimalScaling, maximalScaling] = getSupportedScalingRange();
|
||||
|
||||
int userScaling = settings["video"]["resolution"]["scaling"].Integer();
|
||||
|
||||
if (userScaling == 0) // autodetection
|
||||
{
|
||||
#ifdef VCMI_MOBILE
|
||||
// for mobiles - stay at maximum scaling unless we have large screen
|
||||
// might be better to check screen DPI / physical dimensions, but way more complex, and may result in different edge cases, e.g. chromebooks / tv's
|
||||
int preferredMinimalScaling = 200;
|
||||
#else
|
||||
// for PC - avoid downscaling if possible
|
||||
int preferredMinimalScaling = 100;
|
||||
#endif
|
||||
// prefer a little below maximum - to give space for extended UI
|
||||
int preferredMaximalScaling = maximalScaling * 10 / 12;
|
||||
userScaling = std::max(std::min(maximalScaling, preferredMinimalScaling), preferredMaximalScaling);
|
||||
}
|
||||
|
||||
int scaling = std::clamp(userScaling, minimalScaling, maximalScaling);
|
||||
return scaling;
|
||||
}
|
||||
|
||||
Point ScreenHandler::getPreferredLogicalResolution() const
|
||||
{
|
||||
Point renderResolution = getRenderResolution();
|
||||
double reservedAreaWidth = settings["video"]["reservedWidth"].Float();
|
||||
|
||||
int scaling = getInterfaceScalingPercentage();
|
||||
Point availableResolution = Point(renderResolution.x * (1 - reservedAreaWidth), renderResolution.y);
|
||||
|
||||
auto [minimalScaling, maximalScaling] = getSupportedScalingRange();
|
||||
|
||||
int userScaling = settings["video"]["resolution"]["scaling"].Integer();
|
||||
int scaling = std::clamp(userScaling, minimalScaling, maximalScaling);
|
||||
|
||||
Point logicalResolution = availableResolution * 100.0 / scaling;
|
||||
|
||||
return logicalResolution;
|
||||
}
|
||||
|
||||
@ -335,25 +355,22 @@ EUpscalingFilter ScreenHandler::loadUpscalingFilter() const
|
||||
if (filter != EUpscalingFilter::AUTO)
|
||||
return filter;
|
||||
|
||||
// for now - always fallback to no filter
|
||||
return EUpscalingFilter::NONE;
|
||||
|
||||
// else - autoselect
|
||||
// Point outputResolution = getRenderResolution();
|
||||
// Point logicalResolution = getPreferredLogicalResolution();
|
||||
//
|
||||
// float scaleX = static_cast<float>(outputResolution.x) / logicalResolution.x;
|
||||
// float scaleY = static_cast<float>(outputResolution.x) / logicalResolution.x;
|
||||
// float scaling = std::min(scaleX, scaleY);
|
||||
//
|
||||
// if (scaling <= 1.0f)
|
||||
// return EUpscalingFilter::NONE;
|
||||
// if (scaling <= 2.0f)
|
||||
// return EUpscalingFilter::XBRZ_2;
|
||||
// if (scaling <= 3.0f)
|
||||
// return EUpscalingFilter::XBRZ_3;
|
||||
//
|
||||
// return EUpscalingFilter::XBRZ_4;
|
||||
Point outputResolution = getRenderResolution();
|
||||
Point logicalResolution = getPreferredLogicalResolution();
|
||||
|
||||
float scaleX = static_cast<float>(outputResolution.x) / logicalResolution.x;
|
||||
float scaleY = static_cast<float>(outputResolution.x) / logicalResolution.x;
|
||||
float scaling = std::min(scaleX, scaleY);
|
||||
|
||||
if (scaling <= 1.001f)
|
||||
return EUpscalingFilter::NONE; // running at original resolution or even lower than that - no need for xbrz
|
||||
if (scaling <= 2.001f)
|
||||
return EUpscalingFilter::XBRZ_2; // resolutions below 1200p (including 1080p / FullHD)
|
||||
if (scaling <= 3.001f)
|
||||
return EUpscalingFilter::XBRZ_3; // resolutions below 2400p (including 1440p and 2160p / 4K)
|
||||
|
||||
return EUpscalingFilter::XBRZ_4; // Only for massive displays, e.g. 8K
|
||||
}
|
||||
|
||||
void ScreenHandler::selectUpscalingFilter()
|
||||
|
@ -112,6 +112,8 @@ public:
|
||||
|
||||
int getScalingFactor() const final;
|
||||
|
||||
int getInterfaceScalingPercentage() const final;
|
||||
|
||||
std::vector<Point> getSupportedResolutions() const final;
|
||||
std::vector<Point> getSupportedResolutions(int displayIndex) const;
|
||||
std::tuple<int, int> getSupportedScalingRange() const final;
|
||||
|
@ -194,10 +194,8 @@ GeneralOptionsTab::GeneralOptionsTab()
|
||||
|
||||
build(config);
|
||||
|
||||
const auto & currentResolution = settings["video"]["resolution"];
|
||||
|
||||
std::shared_ptr<CLabel> scalingLabel = widget<CLabel>("scalingLabel");
|
||||
scalingLabel->setText(scalingToLabelString(currentResolution["scaling"].Integer()));
|
||||
scalingLabel->setText(scalingToLabelString(GH.screenHandler().getInterfaceScalingPercentage()));
|
||||
|
||||
std::shared_ptr<CLabel> longTouchLabel = widget<CLabel>("longTouchLabel");
|
||||
if (longTouchLabel)
|
||||
|
@ -184,6 +184,7 @@
|
||||
"targetfps",
|
||||
"vsync",
|
||||
"fontsType",
|
||||
"cursorScalingFactor",
|
||||
"fontScalingFactor",
|
||||
"upscalingFilter",
|
||||
"fontUpscalingFilter",
|
||||
@ -195,22 +196,19 @@
|
||||
"additionalProperties" : false,
|
||||
"required" : [ "width", "height", "scaling" ],
|
||||
"properties" : {
|
||||
"width" : { "type" : "number" },
|
||||
"height" : { "type" : "number" },
|
||||
"scaling" : { "type" : "number" }
|
||||
},
|
||||
"defaultIOS" : {"width" : 800, "height" : 600, "scaling" : 200 },
|
||||
"defaultAndroid" : {"width" : 800, "height" : 600, "scaling" : 200 },
|
||||
"default" : {"width" : 800, "height" : 600, "scaling" : 100 }
|
||||
"width" : { "type" : "number", "default" : 1280 },
|
||||
"height" : { "type" : "number", "default" : 720 },
|
||||
"scaling" : { "type" : "number", "default" : 0 }
|
||||
}
|
||||
},
|
||||
"reservedWidth" : {
|
||||
"type" : "number",
|
||||
"defaultIOS" : 0.1, // iOS camera cutout / notch is excluded from available area by SDL
|
||||
"defaultIOS" : 0.1, // iOS camera cutout / notch is not excluded from available area by SDL, handle it this way
|
||||
"default" : 0
|
||||
},
|
||||
"fullscreen" : {
|
||||
"type" : "boolean",
|
||||
"default" : false
|
||||
"default" : true
|
||||
},
|
||||
"realFullscreen" : {
|
||||
"type" : "boolean",
|
||||
@ -256,6 +254,10 @@
|
||||
"enum" : [ "auto", "original", "scalable" ],
|
||||
"default" : "auto"
|
||||
},
|
||||
"cursorScalingFactor" : {
|
||||
"type" : "number",
|
||||
"default" : 1
|
||||
},
|
||||
"fontScalingFactor" : {
|
||||
"type" : "number",
|
||||
"default" : 1
|
||||
|
@ -127,7 +127,12 @@ void CSettingsView::loadSettings()
|
||||
#endif
|
||||
fillValidScalingRange();
|
||||
|
||||
ui->spinBoxInterfaceScaling->setValue(settings["video"]["resolution"]["scaling"].Float());
|
||||
ui->buttonScalingAuto->setChecked(settings["video"]["resolution"]["scaling"].Integer() == 0);
|
||||
if (settings["video"]["resolution"]["scaling"].Integer() == 0)
|
||||
ui->spinBoxInterfaceScaling->setValue(100);
|
||||
else
|
||||
ui->spinBoxInterfaceScaling->setValue(settings["video"]["resolution"]["scaling"].Float());
|
||||
|
||||
ui->spinBoxFramerateLimit->setValue(settings["video"]["targetfps"].Float());
|
||||
ui->spinBoxFramerateLimit->setDisabled(settings["video"]["vsync"].Bool());
|
||||
ui->sliderReservedArea->setValue(std::round(settings["video"]["reservedWidth"].Float() * 100));
|
||||
@ -174,6 +179,7 @@ void CSettingsView::loadSettings()
|
||||
ui->sliderControllerSticksAcceleration->setValue(settings["input"]["controllerAxisScale"].Float() * 100);
|
||||
ui->lineEditGameLobbyHost->setText(QString::fromStdString(settings["lobby"]["hostname"].String()));
|
||||
ui->spinBoxNetworkPortLobby->setValue(settings["lobby"]["port"].Integer());
|
||||
ui->buttonVSync->setChecked(settings["video"]["vsync"].Bool());
|
||||
|
||||
if (settings["video"]["fontsType"].String() == "auto")
|
||||
ui->buttonFontAuto->setChecked(true);
|
||||
@ -195,7 +201,6 @@ void CSettingsView::loadSettings()
|
||||
void CSettingsView::loadToggleButtonSettings()
|
||||
{
|
||||
setCheckbuttonState(ui->buttonShowIntro, settings["video"]["showIntro"].Bool());
|
||||
setCheckbuttonState(ui->buttonVSync, settings["video"]["vsync"].Bool());
|
||||
setCheckbuttonState(ui->buttonAutoCheck, settings["launcher"]["autoCheckRepositories"].Bool());
|
||||
|
||||
setCheckbuttonState(ui->buttonRepositoryDefault, settings["launcher"]["defaultRepositoryEnabled"].Bool());
|
||||
@ -212,10 +217,15 @@ void CSettingsView::loadToggleButtonSettings()
|
||||
std::string cursorType = settings["video"]["cursor"].String();
|
||||
int cursorTypeIndex = vstd::find_pos(cursorTypesList, cursorType);
|
||||
setCheckbuttonState(ui->buttonCursorType, cursorTypeIndex);
|
||||
ui->sliderScalingCursor->setDisabled(cursorType == "software"); // Not supported
|
||||
ui->labelScalingCursorValue->setDisabled(cursorType == "software"); // Not supported
|
||||
|
||||
int fontScalingPercentage = settings["video"]["fontScalingFactor"].Float() * 100;
|
||||
ui->sliderScalingFont->setValue(fontScalingPercentage / 5);
|
||||
|
||||
int cursorScalingPercentage = settings["video"]["cursorScalingFactor"].Float() * 100;
|
||||
ui->sliderScalingCursor->setValue(cursorScalingPercentage / 5);
|
||||
|
||||
}
|
||||
|
||||
void CSettingsView::fillValidResolutions()
|
||||
@ -494,6 +504,8 @@ void CSettingsView::on_buttonCursorType_toggled(bool value)
|
||||
Settings node = settings.write["video"]["cursor"];
|
||||
node->String() = cursorTypesList[value ? 1 : 0];
|
||||
updateCheckbuttonText(ui->buttonCursorType);
|
||||
ui->sliderScalingCursor->setDisabled(value == 1); // Not supported
|
||||
ui->labelScalingCursorValue->setDisabled(value == 1); // Not supported
|
||||
}
|
||||
|
||||
void CSettingsView::loadTranslation()
|
||||
@ -627,7 +639,6 @@ void CSettingsView::on_buttonVSync_toggled(bool value)
|
||||
Settings node = settings.write["video"]["vsync"];
|
||||
node->Bool() = value;
|
||||
ui->spinBoxFramerateLimit->setDisabled(settings["video"]["vsync"].Bool());
|
||||
updateCheckbuttonText(ui->buttonVSync);
|
||||
}
|
||||
|
||||
void CSettingsView::on_comboBoxEnemyPlayerAI_currentTextChanged(const QString &arg1)
|
||||
@ -816,3 +827,21 @@ void CSettingsView::on_buttonValidationFull_clicked(bool checked)
|
||||
Settings node = settings.write["mods"]["validation"];
|
||||
node->String() = "full";
|
||||
}
|
||||
|
||||
void CSettingsView::on_sliderScalingCursor_valueChanged(int value)
|
||||
{
|
||||
int actualValuePercentage = value * 5;
|
||||
ui->labelScalingCursorValue->setText(QString("%1%").arg(actualValuePercentage));
|
||||
Settings node = settings.write["video"]["cursorScalingFactor"];
|
||||
node->Float() = actualValuePercentage / 100.0;
|
||||
}
|
||||
|
||||
void CSettingsView::on_buttonScalingAuto_toggled(bool checked)
|
||||
{
|
||||
ui->spinBoxInterfaceScaling->setDisabled(checked);
|
||||
ui->spinBoxInterfaceScaling->setValue(100);
|
||||
|
||||
Settings node = settings.write["video"]["resolution"]["scaling"];
|
||||
node->Integer() = checked ? 0 : 100;
|
||||
}
|
||||
|
||||
|
@ -97,6 +97,10 @@ private slots:
|
||||
|
||||
void on_buttonValidationFull_clicked(bool checked);
|
||||
|
||||
void on_sliderScalingCursor_valueChanged(int value);
|
||||
|
||||
void on_buttonScalingAuto_toggled(bool checked);
|
||||
|
||||
private:
|
||||
Ui::CSettingsView * ui;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user