From b4c31e55e2be7848f856279e82da4c5763473b1f Mon Sep 17 00:00:00 2001
From: Connor McLaughlin <stenzek@gmail.com>
Date: Fri, 1 May 2020 01:00:22 +1000
Subject: [PATCH] HostDisplay: Add integer upscaling option

---
 src/core/host_display.cpp   | 20 ++++++++++++++++++--
 src/core/host_display.h     |  2 ++
 src/core/host_interface.cpp |  6 ++++++
 src/core/settings.cpp       |  2 ++
 src/core/settings.h         |  1 +
 5 files changed, 29 insertions(+), 2 deletions(-)

diff --git a/src/core/host_display.cpp b/src/core/host_display.cpp
index 981df7640..67df4e63f 100644
--- a/src/core/host_display.cpp
+++ b/src/core/host_display.cpp
@@ -41,8 +41,16 @@ void HostDisplay::CalculateDrawRect(s32 window_width, s32 window_height, s32* ou
   {
     // align in middle vertically
     scale = static_cast<float>(window_width) / display_width;
+    if (m_display_integer_scaling)
+      scale = std::max(std::floor(scale), 1.0f);
+
     if (out_left_padding)
-      *out_left_padding = 0;
+    {
+      if (m_display_integer_scaling)
+        *out_left_padding = std::max<s32>((window_width - static_cast<s32>(display_width * scale)) / 2, 0);
+      else
+        *out_left_padding = 0;
+    }
     if (out_top_padding)
       *out_top_padding = std::max<s32>((window_height - static_cast<s32>(display_height * scale)) / 2, 0);
   }
@@ -50,10 +58,18 @@ void HostDisplay::CalculateDrawRect(s32 window_width, s32 window_height, s32* ou
   {
     // align in middle horizontally
     scale = static_cast<float>(window_height) / display_height;
+    if (m_display_integer_scaling)
+      scale = std::max(std::floor(scale), 1.0f);
+
     if (out_left_padding)
       *out_left_padding = std::max<s32>((window_width - static_cast<s32>(display_width * scale)) / 2, 0);
     if (out_top_padding)
-      *out_top_padding = 0;
+    {
+      if (m_display_integer_scaling)
+        *out_top_padding = std::max<s32>((window_height - static_cast<s32>(display_height * scale)) / 2, 0);
+      else
+        *out_top_padding = 0;
+    }
   }
 
   *out_width = static_cast<s32>(active_width * scale);
diff --git a/src/core/host_display.h b/src/core/host_display.h
index 8e7faef07..dee65c20c 100644
--- a/src/core/host_display.h
+++ b/src/core/host_display.h
@@ -104,6 +104,7 @@ public:
 
   void SetDisplayLinearFiltering(bool enabled) { m_display_linear_filtering = enabled; }
   void SetDisplayTopMargin(s32 height) { m_display_top_margin = height; }
+  void SetDisplayIntegerScaling(bool enabled) { m_display_integer_scaling = enabled; }
 
   /// Helper function for computing the draw rectangle in a larger window.
   std::tuple<s32, s32, s32, s32> CalculateDrawRect(s32 window_width, s32 window_height, s32 top_margin) const;
@@ -154,4 +155,5 @@ protected:
 
   bool m_display_linear_filtering = false;
   bool m_display_changed = false;
+  bool m_display_integer_scaling = false;
 };
diff --git a/src/core/host_interface.cpp b/src/core/host_interface.cpp
index 734f5ca32..8c92102f7 100644
--- a/src/core/host_interface.cpp
+++ b/src/core/host_interface.cpp
@@ -81,6 +81,7 @@ bool HostInterface::BootSystem(const SystemBootParameters& parameters)
 
   // set host display settings
   m_display->SetDisplayLinearFiltering(m_settings.display_linear_filtering);
+  m_display->SetDisplayIntegerScaling(m_settings.display_integer_scaling);
 
   // create the audio stream. this will never fail, since we'll just fall back to null
   CreateAudioStream();
@@ -958,6 +959,7 @@ void HostInterface::SetDefaultSettings(SettingsInterface& si)
   si.SetStringValue("Display", "CropMode", "Overscan");
   si.SetStringValue("Display", "PixelAspectRatio", "4:3");
   si.SetBoolValue("Display", "LinearFiltering", true);
+  si.SetBoolValue("Display", "IntegerScaling", false);
   si.SetBoolValue("Display", "ShowOSDMessages", true);
   si.SetBoolValue("Display", "ShowFPS", false);
   si.SetBoolValue("Display", "ShowVPS", false);
@@ -1023,6 +1025,7 @@ void HostInterface::UpdateSettings(const std::function<void()>& apply_callback)
   const DisplayCropMode old_display_crop_mode = m_settings.display_crop_mode;
   const DisplayAspectRatio old_display_aspect_ratio = m_settings.display_aspect_ratio;
   const bool old_display_linear_filtering = m_settings.display_linear_filtering;
+  const bool old_display_integer_scaling = m_settings.display_integer_scaling;
   const bool old_cdrom_read_thread = m_settings.cdrom_read_thread;
   std::array<ControllerType, NUM_CONTROLLER_AND_CARD_PORTS> old_controller_types = m_settings.controller_types;
   std::array<MemoryCardType, NUM_CONTROLLER_AND_CARD_PORTS> old_memory_card_types = m_settings.memory_card_types;
@@ -1114,6 +1117,9 @@ void HostInterface::UpdateSettings(const std::function<void()>& apply_callback)
   if (m_display && m_settings.display_linear_filtering != old_display_linear_filtering)
     m_display->SetDisplayLinearFiltering(m_settings.display_linear_filtering);
 
+  if (m_display && m_settings.display_integer_scaling != old_display_integer_scaling)
+    m_display->SetDisplayIntegerScaling(m_settings.display_integer_scaling);
+
   if (m_settings.log_level != old_log_level || m_settings.log_filter != old_log_filter ||
       m_settings.log_to_console != old_log_to_console || m_settings.log_to_window != old_log_to_window ||
       m_settings.log_to_file != old_log_to_file)
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index e15b984d1..90f37bf7a 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -38,6 +38,7 @@ void Settings::Load(SettingsInterface& si)
       si.GetStringValue("Display", "AspectRatio", GetDisplayAspectRatioName(DisplayAspectRatio::R4_3)).c_str())
       .value_or(DisplayAspectRatio::R4_3);
   display_linear_filtering = si.GetBoolValue("Display", "LinearFiltering", true);
+  display_integer_scaling = si.GetBoolValue("Display", "IntegerScaling", false);
   display_show_osd_messages = si.GetBoolValue("Display", "ShowOSDMessages", true);
   display_show_fps = si.GetBoolValue("Display", "ShowFPS", false);
   display_show_vps = si.GetBoolValue("Display", "ShowVPS", false);
@@ -123,6 +124,7 @@ void Settings::Save(SettingsInterface& si) const
   si.SetStringValue("Display", "CropMode", GetDisplayCropModeName(display_crop_mode));
   si.SetStringValue("Display", "AspectRatio", GetDisplayAspectRatioName(display_aspect_ratio));
   si.SetBoolValue("Display", "LinearFiltering", display_linear_filtering);
+  si.SetBoolValue("Display", "IntegerScaling", display_integer_scaling);
   si.SetBoolValue("Display", "ShowOSDMessages", display_show_osd_messages);
   si.SetBoolValue("Display", "ShowFPS", display_show_fps);
   si.SetBoolValue("Display", "ShowVPS", display_show_vps);
diff --git a/src/core/settings.h b/src/core/settings.h
index 8b0057751..724141577 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -57,6 +57,7 @@ struct Settings
   DisplayCropMode display_crop_mode = DisplayCropMode::None;
   DisplayAspectRatio display_aspect_ratio = DisplayAspectRatio::R4_3;
   bool display_linear_filtering = true;
+  bool display_integer_scaling = false;
   bool display_show_osd_messages = false;
   bool display_show_fps = false;
   bool display_show_vps = false;