summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon
diff options
context:
space:
mode:
authorAdam Scott <ascott.ca@gmail.com>2026-03-24 14:18:15 -0400
committerAdam Scott <ascott.ca@gmail.com>2026-04-18 13:08:41 -0400
commita5059cbca81c31fbbfceb2180b82eb59b0fe2fcb (patch)
tree4f326e1b85e1f35ff25250bc36fdc6cb7f9f2cc6 /Source/Core/VideoCommon
parent2b6667a98db9d0d9b8b7f8ae6a2e82b9c84c3e53 (diff)
Add screen crop feature
Diffstat (limited to 'Source/Core/VideoCommon')
-rw-r--r--Source/Core/VideoCommon/Present.cpp112
-rw-r--r--Source/Core/VideoCommon/VideoConfig.cpp7
-rw-r--r--Source/Core/VideoCommon/VideoConfig.h7
3 files changed, 96 insertions, 30 deletions
diff --git a/Source/Core/VideoCommon/Present.cpp b/Source/Core/VideoCommon/Present.cpp
index 4374782464..7c26fb2b5d 100644
--- a/Source/Core/VideoCommon/Present.cpp
+++ b/Source/Core/VideoCommon/Present.cpp
@@ -498,33 +498,76 @@ void Presenter::AdjustRectanglesToFitBounds(MathUtil::Rectangle<int>* target_rec
MathUtil::Rectangle<int>* source_rect, int fb_width,
int fb_height)
{
- const int orig_target_width = target_rect->GetWidth();
- const int orig_target_height = target_rect->GetHeight();
- const int orig_source_width = source_rect->GetWidth();
- const int orig_source_height = source_rect->GetHeight();
- if (target_rect->left < 0)
+ const int efb_scale = g_framebuffer_manager->GetEFBScale();
+ MathUtil::Rectangle<int> original_source_rect = *source_rect;
+
+ // Crop the content.
+ if (g_ActiveConfig.bCropCustom)
{
- const int offset = -target_rect->left;
+ const int crop_left = g_ActiveConfig.iCropCustomLeft * efb_scale;
+ const int crop_right = g_ActiveConfig.iCropCustomRight * efb_scale;
+ const int crop_top = g_ActiveConfig.iCropCustomTop * efb_scale;
+ const int crop_bottom = g_ActiveConfig.iCropCustomBottom * efb_scale;
+
+ source_rect->left = std::min(source_rect->left + crop_left, source_rect->right);
+ source_rect->right = std::max(source_rect->right - crop_right, source_rect->left);
+ source_rect->top = std::min(source_rect->top + crop_top, source_rect->bottom);
+ source_rect->bottom = std::max(source_rect->bottom - crop_bottom, source_rect->top);
+
+ const float ratio_diff_width = static_cast<float>(source_rect->GetWidth()) /
+ static_cast<float>(original_source_rect.GetWidth());
+ const float ratio_diff_height = static_cast<float>(source_rect->GetHeight()) /
+ static_cast<float>(original_source_rect.GetHeight());
+
+ const int new_width = static_cast<float>(target_rect->GetWidth()) * ratio_diff_width;
+ const int new_height = static_cast<float>(target_rect->GetHeight()) * ratio_diff_height;
+
target_rect->left = 0;
- source_rect->left += offset * orig_source_width / orig_target_width;
- }
- if (target_rect->right > fb_width)
- {
- const int offset = target_rect->right - fb_width;
- target_rect->right -= offset;
- source_rect->right -= offset * orig_source_width / orig_target_width;
+ target_rect->right = new_width;
+ target_rect->top = 0;
+ target_rect->bottom = new_height;
}
- if (target_rect->top < 0)
+
+ // Center the content.
+ const double source_aspect_ratio =
+ static_cast<double>(source_rect->GetWidth()) / static_cast<double>(source_rect->GetHeight());
+ const double target_aspect_ratio =
+ static_cast<double>(target_rect->GetWidth()) / static_cast<double>(target_rect->GetHeight());
+ const double framebuffer_aspect_ratio =
+ static_cast<double>(fb_width) / static_cast<double>(fb_height);
+ const double source_target_aspect_ratio = source_aspect_ratio / target_aspect_ratio;
+ const double source_target_framebuffer_aspect_ratio =
+ source_aspect_ratio / source_target_aspect_ratio / framebuffer_aspect_ratio;
+
+ if (source_target_framebuffer_aspect_ratio < 1)
{
- const int offset = -target_rect->top;
+ // Width.
+ const int new_width =
+ static_cast<double>(fb_width) * static_cast<double>(source_target_framebuffer_aspect_ratio);
+ const int width_diff = fb_width - new_width;
+ const int half_width_diff = width_diff / 2;
+
+ target_rect->left = half_width_diff;
+ target_rect->right = fb_width - half_width_diff;
+
+ // Height.
target_rect->top = 0;
- source_rect->top += offset * orig_source_height / orig_target_height;
+ target_rect->bottom = fb_height;
}
- if (target_rect->bottom > fb_height)
+ else
{
- const int offset = target_rect->bottom - fb_height;
- target_rect->bottom -= offset;
- source_rect->bottom -= offset * orig_source_height / orig_target_height;
+ // Width.
+ target_rect->left = 0;
+ target_rect->right = fb_width;
+
+ // Height.
+ const int new_height = static_cast<double>(fb_height) /
+ static_cast<double>(source_target_framebuffer_aspect_ratio);
+ const int height_diff = fb_height - new_height;
+ const int half_height_diff = height_diff / 2;
+
+ target_rect->top = half_height_diff;
+ target_rect->bottom = fb_height - half_height_diff;
}
}
@@ -607,7 +650,8 @@ std::tuple<float, float> Presenter::ApplyStandardAspectCrop(float width, float h
if (!allow_stretch && aspect_mode == AspectMode::Stretch)
aspect_mode = AspectMode::Auto;
- if (!g_ActiveConfig.bCrop || aspect_mode == AspectMode::Stretch || aspect_mode == AspectMode::Raw)
+ if (!g_ActiveConfig.bCropToAspectRatio || aspect_mode == AspectMode::Stretch ||
+ aspect_mode == AspectMode::Raw)
return {width, height};
// Force aspect ratios by cropping the image.
@@ -687,9 +731,9 @@ void Presenter::UpdateDrawRectangle()
}
// The rendering window size
- const float win_width = static_cast<float>(m_backbuffer_width);
- const float win_height = static_cast<float>(m_backbuffer_height);
- const float win_aspect_ratio = win_width / win_height;
+ const int win_width = m_backbuffer_width;
+ const int win_height = m_backbuffer_height;
+ const float win_aspect_ratio = static_cast<float>(win_width) / static_cast<float>(win_height);
// FIXME: this breaks at very low widget sizes
// Make ControllerInterface aware of the render window region actually being used
@@ -734,7 +778,7 @@ void Presenter::UpdateDrawRectangle()
FindClosestIntegerResolution(draw_width, draw_height, updated_draw_aspect_ratio);
int_draw_width = std::get<0>(int_draw_res);
int_draw_height = std::get<1>(int_draw_res);
- if (!g_ActiveConfig.bCrop)
+ if (!g_ActiveConfig.bCropToAspectRatio)
{
if (g_ActiveConfig.aspect_mode != AspectMode::Stretch)
{
@@ -752,8 +796,13 @@ void Presenter::UpdateDrawRectangle()
int_draw_height = m_xfb_rect.GetHeight();
}
- m_target_rectangle.left = static_cast<int>(std::round(win_width / 2.0 - int_draw_width / 2.0));
- m_target_rectangle.top = static_cast<int>(std::round(win_height / 2.0 - int_draw_height / 2.0));
+ const int half_win_width = win_width / 2;
+ const int half_win_height = win_height / 2;
+ const int half_draw_width = int_draw_width / 2;
+ const int half_draw_height = int_draw_height / 2;
+
+ m_target_rectangle.left = half_win_width - half_draw_width;
+ m_target_rectangle.top = half_win_height - half_draw_height;
m_target_rectangle.right = m_target_rectangle.left + int_draw_width;
m_target_rectangle.bottom = m_target_rectangle.top + int_draw_height;
}
@@ -791,7 +840,7 @@ std::tuple<int, int> Presenter::CalculateOutputDimensions(int width, int height,
if (!allow_stretch && aspect_mode == AspectMode::Stretch)
aspect_mode = AspectMode::Auto;
- if (!g_ActiveConfig.bCrop && aspect_mode != AspectMode::Stretch)
+ if (!g_ActiveConfig.bCropToAspectRatio && aspect_mode != AspectMode::Stretch)
{
// Find the closest integer resolution for the aspect ratio,
// this avoids a small black line from being drawn on one of the four edges
@@ -811,6 +860,13 @@ std::tuple<int, int> Presenter::CalculateOutputDimensions(int width, int height,
height = static_cast<int>(std::ceil(scaled_height));
}
+ if (g_ActiveConfig.bCropCustom)
+ {
+ width = std::max(width - (g_ActiveConfig.iCropCustomLeft + g_ActiveConfig.iCropCustomRight), 0);
+ height =
+ std::max(height - (g_ActiveConfig.iCropCustomTop + g_ActiveConfig.iCropCustomBottom), 0);
+ }
+
return std::make_tuple(width, height);
}
diff --git a/Source/Core/VideoCommon/VideoConfig.cpp b/Source/Core/VideoCommon/VideoConfig.cpp
index d2cacd7276..8685adcf15 100644
--- a/Source/Core/VideoCommon/VideoConfig.cpp
+++ b/Source/Core/VideoCommon/VideoConfig.cpp
@@ -98,7 +98,12 @@ void VideoConfig::Refresh()
Config::Get(Config::GFX_WIDESCREEN_HEURISTIC_STANDARD_RATIO);
widescreen_heuristic_widescreen_ratio =
Config::Get(Config::GFX_WIDESCREEN_HEURISTIC_WIDESCREEN_RATIO);
- bCrop = Config::Get(Config::GFX_CROP);
+ bCropToAspectRatio = Config::Get(Config::GFX_CROP_TO_ASPECT_RATIO);
+ bCropCustom = Config::Get(Config::GFX_CROP_CUSTOM);
+ iCropCustomLeft = Config::Get(Config::GFX_CROP_CUSTOM_LEFT);
+ iCropCustomTop = Config::Get(Config::GFX_CROP_CUSTOM_TOP);
+ iCropCustomRight = Config::Get(Config::GFX_CROP_CUSTOM_RIGHT);
+ iCropCustomBottom = Config::Get(Config::GFX_CROP_CUSTOM_BOTTOM);
iSafeTextureCache_ColorSamples = Config::Get(Config::GFX_SAFE_TEXTURE_CACHE_COLOR_SAMPLES);
bShowFPS = Config::Get(Config::GFX_SHOW_FPS);
bShowFTimes = Config::Get(Config::GFX_SHOW_FTIMES);
diff --git a/Source/Core/VideoCommon/VideoConfig.h b/Source/Core/VideoCommon/VideoConfig.h
index 3f82e3afe7..eae2ac6208 100644
--- a/Source/Core/VideoCommon/VideoConfig.h
+++ b/Source/Core/VideoCommon/VideoConfig.h
@@ -206,7 +206,12 @@ struct VideoConfig final
float widescreen_heuristic_aspect_ratio_slop = 0.f;
float widescreen_heuristic_standard_ratio = 0.f;
float widescreen_heuristic_widescreen_ratio = 0.f;
- bool bCrop = false; // Aspect ratio controls.
+ bool bCropToAspectRatio = false;
+ bool bCropCustom = false;
+ int iCropCustomLeft = 0;
+ int iCropCustomTop = 0;
+ int iCropCustomRight = 0;
+ int iCropCustomBottom = 0;
bool bShaderCache = false;
// Enhancements