diff --git a/d2dx-defaults.cfg b/d2dx-defaults.cfg index 3ef456e..277bef4 100644 --- a/d2dx-defaults.cfg +++ b/d2dx-defaults.cfg @@ -1,32 +1,32 @@ # # This is an example config file for D2DX. # -# If you don't like the default settings, you can edit this file, rename it to "d2dx.cfg" -# and place it in the game folder. +# If you don't like the default settings, you can edit this file, rename it to "d2dx.cfg", and place it in the game folder. # [window] -scale=1 # range 1-3, an integer scale factor for the window -position=[-1,-1] # if [-1,-1] the window will be centered, otherwise placed at the explicit position given here -frameless=false # if true, the window frame (caption bar etc) will be removed +scale=1 # range 1-3, an integer scale factor for the window +position=[-1,-1] # if [-1,-1] the window will be centered, otherwise placed at the explicit position given here +frameless=false # if true, the window frame (caption bar etc) will be removed [game] -size=[-1,-1] # if [-1,-1] d2dx will decide a suitable game size, otherwise will use the size given here -filtering=0 # if 0, will use high quality filtering (sharp, more pixelated) - # 1, will use bilinear filtering (blurry) - # 2, will use catmull-rom filtering (higher quality than bilinear) +scale=3.0 # scale the size of the viewport (capped to screen size, maintains aspect ratio) +size=[-1,-1] # if [-1,-1] d2dx will decide a suitable game size, otherwise will use the size given here +filtering=0 # if 0, will use high quality filtering (sharp, more pixelated) + # 1, will use bilinear filtering (blurry) + # 2, will use catmull-rom filtering (higher quality than bilinear) # # Opt-outs from default D2DX behavior # [optouts] -noclipcursor=false # if true, will not lock the mouse cursor to the game window -nofpsfix=false # if true, will not apply the basic fps fix (precludes high fps support) -noresmod=false # if true, will not apply the built-in D2HD resolution mod (precludes widescreen support) -nowide=false # if true, will not choose a widescreen resolution (if noresmod is true, this does nothing) -nologo=false # if true, will not display the D2DX logo on the title screen -novsync=false # if true, will not use vertical sync -noaa=false # if true, will not apply anti-aliasing to jagged edges -nocompatmodefix=false # if true, will not block the use of "Windows XP compatibility mode" -notitlechange=false # if true, will not change the window title text -nomotionprediction=false # if true, will not run the game graphics at high fps +noclipcursor=false # if true, will not lock the mouse cursor to the game window +nofpsfix=false # if true, will not apply the basic fps fix (precludes high fps support) +noresmod=false # if true, will not apply the built-in D2HD resolution mod (precludes widescreen support) +nowide=false # if true, will not choose a widescreen resolution (if noresmod is true, this does nothing) +nologo=false # if true, will not display the D2DX logo on the title screen +novsync=false # if true, will not use vertical sync +noaa=false # if true, will not apply anti-aliasing to jagged edges +nocompatmodefix=false # if true, will not block the use of "Windows XP compatibility mode" +notitlechange=false # if true, will not change the window title text +nomotionprediction=false # if true, will not run the game graphics at high fps diff --git a/src/d2dx/Metrics.cpp b/src/d2dx/Metrics.cpp index 4183adf..cfa86a7 100644 --- a/src/d2dx/Metrics.cpp +++ b/src/d2dx/Metrics.cpp @@ -184,28 +184,27 @@ _Use_decl_annotations_ Rect d2dx::Metrics::GetRenderRect( Size gameSize, Size desktopSize, - bool wide) noexcept + bool wide, + double gameScale) noexcept { - int32_t scaleFactor = 1; - while ( - gameSize.width * (scaleFactor + 1) <= desktopSize.width && - gameSize.height * (scaleFactor + 1) <= desktopSize.height) + bool fitToScreen = false; + + if (gameScale * gameSize.width > desktopSize.width || gameScale * gameSize.height > desktopSize.height) { - ++scaleFactor; + gameScale = 1.0; + fitToScreen = true; } Rect rect { - (desktopSize.width - gameSize.width * scaleFactor) / 2, - (desktopSize.height - gameSize.height * scaleFactor) / 2, - gameSize.width * scaleFactor, - gameSize.height * scaleFactor + (int32_t)((desktopSize.width - gameSize.width * gameScale) / 2), + (int32_t)((desktopSize.height - gameSize.height * gameScale) / 2), + (int32_t)(gameSize.width * gameScale), + (int32_t)(gameSize.height * gameScale) }; - /* Allow for a small amount of black margin on all sides. When more than that, - rescale the image with a non-integer factor. */ - if (rect.offset.x < 0 || rect.offset.y < 0 || (rect.offset.x >= 16 && rect.offset.y >= 16)) + if (fitToScreen || rect.offset.x < 0 || rect.offset.y < 0) { float scaleFactorF = (float)desktopSize.width / rect.size.width; int32_t scaledHeight = (int32_t)(rect.size.height * scaleFactorF); diff --git a/src/d2dx/Metrics.h b/src/d2dx/Metrics.h index b30de5b..aefa0c4 100644 --- a/src/d2dx/Metrics.h +++ b/src/d2dx/Metrics.h @@ -14,7 +14,8 @@ namespace d2dx Rect GetRenderRect( _In_ Size gameSize, _In_ Size desktopSize, - _In_ bool wide) noexcept; + _In_ bool wide, + _In_ double gameScale) noexcept; Buffer GetStandardDesktopSizes() noexcept; } diff --git a/src/d2dx/Options.cpp b/src/d2dx/Options.cpp index 26adff1..acd1f4f 100644 --- a/src/d2dx/Options.cpp +++ b/src/d2dx/Options.cpp @@ -104,6 +104,12 @@ void Options::ApplyCfg( { _filtering = (FilteringOption)filtering.u.i; } + + auto gameScale = toml_double_in(game, "scale"); + if (gameScale.ok) + { + SetGameScale(gameScale.u.d); + } } auto window = toml_table_in(root, "window"); @@ -227,3 +233,21 @@ FilteringOption Options::GetFiltering() const { return _filtering; } + +double Options::GetGameScale() const +{ + return _gameScale; +} + +void Options::SetGameScale( + _In_ double gameScale) +{ + if (gameScale < 1.0) + { + _gameScale = 1.0; + } + else + { + _gameScale = gameScale; + } +} \ No newline at end of file diff --git a/src/d2dx/Options.h b/src/d2dx/Options.h index 9521663..ca1bcc7 100644 --- a/src/d2dx/Options.h +++ b/src/d2dx/Options.h @@ -86,11 +86,17 @@ namespace d2dx FilteringOption GetFiltering() const; + double GetGameScale() const; + + void SetGameScale( + _In_ double gameScale); + private: uint32_t _flags = 0; int32_t _windowScale = 1; Offset _windowPosition{ -1, -1 }; Size _userSpecifiedGameSize{ -1, -1 }; FilteringOption _filtering{ FilteringOption::HighQuality }; + double _gameScale = 3.0; }; } \ No newline at end of file diff --git a/src/d2dx/RenderContext.cpp b/src/d2dx/RenderContext.cpp index 9beb040..30f834b 100644 --- a/src/d2dx/RenderContext.cpp +++ b/src/d2dx/RenderContext.cpp @@ -75,7 +75,8 @@ RenderContext::RenderContext( _renderRect = Metrics::GetRenderRect( gameSize, _screenMode == ScreenMode::FullscreenDefault ? _desktopSize : _windowSize, - !_d2dxContext->GetOptions().GetFlag(OptionsFlag::NoWide)); + !_d2dxContext->GetOptions().GetFlag(OptionsFlag::NoWide), + _d2dxContext->GetOptions().GetGameScale()); #ifndef NDEBUG ShowCursor_Real(TRUE); @@ -849,7 +850,8 @@ void RenderContext::SetSizes( _renderRect = Metrics::GetRenderRect( _gameSize, displaySize, - !_d2dxContext->GetOptions().GetFlag(OptionsFlag::NoWide)); + !_d2dxContext->GetOptions().GetFlag(OptionsFlag::NoWide), + _d2dxContext->GetOptions().GetGameScale()); bool centerOnCurrentPosition = _hasAdjustedWindowPlacement; _hasAdjustedWindowPlacement = true; @@ -892,7 +894,8 @@ void RenderContext::SetSizes( _renderRect = Metrics::GetRenderRect( _gameSize, _windowSize, - !_d2dxContext->GetOptions().GetFlag(OptionsFlag::NoWide)); + !_d2dxContext->GetOptions().GetFlag(OptionsFlag::NoWide), + _d2dxContext->GetOptions().GetGameScale()); windowRect = { 0, 0, _windowSize.width, _windowSize.height }; AdjustWindowRect(&windowRect, windowStyle, FALSE); @@ -1021,13 +1024,22 @@ void RenderContext::GetCurrentMetrics( void RenderContext::ClipCursor() { - if (_d2dxContext->GetOptions().GetFlag(OptionsFlag::NoClipCursor)) + if (_d2dxContext->GetOptions().GetFlag(OptionsFlag::NoClipCursor) && _screenMode == ScreenMode::Windowed) { + UnclipCursor(); return; } RECT clipRect; ::GetClientRect(_hWnd, &clipRect); + + clipRect.left += _renderRect.offset.x; + clipRect.right -= _renderRect.offset.x; + + // Not 100% sure why this works. + clipRect.bottom -= _renderRect.offset.y * 2; + + // Casting a scalar to a point is confusing. ::ClientToScreen(_hWnd, (LPPOINT)&clipRect.left); ::ClientToScreen(_hWnd, (LPPOINT)&clipRect.right); ::ClipCursor(&clipRect); diff --git a/src/d2dxtests/TestMetrics.cpp b/src/d2dxtests/TestMetrics.cpp index 35804b5..53216b5 100644 --- a/src/d2dxtests/TestMetrics.cpp +++ b/src/d2dxtests/TestMetrics.cpp @@ -65,7 +65,9 @@ namespace d2dxtests void AssertThatGameSizeIsIntegerScale(Size desktopSize, bool wide, bool lenient) { auto suggestedGameSize = d2dx::Metrics::GetSuggestedGameSize(desktopSize, wide); - auto renderRect = d2dx::Metrics::GetRenderRect(suggestedGameSize, desktopSize, wide); + + // The gameScale parameter is not fully covered by tests. This should be fixed. + auto renderRect = d2dx::Metrics::GetRenderRect(suggestedGameSize, desktopSize, wide, 999999999.0); Assert::IsTrue(renderRect.offset.x >= 0); Assert::IsTrue(renderRect.offset.y >= 0); Assert::IsTrue(renderRect.size.width > 0); @@ -73,11 +75,6 @@ namespace d2dxtests Assert::IsTrue((renderRect.offset.x + renderRect.size.width) <= desktopSize.width); Assert::IsTrue((renderRect.offset.y + renderRect.size.height) <= desktopSize.height); - if (renderRect.offset.x > 0 && renderRect.offset.y > 0) - { - Assert::IsTrue(renderRect.offset.x < 16 || renderRect.offset.y < 16); - } - int32_t reconstructedDesktopWidth = renderRect.size.width + renderRect.offset.x * 2; int32_t reconstructedDesktopHeight = renderRect.size.height + renderRect.offset.y * 2;