diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt
index ee087b9dd..987fd4302 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/IntSetting.kt
@@ -68,7 +68,8 @@ enum class IntSetting(
ORIENTATION_OPTION("screen_orientation", Settings.SECTION_LAYOUT, 2),
DISABLE_RIGHT_EYE_RENDER("disable_right_eye_render", Settings.SECTION_RENDERER, 0),
TURBO_LIMIT("turbo_limit", Settings.SECTION_CORE, 200),
- PERFORMANCE_OVERLAY_POSITION("performance_overlay_position", Settings.SECTION_LAYOUT, 0);
+ PERFORMANCE_OVERLAY_POSITION("performance_overlay_position", Settings.SECTION_LAYOUT, 0),
+ ASPECT_RATIO("aspect_ratio", Settings.SECTION_LAYOUT, 0);
override var int: Int = defaultValue
diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt
index 29bab03d1..c075aadde 100644
--- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -1083,6 +1083,18 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
IntSetting.PORTRAIT_SCREEN_LAYOUT.defaultValue
)
)
+ add(
+ SingleChoiceSetting(
+ IntSetting.ASPECT_RATIO,
+ R.string.emulation_aspect_ratio,
+ 0,
+ R.array.aspectRatioNames,
+ R.array.aspectRatioValues,
+ IntSetting.ASPECT_RATIO.key,
+ IntSetting.ASPECT_RATIO.defaultValue,
+ isEnabled = IntSetting.SCREEN_LAYOUT.int == 1,
+ )
+ )
add(
SingleChoiceSetting(
IntSetting.SMALL_SCREEN_POSITION,
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp
index e7d878294..ed24f3c8e 100644
--- a/src/android/app/src/main/jni/config.cpp
+++ b/src/android/app/src/main/jni/config.cpp
@@ -197,6 +197,7 @@ void Config::ReadValues() {
ReadSetting("Layout", Settings::values.custom_bottom_x);
ReadSetting("Layout", Settings::values.custom_bottom_y);
ReadSetting("Layout", Settings::values.custom_bottom_width);
+ ReadSetting("Layout", Settings::values.aspect_ratio);
ReadSetting("Layout", Settings::values.custom_bottom_height);
ReadSetting("Layout", Settings::values.cardboard_screen_size);
ReadSetting("Layout", Settings::values.cardboard_x_shift);
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index f1204291f..543b59913 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -63,6 +63,24 @@
- 7
+
+ - @string/aspect_ratio_default
+ - @string/aspect_ratio_16_9
+ - @string/aspect_ratio_4_3
+ - @string/aspect_ratio_21_9_fullscreen
+ - @string/aspect_ratio_16_10_fullscreen_stretched
+ - @string/aspect_ratio_stretch
+
+
+ - 0
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+
+
+
- @string/auto_select
- @string/system_region_jpn
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 0aeebf286..4c50488de 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -326,6 +326,12 @@
Reverse Landscape
Portrait
Reverse Portrait
+ Default
+ 16:9
+ 4:3
+ 21:9
+ 16:10
+ Stretch
Clear
@@ -411,6 +417,7 @@
D-Pad Sliding
Open Settings
Open Cheats
+ Aspect Ratio
Landscape Screen Layout
Portrait Screen Layout
Large Screen
diff --git a/src/common/settings.h b/src/common/settings.h
index d061add3c..ed6d438bd 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -106,6 +106,15 @@ enum class TextureSampling : u32 {
Linear = 2,
};
+enum class AspectRatio : u32 {
+ Default = 0,
+ R16_9 = 1,
+ R4_3 = 2,
+ R21_9 = 3,
+ R16_10 = 4,
+ Stretch = 5,
+};
+
namespace NativeButton {
enum Values {
@@ -522,7 +531,7 @@ struct Values {
Setting custom_bottom_width{640, "custom_bottom_width"};
Setting custom_bottom_height{480, "custom_bottom_height"};
Setting custom_second_layer_opacity{100, "custom_second_layer_opacity"};
-
+ SwitchableSetting aspect_ratio{AspectRatio::Default, "aspect_ratio"};
SwitchableSetting screen_top_stretch{false, "screen_top_stretch"};
Setting screen_top_leftright_padding{0, "screen_top_leftright_padding"};
Setting screen_top_topbottom_padding{0, "screen_top_topbottom_padding"};
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index 8b284b30c..4a1cbf3e5 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -27,11 +27,11 @@ u32 FramebufferLayout::GetScalingRatio() const {
// Finds the largest size subrectangle contained in window area that is confined to the aspect ratio
template
static Common::Rectangle MaxRectangle(Common::Rectangle window_area,
- float screen_aspect_ratio) {
+ float window_aspect_ratio) {
float scale = std::min(static_cast(window_area.GetWidth()),
- window_area.GetHeight() / screen_aspect_ratio);
+ window_area.GetHeight() / window_aspect_ratio);
return Common::Rectangle{0, 0, static_cast(std::round(scale)),
- static_cast(std::round(scale * screen_aspect_ratio))};
+ static_cast(std::round(scale * window_aspect_ratio))};
}
FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool swapped, bool upright) {
@@ -76,15 +76,41 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool up
Common::Rectangle screen_window_area{0, 0, width, height};
Common::Rectangle top_screen;
Common::Rectangle bot_screen;
- float emulation_aspect_ratio;
+
+ // TODO: This is kind of gross, make it platform agnostic. -OS
+#ifdef ANDROID
+ const float window_aspect_ratio = static_cast(height) / width;
+ const auto aspect_ratio_setting = Settings::values.aspect_ratio.GetValue();
+
+ float emulation_aspect_ratio = (swapped) ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO;
+ switch (aspect_ratio_setting) {
+ case Settings::AspectRatio::Default:
+ break;
+ case Settings::AspectRatio::Stretch:
+ emulation_aspect_ratio = window_aspect_ratio;
+ break;
+ default:
+ emulation_aspect_ratio = res.GetAspectRatioValue(aspect_ratio_setting);
+ }
+
+ top_screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
+ bot_screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
+
+ if (window_aspect_ratio < emulation_aspect_ratio) {
+ top_screen =
+ top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2);
+ bot_screen =
+ bot_screen.TranslateX((screen_window_area.GetWidth() - bot_screen.GetWidth()) / 2);
+ } else {
+ top_screen = top_screen.TranslateY((height - top_screen.GetHeight()) / 2);
+ bot_screen = bot_screen.TranslateY((height - bot_screen.GetHeight()) / 2);
+ }
+#else
top_screen = MaxRectangle(screen_window_area, TOP_SCREEN_ASPECT_RATIO);
bot_screen = MaxRectangle(screen_window_area, BOT_SCREEN_ASPECT_RATIO);
- emulation_aspect_ratio = (swapped) ? BOT_SCREEN_ASPECT_RATIO : TOP_SCREEN_ASPECT_RATIO;
const bool stretched = (Settings::values.screen_top_stretch.GetValue() && !swapped) ||
(Settings::values.screen_bottom_stretch.GetValue() && swapped);
- const float window_aspect_ratio = static_cast(height) / width;
-
if (stretched) {
top_screen = {Settings::values.screen_top_leftright_padding.GetValue(),
Settings::values.screen_top_topbottom_padding.GetValue(),
@@ -94,15 +120,12 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool up
Settings::values.screen_bottom_topbottom_padding.GetValue(),
width - Settings::values.screen_bottom_leftright_padding.GetValue(),
height - Settings::values.screen_bottom_topbottom_padding.GetValue()};
- } else if (window_aspect_ratio < emulation_aspect_ratio) {
- top_screen =
- top_screen.TranslateX((screen_window_area.GetWidth() - top_screen.GetWidth()) / 2);
- bot_screen =
- bot_screen.TranslateX((screen_window_area.GetWidth() - bot_screen.GetWidth()) / 2);
} else {
top_screen = top_screen.TranslateY((height - top_screen.GetHeight()) / 2);
bot_screen = bot_screen.TranslateY((height - bot_screen.GetHeight()) / 2);
}
+#endif
+
res.top_screen = top_screen;
res.bottom_screen = bot_screen;
if (upright) {
@@ -125,9 +148,8 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr
FramebufferLayout res{width, height, true, true, {}, {}, !upright};
// Split the window into two parts. Give proportional width to the smaller screen
// To do that, find the total emulation box and maximize that based on window size
- const float window_aspect_ratio = static_cast(height) / width;
- float emulation_aspect_ratio;
u32 gap = (u32)(Settings::values.screen_gap.GetValue() * scale_factor);
+
float large_height =
swapped ? Core::kScreenBottomHeight * scale_factor : Core::kScreenTopHeight * scale_factor;
float small_height =
@@ -137,7 +159,8 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr
float small_width =
static_cast(swapped ? Core::kScreenTopWidth : Core::kScreenBottomWidth);
- float emulation_width, emulation_height;
+ float emulation_width;
+ float emulation_height;
if (vertical) {
// width is just the larger size at this point
emulation_width = std::max(large_width, small_width);
@@ -147,11 +170,15 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr
emulation_height = std::max(large_height, small_height);
}
- emulation_aspect_ratio = emulation_height / emulation_width;
+ const float window_aspect_ratio = static_cast(height) / width;
+ const float emulation_aspect_ratio = emulation_height / emulation_width;
Common::Rectangle screen_window_area{0, 0, width, height};
Common::Rectangle total_rect = MaxRectangle(screen_window_area, emulation_aspect_ratio);
- const float scale_amount = total_rect.GetHeight() * 1.f / emulation_height * 1.f;
+ // TODO: Wtf does this `scale_amount` value represent? -OS
+ const float scale_amount = static_cast(total_rect.GetHeight()) / emulation_height;
+ gap = static_cast(static_cast(gap) * scale_amount);
+
Common::Rectangle large_screen =
Common::Rectangle{total_rect.left, total_rect.top,
static_cast(large_width * scale_amount + total_rect.left),
@@ -168,7 +195,6 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr
// shift the large screen so it is at the top position of the bounding rectangle
large_screen = large_screen.TranslateY((height - total_rect.GetHeight()) / 2);
}
- gap = static_cast(static_cast(gap) * scale_amount);
switch (small_screen_position) {
case Settings::SmallScreenPosition::TopRight:
@@ -676,4 +702,21 @@ std::pair GetMinimumSizeFromLayout(Settings::LayoutOption la
}
}
+float FramebufferLayout::GetAspectRatioValue(Settings::AspectRatio aspect_ratio) {
+ switch (aspect_ratio) {
+ case Settings::AspectRatio::R16_9:
+ return 9.0f / 16.0f;
+ case Settings::AspectRatio::R4_3:
+ return 3.0f / 4.0f;
+ case Settings::AspectRatio::R21_9:
+ return 9.0f / 21.0f;
+ case Settings::AspectRatio::R16_10:
+ return 10.0f / 16.0f;
+ default:
+ LOG_ERROR(Frontend, "Unknown aspect ratio enum value: {}",
+ static_cast::type>(aspect_ratio));
+ return 1.0f; // Arbitrary fallback value
+ }
+}
+
} // namespace Layout
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 81501decd..8c315f515 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -44,6 +44,8 @@ struct FramebufferLayout {
* screen.
*/
u32 GetScalingRatio() const;
+
+ static float GetAspectRatioValue(Settings::AspectRatio aspect_ratio);
};
/**