mirror of
https://github.com/azahar-emu/azahar.git
synced 2026-06-06 02:33:44 -04:00
Implement integer scaling (#1400)
This commit is contained in:
parent
03d62efe13
commit
3255620934
12 changed files with 121 additions and 119 deletions
|
|
@ -34,6 +34,7 @@ enum class BooleanSetting(
|
|||
LLE_APPLETS("lle_applets", Settings.SECTION_SYSTEM, false),
|
||||
NEW_3DS("is_new_3ds", Settings.SECTION_SYSTEM, true),
|
||||
LINEAR_FILTERING("filter_mode", Settings.SECTION_RENDERER, true),
|
||||
USE_INTEGER_SCALING("use_integer_scaling",Settings.SECTION_RENDERER, false),
|
||||
SHADERS_ACCURATE_MUL("shaders_accurate_mul", Settings.SECTION_RENDERER, false),
|
||||
DISK_SHADER_CACHE("use_disk_shader_cache", Settings.SECTION_RENDERER, true),
|
||||
DUMP_TEXTURES("dump_textures", Settings.SECTION_UTILITY, false),
|
||||
|
|
|
|||
|
|
@ -898,6 +898,15 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
|||
IntSetting.RESOLUTION_FACTOR.key,
|
||||
IntSetting.RESOLUTION_FACTOR.defaultValue
|
||||
)
|
||||
)
|
||||
add(
|
||||
SwitchSetting(
|
||||
BooleanSetting.USE_INTEGER_SCALING,
|
||||
R.string.use_integer_scaling,
|
||||
R.string.use_integer_scaling_description,
|
||||
BooleanSetting.USE_INTEGER_SCALING.key,
|
||||
BooleanSetting.USE_INTEGER_SCALING.defaultValue
|
||||
)
|
||||
)
|
||||
add(
|
||||
SwitchSetting(
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ void Config::ReadValues() {
|
|||
Settings::values.pp_shader_name =
|
||||
sdl2_config->GetString("Renderer", "pp_shader_name", default_shader);
|
||||
ReadSetting("Renderer", Settings::values.filter_mode);
|
||||
ReadSetting("Renderer", Settings::values.use_integer_scaling);
|
||||
|
||||
ReadSetting("Renderer", Settings::values.bg_red);
|
||||
ReadSetting("Renderer", Settings::values.bg_green);
|
||||
|
|
|
|||
|
|
@ -148,6 +148,10 @@ use_disk_shader_cache =
|
|||
# factor for the 3DS resolution
|
||||
resolution_factor =
|
||||
|
||||
# Use Integer Scaling when the layout allows
|
||||
# 0: Off (default), 1: On
|
||||
use_integer_scaling =
|
||||
|
||||
# Turns on the frame limiter, which will limit frames output to the target game speed
|
||||
# 0: Off, 1: On (default)
|
||||
use_frame_limit =
|
||||
|
|
|
|||
|
|
@ -250,6 +250,8 @@
|
|||
<string name="async_shaders_description">Compiles shaders in the background to reduce stuttering during gameplay. When enabled expect temporary graphical glitches</string>
|
||||
<string name="linear_filtering">Linear Filtering</string>
|
||||
<string name="linear_filtering_description">Enables linear filtering, which causes game visuals to appear smoother.</string>
|
||||
<string name="use_integer_scaling">Integer Scaling</string>
|
||||
<string name="use_integer_scaling_description">Scales the screens with an integer multiplier of the original 3DS screen. For layouts with two different screen sizes, the largest screen is integer-scaled.</string>
|
||||
<string name="texture_filter_name">Texture Filter</string>
|
||||
<string name="texture_filter_description">Enhances the visuals of applications by applying a filter to textures. The supported filters are Anime4K Ultrafast, Bicubic, ScaleForce, xBRZ freescale, and MMPX.</string>
|
||||
<string name="delay_render_thread">Delay Game Render Thread</string>
|
||||
|
|
|
|||
|
|
@ -700,6 +700,7 @@ void QtConfig::ReadRendererValues() {
|
|||
ReadGlobalSetting(Settings::values.use_vsync);
|
||||
ReadGlobalSetting(Settings::values.use_display_refresh_rate_detection);
|
||||
ReadGlobalSetting(Settings::values.resolution_factor);
|
||||
ReadGlobalSetting(Settings::values.use_integer_scaling);
|
||||
ReadGlobalSetting(Settings::values.frame_limit);
|
||||
ReadGlobalSetting(Settings::values.turbo_limit);
|
||||
|
||||
|
|
@ -1241,6 +1242,7 @@ void QtConfig::SaveRendererValues() {
|
|||
WriteGlobalSetting(Settings::values.use_vsync);
|
||||
WriteGlobalSetting(Settings::values.use_display_refresh_rate_detection);
|
||||
WriteGlobalSetting(Settings::values.resolution_factor);
|
||||
WriteGlobalSetting(Settings::values.use_integer_scaling);
|
||||
WriteGlobalSetting(Settings::values.frame_limit);
|
||||
WriteGlobalSetting(Settings::values.turbo_limit);
|
||||
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ void ConfigureEnhancements::SetConfiguration() {
|
|||
static_cast<int>(Settings::values.mono_render_option.GetValue()));
|
||||
updateShaders(Settings::values.render_3d.GetValue());
|
||||
ui->toggle_linear_filter->setChecked(Settings::values.filter_mode.GetValue());
|
||||
ui->use_integer_scaling->setChecked(Settings::values.use_integer_scaling.GetValue());
|
||||
ui->toggle_dump_textures->setChecked(Settings::values.dump_textures.GetValue());
|
||||
ui->toggle_custom_textures->setChecked(Settings::values.custom_textures.GetValue());
|
||||
ui->toggle_preload_textures->setChecked(Settings::values.preload_textures.GetValue());
|
||||
|
|
@ -127,6 +128,8 @@ void ConfigureEnhancements::ApplyConfiguration() {
|
|||
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.filter_mode,
|
||||
ui->toggle_linear_filter, linear_filter);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_integer_scaling,
|
||||
ui->use_integer_scaling, use_integer_scaling);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.texture_filter,
|
||||
ui->texture_filter_combobox);
|
||||
ConfigurationShared::ApplyPerGameSetting(&Settings::values.dump_textures,
|
||||
|
|
@ -148,6 +151,7 @@ void ConfigureEnhancements::SetupPerGameUI() {
|
|||
ui->widget_resolution->setEnabled(Settings::values.resolution_factor.UsingGlobal());
|
||||
ui->widget_texture_filter->setEnabled(Settings::values.texture_filter.UsingGlobal());
|
||||
ui->toggle_linear_filter->setEnabled(Settings::values.filter_mode.UsingGlobal());
|
||||
ui->use_integer_scaling->setEnabled(Settings::values.use_integer_scaling.UsingGlobal());
|
||||
ui->toggle_dump_textures->setEnabled(Settings::values.dump_textures.UsingGlobal());
|
||||
ui->toggle_custom_textures->setEnabled(Settings::values.custom_textures.UsingGlobal());
|
||||
ui->toggle_preload_textures->setEnabled(Settings::values.preload_textures.UsingGlobal());
|
||||
|
|
@ -166,6 +170,8 @@ void ConfigureEnhancements::SetupPerGameUI() {
|
|||
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_linear_filter, Settings::values.filter_mode,
|
||||
linear_filter);
|
||||
ConfigurationShared::SetColoredTristate(
|
||||
ui->use_integer_scaling, Settings::values.use_integer_scaling, use_integer_scaling);
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_dump_textures,
|
||||
Settings::values.dump_textures, dump_textures);
|
||||
ConfigurationShared::SetColoredTristate(ui->toggle_custom_textures,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2019 Citra Emulator Project
|
||||
// Copyright Citra Emulator Project / Azahar Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
|
|
@ -39,6 +39,7 @@ private:
|
|||
|
||||
std::unique_ptr<Ui::ConfigureEnhancements> ui;
|
||||
ConfigurationShared::CheckState linear_filter;
|
||||
ConfigurationShared::CheckState use_integer_scaling;
|
||||
ConfigurationShared::CheckState dump_textures;
|
||||
ConfigurationShared::CheckState custom_textures;
|
||||
ConfigurationShared::CheckState preload_textures;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>440</width>
|
||||
<width>639</width>
|
||||
<height>950</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
|
@ -110,6 +110,16 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="use_integer_scaling">
|
||||
<property name="text">
|
||||
<string>Use Integer Scaling</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Use Integer Scaling</p><p>Enforces that the larger screen in all layouts is an integer scale of the 240px height of the original 3DS screen.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_linear_filter">
|
||||
<property name="text">
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ void LogSettings() {
|
|||
log_setting("Renderer_ShadersAccurateMul", values.shaders_accurate_mul.GetValue());
|
||||
log_setting("Renderer_UseShaderJit", values.use_shader_jit.GetValue());
|
||||
log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue());
|
||||
log_setting("Renderer_UseIntegerScaling", values.use_integer_scaling.GetValue());
|
||||
log_setting("Renderer_FrameLimit", values.frame_limit.GetValue());
|
||||
log_setting("Renderer_VSyncNew", values.use_vsync.GetValue());
|
||||
log_setting("Renderer_PostProcessingShader", values.pp_shader_name.GetValue());
|
||||
|
|
@ -206,6 +207,7 @@ void RestoreGlobalState(bool is_powered_on) {
|
|||
values.shaders_accurate_mul.SetGlobal(true);
|
||||
values.use_vsync.SetGlobal(true);
|
||||
values.resolution_factor.SetGlobal(true);
|
||||
values.use_integer_scaling.SetGlobal(true);
|
||||
values.frame_limit.SetGlobal(true);
|
||||
values.texture_filter.SetGlobal(true);
|
||||
values.texture_sampling.SetGlobal(true);
|
||||
|
|
|
|||
|
|
@ -524,6 +524,7 @@ struct Values {
|
|||
true, "use_display_refresh_rate_detection"};
|
||||
Setting<bool> use_shader_jit{true, "use_shader_jit"};
|
||||
SwitchableSetting<u32, true> resolution_factor{1, 0, 10, "resolution_factor"};
|
||||
SwitchableSetting<bool> use_integer_scaling{false, "use_integer_scaling"};
|
||||
SwitchableSetting<double, true> frame_limit{100, 0, 1000, "frame_limit"};
|
||||
SwitchableSetting<double, true> turbo_limit{200, 0, 1000, "turbo_limit"};
|
||||
SwitchableSetting<TextureFilter> texture_filter{TextureFilter::NoFilter, "texture_filter"};
|
||||
|
|
|
|||
|
|
@ -25,13 +25,33 @@ u32 FramebufferLayout::GetScalingRatio() const {
|
|||
}
|
||||
|
||||
// Finds the largest size subrectangle contained in window area that is confined to the aspect ratio
|
||||
// aligned to the upper-left corner of the bounding rectangle
|
||||
template <class T>
|
||||
static Common::Rectangle<T> MaxRectangle(Common::Rectangle<T> window_area,
|
||||
float window_aspect_ratio) {
|
||||
float scale = std::min(static_cast<float>(window_area.GetWidth()),
|
||||
window_area.GetHeight() / window_aspect_ratio);
|
||||
return Common::Rectangle<T>{0, 0, static_cast<T>(std::round(scale)),
|
||||
static_cast<T>(std::round(scale * window_aspect_ratio))};
|
||||
return Common::Rectangle<T>{
|
||||
window_area.left, window_area.top, window_area.left + static_cast<T>(std::round(scale)),
|
||||
window_area.top + static_cast<T>(std::round(scale * window_aspect_ratio))};
|
||||
}
|
||||
|
||||
// overload of the above that takes an inner rectangle instead of an aspect ratio, and can be
|
||||
// limited to integer scaling if desired
|
||||
template <class T>
|
||||
static Common::Rectangle<T> MaxRectangle(Common::Rectangle<T> bounding_window,
|
||||
Common::Rectangle<T> inner_window,
|
||||
bool use_integer = false) {
|
||||
float scale =
|
||||
std::min(static_cast<float>(bounding_window.GetWidth()) / inner_window.GetWidth(),
|
||||
static_cast<float>(bounding_window.GetHeight()) / inner_window.GetHeight());
|
||||
if (use_integer && scale >= 1.0) {
|
||||
scale = std::floor(scale);
|
||||
}
|
||||
return Common::Rectangle(bounding_window.left, bounding_window.top,
|
||||
bounding_window.left + static_cast<T>(inner_window.GetWidth() * scale),
|
||||
bounding_window.top +
|
||||
static_cast<T>(inner_window.GetHeight() * scale));
|
||||
}
|
||||
|
||||
FramebufferLayout DefaultFrameLayout(u32 width, u32 height, bool swapped, bool upright) {
|
||||
|
|
@ -74,41 +94,31 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool up
|
|||
FramebufferLayout res{width, height, !swapped, swapped, {}, {}, !upright};
|
||||
|
||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||
Common::Rectangle<u32> top_screen;
|
||||
Common::Rectangle<u32> bot_screen;
|
||||
Common::Rectangle<u32> top_screen{0, 0, Core::kScreenTopWidth, Core::kScreenTopHeight};
|
||||
Common::Rectangle<u32> bot_screen{0, 0, Core::kScreenBottomWidth, Core::kScreenBottomHeight};
|
||||
|
||||
// TODO: This is kind of gross, make it platform agnostic. -OS
|
||||
#ifdef ANDROID
|
||||
const float window_aspect_ratio = static_cast<float>(height) / width;
|
||||
const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(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:
|
||||
// this is the only one where we allow integer scaling to apply
|
||||
// also the only option on desktop
|
||||
top_screen = MaxRectangle(screen_window_area, top_screen,
|
||||
Settings::values.use_integer_scaling.GetValue());
|
||||
bot_screen = MaxRectangle(screen_window_area, bot_screen,
|
||||
Settings::values.use_integer_scaling.GetValue());
|
||||
break;
|
||||
case Settings::AspectRatio::Stretch:
|
||||
emulation_aspect_ratio = window_aspect_ratio;
|
||||
top_screen = MaxRectangle(screen_window_area, window_aspect_ratio);
|
||||
bot_screen = MaxRectangle(screen_window_area, window_aspect_ratio);
|
||||
break;
|
||||
default:
|
||||
emulation_aspect_ratio = res.GetAspectRatioValue(aspect_ratio_setting);
|
||||
float emulation_aspect_ratio = FramebufferLayout::GetAspectRatioValue(aspect_ratio_setting);
|
||||
top_screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
bot_screen = MaxRectangle(screen_window_area, emulation_aspect_ratio);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
const bool stretched = (Settings::values.screen_top_stretch.GetValue() && !swapped) ||
|
||||
(Settings::values.screen_bottom_stretch.GetValue() && swapped);
|
||||
if (stretched) {
|
||||
|
|
@ -126,7 +136,6 @@ FramebufferLayout SingleFrameLayout(u32 width, u32 height, bool swapped, bool up
|
|||
bot_screen = bot_screen.TranslateX((width - bot_screen.GetWidth()) / 2)
|
||||
.TranslateY((height - bot_screen.GetHeight()) / 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
res.top_screen = top_screen;
|
||||
res.bottom_screen = bot_screen;
|
||||
|
|
@ -150,19 +159,17 @@ 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
|
||||
u32 gap = (u32)(Settings::values.screen_gap.GetValue() * scale_factor);
|
||||
u32 gap = (u32)(Settings::values.screen_gap.GetValue());
|
||||
|
||||
float large_height =
|
||||
swapped ? Core::kScreenBottomHeight * scale_factor : Core::kScreenTopHeight * scale_factor;
|
||||
float small_height =
|
||||
static_cast<float>(swapped ? Core::kScreenTopHeight : Core::kScreenBottomHeight);
|
||||
float large_width =
|
||||
swapped ? Core::kScreenBottomWidth * scale_factor : Core::kScreenTopWidth * scale_factor;
|
||||
float small_width =
|
||||
static_cast<float>(swapped ? Core::kScreenTopWidth : Core::kScreenBottomWidth);
|
||||
u32 large_height = swapped ? Core::kScreenBottomHeight : Core::kScreenTopHeight;
|
||||
u32 small_height = static_cast<u32>(swapped ? Core::kScreenTopHeight / scale_factor
|
||||
: Core::kScreenBottomHeight / scale_factor);
|
||||
u32 large_width = swapped ? Core::kScreenBottomWidth : Core::kScreenTopWidth;
|
||||
u32 small_width = static_cast<u32>(swapped ? Core::kScreenTopWidth / scale_factor
|
||||
: Core::kScreenBottomWidth / scale_factor);
|
||||
|
||||
float emulation_width;
|
||||
float emulation_height;
|
||||
u32 emulation_width;
|
||||
u32 emulation_height;
|
||||
if (vertical) {
|
||||
// width is just the larger size at this point
|
||||
emulation_width = std::max(large_width, small_width);
|
||||
|
|
@ -172,12 +179,13 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr
|
|||
emulation_height = std::max(large_height, small_height);
|
||||
}
|
||||
|
||||
const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(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);
|
||||
// TODO: Wtf does this `scale_amount` value represent? -OS
|
||||
Common::Rectangle<u32> total_rect{0, 0, emulation_width, emulation_height};
|
||||
total_rect = MaxRectangle(screen_window_area, total_rect,
|
||||
Settings::values.use_integer_scaling.GetValue());
|
||||
total_rect = total_rect.TranslateX((width - total_rect.GetWidth()) / 2)
|
||||
.TranslateY((height - total_rect.GetHeight()) / 2);
|
||||
|
||||
const float scale_amount = static_cast<float>(total_rect.GetHeight()) / emulation_height;
|
||||
gap = static_cast<u32>(static_cast<float>(gap) * scale_amount);
|
||||
|
||||
|
|
@ -190,61 +198,50 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr
|
|||
static_cast<u32>(small_width * scale_amount + total_rect.left),
|
||||
static_cast<u32>(small_height * scale_amount + total_rect.top)};
|
||||
|
||||
if (window_aspect_ratio < emulation_aspect_ratio) {
|
||||
// shift the large screen so it is at the left position of the bounding rectangle
|
||||
large_screen = large_screen.TranslateX((width - total_rect.GetWidth()) / 2);
|
||||
} else {
|
||||
// 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);
|
||||
}
|
||||
|
||||
switch (small_screen_position) {
|
||||
case Settings::SmallScreenPosition::TopRight:
|
||||
// Shift the small screen to the top right corner
|
||||
small_screen = small_screen.TranslateX(large_screen.right + gap);
|
||||
small_screen = small_screen.TranslateY(large_screen.top);
|
||||
small_screen = small_screen.TranslateX(large_screen.GetWidth() + gap);
|
||||
small_screen = small_screen.TranslateY(large_screen.top - small_screen.top);
|
||||
break;
|
||||
case Settings::SmallScreenPosition::MiddleRight:
|
||||
// Shift the small screen to the center right
|
||||
small_screen = small_screen.TranslateX(large_screen.right + gap);
|
||||
small_screen = small_screen.TranslateY(
|
||||
((large_screen.GetHeight() - small_screen.GetHeight()) / 2) + large_screen.top);
|
||||
small_screen = small_screen.TranslateX(large_screen.GetWidth() + gap);
|
||||
small_screen =
|
||||
small_screen.TranslateY(((large_screen.GetHeight() - small_screen.GetHeight()) / 2) +
|
||||
large_screen.top - small_screen.top);
|
||||
break;
|
||||
case Settings::SmallScreenPosition::BottomRight:
|
||||
// Shift the small screen to the bottom right corner
|
||||
small_screen = small_screen.TranslateX(large_screen.right + gap);
|
||||
small_screen = small_screen.TranslateY(large_screen.bottom - small_screen.GetHeight());
|
||||
small_screen = small_screen.TranslateX(large_screen.GetWidth() + gap);
|
||||
small_screen = small_screen.TranslateY(large_screen.bottom - small_screen.bottom);
|
||||
break;
|
||||
case Settings::SmallScreenPosition::TopLeft:
|
||||
// shift the small screen to the upper left then shift the large screen to its right
|
||||
small_screen = small_screen.TranslateX(large_screen.left);
|
||||
// shift the large screen to the upper right of the small screen
|
||||
large_screen = large_screen.TranslateX(small_screen.GetWidth() + gap);
|
||||
small_screen = small_screen.TranslateY(large_screen.top);
|
||||
break;
|
||||
case Settings::SmallScreenPosition::MiddleLeft:
|
||||
// shift the small screen to the middle left and shift the large screen to its right
|
||||
small_screen = small_screen.TranslateX(large_screen.left);
|
||||
large_screen = large_screen.TranslateX(small_screen.GetWidth() + gap);
|
||||
small_screen = small_screen.TranslateY(
|
||||
((large_screen.GetHeight() - small_screen.GetHeight()) / 2) + large_screen.top);
|
||||
small_screen =
|
||||
small_screen.TranslateY(((large_screen.GetHeight() - small_screen.GetHeight()) / 2));
|
||||
break;
|
||||
case Settings::SmallScreenPosition::BottomLeft:
|
||||
// shift the small screen to the bottom left and shift the large screen to its right
|
||||
small_screen = small_screen.TranslateX(large_screen.left);
|
||||
large_screen = large_screen.TranslateX(small_screen.GetWidth() + gap);
|
||||
small_screen = small_screen.TranslateY(large_screen.bottom - small_screen.GetHeight());
|
||||
small_screen = small_screen.TranslateY(large_screen.bottom - small_screen.bottom);
|
||||
break;
|
||||
case Settings::SmallScreenPosition::AboveLarge:
|
||||
// shift the large screen down and the bottom screen above it
|
||||
small_screen = small_screen.TranslateY(large_screen.top);
|
||||
// shift the large screen down
|
||||
large_screen = large_screen.TranslateY(small_screen.GetHeight() + gap);
|
||||
// If the "large screen" is actually smaller, center it
|
||||
if (large_screen.GetWidth() < total_rect.GetWidth()) {
|
||||
large_screen =
|
||||
large_screen.TranslateX((total_rect.GetWidth() - large_screen.GetWidth()) / 2);
|
||||
}
|
||||
small_screen = small_screen.TranslateX(large_screen.left + large_screen.GetWidth() / 2 -
|
||||
small_screen.GetWidth() / 2);
|
||||
small_screen =
|
||||
small_screen.TranslateX((large_screen.left - total_rect.left) +
|
||||
large_screen.GetWidth() / 2 - small_screen.GetWidth() / 2);
|
||||
break;
|
||||
case Settings::SmallScreenPosition::BelowLarge:
|
||||
// shift the bottom_screen down and then over to the center
|
||||
|
|
@ -253,9 +250,10 @@ FramebufferLayout LargeFrameLayout(u32 width, u32 height, bool swapped, bool upr
|
|||
large_screen =
|
||||
large_screen.TranslateX((total_rect.GetWidth() - large_screen.GetWidth()) / 2);
|
||||
}
|
||||
small_screen = small_screen.TranslateY(large_screen.bottom + gap);
|
||||
small_screen = small_screen.TranslateX(large_screen.left + large_screen.GetWidth() / 2 -
|
||||
small_screen.GetWidth() / 2);
|
||||
small_screen = small_screen.TranslateY(large_screen.GetHeight() + gap);
|
||||
small_screen =
|
||||
small_screen.TranslateX((large_screen.left - total_rect.left) +
|
||||
large_screen.GetWidth() / 2 - small_screen.GetWidth() / 2);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
|
|
@ -276,54 +274,19 @@ FramebufferLayout HybridScreenLayout(u32 width, u32 height, bool swapped, bool u
|
|||
if (upright) {
|
||||
std::swap(width, height);
|
||||
}
|
||||
FramebufferLayout res{width, height, true, true, {}, {}, !upright, false, true, {}};
|
||||
|
||||
// Split the window into two parts. Give 2.25x width to the main screen,
|
||||
// and make a bar on the right side with 1x width top screen and 1.25x width bottom 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) / static_cast<float>(width);
|
||||
const float scale_factor = 2.25f;
|
||||
|
||||
float main_screen_aspect_ratio = TOP_SCREEN_ASPECT_RATIO;
|
||||
float hybrid_area_aspect_ratio = 27.f / 65;
|
||||
float top_screen_aspect_ratio = TOP_SCREEN_ASPECT_RATIO;
|
||||
float bot_screen_aspect_ratio = BOT_SCREEN_ASPECT_RATIO;
|
||||
|
||||
if (swapped) {
|
||||
main_screen_aspect_ratio = BOT_SCREEN_ASPECT_RATIO;
|
||||
hybrid_area_aspect_ratio =
|
||||
Core::kScreenBottomHeight * scale_factor /
|
||||
(Core::kScreenBottomWidth * scale_factor + Core::kScreenTopWidth);
|
||||
}
|
||||
|
||||
Common::Rectangle<u32> screen_window_area{0, 0, width, height};
|
||||
Common::Rectangle<u32> total_rect = MaxRectangle(screen_window_area, hybrid_area_aspect_ratio);
|
||||
Common::Rectangle<u32> large_main_screen = MaxRectangle(total_rect, main_screen_aspect_ratio);
|
||||
Common::Rectangle<u32> side_rect = total_rect.Scale(1.f / scale_factor);
|
||||
Common::Rectangle<u32> small_top_screen = MaxRectangle(side_rect, top_screen_aspect_ratio);
|
||||
Common::Rectangle<u32> small_bottom_screen = MaxRectangle(side_rect, bot_screen_aspect_ratio);
|
||||
|
||||
if (window_aspect_ratio < hybrid_area_aspect_ratio) {
|
||||
large_main_screen = large_main_screen.TranslateX((width - total_rect.GetWidth()) / 2);
|
||||
} else {
|
||||
large_main_screen = large_main_screen.TranslateY((height - total_rect.GetHeight()) / 2);
|
||||
}
|
||||
|
||||
// Scale the bottom screen so it's width is the same as top screen
|
||||
small_bottom_screen = small_bottom_screen.Scale(1.25f);
|
||||
|
||||
// Shift the small bottom screen to the bottom right corner
|
||||
small_bottom_screen = small_bottom_screen.TranslateX(large_main_screen.right)
|
||||
.TranslateY(large_main_screen.GetHeight() + large_main_screen.top -
|
||||
small_bottom_screen.GetHeight());
|
||||
|
||||
// Shift small top screen to upper right corner
|
||||
small_top_screen =
|
||||
small_top_screen.TranslateX(large_main_screen.right).TranslateY(large_main_screen.top);
|
||||
|
||||
res.top_screen = small_top_screen;
|
||||
res.additional_screen = swapped ? small_bottom_screen : large_main_screen;
|
||||
res.bottom_screen = swapped ? large_main_screen : small_bottom_screen;
|
||||
const float scale_factor = swapped ? 2.25 : 1.8;
|
||||
const Settings::SmallScreenPosition pos = swapped ? Settings::SmallScreenPosition::TopRight
|
||||
: Settings::SmallScreenPosition::BottomRight;
|
||||
FramebufferLayout res = LargeFrameLayout(width, height, swapped, upright, scale_factor, pos);
|
||||
const Common::Rectangle<u32> main = swapped ? res.bottom_screen : res.top_screen;
|
||||
const Common::Rectangle<u32> small = swapped ? res.top_screen : res.bottom_screen;
|
||||
res.additional_screen = Common::Rectangle<u32>{small.left, swapped ? small.bottom : main.top,
|
||||
small.right, swapped ? main.bottom : small.top};
|
||||
res.additional_screen_enabled = true;
|
||||
if (upright) {
|
||||
return reverseLayout(res);
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue