GPU: Add "Fine Crop" settings

Allows the image to be cropped while preserving output aspect ratio.
This commit is contained in:
Stenzek
2025-12-13 21:02:30 +10:00
parent 986e66b517
commit c6a44ff569
19 changed files with 451 additions and 71 deletions

View File

@@ -1064,25 +1064,33 @@ bool FullscreenUI::DrawIntRectSetting(SettingsInterface* bsi, std::string_view t
const float midpoint = LayoutScale(150.0f);
const float end = (ImGui::GetCurrentWindow()->WorkRect.GetWidth() - midpoint) + ImGui::GetStyle().WindowPadding.x;
ImGui::PushFont(nullptr, 0.0f, UIStyle.BoldFontWeight);
ImGui::TextUnformatted(IMSTR_START_END(FSUI_VSTR("Left: ")));
ImGui::PopFont();
ImGui::SameLine(midpoint);
ImGui::SetNextItemWidth(end);
const bool left_modified =
ImGui::SliderInt("##left", &dlg_left_value, min_value, max_value, format, ImGuiSliderFlags_NoInput);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
ImGui::PushFont(nullptr, 0.0f, UIStyle.BoldFontWeight);
ImGui::TextUnformatted(IMSTR_START_END(FSUI_VSTR("Top: ")));
ImGui::PopFont();
ImGui::SameLine(midpoint);
ImGui::SetNextItemWidth(end);
const bool top_modified =
ImGui::SliderInt("##top", &dlg_top_value, min_value, max_value, format, ImGuiSliderFlags_NoInput);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
ImGui::PushFont(nullptr, 0.0f, UIStyle.BoldFontWeight);
ImGui::TextUnformatted(IMSTR_START_END(FSUI_VSTR("Right: ")));
ImGui::PopFont();
ImGui::SameLine(midpoint);
ImGui::SetNextItemWidth(end);
const bool right_modified =
ImGui::SliderInt("##right", &dlg_right_value, min_value, max_value, format, ImGuiSliderFlags_NoInput);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
ImGui::PushFont(nullptr, 0.0f, UIStyle.BoldFontWeight);
ImGui::TextUnformatted(IMSTR_START_END(FSUI_VSTR("Bottom: ")));
ImGui::PopFont();
ImGui::SameLine(midpoint);
ImGui::SetNextItemWidth(end);
const bool bottom_modified =
@@ -3874,6 +3882,21 @@ void FullscreenUI::DrawGraphicsSettingsPage()
Settings::DEFAULT_DISPLAY_ROTATION, &Settings::ParseDisplayRotation,
&Settings::GetDisplayRotationName, &Settings::GetDisplayRotationDisplayName, DisplayRotation::Count);
DrawEnumSetting(bsi, FSUI_ICONVSTR(ICON_FA_CROP_SIMPLE, "Fine Crop Mode"),
FSUI_VSTR("Enables manual fine cropping of the display area, while preserving the aspect ratio of "
"the image. Useful for removing black borders in certain games."),
"Display", "FineCropMode", Settings::DEFAULT_DISPLAY_FINE_CROP_MODE,
&Settings::ParseDisplayFineCropMode, &Settings::GetDisplayFineCropModeName,
&Settings::GetDisplayFineCropModeDisplayName, DisplayFineCropMode::MaxCount);
if (Settings::ParseDisplayFineCropMode(GetEffectiveTinyStringSetting(bsi, "Display", "FineCropMode", "").c_str())
.value_or(Settings::DEFAULT_DISPLAY_FINE_CROP_MODE) != DisplayFineCropMode::None)
{
DrawIntRectSetting(bsi, FSUI_ICONVSTR(ICON_FA_CROP_SIMPLE, "Fine Crop Amount"),
FSUI_VSTR("Determines how much to crop the display area."), "Display", "FineCropLeft", 0,
"FineCropTop", 0, "FineCropRight", 0, "FineCropBottom", 0, std::numeric_limits<s16>::min(),
std::numeric_limits<s16>::max(), "%dpx");
}
if (is_hardware)
{
DrawEnumSetting(bsi, FSUI_ICONVSTR(ICON_FA_GRIP_LINES_VERTICAL, "Line Detection"),

View File

@@ -243,6 +243,7 @@ TRANSLATE_NOOP("FullscreenUI", "Determines how much button pressure is ignored b
TRANSLATE_NOOP("FullscreenUI", "Determines how much latency there is between the audio being picked up by the host API, and played through speakers.");
TRANSLATE_NOOP("FullscreenUI", "Determines how much of the area typically not visible on a consumer TV set to crop/hide.");
TRANSLATE_NOOP("FullscreenUI", "Determines how much pressure is simulated when macro is active.");
TRANSLATE_NOOP("FullscreenUI", "Determines how much to crop the display area.");
TRANSLATE_NOOP("FullscreenUI", "Determines how the emulated CPU executes instructions.");
TRANSLATE_NOOP("FullscreenUI", "Determines how the emulated console's output is upscaled or downscaled to your monitor's resolution.");
TRANSLATE_NOOP("FullscreenUI", "Determines quality of audio when not running at 100% speed.");
@@ -325,6 +326,7 @@ TRANSLATE_NOOP("FullscreenUI", "Enables depth testing for semi-transparent polyg
TRANSLATE_NOOP("FullscreenUI", "Enables dumping of textures to image files, which can be replaced. Not compatible with all games.");
TRANSLATE_NOOP("FullscreenUI", "Enables loading of cheats for this game from DuckStation's database.");
TRANSLATE_NOOP("FullscreenUI", "Enables loading of replacement textures. Not compatible with all games.");
TRANSLATE_NOOP("FullscreenUI", "Enables manual fine cropping of the display area, while preserving the aspect ratio of the image. Useful for removing black borders in certain games.");
TRANSLATE_NOOP("FullscreenUI", "Enables smooth scrolling of menus in the Big Picture UI.");
TRANSLATE_NOOP("FullscreenUI", "Enables the cheats that are selected below.");
TRANSLATE_NOOP("FullscreenUI", "Enables the older, less accurate MDEC decoding routines. May be required for old replacement backgrounds to match/load.");
@@ -359,6 +361,8 @@ TRANSLATE_NOOP("FullscreenUI", "Fast forwards through memory card access, both l
TRANSLATE_NOOP("FullscreenUI", "Fast forwards through the early loading process when fast booting, saving time. Results may vary between games.");
TRANSLATE_NOOP("FullscreenUI", "File Size");
TRANSLATE_NOOP("FullscreenUI", "File Title");
TRANSLATE_NOOP("FullscreenUI", "Fine Crop Amount");
TRANSLATE_NOOP("FullscreenUI", "Fine Crop Mode");
TRANSLATE_NOOP("FullscreenUI", "Force 4:3 For FMVs");
TRANSLATE_NOOP("FullscreenUI", "Forces a full rescan of all games previously identified.");
TRANSLATE_NOOP("FullscreenUI", "Forces texture upload tracking to be enabled regardless of whether it is needed.");
@@ -676,6 +680,7 @@ TRANSLATE_NOOP("FullscreenUI", "Settings reset to default.");
TRANSLATE_NOOP("FullscreenUI", "Shader {} added as stage {}.");
TRANSLATE_NOOP("FullscreenUI", "Shared Card Name");
TRANSLATE_NOOP("FullscreenUI", "Show Achievement Trophy Icons");
TRANSLATE_NOOP("FullscreenUI", "Show All");
TRANSLATE_NOOP("FullscreenUI", "Show CPU Usage");
TRANSLATE_NOOP("FullscreenUI", "Show Controller Input");
TRANSLATE_NOOP("FullscreenUI", "Show Enhancement Settings");
@@ -687,6 +692,7 @@ TRANSLATE_NOOP("FullscreenUI", "Show Grid View Titles");
TRANSLATE_NOOP("FullscreenUI", "Show Latency Statistics");
TRANSLATE_NOOP("FullscreenUI", "Show Localized Titles");
TRANSLATE_NOOP("FullscreenUI", "Show Messages");
TRANSLATE_NOOP("FullscreenUI", "Show Nearby");
TRANSLATE_NOOP("FullscreenUI", "Show Resolution");
TRANSLATE_NOOP("FullscreenUI", "Show Speed");
TRANSLATE_NOOP("FullscreenUI", "Show Status Indicators");

View File

@@ -735,18 +735,44 @@ float GPU::ComputeAspectRatioCorrection() const
return (relative_width / relative_height);
}
GSVector2 GPU::ApplyPixelAspectRatioToSize(float par, GSVector2 size)
GSVector2 GPU::CalculateDisplayWindowSize(DisplayFineCropMode mode, std::span<const s16, 4> amount,
float pixel_aspect_ratio, const GSVector2 video_size,
const GSVector2 source_size, const GSVector2 window_size)
{
if (par < 1.0f)
GSVector2 size = video_size;
if (pixel_aspect_ratio < 1.0f)
{
// stretch height, preserve width
size.y = std::ceil(size.y / par);
size.y = size.y / pixel_aspect_ratio;
}
else
{
// stretch width, preserve height
size.x = std::ceil(size.x * par);
size.x = size.x * pixel_aspect_ratio;
}
if (mode != DisplayFineCropMode::None)
{
GSVector4 crop_amount = GSVector4(GSVector4i::loadl<false>(amount.data()).s16to32());
switch (mode)
{
case DisplayFineCropMode::VideoResolution:
break;
case DisplayFineCropMode::InternalResolution:
crop_amount *= GSVector4::xyxy(size / source_size);
break;
case DisplayFineCropMode::WindowResolution:
crop_amount *= GSVector4::xyxy(size / window_size);
break;
DefaultCaseIsUnreachable();
}
size = (size - crop_amount.xy() - crop_amount.zw()).max(GSVector2::cxpr(1.0f));
}
return size;
}
@@ -1342,45 +1368,44 @@ u8 GPU::UpdateOrGetGPUBusyPct()
return m_last_gpu_busy_pct;
}
void GPU::ConvertScreenCoordinatesToDisplayCoordinates(float window_x, float window_y, float* display_x,
float* display_y) const
GSVector2 GPU::ConvertScreenCoordinatesToDisplayCoordinates(GSVector2 window_pos) const
{
const WindowInfo& wi = GPUThread::GetRenderWindowInfo();
if (wi.IsSurfaceless())
{
*display_x = *display_y = -1.0f;
return;
}
return GSVector2::cxpr(-1.0f);
GSVector4 crop_amount = GSVector4::zero();
GSVector4i source_rc, display_rc, draw_rc;
CalculateDrawRect(GSVector2i(wi.surface_width, wi.surface_height), GetCRTCVideoSize(), GetCRTCVideoActiveRect(),
const GSVector2i crtc_video_size = GetCRTCVideoSize();
CalculateDrawRect(GSVector2i(wi.surface_width, wi.surface_height), crtc_video_size, GetCRTCVideoActiveRect(),
GetCRTCVRAMSourceRect(), g_settings.display_rotation, g_settings.display_alignment,
ComputePixelAspectRatio(),
(g_settings.display_scaling == DisplayScalingMode::NearestInteger ||
g_settings.display_scaling == DisplayScalingMode::BilinearInteger),
&source_rc, &display_rc, &draw_rc);
g_settings.display_fine_crop_mode, g_settings.display_fine_crop_amount, &source_rc, &display_rc,
&draw_rc, &crop_amount);
// convert coordinates to active display region, then to full display region
const float scaled_display_x =
(window_x - static_cast<float>(display_rc.left)) / static_cast<float>(display_rc.width());
const float scaled_display_y =
(window_y - static_cast<float>(display_rc.top)) / static_cast<float>(display_rc.height());
const GSVector2 local_pos = window_pos - GSVector2(display_rc.xy());
GSVector2 scaled_display_pos = local_pos / GSVector2(display_rc.rsize());
// scale back to internal resolution
*display_x = scaled_display_x * static_cast<float>(m_crtc_state.display_width);
*display_y = scaled_display_y * static_cast<float>(m_crtc_state.display_height);
const GSVector2 cropped_video_size = GSVector2(crtc_video_size) - crop_amount.xy() - crop_amount.zw();
const GSVector2 display_pos = scaled_display_pos * cropped_video_size + crop_amount.xy();
// TODO: apply rotation matrix
DEV_LOG("win {:.0f},{:.0f} -> local {:.0f},{:.0f}, disp {:.2f},{:.2f} (size {},{} frac {},{})", window_x, window_y,
window_x - display_rc.left, window_y - display_rc.top, *display_x, *display_y, m_crtc_state.display_width,
m_crtc_state.display_height, *display_x / static_cast<float>(m_crtc_state.display_width),
*display_y / static_cast<float>(m_crtc_state.display_height));
DEV_LOG("win {} -> local {}, disp {} (size {} frac {})", window_pos, local_pos, display_pos, cropped_video_size,
display_pos / cropped_video_size);
return display_pos;
}
bool GPU::ConvertDisplayCoordinatesToBeamTicksAndLines(float display_x, float display_y, float x_scale, u32* out_tick,
bool GPU::ConvertDisplayCoordinatesToBeamTicksAndLines(const GSVector2& display_pos, float x_scale, u32* out_tick,
u32* out_line) const
{
float display_x = display_pos.x;
float display_y = display_pos.y;
if (x_scale != 1.0f)
{
const float dw = static_cast<float>(m_crtc_state.display_width);
@@ -1803,12 +1828,14 @@ static bool IntegerScalePreferWidth(float display_width, float display_height, f
void GPU::CalculateDrawRect(const GSVector2i& window_size, const GSVector2i& video_size,
const GSVector4i& video_active_rect, const GSVector4i& source_rect,
DisplayRotation rotation, DisplayAlignment alignment, float pixel_aspect_ratio,
bool integer_scale, GSVector4i* out_source_rect, GSVector4i* out_display_rect,
GSVector4i* out_draw_rect)
bool integer_scale, DisplayFineCropMode fine_crop,
const std::span<const s16, 4>& fine_crop_amount, GSVector4i* out_source_rect,
GSVector4i* out_display_rect, GSVector4i* out_draw_rect, GSVector4* out_crop_amount)
{
GSVector2 fwindow_size = GSVector2(window_size);
GSVector2 fvideo_size = GSVector2(video_size);
GSVector4 fvideo_active_rect = GSVector4(video_active_rect);
GSVector4 fsource_rect = GSVector4(source_rect);
// for integer scale, use whichever gets us a greater effective display size
// this is needed for games like crash where the framebuffer is wide to not lose detail
@@ -1825,6 +1852,51 @@ void GPU::CalculateDrawRect(const GSVector2i& window_size, const GSVector2i& vid
fvideo_active_rect = fvideo_active_rect.blend32<10>(fvideo_active_rect / pixel_aspect_ratio);
}
if (fine_crop != DisplayFineCropMode::None)
{
GSVector4 crop_amount = GSVector4(GSVector4i::loadl<false>(fine_crop_amount.data()).s16to32());
switch (fine_crop)
{
case DisplayFineCropMode::VideoResolution:
break;
case DisplayFineCropMode::InternalResolution:
crop_amount *= GSVector4::xyxy(fvideo_size / fsource_rect.rsize());
break;
case DisplayFineCropMode::WindowResolution:
crop_amount *= GSVector4::xyxy(fvideo_size / fwindow_size);
break;
DefaultCaseIsUnreachable();
}
if (out_crop_amount)
*out_crop_amount = crop_amount;
// apply crop to padding first
const GSVector2 crop_padding_left_top = fvideo_active_rect.xy().min(crop_amount.xy());
const GSVector2 crop_padding_right_bottom = (fvideo_size - fvideo_active_rect.zw()).min(crop_amount.zw());
fvideo_active_rect = fvideo_active_rect - GSVector4::xyxy(crop_padding_left_top);
fvideo_size = fvideo_size - crop_padding_left_top - crop_padding_right_bottom;
crop_amount = crop_amount - GSVector4::xyxy(crop_padding_left_top, crop_padding_right_bottom);
// apply remaining crop to active area
const GSVector2 crop_size = crop_amount.xy() + crop_amount.zw();
fvideo_active_rect -= GSVector4::loadh(crop_size);
fvideo_size -= crop_size;
// need to take it off the source too
const GSVector2 video_to_source = fsource_rect.rsize() / fvideo_active_rect.rsize();
const GSVector4 source_crop_amount = crop_amount * GSVector4::xyxy(video_to_source);
fsource_rect = (fsource_rect + source_crop_amount).blend32<12>(fsource_rect - source_crop_amount);
// ensure we haven't cropped everything away
fvideo_active_rect = fvideo_active_rect.sat(GSVector4::zero(), fvideo_active_rect);
fvideo_size -= crop_size.sat(GSVector2::zero(), GSVector2::cxpr(1.0f, 1.0f));
fsource_rect = fsource_rect.sat(GSVector4::zero(), fsource_rect);
}
// swap width/height when rotated, the flipping of padding is taken care of in the shader with the rotation matrix
if (rotation == DisplayRotation::Rotate90 || rotation == DisplayRotation::Rotate270)
{
@@ -1901,7 +1973,7 @@ void GPU::CalculateDrawRect(const GSVector2i& window_size, const GSVector2i& vid
const GSVector4 padding4 = GSVector4::xyxy(padding);
fvideo_size *= scale;
fvideo_active_rect *= scale;
*out_source_rect = source_rect;
*out_source_rect = GSVector4i(fsource_rect);
*out_draw_rect = GSVector4i(fvideo_active_rect + padding4);
*out_display_rect = GSVector4i(GSVector4::loadh(fvideo_size) + padding4);
}
@@ -2044,7 +2116,8 @@ u8 GPU::CalculateAutomaticResolutionScale() const
CalculateDrawRect(GSVector2i(main_window_info.surface_width, main_window_info.surface_height), GetCRTCVideoSize(),
GetCRTCVideoActiveRect(), GetCRTCVRAMSourceRect(), g_settings.display_rotation,
g_settings.display_alignment, g_settings.gpu_show_vram ? 1.0f : ComputePixelAspectRatio(),
g_settings.IsUsingIntegerDisplayScaling(false), &source_rect, &display_rect, &draw_rect);
g_settings.IsUsingIntegerDisplayScaling(false), g_settings.display_fine_crop_mode,
g_settings.display_fine_crop_amount, &source_rect, &display_rect, &draw_rect);
// We use the draw rect to determine scaling. This way we match the resolution as best we can, regardless of the
// anamorphic aspect ratio.

View File

@@ -197,12 +197,13 @@ public:
float ComputeAspectRatioCorrection() const;
/// Applies the pixel aspect ratio to a given size, preserving the larger dimension.
static GSVector2 ApplyPixelAspectRatioToSize(float par, GSVector2 size);
static GSVector2 CalculateDisplayWindowSize(DisplayFineCropMode mode, std::span<const s16, 4> amount,
float pixel_aspect_ratio, const GSVector2 video_size,
const GSVector2 source_size, const GSVector2 window_size);
// Converts window coordinates into horizontal ticks and scanlines. Returns false if out of range. Used for lightguns.
void ConvertScreenCoordinatesToDisplayCoordinates(float window_x, float window_y, float* display_x,
float* display_y) const;
bool ConvertDisplayCoordinatesToBeamTicksAndLines(float display_x, float display_y, float x_scale, u32* out_tick,
// Converts window coordinates into horizontal ticks and scanlines. Returns -1 if out of range. Used for lightguns.
GSVector2 ConvertScreenCoordinatesToDisplayCoordinates(GSVector2 window_pos) const;
bool ConvertDisplayCoordinatesToBeamTicksAndLines(const GSVector2& display_pos, float x_scale, u32* out_tick,
u32* out_line) const;
// Returns the current beam position.
@@ -244,8 +245,10 @@ public:
static void CalculateDrawRect(const GSVector2i& window_size, const GSVector2i& video_size,
const GSVector4i& video_active_rect, const GSVector4i& source_rect,
DisplayRotation rotation, DisplayAlignment alignment, float pixel_aspect_ratio,
bool integer_scale, GSVector4i* out_source_rect, GSVector4i* out_display_rect,
GSVector4i* out_draw_rect);
bool integer_scale, DisplayFineCropMode fine_crop,
const std::span<const s16, 4>& fine_crop_amount, GSVector4i* out_source_rect,
GSVector4i* out_display_rect, GSVector4i* out_draw_rect,
GSVector4* out_crop_amount = nullptr);
private:
TickCount CRTCTicksToSystemTicks(TickCount crtc_ticks, TickCount fractional_ticks) const;

View File

@@ -703,7 +703,7 @@ bool GPUBackend::RenderScreenshotToBuffer(u32 width, u32 height, bool postfx, bo
// Crop it if border overlay isn't enabled.
GSVector4i source_rect, draw_rect, display_rect;
backend->GetPresenter().CalculateDrawRect(GSVector2i(static_cast<s32>(width), static_cast<s32>(height)),
apply_aspect_ratio, false, false, &source_rect, &display_rect,
apply_aspect_ratio, false, true, false, &source_rect, &display_rect,
&draw_rect);
image_width = static_cast<u32>(display_rect.width());
image_height = static_cast<u32>(display_rect.height());

View File

@@ -457,8 +457,8 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
GSVector4i(GSVector4(m_border_overlay_display_rect) * GSVector4::xyxy(scale)).add32(overlay_rect.xyxy());
// Draw to the overlay area instead of the whole screen. Always align in center, we align the overlay instead.
CalculateDrawRect(overlay_display_rect.rsize(), apply_aspect_ratio, integer_scale, false, &source_rect,
&display_rect_without_overlay, &draw_rect_without_overlay);
CalculateDrawRect(overlay_display_rect.rsize(), apply_aspect_ratio, integer_scale, !is_vram_view, false,
&source_rect, &display_rect_without_overlay, &draw_rect_without_overlay);
// Apply overlay area offset.
display_rect = display_rect_without_overlay.add32(overlay_display_rect.xyxy());
@@ -466,8 +466,8 @@ GPUDevice::PresentResult GPUPresenter::RenderDisplay(GPUTexture* target, const G
}
else
{
CalculateDrawRect(target_size, apply_aspect_ratio, integer_scale, true, &source_rect, &display_rect_without_overlay,
&draw_rect_without_overlay);
CalculateDrawRect(target_size, apply_aspect_ratio, integer_scale, !is_vram_view, true, &source_rect,
&display_rect_without_overlay, &draw_rect_without_overlay);
display_rect = display_rect_without_overlay;
draw_rect = draw_rect_without_overlay;
}
@@ -738,7 +738,7 @@ void GPUPresenter::DrawDisplay(const GSVector2i target_size, const GSVector2i fi
const GSVector2 region_range =
(GSVector2(display_rect.rsize()) / GSVector2(display_source_rect)).floor().max(GSVector2::cxpr(1.0f));
GSVector2::store<true>(&uniforms.params[0], region_range);
GSVector2::store<true>(&uniforms.params[1], GSVector2::cxpr(0.5f) - (GSVector2::cxpr(0.5f) / region_range));
GSVector2::store<true>(&uniforms.params[2], GSVector2::cxpr(0.5f) - (GSVector2::cxpr(0.5f) / region_range));
texture_filter_linear = true;
}
break;
@@ -1175,13 +1175,15 @@ bool GPUPresenter::ApplyChromaSmoothing()
}
void GPUPresenter::CalculateDrawRect(const GSVector2i& window_size, bool apply_aspect_ratio, bool integer_scale,
bool apply_alignment, GSVector4i* source_rect, GSVector4i* display_rect,
GSVector4i* draw_rect) const
bool apply_crop, bool apply_alignment, GSVector4i* source_rect,
GSVector4i* display_rect, GSVector4i* draw_rect) const
{
GPU::CalculateDrawRect(
window_size, m_video_size, m_video_active_rect, m_display_texture_rect, g_gpu_settings.display_rotation,
apply_alignment ? g_gpu_settings.display_alignment : DisplayAlignment::Center,
apply_aspect_ratio ? m_display_pixel_aspect_ratio : 1.0f, integer_scale, source_rect, display_rect, draw_rect);
GPU::CalculateDrawRect(window_size, m_video_size, m_video_active_rect, m_display_texture_rect,
g_gpu_settings.display_rotation,
apply_alignment ? g_gpu_settings.display_alignment : DisplayAlignment::Center,
apply_aspect_ratio ? m_display_pixel_aspect_ratio : 1.0f, integer_scale,
apply_crop ? g_gpu_settings.display_fine_crop_mode : DisplayFineCropMode::None,
g_gpu_settings.display_fine_crop_amount, source_rect, display_rect, draw_rect);
}
bool GPUPresenter::PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bool allow_skip_present, u64 present_time)
@@ -1362,6 +1364,8 @@ bool GPUPresenter::RenderScreenshotToBuffer(u32 width, u32 height, bool postfx,
GSVector2i GPUPresenter::CalculateScreenshotSize(DisplayScreenshotMode mode) const
{
const GSVector2i window_size =
g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetSizeVec() : GSVector2i::cxpr(1, 1);
if (m_display_texture)
{
if (g_gpu_settings.gpu_show_vram)
@@ -1370,10 +1374,15 @@ GSVector2i GPUPresenter::CalculateScreenshotSize(DisplayScreenshotMode mode) con
}
else if (mode != DisplayScreenshotMode::ScreenResolution)
{
GSVector2 f_size =
GSVector2(m_video_size) * (GSVector2(m_display_texture_rect.rsize()) / GSVector2(m_video_active_rect.rsize()));
if (mode != DisplayScreenshotMode::UncorrectedInternalResolution)
f_size = GPU::ApplyPixelAspectRatioToSize(m_display_pixel_aspect_ratio, f_size);
const GSVector2 fvideo_size = GSVector2(m_video_size);
const GSVector2 fsource_size = GSVector2(m_display_texture_rect.rsize());
const GSVector2 factive_size = GSVector2(m_video_active_rect.rsize());
const GSVector2 fscale = (fsource_size / factive_size);
GSVector2 f_size = GPU::CalculateDisplayWindowSize(
g_gpu_settings.display_fine_crop_mode, g_gpu_settings.display_fine_crop_amount,
(mode != DisplayScreenshotMode::UncorrectedInternalResolution) ? m_display_pixel_aspect_ratio : 1.0f,
fvideo_size, fsource_size, GSVector2(window_size));
f_size *= fscale;
// DX11 won't go past 16K texture size.
const float max_texture_size = static_cast<float>(g_gpu_device->GetMaxTextureSize());
@@ -1392,7 +1401,7 @@ GSVector2i GPUPresenter::CalculateScreenshotSize(DisplayScreenshotMode mode) con
}
}
return g_gpu_device->HasMainSwapChain() ? g_gpu_device->GetMainSwapChain()->GetSizeVec() : GSVector2i(1, 1);
return window_size;
}
void GPUPresenter::LoadPostProcessingSettings(bool force_load)

View File

@@ -54,7 +54,7 @@ public:
bool ApplyChromaSmoothing();
/// Helper function for computing the draw rectangle in a larger window.
void CalculateDrawRect(const GSVector2i& window_size, bool apply_aspect_ratio, bool integer_scale,
void CalculateDrawRect(const GSVector2i& window_size, bool apply_aspect_ratio, bool integer_scale, bool apply_crop,
bool apply_alignment, GSVector4i* source_rect, GSVector4i* display_rect,
GSVector4i* draw_rect) const;

View File

@@ -11,6 +11,7 @@
#include "util/state_wrapper.h"
#include "common/assert.h"
#include "common/gsvector_formatter.h"
#include "common/log.h"
#include "common/path.h"
#include "common/string_util.h"
@@ -204,16 +205,14 @@ bool GunCon::Transfer(const u8 data_in, u8* data_out)
void GunCon::UpdatePosition()
{
float display_x, display_y;
const auto& [window_x, window_y] = (m_has_relative_binds) ? GetAbsolutePositionFromRelativeAxes() :
InputManager::GetPointerAbsolutePosition(m_cursor_index);
g_gpu.ConvertScreenCoordinatesToDisplayCoordinates(window_x, window_y, &display_x, &display_y);
const GSVector2 display_pos = g_gpu.ConvertScreenCoordinatesToDisplayCoordinates(GSVector2(window_x, window_y));
// are we within the active display area?
u32 tick, line;
if (display_x < 0 || display_y < 0 ||
!g_gpu.ConvertDisplayCoordinatesToBeamTicksAndLines(display_x, display_y, m_x_scale, &tick, &line) ||
m_shoot_offscreen)
if ((display_pos < GSVector2::zero()).anytrue() ||
!g_gpu.ConvertDisplayCoordinatesToBeamTicksAndLines(display_pos, m_x_scale, &tick, &line) || m_shoot_offscreen)
{
DEBUG_LOG("Lightgun out of range for window coordinates {:.0f},{:.0f}", window_x, window_y);
m_position_x = 0x01;
@@ -225,8 +224,7 @@ void GunCon::UpdatePosition()
const double divider = static_cast<double>(g_gpu.GetCRTCFrequency()) / 8000000.0;
m_position_x = static_cast<u16>(static_cast<float>(tick) / static_cast<float>(divider));
m_position_y = static_cast<u16>(line);
DEBUG_LOG("Lightgun window coordinates {:.0f},{:.0f} -> tick {} line {} 8mhz ticks {}", display_x, display_y, tick,
line, m_position_x);
DEBUG_LOG("Lightgun window coordinates {} -> tick {} line {} 8mhz ticks {}", display_pos, tick, line, m_position_x);
}
std::pair<float, float> GunCon::GetAbsolutePositionFromRelativeAxes() const

View File

@@ -12,6 +12,7 @@
#include "util/state_wrapper.h"
#include "common/assert.h"
#include "common/gsvector_formatter.h"
#include "common/log.h"
#include "common/path.h"
#include "common/string_util.h"
@@ -220,16 +221,14 @@ void Justifier::UpdatePosition()
return;
}
float display_x, display_y;
const auto [window_x, window_y] = (m_has_relative_binds) ? GetAbsolutePositionFromRelativeAxes() :
InputManager::GetPointerAbsolutePosition(m_cursor_index);
g_gpu.ConvertScreenCoordinatesToDisplayCoordinates(window_x, window_y, &display_x, &display_y);
const GSVector2 display_pos = g_gpu.ConvertScreenCoordinatesToDisplayCoordinates(GSVector2(window_x, window_y));
// are we within the active display area?
u32 tick, line;
if (display_x < 0 || display_y < 0 ||
!g_gpu.ConvertDisplayCoordinatesToBeamTicksAndLines(display_x, display_y, m_x_scale, &tick, &line) ||
m_shoot_offscreen)
if ((display_pos < GSVector2::zero()).anytrue() ||
!g_gpu.ConvertDisplayCoordinatesToBeamTicksAndLines(display_pos, m_x_scale, &tick, &line) || m_shoot_offscreen)
{
DEV_LOG("Lightgun out of range for window coordinates {:.0f},{:.0f}", window_x, window_y);
m_position_valid = false;
@@ -248,8 +247,8 @@ void Justifier::UpdatePosition()
static_cast<s32>(g_gpu.GetCRTCActiveStartLine()),
static_cast<s32>(g_gpu.GetCRTCActiveEndLine())));
DEV_LOG("Lightgun window coordinates {},{} -> dpy {},{} -> tick {} line {} [{}-{}]", window_x, window_y, display_x,
display_y, tick, line, m_irq_first_line, m_irq_last_line);
DEV_LOG("Lightgun window coordinates {},{} -> dpy {} -> tick {} line {} [{}-{}]", window_x, window_y, display_pos,
tick, line, m_irq_first_line, m_irq_last_line);
UpdateIRQEvent();
}

View File

@@ -319,6 +319,16 @@ void Settings::Load(const SettingsInterface& si, const SettingsInterface& contro
.value_or(DEFAULT_DISPLAY_CROP_MODE);
display_aspect_ratio =
ParseDisplayAspectRatio(si.GetStringValue("Display", "AspectRatio")).value_or(DEFAULT_DISPLAY_ASPECT_RATIO);
display_fine_crop_mode = ParseDisplayFineCropMode(si.GetStringValue("Display", "FineCropMode").c_str())
.value_or(DEFAULT_DISPLAY_FINE_CROP_MODE);
display_fine_crop_amount[0] = static_cast<s16>(std::clamp<s32>(
si.GetIntValue("Display", "FineCropLeft", 0), std::numeric_limits<s16>::min(), std::numeric_limits<s16>::max()));
display_fine_crop_amount[1] = static_cast<s16>(std::clamp<s32>(
si.GetIntValue("Display", "FineCropTop", 0), std::numeric_limits<s16>::min(), std::numeric_limits<s16>::max()));
display_fine_crop_amount[2] = static_cast<s16>(std::clamp<s32>(
si.GetIntValue("Display", "FineCropRight", 0), std::numeric_limits<s16>::min(), std::numeric_limits<s16>::max()));
display_fine_crop_amount[3] = static_cast<s16>(std::clamp<s32>(
si.GetIntValue("Display", "FineCropBottom", 0), std::numeric_limits<s16>::min(), std::numeric_limits<s16>::max()));
display_alignment =
ParseDisplayAlignment(
si.GetStringValue("Display", "Alignment", GetDisplayAlignmentName(DEFAULT_DISPLAY_ALIGNMENT)).c_str())
@@ -695,6 +705,11 @@ void Settings::Save(SettingsInterface& si, bool ignore_base) const
si.SetIntValue("Display", "LineEndOffset", display_line_end_offset);
si.SetBoolValue("Display", "Force4_3For24Bit", display_force_4_3_for_24bit);
si.SetStringValue("Display", "AspectRatio", GetDisplayAspectRatioName(display_aspect_ratio).c_str());
si.SetStringValue("Display", "FineCropMode", GetDisplayFineCropModeName(display_fine_crop_mode));
si.SetIntValue("Display", "FineCropLeft", display_fine_crop_amount[0]);
si.SetIntValue("Display", "FineCropTop", display_fine_crop_amount[1]);
si.SetIntValue("Display", "FineCropRight", display_fine_crop_amount[2]);
si.SetIntValue("Display", "FineCropBottom", display_fine_crop_amount[3]);
si.SetStringValue("Display", "Alignment", GetDisplayAlignmentName(display_alignment));
si.SetStringValue("Display", "Rotation", GetDisplayRotationName(display_rotation));
si.SetStringValue("Display", "Scaling", GetDisplayScalingName(display_scaling));
@@ -1910,6 +1925,46 @@ const char* Settings::GetDisplayCropModeDisplayName(DisplayCropMode crop_mode)
"DisplayCropMode");
}
static constexpr const std::array s_display_fine_crop_mode_names = {
"None",
"VideoResolution",
"InternalResolution",
"WindowResolution",
};
static constexpr const std::array s_display_fine_crop_mode_display_names = {
TRANSLATE_DISAMBIG_NOOP("Settings", "None", "DisplayFineCropMode"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Video Resolution", "DisplayFineCropMode"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Internal Resolution", "DisplayFineCropMode"),
TRANSLATE_DISAMBIG_NOOP("Settings", "Window Resolution", "DisplayFineCropMode"),
};
static_assert(s_display_fine_crop_mode_names.size() == static_cast<size_t>(DisplayFineCropMode::MaxCount));
static_assert(s_display_fine_crop_mode_display_names.size() == static_cast<size_t>(DisplayFineCropMode::MaxCount));
std::optional<DisplayFineCropMode> Settings::ParseDisplayFineCropMode(const char* str)
{
int index = 0;
for (const char* name : s_display_fine_crop_mode_names)
{
if (StringUtil::Strcasecmp(name, str) == 0)
return static_cast<DisplayFineCropMode>(index);
index++;
}
return std::nullopt;
}
const char* Settings::GetDisplayFineCropModeName(DisplayFineCropMode mode)
{
return s_display_fine_crop_mode_names[static_cast<size_t>(mode)];
}
const char* Settings::GetDisplayFineCropModeDisplayName(DisplayFineCropMode mode)
{
return Host::TranslateToCString("Settings", s_display_fine_crop_mode_display_names[static_cast<size_t>(mode)],
"DisplayFineCropMode");
}
static constexpr const std::string_view s_auto_aspect_ratio_name =
TRANSLATE_DISAMBIG_NOOP("Settings", "Auto (Game Native)", "DisplayAspectRatio");
static constexpr const std::string_view s_stretch_aspect_ratio_name =

View File

@@ -43,6 +43,7 @@ struct GPUSettings
DisplayDeinterlacingMode display_deinterlacing_mode = DEFAULT_DISPLAY_DEINTERLACING_MODE;
DisplayAspectRatio display_aspect_ratio = DEFAULT_DISPLAY_ASPECT_RATIO;
DisplayCropMode display_crop_mode = DEFAULT_DISPLAY_CROP_MODE;
DisplayFineCropMode display_fine_crop_mode = DEFAULT_DISPLAY_FINE_CROP_MODE;
DisplayAlignment display_alignment = DEFAULT_DISPLAY_ALIGNMENT;
DisplayRotation display_rotation = DEFAULT_DISPLAY_ROTATION;
DisplayScalingMode display_scaling = DEFAULT_DISPLAY_SCALING;
@@ -119,6 +120,8 @@ struct GPUSettings
float gpu_pgxp_tolerance = -1.0f;
float gpu_pgxp_depth_clear_threshold = 0.0f;
std::array<s16, 4> display_fine_crop_amount = {};
float display_osd_scale = DEFAULT_OSD_SCALE;
float display_osd_margin = 0.0f;
@@ -218,6 +221,7 @@ struct GPUSettings
static constexpr DisplayDeinterlacingMode DEFAULT_DISPLAY_DEINTERLACING_MODE = DisplayDeinterlacingMode::Progressive;
static constexpr DisplayCropMode DEFAULT_DISPLAY_CROP_MODE = DisplayCropMode::Overscan;
static constexpr DisplayAspectRatio DEFAULT_DISPLAY_ASPECT_RATIO = DisplayAspectRatio::Auto();
static constexpr DisplayFineCropMode DEFAULT_DISPLAY_FINE_CROP_MODE = DisplayFineCropMode::None;
static constexpr DisplayAlignment DEFAULT_DISPLAY_ALIGNMENT = DisplayAlignment::Center;
static constexpr DisplayRotation DEFAULT_DISPLAY_ROTATION = DisplayRotation::Normal;
static constexpr DisplayScalingMode DEFAULT_DISPLAY_SCALING = DisplayScalingMode::BilinearSmooth;
@@ -506,6 +510,10 @@ struct Settings : public GPUSettings
static const char* GetDisplayCropModeName(DisplayCropMode crop_mode);
static const char* GetDisplayCropModeDisplayName(DisplayCropMode crop_mode);
static std::optional<DisplayFineCropMode> ParseDisplayFineCropMode(const char* str);
static const char* GetDisplayFineCropModeName(DisplayFineCropMode mode);
static const char* GetDisplayFineCropModeDisplayName(DisplayFineCropMode mode);
static std::optional<DisplayAspectRatio> ParseDisplayAspectRatio(std::string_view str);
static TinyString GetDisplayAspectRatioName(DisplayAspectRatio ar);
static TinyString GetDisplayAspectRatioDisplayName(DisplayAspectRatio ar);

View File

@@ -4603,6 +4603,8 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
g_settings.display_aspect_ratio != old_settings.display_aspect_ratio ||
g_settings.display_scaling != old_settings.display_scaling ||
g_settings.display_scaling_24bit != old_settings.display_scaling_24bit ||
g_settings.display_fine_crop_mode != old_settings.display_fine_crop_mode ||
g_settings.display_fine_crop_amount != old_settings.display_fine_crop_amount ||
g_settings.display_alignment != old_settings.display_alignment ||
g_settings.display_rotation != old_settings.display_rotation ||
g_settings.display_deinterlacing_mode != old_settings.display_deinterlacing_mode ||
@@ -6070,12 +6072,15 @@ void System::RequestDisplaySize(float scale /*= 0.0f*/)
GSVector2 requested_size;
if (g_settings.gpu_show_vram)
{
requested_size = GSVector2::cxpr(static_cast<s32>(VRAM_WIDTH), static_cast<s32>(VRAM_HEIGHT)) * scale;
requested_size = GSVector2::cxpr(static_cast<s32>(VRAM_WIDTH), static_cast<s32>(VRAM_HEIGHT));
}
else
{
requested_size =
g_gpu.ApplyPixelAspectRatioToSize(g_gpu.ComputePixelAspectRatio(), GSVector2(g_gpu.GetCRTCVideoSize()) * scale);
const WindowInfo& wi = GPUThread::GetRenderWindowInfo();
requested_size = GPU::CalculateDisplayWindowSize(
g_settings.display_fine_crop_mode, g_settings.display_fine_crop_amount, g_gpu.ComputePixelAspectRatio(),
GSVector2(g_gpu.GetCRTCVideoSize()), GSVector2(g_gpu.GetCRTCVRAMSourceRect().rsize()) * scale,
GSVector2(GSVector2i(wi.surface_width, wi.surface_height)));
}
if (g_settings.display_rotation == DisplayRotation::Rotate90 ||
@@ -6084,6 +6089,8 @@ void System::RequestDisplaySize(float scale /*= 0.0f*/)
requested_size = requested_size.yx();
}
requested_size *= scale;
const GSVector2i requested_sizei = GSVector2i(requested_size.ceil());
Host::RequestResizeHostDisplay(requested_sizei.x, requested_sizei.y);
}

View File

@@ -163,6 +163,15 @@ enum class DisplayCropMode : u8
MaxCount
};
enum class DisplayFineCropMode : u8
{
None,
VideoResolution,
InternalResolution,
WindowResolution,
MaxCount
};
enum class DisplayAlignment : u8
{
LeftOrTop,

View File

@@ -139,6 +139,14 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
&Settings::ParseDisplayRotation, &Settings::GetDisplayRotationName,
&Settings::GetDisplayRotationDisplayName,
Settings::DEFAULT_DISPLAY_ROTATION, DisplayRotation::Count);
SettingWidgetBinder::BindWidgetToEnumSetting(
sif, m_ui.displayFineCropMode, "Display", "FineCropMode", &Settings::ParseDisplayFineCropMode,
&Settings::GetDisplayFineCropModeName, &Settings::GetDisplayFineCropModeDisplayName,
Settings::DEFAULT_DISPLAY_FINE_CROP_MODE, DisplayFineCropMode::MaxCount);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayFineCropLeft, "Display", "FineCropLeft", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayFineCropTop, "Display", "FineCropTop", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayFineCropRight, "Display", "FineCropRight", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.displayFineCropBottom, "Display", "FineCropBottom", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableMailboxPresentation, "Display",
"DisableMailboxPresentation", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.automaticallyResizeWindow, "Display", "AutoResizeWindow",
@@ -159,6 +167,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.forceRoundedTexcoords, "GPU", "ForceRoundTextureCoordinates",
false);
connect(m_ui.displayFineCropMode, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&GraphicsSettingsWidget::onFineCropModeChanged);
connect(m_ui.displayFineCropReset, &QPushButton::clicked, this, &GraphicsSettingsWidget::onFineCropResetClicked);
connect(m_ui.gpuThread, &QCheckBox::checkStateChanged, this, &GraphicsSettingsWidget::onGPUThreadChanged);
SettingWidgetBinder::SetAvailability(m_ui.scaledInterlacing,
@@ -377,6 +388,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
// Init all dependent options.
updateRendererDependentOptions();
onDownsampleModeChanged();
onFineCropModeChanged();
updateResolutionDependentOptions();
onOSDShowMessagesChanged();
onMediaCaptureBackendChanged();
@@ -478,6 +490,9 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
m_ui.displayAlignment, tr("Position"),
QString::fromUtf8(Settings::GetDisplayAlignmentDisplayName(Settings::DEFAULT_DISPLAY_ALIGNMENT)),
tr("Determines the position on the screen when black borders must be added."));
dialog->registerWidgetHelp(m_ui.displayFineCropMode, tr("Fine Crop Mode"), tr("None"),
tr("Enables manual fine cropping of the display area, while preserving the aspect ratio "
"of the image. Useful for removing black borders in certain games."));
dialog->registerWidgetHelp(
m_ui.disableMailboxPresentation, tr("Disable Mailbox Presentation"), tr("Unchecked"),
tr("Forces the use of FIFO over Mailbox presentation, i.e. double buffering instead of triple buffering. "
@@ -1162,6 +1177,54 @@ void GraphicsSettingsWidget::onDownsampleModeChanged()
}
}
void GraphicsSettingsWidget::onFineCropModeChanged()
{
const DisplayFineCropMode mode =
Settings::ParseDisplayFineCropMode(m_dialog->getEffectiveStringValue("Display", "FineCropMode", "").c_str())
.value_or(Settings::DEFAULT_DISPLAY_FINE_CROP_MODE);
const bool enabled = (mode != DisplayFineCropMode::None);
m_ui.displayFineCropLabel->setEnabled(enabled);
m_ui.displayFineCropLeftLabel->setEnabled(enabled);
m_ui.displayFineCropLeft->setEnabled(enabled);
m_ui.displayFineCropTopLabel->setEnabled(enabled);
m_ui.displayFineCropTop->setEnabled(enabled);
m_ui.displayFineCropRightLabel->setEnabled(enabled);
m_ui.displayFineCropRight->setEnabled(enabled);
m_ui.displayFineCropBottomLabel->setEnabled(enabled);
m_ui.displayFineCropBottom->setEnabled(enabled);
}
void GraphicsSettingsWidget::onFineCropResetClicked()
{
if (m_dialog->isPerGameSettings())
{
// Super nasty.. need to set the default without the signal firing and writing the global value to the ini...
const auto reset = [this]<typename T>(T* widget, const char* key) {
const QSignalBlocker sb(widget);
if constexpr (std::is_same_v<T, QComboBox>)
SettingWidgetBinder::SettingAccessor<T>::setNullableStringValue(widget, std::nullopt);
else
SettingWidgetBinder::SettingAccessor<QSpinBox>::setNullableIntValue(widget, std::nullopt);
m_dialog->removeSettingValue("Display", key);
};
reset(m_ui.displayFineCropMode, "FineCropMode");
reset(m_ui.displayFineCropLeft, "FineCropLeft");
reset(m_ui.displayFineCropTop, "FineCropTop");
reset(m_ui.displayFineCropRight, "FineCropRight");
reset(m_ui.displayFineCropBottom, "FineCropBottom");
}
else
{
m_ui.displayFineCropMode->setCurrentIndex(0);
m_ui.displayFineCropLeft->setValue(0);
m_ui.displayFineCropTop->setValue(0);
m_ui.displayFineCropRight->setValue(0);
m_ui.displayFineCropBottom->setValue(0);
}
}
void GraphicsSettingsWidget::onOSDShowMessagesChanged()
{
const bool enabled = m_dialog->getEffectiveBoolValue("Display", "ShowOSDMessages", true);

View File

@@ -48,6 +48,8 @@ private:
void updateResolutionDependentOptions();
void onDownsampleModeChanged();
void onFineCropModeChanged();
void onFineCropResetClicked();
void onOSDShowMessagesChanged();

View File

@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>655</width>
<width>734</width>
<height>511</height>
</rect>
</property>
@@ -351,7 +351,128 @@
</item>
</layout>
</item>
<item row="2" column="0" colspan="2">
<item row="2" column="0">
<widget class="QLabel" name="displayFineCropModeLabel">
<property name="text">
<string>Fine Crop Mode:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="1,0">
<item>
<widget class="QComboBox" name="displayFineCropMode"/>
</item>
<item>
<widget class="QPushButton" name="displayFineCropReset">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Reset Fine Cropping</string>
</property>
<property name="icon">
<iconset theme="delete-back-2-line"/>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QLabel" name="displayFineCropLabel">
<property name="text">
<string>Fine Crop Size:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3" stretch="0,1,0,1,0,1,0,1">
<item>
<widget class="QLabel" name="displayFineCropLeftLabel">
<property name="text">
<string>Left:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="displayFineCropLeft">
<property name="suffix">
<string>px</string>
</property>
<property name="minimum">
<number>-32768</number>
</property>
<property name="maximum">
<number>32767</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="displayFineCropTopLabel">
<property name="text">
<string>Top:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="displayFineCropTop">
<property name="suffix">
<string>px</string>
</property>
<property name="minimum">
<number>-32768</number>
</property>
<property name="maximum">
<number>32767</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="displayFineCropRightLabel">
<property name="text">
<string>Right:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="displayFineCropRight">
<property name="suffix">
<string>px</string>
</property>
<property name="minimum">
<number>-32768</number>
</property>
<property name="maximum">
<number>32767</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="displayFineCropBottomLabel">
<property name="text">
<string>Bottom:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="displayFineCropBottom">
<property name="suffix">
<string>px</string>
</property>
<property name="minimum">
<number>-32768</number>
</property>
<property name="maximum">
<number>32767</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<layout class="QGridLayout" name="advancedDisplayOptionsLayout">
<item row="0" column="0">
<widget class="QCheckBox" name="automaticallyResizeWindow">

View File

@@ -41,6 +41,7 @@
<file>icons/black/svg/debugger-go-to-address.svg</file>
<file>icons/black/svg/debugger-go-to-cursor.svg</file>
<file>icons/black/svg/debugger-go-to-pc.svg</file>
<file>icons/black/svg/delete-back-2-line.svg</file>
<file>icons/black/svg/disc-eject-line.svg</file>
<file>icons/black/svg/disc-line.svg</file>
<file>icons/black/svg/door-open-line.svg</file>
@@ -200,6 +201,7 @@
<file>icons/white/svg/debugger-go-to-address.svg</file>
<file>icons/white/svg/debugger-go-to-cursor.svg</file>
<file>icons/white/svg/debugger-go-to-pc.svg</file>
<file>icons/white/svg/delete-back-2-line.svg</file>
<file>icons/white/svg/disc-eject-line.svg</file>
<file>icons/white/svg/disc-line.svg</file>
<file>icons/white/svg/door-open-line.svg</file>

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M6.53451 3H20.9993C21.5516 3 21.9993 3.44772 21.9993 4V20C21.9993 20.5523 21.5516 21 20.9993 21H6.53451C6.20015 21 5.88792 20.8329 5.70246 20.5547L0.369122 12.5547C0.145189 12.2188 0.145189 11.7812 0.369122 11.4453L5.70246 3.4453C5.88792 3.1671 6.20015 3 6.53451 3ZM7.06969 5L2.40302 12L7.06969 19H19.9993V5H7.06969ZM12.9993 10.5858L15.8277 7.75736L17.242 9.17157L14.4135 12L17.242 14.8284L15.8277 16.2426L12.9993 13.4142L10.1709 16.2426L8.75668 14.8284L11.5851 12L8.75668 9.17157L10.1709 7.75736L12.9993 10.5858Z"></path></svg>

After

Width:  |  Height:  |  Size: 597 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#ffffff"><path d="M6.53451 3H20.9993C21.5516 3 21.9993 3.44772 21.9993 4V20C21.9993 20.5523 21.5516 21 20.9993 21H6.53451C6.20015 21 5.88792 20.8329 5.70246 20.5547L0.369122 12.5547C0.145189 12.2188 0.145189 11.7812 0.369122 11.4453L5.70246 3.4453C5.88792 3.1671 6.20015 3 6.53451 3ZM7.06969 5L2.40302 12L7.06969 19H19.9993V5H7.06969ZM12.9993 10.5858L15.8277 7.75736L17.242 9.17157L14.4135 12L17.242 14.8284L15.8277 16.2426L12.9993 13.4142L10.1709 16.2426L8.75668 14.8284L11.5851 12L8.75668 9.17157L10.1709 7.75736L12.9993 10.5858Z"></path></svg>

After

Width:  |  Height:  |  Size: 612 B