android: Add aspect ratio setting for single screen layout mode (#913)

* android: Add aspect ratio setting for single screen layout mode

Co-Authored-By: Morph <39850852+Morph1984@users.noreply.github.com>

* Partial rewrite to address issues and improve readability

---------

Co-authored-by: Morph <39850852+Morph1984@users.noreply.github.com>
Co-authored-by: OpenSauce04 <opensauce04@gmail.com>
This commit is contained in:
Briar 2025-05-12 20:58:01 +02:00 committed by GitHub
parent c204ebf021
commit 580d46e3d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 113 additions and 20 deletions

View File

@ -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

View File

@ -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,

View File

@ -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);

View File

@ -63,6 +63,24 @@
<item>7</item>
</integer-array>
<string-array name="aspectRatioNames">
<item>@string/aspect_ratio_default</item>
<item>@string/aspect_ratio_16_9</item>
<item>@string/aspect_ratio_4_3</item>
<item>@string/aspect_ratio_21_9_fullscreen</item>
<item>@string/aspect_ratio_16_10_fullscreen_stretched</item>
<item>@string/aspect_ratio_stretch</item>
</string-array>
<integer-array name="aspectRatioValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
</integer-array>
<string-array name="regionNames">
<item>@string/auto_select</item>
<item>@string/system_region_jpn</item>

View File

@ -326,6 +326,12 @@
<string name="layout_screen_orientation_landscape_reverse">Reverse Landscape</string>
<string name="layout_screen_orientation_portrait">Portrait</string>
<string name="layout_screen_orientation_portrait_reverse">Reverse Portrait</string>
<string name="aspect_ratio_default">Default</string>
<string name="aspect_ratio_16_9">16:9</string>
<string name="aspect_ratio_4_3">4:3</string>
<string name="aspect_ratio_21_9_fullscreen">21:9</string>
<string name="aspect_ratio_16_10_fullscreen_stretched">16:10</string>
<string name="aspect_ratio_stretch">Stretch</string>
<!-- Miscellaneous -->
<string name="clear">Clear</string>
@ -411,6 +417,7 @@
<string name="emulation_control_dpad_slide_enable">D-Pad Sliding</string>
<string name="emulation_open_settings">Open Settings</string>
<string name="emulation_open_cheats">Open Cheats</string>
<string name="emulation_aspect_ratio">Aspect Ratio</string>
<string name="emulation_switch_screen_layout">Landscape Screen Layout</string>
<string name="emulation_switch_portrait_layout">Portrait Screen Layout</string>
<string name="emulation_screen_layout_largescreen">Large Screen</string>

View File

@ -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<u16> custom_bottom_width{640, "custom_bottom_width"};
Setting<u16> custom_bottom_height{480, "custom_bottom_height"};
Setting<u16> custom_second_layer_opacity{100, "custom_second_layer_opacity"};
SwitchableSetting<AspectRatio> aspect_ratio{AspectRatio::Default, "aspect_ratio"};
SwitchableSetting<bool> screen_top_stretch{false, "screen_top_stretch"};
Setting<u16> screen_top_leftright_padding{0, "screen_top_leftright_padding"};
Setting<u16> screen_top_topbottom_padding{0, "screen_top_topbottom_padding"};

View File

@ -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 <class T>
static Common::Rectangle<T> MaxRectangle(Common::Rectangle<T> window_area,
float screen_aspect_ratio) {
float window_aspect_ratio) {
float scale = std::min(static_cast<float>(window_area.GetWidth()),
window_area.GetHeight() / screen_aspect_ratio);
window_area.GetHeight() / window_aspect_ratio);
return Common::Rectangle<T>{0, 0, static_cast<T>(std::round(scale)),
static_cast<T>(std::round(scale * screen_aspect_ratio))};
static_cast<T>(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<u32> screen_window_area{0, 0, width, height};
Common::Rectangle<u32> top_screen;
Common::Rectangle<u32> 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<float>(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<float>(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<float>(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<float>(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<float>(height) / width;
const float emulation_aspect_ratio = emulation_height / emulation_width;
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
Common::Rectangle<u32> 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<float>(total_rect.GetHeight()) / emulation_height;
gap = static_cast<u32>(static_cast<float>(gap) * scale_amount);
Common::Rectangle<u32> large_screen =
Common::Rectangle<u32>{total_rect.left, total_rect.top,
static_cast<u32>(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<u32>(static_cast<float>(gap) * scale_amount);
switch (small_screen_position) {
case Settings::SmallScreenPosition::TopRight:
@ -676,4 +702,21 @@ std::pair<unsigned, unsigned> 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<std::underlying_type<Settings::AspectRatio>::type>(aspect_ratio));
return 1.0f; // Arbitrary fallback value
}
}
} // namespace Layout

View File

@ -44,6 +44,8 @@ struct FramebufferLayout {
* screen.
*/
u32 GetScalingRatio() const;
static float GetAspectRatioValue(Settings::AspectRatio aspect_ratio);
};
/**