From d9275b77570562a94c726f3fe630886c96850396 Mon Sep 17 00:00:00 2001
From: lat9nq <22451773+lat9nq@users.noreply.github.com>
Date: Tue, 15 Aug 2023 22:42:28 -0400
Subject: [PATCH] yuzu-qt: Enable specifying screenshot resolution

---
 src/yuzu/bootmanager.cpp                | 16 ++++-
 src/yuzu/configuration/configure_ui.cpp | 93 ++++++++++++++++++++++++-
 src/yuzu/configuration/configure_ui.ui  | 51 ++++++++++++++
 src/yuzu/uisettings.cpp                 | 31 +++++++++
 src/yuzu/uisettings.h                   | 10 +++
 5 files changed, 198 insertions(+), 3 deletions(-)

diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index bdd1497b52..593e59e8e9 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -11,6 +11,8 @@
 #include <glad/glad.h>
 
 #include <QtCore/qglobal.h>
+#include "common/settings_enums.h"
+#include "uisettings.h"
 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) && YUZU_USE_QT_MULTIMEDIA
 #include <QCamera>
 #include <QCameraImageCapture>
@@ -924,7 +926,19 @@ void GRenderWindow::CaptureScreenshot(const QString& screenshot_path) {
         return;
     }
 
-    const Layout::FramebufferLayout layout{Layout::FrameLayoutFromResolutionScale(res_scale)};
+    const Layout::FramebufferLayout layout{[res_scale]() {
+        if (UISettings::values.screenshot_height.GetValue() == 0 &&
+            UISettings::values.screenshot_aspect_ratio.GetValue() ==
+                Settings::ScreenshotAspectRatio::Auto) {
+            return Layout::FrameLayoutFromResolutionScale(res_scale);
+        }
+        const u32 height = UISettings::values.screenshot_height.GetValue();
+        const u32 width = UISettings::CalculateWidth(
+            height, UISettings::ConvertScreenshotRatioToRatio(
+                        UISettings::values.screenshot_aspect_ratio.GetValue()));
+        return Layout::DefaultFrameLayout(width, height);
+    }()};
+
     screenshot_image = QImage(QSize(layout.width, layout.height), QImage::Format_RGB32);
     renderer.RequestScreenshot(
         screenshot_image.bits(),
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp
index 2ebb803027..3c99c5709b 100644
--- a/src/yuzu/configuration/configure_ui.cpp
+++ b/src/yuzu/configuration/configure_ui.cpp
@@ -1,18 +1,30 @@
 // SPDX-FileCopyrightText: 2016 Citra Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+#include "yuzu/configuration/configure_ui.h"
+
 #include <array>
+#include <set>
+#include <stdexcept>
+#include <string>
 #include <utility>
-#include <QFileDialog>
 
+#include <QCheckBox>
+#include <QComboBox>
+#include <QCoreApplication>
 #include <QDirIterator>
+#include <QFileDialog>
+#include <QString>
+#include <QToolButton>
+#include <QVariant>
+
 #include "common/common_types.h"
 #include "common/fs/path_util.h"
 #include "common/logging/log.h"
 #include "common/settings.h"
+#include "common/settings_enums.h"
 #include "core/core.h"
 #include "ui_configure_ui.h"
-#include "yuzu/configuration/configure_ui.h"
 #include "yuzu/uisettings.h"
 
 namespace {
@@ -54,6 +66,52 @@ QString GetTranslatedRowTextName(size_t index) {
 }
 } // Anonymous namespace
 
+constexpr static std::array<std::pair<Settings::ScreenshotAspectRatio, std::string>, 5>
+    screenshot_aspect_ratio_translations = {
+        std::pair{Settings::ScreenshotAspectRatio::Auto, "Auto"},
+        std::pair{Settings::ScreenshotAspectRatio::R16_9, "16:9"},
+        std::pair{Settings::ScreenshotAspectRatio::R4_3, "4:3"},
+        std::pair{Settings::ScreenshotAspectRatio::R21_9, "21:9"},
+        std::pair{Settings::ScreenshotAspectRatio::R16_10, "16:10"},
+};
+
+static void PopulateAspectRatioComboBox(QComboBox* screenshot_aspect_ratio) {
+    screenshot_aspect_ratio->clear();
+
+    for (const auto& [value, name] : screenshot_aspect_ratio_translations) {
+        screenshot_aspect_ratio->addItem(QString::fromStdString(name));
+    }
+}
+
+static void PopulateResolutionComboBox(QComboBox* screenshot_height) {
+    screenshot_height->clear();
+
+    const auto& enumeration =
+        Settings::EnumMetadata<Settings::ResolutionSetup>::Canonicalizations();
+    Settings::ResolutionScalingInfo info{};
+    std::set<u32> resolutions{};
+    for (const auto& [name, value] : enumeration) {
+        Settings::TranslateResolutionInfo(value, info);
+        u32 height_undocked = 720 * info.up_factor;
+        u32 height_docked = 1080 * info.up_factor;
+        resolutions.emplace(height_undocked);
+        resolutions.emplace(height_docked);
+    }
+
+    screenshot_height->addItem(QStringLiteral("0"));
+    for (const auto res : resolutions) {
+        screenshot_height->addItem(QString::fromStdString(std::to_string(res)));
+    }
+}
+
+static u32 HeightToInt(const QString& height) {
+    try {
+        return std::stoi(height.toStdString());
+    } catch (std::invalid_argument& e) {
+        return 0;
+    }
+}
+
 ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent)
     : QWidget(parent), ui{std::make_unique<Ui::ConfigureUi>()}, system{system_} {
     ui->setupUi(this);
@@ -68,6 +126,9 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent)
     InitializeIconSizeComboBox();
     InitializeRowComboBoxes();
 
+    PopulateAspectRatioComboBox(ui->screenshot_aspect_ratio);
+    PopulateResolutionComboBox(ui->screenshot_height);
+
     SetConfiguration();
 
     // Force game list reload if any of the relevant settings are changed.
@@ -104,6 +165,25 @@ ConfigureUi::ConfigureUi(Core::System& system_, QWidget* parent)
             ui->screenshot_path_edit->setText(dir);
         }
     });
+
+    const auto update_height_text = [this]() {
+        const auto index = ui->screenshot_aspect_ratio->currentIndex();
+        const Settings::AspectRatio ratio = UISettings::ConvertScreenshotRatioToRatio(
+            screenshot_aspect_ratio_translations[index].first);
+        const auto height = HeightToInt(ui->screenshot_height->currentText());
+        const auto width = UISettings::CalculateWidth(height, ratio);
+        if (height == 0) {
+            ui->screenshot_width->setText(QString::fromStdString(fmt::format("Auto")));
+        } else {
+            ui->screenshot_width->setText(QString::fromStdString(std::to_string(width)));
+        }
+    };
+
+    connect(ui->screenshot_aspect_ratio, QOverload<int>::of(&QComboBox::currentIndexChanged),
+            update_height_text);
+    connect(ui->screenshot_height, &QComboBox::currentTextChanged, update_height_text);
+
+    update_height_text();
 }
 
 ConfigureUi::~ConfigureUi() = default;
@@ -123,6 +203,15 @@ void ConfigureUi::ApplyConfiguration() {
     UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked();
     Common::FS::SetYuzuPath(Common::FS::YuzuPath::ScreenshotsDir,
                             ui->screenshot_path_edit->text().toStdString());
+
+    const auto ratio =
+        screenshot_aspect_ratio_translations[ui->screenshot_aspect_ratio->currentIndex()].first;
+    UISettings::values.screenshot_aspect_ratio.SetValue(ratio);
+    const u32 height = HeightToInt(ui->screenshot_height->currentText());
+    UISettings::values.screenshot_height.SetValue(height);
+    UISettings::values.screenshot_width.SetValue(
+        UISettings::CalculateWidth(height, UISettings::ConvertScreenshotRatioToRatio(ratio)));
+
     system.ApplySettings();
 }
 
diff --git a/src/yuzu/configuration/configure_ui.ui b/src/yuzu/configuration/configure_ui.ui
index 10bb273121..906fdd5b30 100644
--- a/src/yuzu/configuration/configure_ui.ui
+++ b/src/yuzu/configuration/configure_ui.ui
@@ -201,6 +201,57 @@
           </item>
          </layout>
         </item>
+        <item>
+         <layout class="QGridLayout" name="gridLayout">
+          <property name="spacing">
+           <number>6</number>
+          </property>
+          <item row="1" column="0">
+           <widget class="QLabel" name="label_3">
+            <property name="text">
+             <string>Resolution:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="0" column="1">
+           <widget class="QComboBox" name="screenshot_aspect_ratio"/>
+          </item>
+          <item row="0" column="0">
+           <widget class="QLabel" name="label_2">
+            <property name="text">
+             <string>Aspect Ratio:</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="1">
+           <layout class="QHBoxLayout" name="horizontalLayout_5">
+            <item>
+             <widget class="QLineEdit" name="screenshot_width">
+              <property name="enabled">
+               <bool>true</bool>
+              </property>
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+                <horstretch>0</horstretch>
+                <verstretch>0</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="readOnly">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+            <item>
+             <widget class="QComboBox" name="screenshot_height">
+              <property name="editable">
+               <bool>true</bool>
+              </property>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </item>
        </layout>
       </item>
      </layout>
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index f03dc01ddc..3ab0d1b454 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -36,4 +36,35 @@ bool IsDarkTheme() {
 
 Values values = {};
 
+u32 CalculateWidth(u32 height, Settings::AspectRatio ratio) {
+    switch (ratio) {
+    case Settings::AspectRatio::R4_3:
+        return height * 4 / 3;
+    case Settings::AspectRatio::R21_9:
+        return height * 21 / 9;
+    case Settings::AspectRatio::R16_10:
+        return height * 16 / 10;
+    case Settings::AspectRatio::R16_9:
+    case Settings::AspectRatio::Stretch:
+        break;
+    }
+    return height * 16 / 9;
+}
+
+Settings::AspectRatio ConvertScreenshotRatioToRatio(Settings::ScreenshotAspectRatio ratio) {
+    switch (ratio) {
+    case Settings::ScreenshotAspectRatio::Auto:
+        return Settings::values.aspect_ratio.GetValue();
+    case Settings::ScreenshotAspectRatio::R16_9:
+        return Settings::AspectRatio::R16_9;
+    case Settings::ScreenshotAspectRatio::R4_3:
+        return Settings::AspectRatio::R4_3;
+    case Settings::ScreenshotAspectRatio::R21_9:
+        return Settings::AspectRatio::R21_9;
+    case Settings::ScreenshotAspectRatio::R16_10:
+        return Settings::AspectRatio::R16_10;
+    }
+    return Settings::AspectRatio::R16_9;
+}
+
 } // namespace UISettings
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index c9c89cee49..7b5d630d7b 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -13,6 +13,7 @@
 #include <QVector>
 #include "common/common_types.h"
 #include "common/settings.h"
+#include "common/settings_enums.h"
 
 using Settings::Category;
 using Settings::Setting;
@@ -127,8 +128,14 @@ struct Values {
     // logging
     Setting<bool> show_console{linkage, false, "showConsole", Category::Ui};
 
+    // Screenshots
     Setting<bool> enable_screenshot_save_as{linkage, true, "enable_screenshot_save_as",
                                             Category::Screenshots};
+    Setting<u32> screenshot_height{linkage, 0, "screenshot_height", Category::Screenshots};
+    Setting<u32> screenshot_width{linkage, 0, "screenshot_width", Category::Screenshots};
+    Setting<Settings::ScreenshotAspectRatio> screenshot_aspect_ratio{
+        linkage, Settings::ScreenshotAspectRatio::Auto, "screenshot_aspect_ratio",
+        Category::Screenshots};
 
     QString roms_path;
     QString symbols_path;
@@ -187,6 +194,9 @@ struct Values {
 
 extern Values values;
 
+u32 CalculateWidth(u32 height, Settings::AspectRatio ratio);
+Settings::AspectRatio ConvertScreenshotRatioToRatio(Settings::ScreenshotAspectRatio ratio);
+
 } // namespace UISettings
 
 Q_DECLARE_METATYPE(UISettings::GameDir*);
-- 
GitLab