1
0
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:
Ivan Savenko 2024-10-10 15:00:40 +03:00 committed by GitHub
commit 3113b7f60a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 1301 additions and 1179 deletions

View File

@ -250,8 +250,8 @@ void CGuiHandler::setStatusbar(std::shared_ptr<IStatusBar> newStatusBar)
void CGuiHandler::onScreenResize(bool resolutionChanged) void CGuiHandler::onScreenResize(bool resolutionChanged)
{ {
if(resolutionChanged) if(resolutionChanged)
{
screenHandler().onScreenResize(); screenHandler().onScreenResize();
}
windows().onScreenResize(); windows().onScreenResize();
CCS->curh->onScreenResize();
} }

View File

@ -312,3 +312,8 @@ void CursorHandler::changeCursor(Cursor::ShowType newShowType)
break; break;
} }
} }
void CursorHandler::onScreenResize()
{
cursor->setImage(getCurrentImage(), getPivotOffset());
}

View File

@ -182,6 +182,7 @@ public:
void hide(); void hide();
void show(); void show();
void onScreenResize();
/// change cursor's positions to (x, y) /// change cursor's positions to (x, y)
void cursorMove(const int & x, const int & y); void cursorMove(const int & x, const int & y);

View File

@ -44,6 +44,8 @@ public:
/// Dimensions of logical output. Can be different if scaling is used /// Dimensions of logical output. Can be different if scaling is used
virtual Point getLogicalResolution() const = 0; virtual Point getLogicalResolution() const = 0;
virtual int getInterfaceScalingPercentage() const = 0;
virtual int getScalingFactor() const = 0; virtual int getScalingFactor() const = 0;
/// Window has focus /// Window has focus

View File

@ -11,11 +11,14 @@
#include "StdInc.h" #include "StdInc.h"
#include "CursorHardware.h" #include "CursorHardware.h"
#include "SDL_Extensions.h"
#include "../gui/CGuiHandler.h" #include "../gui/CGuiHandler.h"
#include "../render/IScreenHandler.h" #include "../render/IScreenHandler.h"
#include "../render/Colors.h" #include "../render/Colors.h"
#include "../render/IImage.h" #include "../render/IImage.h"
#include "SDL_Extensions.h"
#include "../../lib/CConfigHandler.h"
#include <SDL_render.h> #include <SDL_render.h>
#include <SDL_events.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) 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)); CSDL_Ext::fillSurface(cursorSurface, CSDL_Ext::toSDL(Colors::TRANSPARENCY));
image->draw(cursorSurface, Point(0,0)); image->draw(cursorSurface, Point(0,0));
auto cursorSurfaceScaled = CSDL_Ext::scaleSurface(cursorSurface, cursorDimensionsScaled.x, cursorDimensionsScaled.y );
auto oldCursor = cursor; auto oldCursor = cursor;
cursor = SDL_CreateColorCursor(cursorSurface, pivotOffset.x, pivotOffset.y); cursor = SDL_CreateColorCursor(cursorSurfaceScaled, pivotOffsetScaled.x, pivotOffsetScaled.y);
if (!cursor) if (!cursor)
logGlobal->error("Failed to set cursor! SDL says %s", SDL_GetError()); logGlobal->error("Failed to set cursor! SDL says %s", SDL_GetError());
SDL_FreeSurface(cursorSurface); SDL_FreeSurface(cursorSurface);
SDL_FreeSurface(cursorSurfaceScaled);
GH.dispatchMainThread([this, oldCursor](){ GH.dispatchMainThread([this, oldCursor](){
SDL_SetCursor(cursor); SDL_SetCursor(cursor);

View File

@ -84,19 +84,39 @@ Rect ScreenHandler::convertLogicalPointsToWindow(const Rect & input) const
return result; 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 ScreenHandler::getPreferredLogicalResolution() const
{ {
Point renderResolution = getRenderResolution(); Point renderResolution = getRenderResolution();
double reservedAreaWidth = settings["video"]["reservedWidth"].Float(); double reservedAreaWidth = settings["video"]["reservedWidth"].Float();
int scaling = getInterfaceScalingPercentage();
Point availableResolution = Point(renderResolution.x * (1 - reservedAreaWidth), renderResolution.y); 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; Point logicalResolution = availableResolution * 100.0 / scaling;
return logicalResolution; return logicalResolution;
} }
@ -335,25 +355,22 @@ EUpscalingFilter ScreenHandler::loadUpscalingFilter() const
if (filter != EUpscalingFilter::AUTO) if (filter != EUpscalingFilter::AUTO)
return filter; return filter;
// for now - always fallback to no filter
return EUpscalingFilter::NONE;
// else - autoselect // else - autoselect
// Point outputResolution = getRenderResolution(); Point outputResolution = getRenderResolution();
// Point logicalResolution = getPreferredLogicalResolution(); Point logicalResolution = getPreferredLogicalResolution();
//
// float scaleX = static_cast<float>(outputResolution.x) / logicalResolution.x; float scaleX = static_cast<float>(outputResolution.x) / logicalResolution.x;
// float scaleY = static_cast<float>(outputResolution.x) / logicalResolution.x; float scaleY = static_cast<float>(outputResolution.x) / logicalResolution.x;
// float scaling = std::min(scaleX, scaleY); float scaling = std::min(scaleX, scaleY);
//
// if (scaling <= 1.0f) if (scaling <= 1.001f)
// return EUpscalingFilter::NONE; return EUpscalingFilter::NONE; // running at original resolution or even lower than that - no need for xbrz
// if (scaling <= 2.0f) if (scaling <= 2.001f)
// return EUpscalingFilter::XBRZ_2; return EUpscalingFilter::XBRZ_2; // resolutions below 1200p (including 1080p / FullHD)
// if (scaling <= 3.0f) if (scaling <= 3.001f)
// return EUpscalingFilter::XBRZ_3; return EUpscalingFilter::XBRZ_3; // resolutions below 2400p (including 1440p and 2160p / 4K)
//
// return EUpscalingFilter::XBRZ_4; return EUpscalingFilter::XBRZ_4; // Only for massive displays, e.g. 8K
} }
void ScreenHandler::selectUpscalingFilter() void ScreenHandler::selectUpscalingFilter()

View File

@ -112,6 +112,8 @@ public:
int getScalingFactor() const final; int getScalingFactor() const final;
int getInterfaceScalingPercentage() const final;
std::vector<Point> getSupportedResolutions() const final; std::vector<Point> getSupportedResolutions() const final;
std::vector<Point> getSupportedResolutions(int displayIndex) const; std::vector<Point> getSupportedResolutions(int displayIndex) const;
std::tuple<int, int> getSupportedScalingRange() const final; std::tuple<int, int> getSupportedScalingRange() const final;

View File

@ -194,10 +194,8 @@ GeneralOptionsTab::GeneralOptionsTab()
build(config); build(config);
const auto & currentResolution = settings["video"]["resolution"];
std::shared_ptr<CLabel> scalingLabel = widget<CLabel>("scalingLabel"); 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"); std::shared_ptr<CLabel> longTouchLabel = widget<CLabel>("longTouchLabel");
if (longTouchLabel) if (longTouchLabel)

View File

@ -184,6 +184,7 @@
"targetfps", "targetfps",
"vsync", "vsync",
"fontsType", "fontsType",
"cursorScalingFactor",
"fontScalingFactor", "fontScalingFactor",
"upscalingFilter", "upscalingFilter",
"fontUpscalingFilter", "fontUpscalingFilter",
@ -195,22 +196,19 @@
"additionalProperties" : false, "additionalProperties" : false,
"required" : [ "width", "height", "scaling" ], "required" : [ "width", "height", "scaling" ],
"properties" : { "properties" : {
"width" : { "type" : "number" }, "width" : { "type" : "number", "default" : 1280 },
"height" : { "type" : "number" }, "height" : { "type" : "number", "default" : 720 },
"scaling" : { "type" : "number" } "scaling" : { "type" : "number", "default" : 0 }
}, }
"defaultIOS" : {"width" : 800, "height" : 600, "scaling" : 200 },
"defaultAndroid" : {"width" : 800, "height" : 600, "scaling" : 200 },
"default" : {"width" : 800, "height" : 600, "scaling" : 100 }
}, },
"reservedWidth" : { "reservedWidth" : {
"type" : "number", "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 "default" : 0
}, },
"fullscreen" : { "fullscreen" : {
"type" : "boolean", "type" : "boolean",
"default" : false "default" : true
}, },
"realFullscreen" : { "realFullscreen" : {
"type" : "boolean", "type" : "boolean",
@ -256,6 +254,10 @@
"enum" : [ "auto", "original", "scalable" ], "enum" : [ "auto", "original", "scalable" ],
"default" : "auto" "default" : "auto"
}, },
"cursorScalingFactor" : {
"type" : "number",
"default" : 1
},
"fontScalingFactor" : { "fontScalingFactor" : {
"type" : "number", "type" : "number",
"default" : 1 "default" : 1

View File

@ -127,7 +127,12 @@ void CSettingsView::loadSettings()
#endif #endif
fillValidScalingRange(); 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->setValue(settings["video"]["targetfps"].Float());
ui->spinBoxFramerateLimit->setDisabled(settings["video"]["vsync"].Bool()); ui->spinBoxFramerateLimit->setDisabled(settings["video"]["vsync"].Bool());
ui->sliderReservedArea->setValue(std::round(settings["video"]["reservedWidth"].Float() * 100)); 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->sliderControllerSticksAcceleration->setValue(settings["input"]["controllerAxisScale"].Float() * 100);
ui->lineEditGameLobbyHost->setText(QString::fromStdString(settings["lobby"]["hostname"].String())); ui->lineEditGameLobbyHost->setText(QString::fromStdString(settings["lobby"]["hostname"].String()));
ui->spinBoxNetworkPortLobby->setValue(settings["lobby"]["port"].Integer()); ui->spinBoxNetworkPortLobby->setValue(settings["lobby"]["port"].Integer());
ui->buttonVSync->setChecked(settings["video"]["vsync"].Bool());
if (settings["video"]["fontsType"].String() == "auto") if (settings["video"]["fontsType"].String() == "auto")
ui->buttonFontAuto->setChecked(true); ui->buttonFontAuto->setChecked(true);
@ -195,7 +201,6 @@ void CSettingsView::loadSettings()
void CSettingsView::loadToggleButtonSettings() void CSettingsView::loadToggleButtonSettings()
{ {
setCheckbuttonState(ui->buttonShowIntro, settings["video"]["showIntro"].Bool()); setCheckbuttonState(ui->buttonShowIntro, settings["video"]["showIntro"].Bool());
setCheckbuttonState(ui->buttonVSync, settings["video"]["vsync"].Bool());
setCheckbuttonState(ui->buttonAutoCheck, settings["launcher"]["autoCheckRepositories"].Bool()); setCheckbuttonState(ui->buttonAutoCheck, settings["launcher"]["autoCheckRepositories"].Bool());
setCheckbuttonState(ui->buttonRepositoryDefault, settings["launcher"]["defaultRepositoryEnabled"].Bool()); setCheckbuttonState(ui->buttonRepositoryDefault, settings["launcher"]["defaultRepositoryEnabled"].Bool());
@ -212,10 +217,15 @@ void CSettingsView::loadToggleButtonSettings()
std::string cursorType = settings["video"]["cursor"].String(); std::string cursorType = settings["video"]["cursor"].String();
int cursorTypeIndex = vstd::find_pos(cursorTypesList, cursorType); int cursorTypeIndex = vstd::find_pos(cursorTypesList, cursorType);
setCheckbuttonState(ui->buttonCursorType, cursorTypeIndex); 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; int fontScalingPercentage = settings["video"]["fontScalingFactor"].Float() * 100;
ui->sliderScalingFont->setValue(fontScalingPercentage / 5); ui->sliderScalingFont->setValue(fontScalingPercentage / 5);
int cursorScalingPercentage = settings["video"]["cursorScalingFactor"].Float() * 100;
ui->sliderScalingCursor->setValue(cursorScalingPercentage / 5);
} }
void CSettingsView::fillValidResolutions() void CSettingsView::fillValidResolutions()
@ -494,6 +504,8 @@ void CSettingsView::on_buttonCursorType_toggled(bool value)
Settings node = settings.write["video"]["cursor"]; Settings node = settings.write["video"]["cursor"];
node->String() = cursorTypesList[value ? 1 : 0]; node->String() = cursorTypesList[value ? 1 : 0];
updateCheckbuttonText(ui->buttonCursorType); updateCheckbuttonText(ui->buttonCursorType);
ui->sliderScalingCursor->setDisabled(value == 1); // Not supported
ui->labelScalingCursorValue->setDisabled(value == 1); // Not supported
} }
void CSettingsView::loadTranslation() void CSettingsView::loadTranslation()
@ -627,7 +639,6 @@ void CSettingsView::on_buttonVSync_toggled(bool value)
Settings node = settings.write["video"]["vsync"]; Settings node = settings.write["video"]["vsync"];
node->Bool() = value; node->Bool() = value;
ui->spinBoxFramerateLimit->setDisabled(settings["video"]["vsync"].Bool()); ui->spinBoxFramerateLimit->setDisabled(settings["video"]["vsync"].Bool());
updateCheckbuttonText(ui->buttonVSync);
} }
void CSettingsView::on_comboBoxEnemyPlayerAI_currentTextChanged(const QString &arg1) 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"]; Settings node = settings.write["mods"]["validation"];
node->String() = "full"; 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;
}

View File

@ -97,6 +97,10 @@ private slots:
void on_buttonValidationFull_clicked(bool checked); void on_buttonValidationFull_clicked(bool checked);
void on_sliderScalingCursor_valueChanged(int value);
void on_buttonScalingAuto_toggled(bool checked);
private: private:
Ui::CSettingsView * ui; Ui::CSettingsView * ui;

File diff suppressed because it is too large Load Diff