From a5059cbca81c31fbbfceb2180b82eb59b0fe2fcb Mon Sep 17 00:00:00 2001 From: Adam Scott Date: Tue, 24 Mar 2026 14:18:15 -0400 Subject: Add screen crop feature --- Source/Core/VideoCommon/Present.cpp | 112 +++++++++++++++++++++++++++--------- 1 file changed, 84 insertions(+), 28 deletions(-) (limited to 'Source/Core/VideoCommon/Present.cpp') 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* target_rec MathUtil::Rectangle* 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 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(source_rect->GetWidth()) / + static_cast(original_source_rect.GetWidth()); + const float ratio_diff_height = static_cast(source_rect->GetHeight()) / + static_cast(original_source_rect.GetHeight()); + + const int new_width = static_cast(target_rect->GetWidth()) * ratio_diff_width; + const int new_height = static_cast(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(source_rect->GetWidth()) / static_cast(source_rect->GetHeight()); + const double target_aspect_ratio = + static_cast(target_rect->GetWidth()) / static_cast(target_rect->GetHeight()); + const double framebuffer_aspect_ratio = + static_cast(fb_width) / static_cast(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(fb_width) * static_cast(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(fb_height) / + static_cast(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 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(m_backbuffer_width); - const float win_height = static_cast(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(win_width) / static_cast(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(std::round(win_width / 2.0 - int_draw_width / 2.0)); - m_target_rectangle.top = static_cast(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 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 Presenter::CalculateOutputDimensions(int width, int height, height = static_cast(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); } -- cgit v1.2.3