From 4c3529fb0805569ad431e14c2244b79cb34a1cfe Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Wed, 2 Nov 2022 19:25:04 -0700 Subject: [PATCH 01/27] Adding backing scale to the pipeline This is a better map for the display scale handling. Adding scaling to the intro movie. # Conflicts: # Sources/Plasma/Apps/plClient/OSX/main.mm --- Sources/Plasma/Apps/plClient/plClient.cpp | 2 +- Sources/Plasma/NucleusLib/inc/plPipeline.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Sources/Plasma/Apps/plClient/plClient.cpp b/Sources/Plasma/Apps/plClient/plClient.cpp index 0571c4f9a0..424eb138e7 100644 --- a/Sources/Plasma/Apps/plClient/plClient.cpp +++ b/Sources/Plasma/Apps/plClient/plClient.cpp @@ -1422,7 +1422,7 @@ bool plClient::StartInit() bool plClient::BeginGame() { plNetClientMgr::GetInstance()->Init(); - IPlayIntroMovie("avi/CyanWorlds.webm", 0.f, 0.f, 0.f, 1.f, 1.f, 0.75); + IPlayIntroMovie("avi/CyanWorlds.webm", 0.f, 0.f, 0.f, fPipeline->fBackingScale, fPipeline->fBackingScale, 0.75); if (GetDone()) return false; if (NetCommGetStartupAge()->ageDatasetName.compare_i("StartUp") == 0) { // This is needed because there is no auth step in this case diff --git a/Sources/Plasma/NucleusLib/inc/plPipeline.h b/Sources/Plasma/NucleusLib/inc/plPipeline.h index 9d20fd5a73..42b410cfde 100644 --- a/Sources/Plasma/NucleusLib/inc/plPipeline.h +++ b/Sources/Plasma/NucleusLib/inc/plPipeline.h @@ -353,6 +353,9 @@ class plPipeline : public plCreatable plDisplayMode fDesktopParams; virtual size_t GetViewStackSize() const = 0; + + float fBackingScale = 1.0f; + void SetBackingScale(float scale) { fBackingScale = scale; }; }; #endif // plPipeline_inc From fd512f9449b9dfb3767ddfd6bbe69f0d2da83b28 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 4 Dec 2022 20:32:48 -0800 Subject: [PATCH 02/27] Adding scale support to progress bar --- .../PubUtilLib/plPipeline/plDTProgressMgr.cpp | 21 ++++++++++--------- .../PubUtilLib/plPipeline/plDTProgressMgr.h | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp b/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp index 9046178b99..baa1918d12 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp @@ -132,6 +132,7 @@ void plDTProgressMgr::Draw( plPipeline *p ) { uint16_t scrnWidth, scrnHeight, width, height, x, y; plDebugText &text = plDebugText::Instance(); + float drawScale = p->fBackingScale; plOperationProgress *prog; @@ -143,7 +144,7 @@ void plDTProgressMgr::Draw( plPipeline *p ) scrnHeight = (uint16_t)p->Height(); width = scrnWidth - 64; - height = 16; + height = 16 * drawScale; x = ( scrnWidth - width ) >> 1; y = scrnHeight - 44 - (2 * height) - text.GetFontSize(); @@ -171,8 +172,8 @@ void plDTProgressMgr::Draw( plPipeline *p ) for (prog = fOperations; prog != nullptr; prog = prog->GetNext()) { - if (IDrawTheStupidThing(p, prog, x, y, width, height)) - y -= text.GetFontSize() + 8 + height + 4; + if (IDrawTheStupidThing(p, prog, x, y, width, height, drawScale)) + y -= text.GetFontSize() + (8 * drawScale) + height + (4 * drawScale); } text.SetDrawOnTopMode( false ); @@ -181,11 +182,11 @@ void plDTProgressMgr::Draw( plPipeline *p ) //// IDrawTheStupidThing ///////////////////////////////////////////////////// bool plDTProgressMgr::IDrawTheStupidThing(plPipeline *p, plOperationProgress *prog, - uint16_t x, uint16_t y, uint16_t width, uint16_t height) + uint16_t x, uint16_t y, uint16_t width, uint16_t height, float scale) { plDebugText &text = plDebugText::Instance(); bool drew_something = false; - uint16_t downsz = (text.GetFontSize() << 1) + 4; + uint16_t downsz = (text.GetFontHeight() << 1) + (4 * scale); // draw the title if (!prog->GetTitle().empty()) { @@ -199,10 +200,10 @@ bool plDTProgressMgr::IDrawTheStupidThing(plPipeline *p, plOperationProgress if (prog->GetMax() > 0.f) { text.Draw3DBorder(x, y, x + width - 1, y + height - 1, kProgressBarColor, kProgressBarColor); - x += 2; - y += 2; - width -= 4; - height -= 4; + x += (2 * scale); + y += (2 * scale); + width -= (4 * scale); + height -= (4 * scale); uint16_t drawWidth = width; int16_t drawX = x; @@ -214,7 +215,7 @@ bool plDTProgressMgr::IDrawTheStupidThing(plPipeline *p, plOperationProgress rightX = drawX + drawWidth; if (drawWidth > 0) text.DrawRect(drawX, y, rightX, y + height, kProgressBarColor); - y += height + 2; + y += height + (2 * scale); drew_something = true; } diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.h b/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.h index ece3633265..7884f16aac 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.h +++ b/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.h @@ -71,7 +71,7 @@ class plDTProgressMgr : public plProgressMgr void Deactivate() override; bool IDrawTheStupidThing( plPipeline *p, plOperationProgress *prog, - uint16_t x, uint16_t y, uint16_t width, uint16_t height ); + uint16_t x, uint16_t y, uint16_t width, uint16_t height, float scale ); public: From 241bbdb5dfa6135767ec594062c8205cbdb592db Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Fri, 9 Dec 2022 23:28:17 -0800 Subject: [PATCH 03/27] Fixing variable DPI line spacing in progress bar. The line spacing had a hardcoded value that seemed calibrated to the 72 DPI font size. Moving to the actual font pixel height as the basis for line spacing required some changes. --- .../Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp b/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp index baa1918d12..321079366d 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plPipeline/plDTProgressMgr.cpp @@ -186,7 +186,17 @@ bool plDTProgressMgr::IDrawTheStupidThing(plPipeline *p, plOperationProgress { plDebugText &text = plDebugText::Instance(); bool drew_something = false; - uint16_t downsz = (text.GetFontHeight() << 1) + (4 * scale); + /* + * downsz used to be set to double the font size, plus the margin + * This was refactored to use font height instead, which accurately + * accounts for display DPI. However - the font spacing became too large. + * Instead of doubling, the margin is now the font height * 1.5. + * Font size is at 72 DPI, but Windows renders at 96 DPI by default. + * Therefor to infer the original spacing ratio, we can take: + * (72 * 2) / 96 to translate the intended line spacing out of font + * space and into device render space. + */ + uint16_t downsz = (text.GetFontHeight() * 1.50f) + (4 * scale); // draw the title if (!prog->GetTitle().empty()) { From a2cdf27f276d7d87142f9cecfdb765371d27b9c1 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Fri, 17 Jun 2022 20:38:21 -0700 Subject: [PATCH 04/27] Adding cursor scale Cursor scale corrects corrects the size of the cursor on Retina displays --- Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp | 9 ++++++++- Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp b/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp index bc82989749..72d9b3489f 100644 --- a/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp +++ b/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp @@ -230,6 +230,7 @@ bool plMouseDevice::bCursorOverride = false; bool plMouseDevice::bInverted = false; float plMouseDevice::fWidth = BASE_WIDTH; float plMouseDevice::fHeight = BASE_HEIGHT; +float plMouseDevice::fScale = 1.0f; plMouseDevice* plMouseDevice::fInstance = nullptr; plMouseDevice::plMouseDevice() @@ -256,6 +257,12 @@ void plMouseDevice::SetDisplayResolution(float Width, float Height) IUpdateCursorSize(); } +void plMouseDevice::SetScale(float Scale) +{ + fScale = Scale; + IUpdateCursorSize(); +} + void plMouseDevice::CreateCursor(const ST::string& cursor) { if (fCursor == nullptr) @@ -279,7 +286,7 @@ void plMouseDevice::IUpdateCursorSize() if(fCursor) { // set the size of the cursor based on resolution. - fCursor->SetSize( 2*fCursor->GetMipmap()->GetWidth()/fWidth, 2*fCursor->GetMipmap()->GetHeight()/fHeight ); + fCursor->SetSize( fScale * 2*fCursor->GetMipmap()->GetWidth()/fWidth, fScale * 2*fCursor->GetMipmap()->GetHeight()/fHeight ); } } diff --git a/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.h b/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.h index 4273aff215..29ec0cbb11 100644 --- a/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.h +++ b/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.h @@ -170,6 +170,7 @@ class plMouseDevice : public plInputDevice uint32_t GetButtonState() { return fButtonState; } float GetCursorOpacity() { return fOpacity; } void SetDisplayResolution(float Width, float Height); + void SetScale(float Scale); bool MsgReceive(plMessage* msg) override; @@ -218,6 +219,7 @@ class plMouseDevice : public plInputDevice static bool bCursorOverride; static bool bInverted; static float fWidth, fHeight; + static float fScale; }; From 8ca369efbbd10e33ce4a4fd98ecc286ddb136057 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Mon, 1 Aug 2022 22:26:39 -0700 Subject: [PATCH 05/27] Refining cursor scale and fixing cursor text --- .../Plasma/PubUtilLib/plInputCore/plInputDevice.cpp | 12 ++++++------ .../Plasma/PubUtilLib/plInputCore/plInputDevice.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp b/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp index 72d9b3489f..ea784e3831 100644 --- a/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp +++ b/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp @@ -257,7 +257,7 @@ void plMouseDevice::SetDisplayResolution(float Width, float Height) IUpdateCursorSize(); } -void plMouseDevice::SetScale(float Scale) +void plMouseDevice::SetDisplayScale(float Scale) { fScale = Scale; IUpdateCursorSize(); @@ -295,7 +295,7 @@ void plMouseDevice::AddNameToCursor(const ST::string& name) if (fInstance && !name.empty()) { plDebugText &txt = plDebugText::Instance(); - txt.DrawString(fInstance->fWXPos + 12 ,fInstance->fWYPos - 7,name); + txt.DrawString(fInstance->fWXPos + 12 * fScale, fInstance->fWYPos - txt.GetFontHeight() / 2,name); } } void plMouseDevice::AddCCRToCursor() @@ -303,7 +303,7 @@ void plMouseDevice::AddCCRToCursor() if (fInstance) { plDebugText &txt = plDebugText::Instance(); - txt.DrawString(fInstance->fWXPos + 12, fInstance->fWYPos - 17, "CCR"); + txt.DrawString(fInstance->fWXPos + 12 * fScale, fInstance->fWYPos - 17, "CCR"); } } void plMouseDevice::AddIDNumToCursor(uint32_t idNum) @@ -311,7 +311,7 @@ void plMouseDevice::AddIDNumToCursor(uint32_t idNum) if (fInstance && idNum) { plDebugText &txt = plDebugText::Instance(); - txt.DrawString(fInstance->fWXPos + 12, fInstance->fWYPos + 3, ST::string::from_uint(idNum)); + txt.DrawString(fInstance->fWXPos + 12 * fScale,fInstance->fWYPos + 3, ST::string::from_uint(idNum)); } } @@ -491,7 +491,7 @@ bool plMouseDevice::MsgReceive(plMessage* msg) fXPos = pXMsg->fX; SetCursorX(fXPos); - fWXPos = pXMsg->fWx; + fWXPos = pXMsg->fWx * fScale; return true; } @@ -528,7 +528,7 @@ bool plMouseDevice::MsgReceive(plMessage* msg) else fYPos = pYMsg->fY; - fWYPos = pYMsg->fWy; + fWYPos = pYMsg->fWy * fScale; SetCursorY(fYPos); return true; diff --git a/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.h b/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.h index 29ec0cbb11..294d1aaea8 100644 --- a/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.h +++ b/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.h @@ -170,7 +170,7 @@ class plMouseDevice : public plInputDevice uint32_t GetButtonState() { return fButtonState; } float GetCursorOpacity() { return fOpacity; } void SetDisplayResolution(float Width, float Height); - void SetScale(float Scale); + void SetDisplayScale(float Scale); bool MsgReceive(plMessage* msg) override; From 6795e67286c46a5eef0e60e9e24b5a9901e47591 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sat, 10 Dec 2022 11:14:17 -0800 Subject: [PATCH 06/27] Hacking support for cursor + backing scale into the Win32 client --- Sources/Plasma/Apps/plClient/win32/winmain.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Sources/Plasma/Apps/plClient/win32/winmain.cpp b/Sources/Plasma/Apps/plClient/win32/winmain.cpp index c1eecef8b1..7fae76b3a7 100644 --- a/Sources/Plasma/Apps/plClient/win32/winmain.cpp +++ b/Sources/Plasma/Apps/plClient/win32/winmain.cpp @@ -347,6 +347,9 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) RECT r; ::GetClientRect(hWnd, &r); gClient->GetPipeline()->Resize(r.right - r.left, r.bottom - r.top); + + HDC hDC = CreateCompatibleDC(nullptr); + gClient->GetPipeline()->SetBackingScale(GetDeviceCaps(hDC, LOGPIXELSY) / 96.0f); } break; @@ -1246,6 +1249,11 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC ::DestroyWindow(splashDialog); } + HDC hDC = CreateCompatibleDC(nullptr); + float scale = GetDeviceCaps(hDC, LOGPIXELSY) / 96.0f; + gClient->GetPipeline()->SetBackingScale(scale); + plMouseDevice::Instance()->SetDisplayScale(scale); + // Main loop if (gClient && !gClient->GetDone()) { // Must be done here due to the plClient* dereference. @@ -1264,6 +1272,9 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC MSG msg; do { + + //set the current cursor scale for each loop + gClient->MainLoop(); if (gClient->GetDone()) break; From 9c05355073caa02c188c26757840063f93abab8d Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 7 Jan 2023 18:06:44 -0500 Subject: [PATCH 07/27] Support Windows DPI Awareness from at least Vista. There are three different DPI Awareness modes that have been added to Windows, starting in Windows Vista. This is an attempt to gracefully support DPI scaling since Vista. There is still some missing documentation in the code, but it appears to work well. --- Sources/Plasma/Apps/plClient/CMakeLists.txt | 5 + Sources/Plasma/Apps/plClient/plClient.cpp | 68 ++++- Sources/Plasma/Apps/plClient/plClient.h | 2 + .../Apps/plClient/win32/plClient_Win.cpp | 25 +- .../Plasma/Apps/plClient/win32/plWinDpi.cpp | 254 ++++++++++++++++++ Sources/Plasma/Apps/plClient/win32/plWinDpi.h | 135 ++++++++++ .../Plasma/Apps/plClient/win32/winmain.cpp | 39 +-- Sources/Plasma/CoreLib/hsWindows.h | 50 ++++ .../FeatureLib/pfDXPipeline/plDXPipeline.cpp | 1 + .../Plasma/NucleusLib/inc/plCreatableIndex.h | 1 + .../PubUtilLib/plInputCore/plInputDevice.cpp | 9 +- .../PubUtilLib/plMessage/CMakeLists.txt | 1 + .../plMessage/plDisplayScaleChangedMsg.h | 85 ++++++ .../PubUtilLib/plMessage/plMessageCreatable.h | 3 + 14 files changed, 641 insertions(+), 37 deletions(-) create mode 100644 Sources/Plasma/Apps/plClient/win32/plWinDpi.cpp create mode 100644 Sources/Plasma/Apps/plClient/win32/plWinDpi.h create mode 100644 Sources/Plasma/PubUtilLib/plMessage/plDisplayScaleChangedMsg.h diff --git a/Sources/Plasma/Apps/plClient/CMakeLists.txt b/Sources/Plasma/Apps/plClient/CMakeLists.txt index 56a7e27994..23b25527aa 100644 --- a/Sources/Plasma/Apps/plClient/CMakeLists.txt +++ b/Sources/Plasma/Apps/plClient/CMakeLists.txt @@ -76,8 +76,13 @@ set(plClient_TEXT ) if(WIN32) + list(APPEND plClient_HEADERS + win32/plWinDpi.h + ) + list(APPEND plClient_SOURCES win32/plClient_Win.cpp + win32/plWinDpi.cpp win32/winmain.cpp ) diff --git a/Sources/Plasma/Apps/plClient/plClient.cpp b/Sources/Plasma/Apps/plClient/plClient.cpp index 424eb138e7..9c54fcab1e 100644 --- a/Sources/Plasma/Apps/plClient/plClient.cpp +++ b/Sources/Plasma/Apps/plClient/plClient.cpp @@ -56,6 +56,10 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plClient.h" +#ifdef HS_BUILD_FOR_WIN32 +# include "win32/plWinDpi.h" +#endif + #include "pnDispatch/plDispatch.h" #include "pnDispatch/plDispatchLogBase.h" #include "pnKeyedObject/plFixedKey.h" @@ -92,6 +96,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plInputCore/plInputManager.h" #include "plMessage/plAgeLoadedMsg.h" #include "plMessage/plAnimCmdMsg.h" +#include "plMessage/plDisplayScaleChangedMsg.h" #include "plMessage/plInputEventMsg.h" #include "plMessage/plLinkToAgeMsg.h" #include "plMessage/plMovieMsg.h" @@ -382,6 +387,7 @@ void plClient::InitInputs() plgDispatch::Dispatch()->RegisterForExactType(plIMouseYEventMsg::Index(), fInputManager->GetKey()); plgDispatch::Dispatch()->RegisterForExactType(plIMouseBEventMsg::Index(), fInputManager->GetKey()); plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), fInputManager->GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plDisplayScaleChangedMsg::Index(), fInputManager->GetKey()); plInputDevice* pKeyboard = new plKeyboardDevice(); fInputManager->AddInputDevice(pKeyboard); @@ -710,6 +716,44 @@ bool plClient::MsgReceive(plMessage* msg) } return true; } + + plDisplayScaleChangedMsg* pDSChangedMsg = plDisplayScaleChangedMsg::ConvertNoRef(msg); + if (pDSChangedMsg) { + fPipeline->SetBackingScale(pDSChangedMsg->GetScale()); + if (pDSChangedMsg->GetSuggestedLocation().has_value()) { + int width = pDSChangedMsg->GetSuggestedLocation()->fRight - pDSChangedMsg->GetSuggestedLocation()->fLeft; + int height = pDSChangedMsg->GetSuggestedLocation()->fBottom - pDSChangedMsg->GetSuggestedLocation()->fTop; + IResizeWindow(width, height); + if (fPipeline) { + // NOTE: Trying to resize the pipeline while it spans multiple + // monitors seems to crash on D3D9. + fPipeline->ResetDisplayDevice( + width, + height, + fPipeline->ColorDepth(), + !fPipeline->IsFullScreen(), + // FIXME... There doesn't seem to be a way to get the current AA value? + fPipeline->GetMaxAntiAlias(width, height, fPipeline->ColorDepth()), + fPipeline->GetMaxAnisotropicSamples(), + // FIXME... There doesn't seem to be a way tog et the current VSync value? + true + ); + } +#ifdef HS_BUILD_FOR_WIN32 + SetWindowPos( + fWindowHndl, + nullptr, + pDSChangedMsg->GetSuggestedLocation()->fLeft, + pDSChangedMsg->GetSuggestedLocation()->fTop, + width, + height, + SWP_NOZORDER | SWP_NOACTIVATE + ); +#endif + } + return true; + } + plRenderRequestMsg* rendReq = plRenderRequestMsg::ConvertNoRef(msg); if( rendReq ) { @@ -1414,6 +1458,8 @@ bool plClient::StartInit() plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), pLMod->GetKey()); plgDispatch::Dispatch()->RegisterForExactType(plAudioSysMsg::Index(), pLMod->GetKey()); + plgDispatch::Dispatch()->RegisterForExactType(plDisplayScaleChangedMsg::Index(), GetKey()); + plSynchedObject::PushSynchDisabled(false); // enable dirty tracking return true; } @@ -1870,6 +1916,16 @@ void plClient::IAddRenderRequest(plRenderRequest* req) } } +void plClient::IResizeWindow(int Width, int Height) +{ + if (plMouseDevice::Instance()) + plMouseDevice::Instance()->SetDisplayResolution((float)Width, (float)Height); + + float aspectratio = (float)Width / (float)Height; + if (pfGameGUIMgr::GetInstance()) + pfGameGUIMgr::GetInstance()->SetAspectRatio(aspectratio); +} + void plClient::ResetDisplayDevice(int Width, int Height, int ColorDepth, bool Windowed, int NumAASamples, int MaxAnisotropicSamples, bool VSync) { if(!fPipeline) return; @@ -1885,13 +1941,7 @@ void plClient::ResetDisplayDevice(int Width, int Height, int ColorDepth, bool Wi void plClient::ResizeDisplayDevice(int Width, int Height, bool Windowed) { - - if (plMouseDevice::Instance()) - plMouseDevice::Instance()->SetDisplayResolution((float)Width, (float)Height); - - float aspectratio = (float)Width / (float)Height; - if (pfGameGUIMgr::GetInstance()) - pfGameGUIMgr::GetInstance()->SetAspectRatio( aspectratio ); + IResizeWindow(Width, Height); // Direct3D no longer uses exclusive fullscreen mode, ergo, we must resize the display if (!Windowed) @@ -1949,8 +1999,8 @@ void plClient::IDetectAudioVideoSettings() #ifdef HS_BUILD_FOR_WIN32 if(!plPipeline::fDefaultPipeParams.Windowed) { - plPipeline::fDefaultPipeParams.Width = GetSystemMetrics(SM_CXSCREEN); - plPipeline::fDefaultPipeParams.Height = GetSystemMetrics(SM_CYSCREEN); + plPipeline::fDefaultPipeParams.Width = plWinDpi::Instance().GetSystemMetrics(SM_CXSCREEN, fWindowHndl); + plPipeline::fDefaultPipeParams.Height = plWinDpi::Instance().GetSystemMetrics(SM_CYSCREEN, fWindowHndl); } else #endif diff --git a/Sources/Plasma/Apps/plClient/plClient.h b/Sources/Plasma/Apps/plClient/plClient.h index dca8e1f659..dd839cb5c8 100644 --- a/Sources/Plasma/Apps/plClient/plClient.h +++ b/Sources/Plasma/Apps/plClient/plClient.h @@ -224,6 +224,8 @@ class plClient : public hsKeyedObject void IChangeResolution(int width, int height); void IUpdateProgressIndicator(plOperationProgress* progress); + void IResizeWindow(int width, int height); + public: plClient(); diff --git a/Sources/Plasma/Apps/plClient/win32/plClient_Win.cpp b/Sources/Plasma/Apps/plClient/win32/plClient_Win.cpp index 9a46293287..3be725d470 100644 --- a/Sources/Plasma/Apps/plClient/win32/plClient_Win.cpp +++ b/Sources/Plasma/Apps/plClient/win32/plClient_Win.cpp @@ -49,6 +49,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include #include "plClient.h" +#include "win32/plWinDpi.h" #include "pnFactory/plFactory.h" #include "pnNetCommon/plNetApp.h" @@ -72,17 +73,23 @@ void plClient::IResizeNativeDisplayDevice(int width, int height, bool windowed) SetWindowLongPtr(fWindowHndl, GWL_EXSTYLE, winExStyle); uint32_t flags = SWP_NOCOPYBITS | SWP_SHOWWINDOW | SWP_FRAMECHANGED; - uint32_t outsideWidth, outsideHeight; + + // The window rect will be (left, top, width, height) + RECT winRect{ 0, 0, width, height }; if (windowed) { - RECT winRect = { 0, 0, width, height }; - AdjustWindowRectEx(&winRect, winStyle, false, winExStyle); - outsideWidth = winRect.right - winRect.left; - outsideHeight = winRect.bottom - winRect.top; - } else { - outsideWidth = width; - outsideHeight = height; + if (GetClientRect(fWindowHndl, &winRect) != FALSE) { + MapWindowPoints(fWindowHndl, nullptr, reinterpret_cast(&winRect), 2); + winRect.right = winRect.left + width; + winRect.bottom = winRect.top + height; + } + + UINT dpi = plWinDpi::Instance().GetDpi(fWindowHndl); + plWinDpi::Instance().AdjustWindowRectEx(&winRect, winStyle, false, winExStyle, dpi); + + winRect.right = winRect.right - winRect.left; + winRect.bottom = winRect.bottom - winRect.top; } - SetWindowPos(fWindowHndl, HWND_NOTOPMOST, 0, 0, outsideWidth, outsideHeight, flags); + SetWindowPos(fWindowHndl, HWND_NOTOPMOST, winRect.left, winRect.top, winRect.right, winRect.bottom, flags); } void plClient::IChangeResolution(int width, int height) diff --git a/Sources/Plasma/Apps/plClient/win32/plWinDpi.cpp b/Sources/Plasma/Apps/plClient/win32/plWinDpi.cpp new file mode 100644 index 0000000000..795df85884 --- /dev/null +++ b/Sources/Plasma/Apps/plClient/win32/plWinDpi.cpp @@ -0,0 +1,254 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#include "plWinDpi.h" + +#include +#include + +#include "hsResMgr.h" + +#include "pnKeyedObject/plFixedKey.h" +#include "pnMessage/plClientMsg.h" + +static std::unique_ptr s_instance; + +plWinDpi& plWinDpi::Instance() +{ + if (!s_instance) + s_instance = std::make_unique(); + return *s_instance.get(); +} + +plWinDpi::plWinDpi() + : fAdjustWindowRectExForDpi(L"user32", "AdjustWindowRectExForDpi"), + fAreDpiAwarenessContextsEqual(L"user32", "AreDpiAwarenessContextsEqual"), + fEnableNonClientDpiScaling(L"user32", "EnableNonClientDpiScaling"), + fGetDpiForMonitor(L"shcore", "GetDpiForMonitor"), + fGetDpiForSystem(L"user32", "GetDpiForSystem"), + fGetDpiForWindow(L"user32", "GetDpiForWindow"), + fGetSystemDpiForProcess(L"user32", "GetSystemDpiForProcess"), + fGetSystemMetricsForDpi(L"user32", "GetSystemMetricsForDpi"), + fGetThreadDpiAwarenessContext(L"user32", "GetThreadDpiAwarenessContext"), + fSetProcessDpiAwareness(L"shcore", "SetProcessDpiAwareness"), + fSetProcessDpiAwarenessContext(L"user32", "SetProcessDpiAwarenessContext") +{ + Initialize(); +} + +void plWinDpi::Initialize() const +{ + // There are three different levels of DPI awareness that we can deal with, + // each was added in newer versions of Windows as monitors became more and + // more sexy. + // In Vista, the entire system had an adjustable display scale. + // In Windows 8, each monitor was allowed to have a separate scale. + // In Windows 10, the per-monitor scaling was slowly improved to automatically + // scale dialog boxes and things like that. + // So we need to try to handle all of that. We will assume the baseline is Vista. + // Any API added later than that will need to be called on a provisional basis. + LogWhite("--- Begin DPI Awareness ---"); + do { + // Per Monitor V2 for Windows 10 v1703. + auto perMonitorV2Result = fSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + if (perMonitorV2Result.has_value()) { + if (perMonitorV2Result.value() == TRUE) { + LogGreen("Using Per Monitor v2"); + break; + } + LogRed("Failed to set DPI Awareness Per Monitor v2: {}", hsCOMError(hsLastWin32Error, GetLastError())); + } else { + LogYellow("Per Monitor v2 isn't supported on this OS."); + } + + // Per Monitor v1 for Windows 8.1. + auto perMonitorV1Result = fSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); + if (perMonitorV1Result.has_value()) { + if (SUCCEEDED(perMonitorV1Result.value())) { + LogGreen("Using Per Monitor V1"); + break; + } else { + LogRed("Failed to set DPI Awareness Per Monitor v1: {}", hsCOMError(perMonitorV1Result.value())); + } + } + + // System DPI Awareness. + if (SetProcessDPIAware() != FALSE) { + LogGreen("Using System DPI Awareness"); + break; + } else { + LogRed("Failed to set System DPI Awareness: {}", hsCOMError(hsLastWin32Error, GetLastError())); + } + } while (false); + LogWhite("--- End DPI Awareness ---"); +} + +BOOL plWinDpi::AdjustWindowRectEx(LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi) const +{ + // If the application is per-monitor aware, MSDN explicitly says to NOT use AdjustWindowRectEx, so + // we use the proper function if it's available. + auto dpiResult = fAdjustWindowRectExForDpi(lpRect, dwStyle, bMenu, dwExStyle, dpi); + if (dpiResult.has_value()) + return dpiResult.value(); + + // The ForDpi variant only scales the non-client part of the window rect. If we've enabled + // non-client scaling, then the ForDpi variant should be available, so it's ok to simply + // fall through to the old non-dpi aware variant. + return ::AdjustWindowRectEx(lpRect, dwStyle, bMenu, dwExStyle); +} + +UINT plWinDpi::GetDpi(HWND hWnd) const +{ + if (hWnd == nullptr) { + // Windows 10 v1803 for per-process system DPI. + auto systemDpiForProcess = fGetSystemDpiForProcess(GetCurrentProcess()); + if (systemDpiForProcess.has_value()) + return systemDpiForProcess.value(); + + // Windows 10 v1607 for system DPI. + auto systemDpi = fGetDpiForSystem(); + if (systemDpi.has_value()) + return systemDpi.value(); + } else { + // Windows 10 v107 for per-window DPI. + auto windowDpi = fGetDpiForWindow(hWnd); + if (windowDpi.has_value()) + return windowDpi.value(); + } + + // Windows 8.1 for per-monitor DPI. + HMONITOR monitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); + UINT dpiX, dpiY; + auto dpiForMonitor = fGetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY); + if (dpiForMonitor.has_value()) { + if (SUCCEEDED(dpiForMonitor.value())) + return UINT(float(dpiY) / 96.0f); + LogRed("Per-Monitor DPI failed: {}", hsCOMError(dpiForMonitor.value())); + } + + // Legacy DPI computation... This is both slow and lacks much knowledge about DPI. + HDC hdc = GetDC(hWnd); + int ydpi = GetDeviceCaps(hdc, LOGPIXELSY); + ReleaseDC(hWnd, hdc); + return UINT(float(ydpi) / 96.0f); +} + +int plWinDpi::GetSystemMetrics(int nIndex, std::variant dpiArg) const +{ + UINT dpi; + std::visit( + [this, &dpi](auto&& arg) { + using _DpiArgT = std::decay_t; + if constexpr (std::is_same_v<_DpiArgT, UINT>) { + dpi = arg; + } else if constexpr (std::is_same_v<_DpiArgT, HWND>) { + dpi = GetDpi(arg); + } else { + dpi = GetDpi(); + } + }, dpiArg + ); + + // Windows 10 v1607 + std::optional metricsForDpi = fGetSystemMetricsForDpi(nIndex, dpi); + if (metricsForDpi.has_value()) + return metricsForDpi.value(); + + // Welcome to the late Triassic + int metrics = ::GetSystemMetrics(nIndex); + return MulDiv(metrics, dpi, 96); +} + +void plWinDpi::IEnableNCScaling(HWND hWnd) const +{ + // The code would be more complicated with std::optional, so just grab + // weak references to the function pointers and bail if one isn't available. + auto* GetThreadDpiAwarenessContext = fGetThreadDpiAwarenessContext.Get(); + auto* AreDpiAwarenessContextsEqual = fAreDpiAwarenessContextsEqual.Get(); + auto* EnableNonClientDpiScaling = fEnableNonClientDpiScaling.Get(); + if (!(GetThreadDpiAwarenessContext && AreDpiAwarenessContextsEqual && EnableNonClientDpiScaling)) { + LogYellow("Non-client DPI scaling is not supported on this OS."); + return; + } + + // Windows 10 v1607 added non-client area scaling to Per Monitor Awareness v1, but + // it needs to be explicitly enabled. Per Monitor Awareness v2 does this automatically, + // so there is no need to do anything. + DPI_AWARENESS_CONTEXT dpiAwarenessContext = GetThreadDpiAwarenessContext(); + if (AreDpiAwarenessContextsEqual(dpiAwarenessContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE)) { + LogWhite("Enabling non-client DPI scaling..."); + if (EnableNonClientDpiScaling(hWnd) != FALSE) + LogGreen("... success!"); + else + LogRed("... failed: {}", hsCOMError(hsLastWin32Error, GetLastError())); + } else if (AreDpiAwarenessContextsEqual(dpiAwarenessContext, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)) { + LogGreen("We already have non-client DPI scaling!"); + } else { + LogYellow("The DPI Awareness mode we're using doesn't support non-client DPI scaling :("); + } +} + +void plWinDpi::IHandleDpiChange(HWND hWnd, UINT dpi, const RECT& rect) const +{ + float scale = float(dpi) / 96.0f; + LogWhite("Window DPI changed to {} (scale factor: {.02f})", dpi, scale); + + // Inform the engine about the new DPI. + auto* msg = new plDisplayScaleChangedMsg(scale, ConvertRect(rect)); + msg->Send(); +} + +std::optional plWinDpi::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) const +{ + switch (msg) { + case WM_NCCREATE: + IEnableNCScaling(hWnd); + break; + case WM_DPICHANGED: + // The goal is for us to transparently handle DPI stuff, so eat this message. + IHandleDpiChange(hWnd, LOWORD(wParam), *((LPRECT)lParam)); + return 0; + } + + // Indicates that the user may process the window message. + return std::nullopt; +} diff --git a/Sources/Plasma/Apps/plClient/win32/plWinDpi.h b/Sources/Plasma/Apps/plClient/win32/plWinDpi.h new file mode 100644 index 0000000000..0961823d65 --- /dev/null +++ b/Sources/Plasma/Apps/plClient/win32/plWinDpi.h @@ -0,0 +1,135 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef _plWinDpi_h_inc_ +#define _plWinDpi_h_inc_ + +#include +#include + +#include "hsWindows.h" +#include + +#include "plMessage/plDisplayScaleChangedMsg.h" +#include "plStatusLog/plStatusLog.h" + +class plWinDpi +{ +protected: + plOptionalWinCall fAdjustWindowRectExForDpi; + plOptionalWinCall fAreDpiAwarenessContextsEqual; + plOptionalWinCall fEnableNonClientDpiScaling; + plOptionalWinCall fGetDpiForMonitor; + plOptionalWinCall fGetDpiForSystem; + plOptionalWinCall fGetDpiForWindow; + plOptionalWinCall fGetSystemDpiForProcess; + plOptionalWinCall fGetSystemMetricsForDpi; + plOptionalWinCall fGetThreadDpiAwarenessContext; + plOptionalWinCall fSetProcessDpiAwareness; + plOptionalWinCall fSetProcessDpiAwarenessContext; + + template + void LogGreen(const char* fmt, _ArgsT&&... args) const + { + plStatusLog::AddLineSF("plasmadbg.log", plStatusLog::kGreen, fmt, std::forward<_ArgsT>(args)...); + } + + template + void LogRed(const char* fmt, _ArgsT&&... args) const + { + plStatusLog::AddLineSF("plasmadbg.log", plStatusLog::kRed, fmt, std::forward<_ArgsT>(args)...); + } + + template + void LogWhite(const char* fmt, _ArgsT&&... args) const + { + plStatusLog::AddLineSF("plasmadbg.log", plStatusLog::kWhite, fmt, std::forward<_ArgsT>(args)...); + } + + template + void LogYellow(const char* fmt, _ArgsT&&... args) const + { + plStatusLog::AddLineSF("plasmadbg.log", plStatusLog::kYellow, fmt, std::forward<_ArgsT>(args)...); + } + + void Initialize() const; + +public: + plWinDpi(); + static plWinDpi& Instance(); + +public: + /** Polyfill for AdjustsWindowRectExForDpi */ + BOOL AdjustWindowRectEx(LPRECT lprect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi) const; + + UINT GetDpi(HWND hWnd = nullptr) const; + + /** + * Polyfill for GetSystemMetricsForDpi + * \param in[dpi] This can be either a DPI value, a window handle, or null. If a window + * is supplied, we do our best to determine the DPI of the supplied window. + * Otherwise, the system DPI is used. + */ + int GetSystemMetrics(int nIndex, std::variant dpi = nullptr) const; + +public: + float GetScale(HWND hWnd = nullptr) const { return float(GetDpi(hWnd)) / 96.0f; } + + static RECT ConvertRect(const plDisplayScaleChangedMsg::ClientWindow& rect) + { + return *((const LPRECT)&rect); + } + + static plDisplayScaleChangedMsg::ClientWindow ConvertRect(const RECT& rect) + { + return *((const plDisplayScaleChangedMsg::ClientWindow*)&rect); + } + +private: + void IEnableNCScaling(HWND hWnd) const; + void IHandleDpiChange(HWND hWnd, UINT dpi, const RECT& rect) const; + +public: + std::optional WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) const; +}; + +#endif diff --git a/Sources/Plasma/Apps/plClient/win32/winmain.cpp b/Sources/Plasma/Apps/plClient/win32/winmain.cpp index 7fae76b3a7..cc6fe879fe 100644 --- a/Sources/Plasma/Apps/plClient/win32/winmain.cpp +++ b/Sources/Plasma/Apps/plClient/win32/winmain.cpp @@ -59,6 +59,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plClient.h" #include "plClientLoader.h" #include "res/resource.h" +#include "plWinDpi.h" #include "pnEncryption/plChallengeHash.h" @@ -122,12 +123,6 @@ static const plCmdArgDef s_cmdLineArgs[] = { { kCmdArgFlagged | kCmdTypeString, "Renderer", kArgRenderer }, }; -/// Made globals now, so we can set them to zero if we take the border and -/// caption styles out ala fullscreen (8.11.2000 mcn) -int gWinBorderDX = GetSystemMetrics( SM_CXSIZEFRAME ); -int gWinBorderDY = GetSystemMetrics( SM_CYSIZEFRAME ); -int gWinMenuDY = GetSystemMetrics( SM_CYCAPTION ); - plClientLoader gClient; bool gPendingActivate = false; bool gPendingActivateFlag = false; @@ -183,6 +178,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) static bool gDragging = false; static uint8_t mouse_down = 0; + // DPI Helper can eat messages. + auto result = plWinDpi::Instance().WndProc(hWnd, message, wParam, lParam); + if (result.has_value()) + return result.value(); + // Messages we registered for manually (no const value) if (message == s_WmTaskbarList) { @@ -347,9 +347,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) RECT r; ::GetClientRect(hWnd, &r); gClient->GetPipeline()->Resize(r.right - r.left, r.bottom - r.top); - - HDC hDC = CreateCompatibleDC(nullptr); - gClient->GetPipeline()->SetBackingScale(GetDeviceCaps(hDC, LOGPIXELSY) / 96.0f); + gClient->GetPipeline()->SetBackingScale(plWinDpi::Instance().GetScale(hWnd)); } break; @@ -1012,6 +1010,9 @@ PF_CONSOLE_LINK_ALL() bool WinInit(HINSTANCE hInst) { + // Initialize the DPI helpers + plWinDpi::Instance(); + // Fill out WNDCLASS info WNDCLASS wndClass; wndClass.style = CS_DBLCLKS; // CS_HREDRAW | CS_VREDRAW; @@ -1030,13 +1031,17 @@ bool WinInit(HINSTANCE hInst) if (!RegisterClass(&wndClass)) return false; + int winBorderDX = plWinDpi::Instance().GetSystemMetrics(SM_CXSIZEFRAME); + int winBorderDY = plWinDpi::Instance().GetSystemMetrics(SM_CYSIZEFRAME); + int winMenuDY = plWinDpi::Instance().GetSystemMetrics(SM_CYCAPTION); + // Create a window HWND hWnd = CreateWindow( CLASSNAME, plProduct::LongName().c_str(), WS_OVERLAPPEDWINDOW, 0, 0, - 800 + gWinBorderDX * 2, - 600 + gWinBorderDY * 2 + gWinMenuDY, + 800 + winBorderDX * 2, + 600 + winBorderDY * 2 + winMenuDY, nullptr, nullptr, hInst, nullptr ); HDC hDC = GetDC(hWnd); @@ -1249,10 +1254,11 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC ::DestroyWindow(splashDialog); } - HDC hDC = CreateCompatibleDC(nullptr); - float scale = GetDeviceCaps(hDC, LOGPIXELSY) / 96.0f; - gClient->GetPipeline()->SetBackingScale(scale); - plMouseDevice::Instance()->SetDisplayScale(scale); + // Tell everybody about the current display scaling + { + plDisplayScaleChangedMsg* msg = new plDisplayScaleChangedMsg(plWinDpi::Instance().GetScale()); + msg->Send(); + } // Main loop if (gClient && !gClient->GetDone()) { @@ -1272,9 +1278,6 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC MSG msg; do { - - //set the current cursor scale for each loop - gClient->MainLoop(); if (gClient->GetDone()) break; diff --git a/Sources/Plasma/CoreLib/hsWindows.h b/Sources/Plasma/CoreLib/hsWindows.h index 94356a9184..89a00f5605 100644 --- a/Sources/Plasma/CoreLib/hsWindows.h +++ b/Sources/Plasma/CoreLib/hsWindows.h @@ -43,6 +43,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #ifndef _hsWindows_inc_ #define _hsWindows_inc_ +#include #include /** \file hsWindows.h @@ -142,6 +143,55 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com ST::format_type(format, output, hr.fResult); } } + + template + class plOptionalWinCall; + + template< + class _ReturnT, + class... _ArgsT + > + class plOptionalWinCall<_ReturnT(_ArgsT...)> + { + using _FuncPtrT = _ReturnT(*)(_ArgsT...); + + HMODULE fHModule; + _FuncPtrT fProc; + + public: + plOptionalWinCall(const wchar_t* mod, const char* func) + : fHModule(), fProc() + { + fHModule = LoadLibraryW(mod); + if (fHModule) { + fProc = (_FuncPtrT)GetProcAddress(fHModule, func); + if (!fProc) { + FreeLibrary(fHModule); + fHModule = nullptr; + } + } + } + + ~plOptionalWinCall() + { + if (fHModule) + FreeLibrary(fHModule); + } + + std::optional<_ReturnT> operator()(_ArgsT... args) const + { + if (fProc) + return (_ReturnT)fProc(std::forward<_ArgsT>(args)...); + return std::nullopt; + } + + operator bool() const + { + return fProc != nullptr; + } + + _FuncPtrT Get() const { return fProc; } + }; #endif // HS_BUILD_FOR_WIN32 #endif // _hsWindows_inc_ diff --git a/Sources/Plasma/FeatureLib/pfDXPipeline/plDXPipeline.cpp b/Sources/Plasma/FeatureLib/pfDXPipeline/plDXPipeline.cpp index 85488d67bc..b98973074e 100644 --- a/Sources/Plasma/FeatureLib/pfDXPipeline/plDXPipeline.cpp +++ b/Sources/Plasma/FeatureLib/pfDXPipeline/plDXPipeline.cpp @@ -3337,6 +3337,7 @@ bool plDXPipeline::CaptureScreen( plMipmap *dest, bool flipVertical, uint16_t d } else { + // FIXME: DPI awareness bigWidth = GetSystemMetrics( SM_CXSCREEN ); bigHeight = GetSystemMetrics( SM_CYSCREEN ); diff --git a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h index dab32ac045..e9d97517f1 100644 --- a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h +++ b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h @@ -960,6 +960,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(plConfirmationMsg), CLASS_INDEX(plLocalizedConfirmationMsg), CLASS_INDEX(plSubtitleMsg), + CLASS_INDEX(plDisplayScaleChangedMsg), CLASS_INDEX_LIST_END #endif // plCreatableIndex_inc diff --git a/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp b/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp index ea784e3831..41844fe36c 100644 --- a/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp +++ b/Sources/Plasma/PubUtilLib/plInputCore/plInputDevice.cpp @@ -49,6 +49,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plAvatarInputInterface.h" #include +#include "plMessage/plDisplayScaleChangedMsg.h" #include "plMessage/plInputEventMsg.h" #include "pnMessage/plTimeMsg.h" @@ -389,7 +390,13 @@ void plMouseDevice::SetCursorOpacity( float opacity ) } bool plMouseDevice::MsgReceive(plMessage* msg) -{ +{ + plDisplayScaleChangedMsg* pDSChangedMsg = plDisplayScaleChangedMsg::ConvertNoRef(msg); + if (pDSChangedMsg) { + SetDisplayScale(pDSChangedMsg->GetScale()); + return true; + } + plEvalMsg* pEMsg = plEvalMsg::ConvertNoRef(msg); if (pEMsg) { diff --git a/Sources/Plasma/PubUtilLib/plMessage/CMakeLists.txt b/Sources/Plasma/PubUtilLib/plMessage/CMakeLists.txt index 2a62162f1b..68f6d9d76d 100644 --- a/Sources/Plasma/PubUtilLib/plMessage/CMakeLists.txt +++ b/Sources/Plasma/PubUtilLib/plMessage/CMakeLists.txt @@ -72,6 +72,7 @@ set(plMessage_HEADERS plConsoleMsg.h plDampMsg.h plDeviceRecreateMsg.h + plDisplayScaleChangedMsg.h plDynaDecalEnableMsg.h plDynamicEnvMapMsg.h plDynamicTextMsg.h diff --git a/Sources/Plasma/PubUtilLib/plMessage/plDisplayScaleChangedMsg.h b/Sources/Plasma/PubUtilLib/plMessage/plDisplayScaleChangedMsg.h new file mode 100644 index 0000000000..5b63bfcd11 --- /dev/null +++ b/Sources/Plasma/PubUtilLib/plMessage/plDisplayScaleChangedMsg.h @@ -0,0 +1,85 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef _plDisplayScaleChangedMsg_h_inc_ +#define _plDisplayScaleChangedMsg_h_inc_ + +#include + +#include "pnMessage/plMessage.h" + +class plDisplayScaleChangedMsg : public plMessage +{ +public: + struct ClientWindow + { + int32_t fLeft; + int32_t fTop; + int32_t fRight; + int32_t fBottom; + }; + +protected: + float fScale; + std::optional fNewLocation; + +public: + plDisplayScaleChangedMsg(float scale, std::optional newLocation = std::nullopt) + : plMessage(), fScale(scale), fNewLocation(newLocation) + { + SetBCastFlag(plMessage::kBCastByExactType); + } + +public: + CLASSNAME_REGISTER(plDisplayScaleChangedMsg); + GETINTERFACE_ANY(plDisplayScaleChangedMsg, plMessage); + +public: + void Read(hsStream*, hsResMgr*) override { hsAssert(0, "nope"); } + void Write(hsStream*, hsResMgr*) override { hsAssert(0, "nope"); } + +public: + float GetScale() const { return fScale; } + std::optional GetSuggestedLocation() const { return fNewLocation; } +}; + +#endif diff --git a/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h b/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h index ce7c9c0bab..8aab136169 100644 --- a/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h +++ b/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h @@ -123,6 +123,9 @@ REGISTER_CREATABLE(plDampMsg); #include "plDeviceRecreateMsg.h" REGISTER_CREATABLE(plDeviceRecreateMsg); +#include "plDisplayScaleChangedMsg.h" +REGISTER_NONCREATABLE(plDisplayScaleChangedMsg); + #include "plDynaDecalEnableMsg.h" REGISTER_CREATABLE(plDynaDecalEnableMsg); From 496499a63446b90b88738334e84e4a0f12f44496 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 19 Feb 2023 16:37:41 -0800 Subject: [PATCH 08/27] Fixing typo --- Sources/Plasma/Apps/plClient/plClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Plasma/Apps/plClient/plClient.cpp b/Sources/Plasma/Apps/plClient/plClient.cpp index 9c54fcab1e..ae07acdb2c 100644 --- a/Sources/Plasma/Apps/plClient/plClient.cpp +++ b/Sources/Plasma/Apps/plClient/plClient.cpp @@ -735,7 +735,7 @@ bool plClient::MsgReceive(plMessage* msg) // FIXME... There doesn't seem to be a way to get the current AA value? fPipeline->GetMaxAntiAlias(width, height, fPipeline->ColorDepth()), fPipeline->GetMaxAnisotropicSamples(), - // FIXME... There doesn't seem to be a way tog et the current VSync value? + // FIXME... There doesn't seem to be a way to get the current VSync value? true ); } From a8bc7a41422667e0fae2cbf1f98697d53ae051ec Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sat, 14 Jan 2023 13:07:30 -0500 Subject: [PATCH 09/27] Maintain game window size when the DPI is changing. Per Colin, on macoS, when the scale factor is changed, the game window stays the same size. This uses a window message added in Windows 10 v1703 to ensure that the game window stays the same size when the DPI changes. Unfortunately, at this time, Windows 8.1 - Windows 10 v1607 will still resize. --- .../Plasma/Apps/plClient/win32/plWinDpi.cpp | 35 ++++++++++++++++++- Sources/Plasma/Apps/plClient/win32/plWinDpi.h | 1 + 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Sources/Plasma/Apps/plClient/win32/plWinDpi.cpp b/Sources/Plasma/Apps/plClient/win32/plWinDpi.cpp index 795df85884..dd53588658 100644 --- a/Sources/Plasma/Apps/plClient/win32/plWinDpi.cpp +++ b/Sources/Plasma/Apps/plClient/win32/plWinDpi.cpp @@ -50,6 +50,10 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "pnKeyedObject/plFixedKey.h" #include "pnMessage/plClientMsg.h" +#ifndef WM_GETDPISCALEDSIZE +# define WM_GETDPISCALEDSIZE 0x02E4 +#endif + static std::unique_ptr s_instance; plWinDpi& plWinDpi::Instance() @@ -198,6 +202,27 @@ int plWinDpi::GetSystemMetrics(int nIndex, std::variant plWinDpi::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPA IEnableNCScaling(hWnd); break; case WM_DPICHANGED: - // The goal is for us to transparently handle DPI stuff, so eat this message. + // For Windows 8.1 and higher. Allows us to handle DPI changes while the game is running, + // eg by moving the game window to another monitor with a different scale factor or + // the user changed the scale factor while the game is running. IHandleDpiChange(hWnd, LOWORD(wParam), *((LPRECT)lParam)); return 0; + case WM_GETDPISCALEDSIZE: + // For Windows 10 v1703 and higher. This window message allows us to tell the + // OS the desired size of the application window sent by WM_DPICHANGED. This + // does mean that on Windows 10 v1607 and lower that the game window itself + // will change size. These versions of Windows are all EOL, however. + return ICalcWinSize(hWnd, (UINT)wParam, *(SIZE*)lParam); } // Indicates that the user may process the window message. diff --git a/Sources/Plasma/Apps/plClient/win32/plWinDpi.h b/Sources/Plasma/Apps/plClient/win32/plWinDpi.h index 0961823d65..ac486e2ad6 100644 --- a/Sources/Plasma/Apps/plClient/win32/plWinDpi.h +++ b/Sources/Plasma/Apps/plClient/win32/plWinDpi.h @@ -125,6 +125,7 @@ class plWinDpi } private: + BOOL ICalcWinSize(HWND hWnd, UINT dpi, SIZE& hWndSize) const; void IEnableNCScaling(HWND hWnd) const; void IHandleDpiChange(HWND hWnd, UINT dpi, const RECT& rect) const; From 69e429af83523db283ad0c4df87470340ae8500d Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sat, 8 Apr 2023 18:17:20 -0700 Subject: [PATCH 10/27] Preparing for FreeType - FreeType now required for Plasma and linked --- CMakeLists.txt | 2 +- Sources/Plasma/PubUtilLib/plPipeline/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d4f07df78..bc5f2061d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,7 +98,7 @@ find_package(CURL REQUIRED) find_package(DirectX) find_package(epoxy) find_package(expat REQUIRED) -find_package(freetype) +find_package(freetype REQUIRED) find_package(JPEG REQUIRED) find_package(libwebm) find_package(Ogg REQUIRED) diff --git a/Sources/Plasma/PubUtilLib/plPipeline/CMakeLists.txt b/Sources/Plasma/PubUtilLib/plPipeline/CMakeLists.txt index d28591c08e..a0c6a6c508 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/CMakeLists.txt +++ b/Sources/Plasma/PubUtilLib/plPipeline/CMakeLists.txt @@ -71,6 +71,7 @@ target_link_libraries(plPipeline plSurface pfCamera # plCaptureRender, plDynamicEnvMap, plDXPipeline pfGameGUIMgr # plCaptureRender + freetype INTERFACE pnFactory ) From 075df94f7f6097d98f55c863042a43817b17799a Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 9 Apr 2023 21:53:51 -0700 Subject: [PATCH 11/27] Initial cut of Freetype code. Stilll a bit of a mess. Still a lot of GDI. --- .../PubUtilLib/plPipeline/plTextFont.cpp | 64 ++++++++++++++++++- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp index 777e4c49da..777c3e0063 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp +++ b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp @@ -57,6 +57,12 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plDebugText.h" +#include +#include FT_FREETYPE_H +#include FT_GLYPH_H + +#include + #if defined(HS_BUILD_FOR_APPLE) #import #import @@ -121,6 +127,11 @@ uint16_t *plTextFont::IInitFontTexture() nHeight = -MulDiv( fSize, GetDeviceCaps( hDC, LOGPIXELSY ), 72 ); fFontHeight = -nHeight; + FT_Library library; + FT_Face face; + FT_Error ftError = FT_Init_FreeType(&library); + hsAssert(ftError == FT_Err_Ok, "FreeType did not initialize"); + hFont = CreateFont( nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, fFace ); hsAssert(hFont != nullptr, "Cannot create Windows font"); @@ -128,6 +139,16 @@ uint16_t *plTextFont::IInitFontTexture() SelectObject( hDC, hBitmap ); SelectObject( hDC, hFont ); + // Get the font data + + DWORD fontDataSize = GetFontData( hDC, 0, 0, 0, 0 ); + void* fontData = std::malloc( fontDataSize ); + GetFontData(hDC, 0, 0, fontData, fontDataSize); + ftError = FT_New_Memory_Face(library, (FT_Byte *) fontData, fontDataSize, 0, &face); + + FT_UInt freeTypeResolution = GetDeviceCaps(hDC, LOGPIXELSY); + ftError = FT_Set_Char_Size(face, 0, fSize * 64, freeTypeResolution, freeTypeResolution); + // Set text colors SetTextColor( hDC, RGB( 255, 255, 255 ) ); SetBkColor( hDC, 0 ); @@ -136,7 +157,8 @@ uint16_t *plTextFont::IInitFontTexture() // Loop through characters, drawing them one at a time RECT r; r.left = r.top = 0; - r.right = r.bottom = 10; + r.right = 1; + r.bottom = 10; FillRect( hDC, &r, (HBRUSH)GetStockObject( GRAY_BRUSH ) ); // (Make first character a black dot, for filling rectangles) @@ -144,15 +166,48 @@ uint16_t *plTextFont::IInitFontTexture() for( c = 32, x = 1, y = 0; c < 127; c++ ) { myChar[ 0 ] = c; - GetTextExtentPoint32( hDC, myChar, 1, &size ); + ftError = FT_Load_Char(face, c, FT_LOAD_RENDER | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME ); + + FT_GlyphSlot slot = face->glyph; + + FT_Glyph glyph; + FT_Get_Glyph(slot, &glyph); + + FT_BBox bbox; + FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox); + + size.cx = slot->metrics.horiAdvance / 64; + size.cy = face->height/ 64; + + FT_Bitmap bitmap = slot->bitmap; if( (uint32_t)( x + size.cx + 1 ) > fTextureWidth ) { x = 0; y += size.cy + 1; } + int offset = size.cy - face->glyph->bitmap_top - ceil( - face->descender / 64 ); + + //blit the bitmap into place + for (int glyphY = bitmap.rows - 1; glyphY >= 0 ; glyphY--) { + for (int glyphX = 0; glyphX < bitmap.width; glyphX++) { + //1 bit Bitmap as source + int pitch = abs(slot->bitmap.pitch); + unsigned char* row = &slot->bitmap.buffer[pitch * glyphY]; + unsigned char cValue = row[glyphX >> 3]; - ExtTextOut(hDC, x, y, ETO_OPAQUE, nullptr, myChar, 1, nullptr); + uint8_t src = 0; + + if ((cValue & (128 >> (glyphX & 7))) != 0) { + src = UINT8_MAX; + } + int destY = y + glyphY + offset; + int destX = glyphX + x + face->glyph->bitmap_left; + int destIndex = (destY * fTextureWidth) + destX; + + bitmapBits[(destIndex )] = src; + } + } fCharInfo[ c ].fW = (uint16_t)size.cx; fCharInfo[ c ].fH = (uint16_t)size.cy; @@ -191,6 +246,9 @@ uint16_t *plTextFont::IInitFontTexture() } } + FT_Done_Face(face); + FT_Done_FreeType(library); + // Cleanup and return DeleteObject( hBitmap ); DeleteDC( hDC ); From 6d311d3ff6d2560dc46965376826f6a944523ae0 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 16 Apr 2023 20:38:14 -0700 Subject: [PATCH 12/27] Cleaning up size issues and removing likely dead code --- .../PubUtilLib/plPipeline/plTextFont.cpp | 61 ++++--------------- 1 file changed, 11 insertions(+), 50 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp index 777c3e0063..67ccd176a2 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp +++ b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp @@ -91,12 +91,9 @@ uint16_t *plTextFont::IInitFontTexture() #ifdef HS_BUILD_FOR_WIN32 int nHeight, x, y, c; char myChar[ 2 ] = "x"; - uint16_t *tBits; - DWORD *bitmapBits; BITMAPINFO bmi; HDC hDC; - HBITMAP hBitmap; HFONT hFont; SIZE size; BYTE bAlpha; @@ -110,18 +107,10 @@ uint16_t *plTextFont::IInitFontTexture() else fTextureWidth = fTextureHeight = 256; - - // Create a new DC and bitmap that we can draw characters to - memset( &bmi.bmiHeader, 0, sizeof( BITMAPINFOHEADER ) ); - bmi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER ); - bmi.bmiHeader.biWidth = fTextureWidth; - bmi.bmiHeader.biHeight = -(int)fTextureHeight; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biBitCount = 32; + /// Now create the data block + uint16_t* data = new uint16_t[fTextureWidth * fTextureHeight](); hDC = CreateCompatibleDC(nullptr); - hBitmap = CreateDIBSection(hDC, &bmi, DIB_RGB_COLORS, (void **)&bitmapBits, nullptr, 0); SetMapMode( hDC, MM_TEXT ); nHeight = -MulDiv( fSize, GetDeviceCaps( hDC, LOGPIXELSY ), 72 ); @@ -136,9 +125,7 @@ uint16_t *plTextFont::IInitFontTexture() CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, fFace ); hsAssert(hFont != nullptr, "Cannot create Windows font"); - SelectObject( hDC, hBitmap ); SelectObject( hDC, hFont ); - // Get the font data DWORD fontDataSize = GetFontData( hDC, 0, 0, 0, 0 ); @@ -154,30 +141,23 @@ uint16_t *plTextFont::IInitFontTexture() SetBkColor( hDC, 0 ); SetTextAlign( hDC, TA_TOP ); - // Loop through characters, drawing them one at a time - RECT r; - r.left = r.top = 0; - r.right = 1; - r.bottom = 10; - FillRect( hDC, &r, (HBRUSH)GetStockObject( GRAY_BRUSH ) ); + fFontHeight = face->size->metrics.height / 64; + + data[0] = 0xffff; - // (Make first character a black dot, for filling rectangles) - SetPixel( hDC, 0, 0, RGB( 255, 255, 255 ) ); + // Loop through characters, drawing them one at a time for( c = 32, x = 1, y = 0; c < 127; c++ ) { myChar[ 0 ] = c; - ftError = FT_Load_Char(face, c, FT_LOAD_RENDER | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME ); + ftError = FT_Load_Char(face, c, FT_LOAD_RENDER | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME | FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING ); FT_GlyphSlot slot = face->glyph; FT_Glyph glyph; FT_Get_Glyph(slot, &glyph); - FT_BBox bbox; - FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox); - size.cx = slot->metrics.horiAdvance / 64; - size.cy = face->height/ 64; + size.cy = face->ascender / 64; FT_Bitmap bitmap = slot->bitmap; @@ -196,16 +176,16 @@ uint16_t *plTextFont::IInitFontTexture() unsigned char* row = &slot->bitmap.buffer[pitch * glyphY]; unsigned char cValue = row[glyphX >> 3]; - uint8_t src = 0; + uint16_t src = 0; if ((cValue & (128 >> (glyphX & 7))) != 0) { - src = UINT8_MAX; + src = UINT16_MAX; } int destY = y + glyphY + offset; int destX = glyphX + x + face->glyph->bitmap_left; int destIndex = (destY * fTextureWidth) + destX; - bitmapBits[(destIndex )] = src; + data[(destIndex )] = src; } } @@ -228,29 +208,10 @@ uint16_t *plTextFont::IInitFontTexture() fCharInfo[ '\t' ].fW = fCharInfo[ 32 ].fW * 4; fCharInfo[ '\t' ].fH = fCharInfo[ 32 ].fH; - /// Now create the data block - uint16_t *data = new uint16_t[ fTextureWidth * fTextureHeight ]; - tBits = data; - for( y = 0; y < fTextureHeight; y++ ) - { - for( x = 0; x < fTextureWidth; x++ ) - { - bAlpha = (BYTE)( ( bitmapBits[ fTextureWidth * y + x ] & 0xff ) >> 4 ); - - if( bitmapBits[ fTextureWidth * y + x ] ) - *tBits = 0xffff; - else - *tBits = 0; - - tBits++; - } - } - FT_Done_Face(face); FT_Done_FreeType(library); // Cleanup and return - DeleteObject( hBitmap ); DeleteDC( hDC ); DeleteObject( hFont ); From 808ad18fe58829d87552d0238128ebfe8348f542 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Thu, 27 Apr 2023 17:24:14 -0700 Subject: [PATCH 13/27] Fixes for FreeType font layout --- .../PubUtilLib/plPipeline/plTextFont.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp index 67ccd176a2..84a3a883d2 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp +++ b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp @@ -113,7 +113,7 @@ uint16_t *plTextFont::IInitFontTexture() hDC = CreateCompatibleDC(nullptr); SetMapMode( hDC, MM_TEXT ); - nHeight = -MulDiv( fSize, GetDeviceCaps( hDC, LOGPIXELSY ), 72 ); + nHeight = -MulDiv( fSize, GetDeviceCaps(hDC, LOGPIXELSY), 72 ); fFontHeight = -nHeight; FT_Library library; @@ -141,23 +141,30 @@ uint16_t *plTextFont::IInitFontTexture() SetBkColor( hDC, 0 ); SetTextAlign( hDC, TA_TOP ); - fFontHeight = face->size->metrics.height / 64; - + FT_Size_Metrics fontMetrics = face->size->metrics; + + fFontHeight = int(fontMetrics.height / 64.f); + data[0] = 0xffff; + int maxDescent = abs(int(fontMetrics.descender / 64.f)); + // Loop through characters, drawing them one at a time for( c = 32, x = 1, y = 0; c < 127; c++ ) { myChar[ 0 ] = c; - ftError = FT_Load_Char(face, c, FT_LOAD_RENDER | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME | FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING ); + ftError = FT_Load_Char( face, c, FT_LOAD_RENDER | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME | FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING ); FT_GlyphSlot slot = face->glyph; FT_Glyph glyph; FT_Get_Glyph(slot, &glyph); + FT_BBox cBox; + FT_Glyph_Get_CBox( glyph, FT_GLYPH_BBOX_TRUNCATE, &cBox ); + size.cx = slot->metrics.horiAdvance / 64; - size.cy = face->ascender / 64; + size.cy = fFontHeight + maxDescent; FT_Bitmap bitmap = slot->bitmap; @@ -166,7 +173,7 @@ uint16_t *plTextFont::IInitFontTexture() x = 0; y += size.cy + 1; } - int offset = size.cy - face->glyph->bitmap_top - ceil( - face->descender / 64 ); + int offset = fFontHeight - cBox.yMax; //blit the bitmap into place for (int glyphY = bitmap.rows - 1; glyphY >= 0 ; glyphY--) { From f72b8904caa0ed748ece53df360c34b5909501e2 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Fri, 5 May 2023 16:26:29 -0700 Subject: [PATCH 14/27] Allowing banner images in login screens to scale and hacking HiDPI into the patcher --- .../Apps/plClient/win32/res/plClient.rc | 2 +- .../Apps/plUruLauncher/plUruLauncher.rc | 2 +- Sources/Plasma/Apps/plUruLauncher/winmain.cpp | 41 +++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/Sources/Plasma/Apps/plClient/win32/res/plClient.rc b/Sources/Plasma/Apps/plClient/win32/res/plClient.rc index 5868e02235..50d943888b 100644 --- a/Sources/Plasma/Apps/plClient/win32/res/plClient.rc +++ b/Sources/Plasma/Apps/plClient/win32/res/plClient.rc @@ -94,7 +94,7 @@ FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN DEFPUSHBUTTON "Login",IDOK,87,225,50,14 PUSHBUTTON "Quit",IDCANCEL,163,225,50,14 - CONTROL 151,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZEIMAGE | WS_BORDER,7,7,289,36 + CONTROL 151,IDC_STATIC,"Static",SS_BITMAP | SS_REALSIZECONTROL | WS_BORDER,7,7,289,36 LTEXT "Account name:",IDC_STATIC,69,119,49,10 LTEXT "Password:",IDC_STATIC,69,135,49,10 EDITTEXT IDC_URULOGIN_USERNAME,123,119,108,12,ES_AUTOHSCROLL diff --git a/Sources/Plasma/Apps/plUruLauncher/plUruLauncher.rc b/Sources/Plasma/Apps/plUruLauncher/plUruLauncher.rc index 0a6e552a4c..882990ba95 100644 --- a/Sources/Plasma/Apps/plUruLauncher/plUruLauncher.rc +++ b/Sources/Plasma/Apps/plUruLauncher/plUruLauncher.rc @@ -61,7 +61,7 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | EXSTYLE WS_EX_APPWINDOW FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - CONTROL 109,IDB_BITMAP,"Static",SS_BITMAP | SS_SUNKEN,7,7,288,36 + CONTROL 109,IDB_BITMAP,"Static",SS_BITMAP | SS_SUNKEN | SS_REALSIZECONTROL,7,7,288,36 CONTROL "",IDC_PROGRESS,"msctls_progress32",WS_BORDER | 0x1,7, 111,234,11 CONTROL "", IDC_MARQUEE, "msctls_progress32", WS_BORDER | 0x8, 7, diff --git a/Sources/Plasma/Apps/plUruLauncher/winmain.cpp b/Sources/Plasma/Apps/plUruLauncher/winmain.cpp index f7210b5d19..24b4bcb3d6 100644 --- a/Sources/Plasma/Apps/plUruLauncher/winmain.cpp +++ b/Sources/Plasma/Apps/plUruLauncher/winmain.cpp @@ -55,6 +55,7 @@ Mead, WA 99021 #include #include #include +#include // =================================================== @@ -188,6 +189,44 @@ static void PumpMessages() } while (s_launcher->PumpNetCore()); } +void SetDPIAwareness() const +{ + plOptionalWinCall fSetProcessDpiAwarenessContext(L"user32", "SetProcessDpiAwarenessContext"); + plOptionalWinCall fSetProcessDpiAwareness(L"user32", "SetProcessDpiAwarenessContext"); + + // There are three different levels of DPI awareness that we can deal with, + // each was added in newer versions of Windows as monitors became more and + // more sexy. + // In Vista, the entire system had an adjustable display scale. + // In Windows 8, each monitor was allowed to have a separate scale. + // In Windows 10, the per-monitor scaling was slowly improved to automatically + // scale dialog boxes and things like that. + // So we need to try to handle all of that. We will assume the baseline is Vista. + // Any API added later than that will need to be called on a provisional basis. + do { + // Per Monitor V2 for Windows 10 v1703. + auto perMonitorV2Result = fSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + if (perMonitorV2Result.has_value()) { + if (perMonitorV2Result.value() == TRUE) { + break; + } + } + + // Per Monitor v1 for Windows 8.1. + auto perMonitorV1Result = fSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); + if (perMonitorV1Result.has_value()) { + if (SUCCEEDED(perMonitorV1Result.value())) { + break; + } + } + + // System DPI Awareness. + if (SetProcessDPIAware() != FALSE) { + break; + } + } while (false); +} + // =================================================== static void IOnDownloadBegin(const plFileName& file) @@ -380,6 +419,8 @@ static pfPatcher* IPatcherFactory() int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLink, int nCmdShow) { + SetProcessDPIAware(); + plClientLauncher launcher; s_launcher = &launcher; From 92ecbd47470d40aa6c85fb689588a6375be095af Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sat, 6 May 2023 15:48:31 -0700 Subject: [PATCH 15/27] More changes to make plWinDPI modular --- Sources/Plasma/Apps/plClient/CMakeLists.txt | 6 +-- Sources/Plasma/Apps/plClient/plClient.cpp | 2 +- .../Apps/plClient/win32/plClient_Win.cpp | 2 +- .../Plasma/Apps/plClient/win32/winmain.cpp | 12 ++++- .../Plasma/Apps/plUruLauncher/CMakeLists.txt | 1 + Sources/Plasma/Apps/plUruLauncher/winmain.cpp | 47 ++++--------------- Sources/Plasma/PubUtilLib/CMakeLists.txt | 1 + .../plMessage/plDisplayScaleChangedMsg.h | 12 +++++ .../Plasma/PubUtilLib/plWinDpi/CMakeLists.txt | 22 +++++++++ .../plWinDpi}/plWinDpi.cpp | 25 ++++------ .../win32 => PubUtilLib/plWinDpi}/plWinDpi.h | 14 +----- 11 files changed, 66 insertions(+), 78 deletions(-) create mode 100644 Sources/Plasma/PubUtilLib/plWinDpi/CMakeLists.txt rename Sources/Plasma/{Apps/plClient/win32 => PubUtilLib/plWinDpi}/plWinDpi.cpp (95%) rename Sources/Plasma/{Apps/plClient/win32 => PubUtilLib/plWinDpi}/plWinDpi.h (91%) diff --git a/Sources/Plasma/Apps/plClient/CMakeLists.txt b/Sources/Plasma/Apps/plClient/CMakeLists.txt index 23b25527aa..6f451b079b 100644 --- a/Sources/Plasma/Apps/plClient/CMakeLists.txt +++ b/Sources/Plasma/Apps/plClient/CMakeLists.txt @@ -76,13 +76,8 @@ set(plClient_TEXT ) if(WIN32) - list(APPEND plClient_HEADERS - win32/plWinDpi.h - ) - list(APPEND plClient_SOURCES win32/plClient_Win.cpp - win32/plWinDpi.cpp win32/winmain.cpp ) @@ -151,6 +146,7 @@ target_link_libraries( plStatGather plStatusLog plUnifiedTime + $<$:plWinDpi> pfAnimation pfAudio pfCharacter diff --git a/Sources/Plasma/Apps/plClient/plClient.cpp b/Sources/Plasma/Apps/plClient/plClient.cpp index ae07acdb2c..9e2bd3121b 100644 --- a/Sources/Plasma/Apps/plClient/plClient.cpp +++ b/Sources/Plasma/Apps/plClient/plClient.cpp @@ -57,7 +57,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plClient.h" #ifdef HS_BUILD_FOR_WIN32 -# include "win32/plWinDpi.h" +# include "plWinDpi/plWinDpi.h" #endif #include "pnDispatch/plDispatch.h" diff --git a/Sources/Plasma/Apps/plClient/win32/plClient_Win.cpp b/Sources/Plasma/Apps/plClient/win32/plClient_Win.cpp index 3be725d470..0604e448c5 100644 --- a/Sources/Plasma/Apps/plClient/win32/plClient_Win.cpp +++ b/Sources/Plasma/Apps/plClient/win32/plClient_Win.cpp @@ -49,7 +49,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include #include "plClient.h" -#include "win32/plWinDpi.h" +#include "plWinDpi/plWinDpi.h" #include "pnFactory/plFactory.h" #include "pnNetCommon/plNetApp.h" diff --git a/Sources/Plasma/Apps/plClient/win32/winmain.cpp b/Sources/Plasma/Apps/plClient/win32/winmain.cpp index cc6fe879fe..86cba326c7 100644 --- a/Sources/Plasma/Apps/plClient/win32/winmain.cpp +++ b/Sources/Plasma/Apps/plClient/win32/winmain.cpp @@ -59,7 +59,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plClient.h" #include "plClientLoader.h" #include "res/resource.h" -#include "plWinDpi.h" #include "pnEncryption/plChallengeHash.h" @@ -68,12 +67,14 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plInputCore/plInputManager.h" #include "plNetClient/plNetClientMgr.h" #include "plNetGameLib/plNetGameLib.h" +#include "plMessage/plDisplayScaleChangedMsg.h" #include "plPhysX/plPXSimulation.h" #include "plPipeline/hsG3DDeviceSelector.h" #include "plResMgr/plLocalization.h" #include "plResMgr/plResManager.h" #include "plResMgr/plVersion.h" #include "plStatusLog/plStatusLog.h" +#include "plWinDpi/plWinDpi.h" #include "pfConsoleCore/pfConsoleEngine.h" #include "pfCrashHandler/plCrashCli.h" @@ -172,6 +173,13 @@ static void AuthFailedStrings (ENetError authError, void DebugMsgF(const char* format, ...); +void HandleDpiChange(HWND hWnd, UINT dpi, float scale, const RECT& rect) +{ + // Inform the engine about the new DPI. + auto* msg = new plDisplayScaleChangedMsg(scale, plDisplayScaleChangedMsg::ConvertRect(rect)); + msg->Send(); +} + // Handles all the windows messages we might receive LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -179,7 +187,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) static uint8_t mouse_down = 0; // DPI Helper can eat messages. - auto result = plWinDpi::Instance().WndProc(hWnd, message, wParam, lParam); + auto result = plWinDpi::Instance().WndProc(hWnd, message, wParam, lParam, HandleDpiChange); if (result.has_value()) return result.value(); diff --git a/Sources/Plasma/Apps/plUruLauncher/CMakeLists.txt b/Sources/Plasma/Apps/plUruLauncher/CMakeLists.txt index 27a6ef84e4..e32571fb10 100644 --- a/Sources/Plasma/Apps/plUruLauncher/CMakeLists.txt +++ b/Sources/Plasma/Apps/plUruLauncher/CMakeLists.txt @@ -29,6 +29,7 @@ target_link_libraries( pnNetBase plNetGameLib plStatusLog + plWinDpi pfConsoleCore pfPatcher CURL::libcurl diff --git a/Sources/Plasma/Apps/plUruLauncher/winmain.cpp b/Sources/Plasma/Apps/plUruLauncher/winmain.cpp index 24b4bcb3d6..aa454abbb8 100644 --- a/Sources/Plasma/Apps/plUruLauncher/winmain.cpp +++ b/Sources/Plasma/Apps/plUruLauncher/winmain.cpp @@ -47,6 +47,8 @@ Mead, WA 99021 #include "pfPatcher/plManifests.h" #include "pfPatcher/pfPatcher.h" +#include "plWinDpi/plWinDpi.h" + #include "plClientLauncher.h" #include "hsWindows.h" @@ -125,6 +127,11 @@ static inline void IShowMarquee(bool marquee=true) INT_PTR CALLBACK PatcherDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { + // DPI Helper can eat messages. + auto result = plWinDpi::Instance().WndProc(hwndDlg, uMsg, wParam, lParam, nullptr); + if (result.has_value()) + return result.value(); + // NT6 Taskbar Majick if (uMsg == s_taskbarCreated) { hsRequireCOM(); @@ -189,44 +196,6 @@ static void PumpMessages() } while (s_launcher->PumpNetCore()); } -void SetDPIAwareness() const -{ - plOptionalWinCall fSetProcessDpiAwarenessContext(L"user32", "SetProcessDpiAwarenessContext"); - plOptionalWinCall fSetProcessDpiAwareness(L"user32", "SetProcessDpiAwarenessContext"); - - // There are three different levels of DPI awareness that we can deal with, - // each was added in newer versions of Windows as monitors became more and - // more sexy. - // In Vista, the entire system had an adjustable display scale. - // In Windows 8, each monitor was allowed to have a separate scale. - // In Windows 10, the per-monitor scaling was slowly improved to automatically - // scale dialog boxes and things like that. - // So we need to try to handle all of that. We will assume the baseline is Vista. - // Any API added later than that will need to be called on a provisional basis. - do { - // Per Monitor V2 for Windows 10 v1703. - auto perMonitorV2Result = fSetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - if (perMonitorV2Result.has_value()) { - if (perMonitorV2Result.value() == TRUE) { - break; - } - } - - // Per Monitor v1 for Windows 8.1. - auto perMonitorV1Result = fSetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); - if (perMonitorV1Result.has_value()) { - if (SUCCEEDED(perMonitorV1Result.value())) { - break; - } - } - - // System DPI Awareness. - if (SetProcessDPIAware() != FALSE) { - break; - } - } while (false); -} - // =================================================== static void IOnDownloadBegin(const plFileName& file) @@ -419,7 +388,7 @@ static pfPatcher* IPatcherFactory() int __stdcall WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLink, int nCmdShow) { - SetProcessDPIAware(); + plWinDpi::Instance(); plClientLauncher launcher; s_launcher = &launcher; diff --git a/Sources/Plasma/PubUtilLib/CMakeLists.txt b/Sources/Plasma/PubUtilLib/CMakeLists.txt index df5defcad0..522729163c 100644 --- a/Sources/Plasma/PubUtilLib/CMakeLists.txt +++ b/Sources/Plasma/PubUtilLib/CMakeLists.txt @@ -45,3 +45,4 @@ add_subdirectory(plSurface) add_subdirectory(plTransform) add_subdirectory(plUnifiedTime) add_subdirectory(plVault) +add_subdirectory(plWinDpi) diff --git a/Sources/Plasma/PubUtilLib/plMessage/plDisplayScaleChangedMsg.h b/Sources/Plasma/PubUtilLib/plMessage/plDisplayScaleChangedMsg.h index 5b63bfcd11..1126a80b81 100644 --- a/Sources/Plasma/PubUtilLib/plMessage/plDisplayScaleChangedMsg.h +++ b/Sources/Plasma/PubUtilLib/plMessage/plDisplayScaleChangedMsg.h @@ -80,6 +80,18 @@ class plDisplayScaleChangedMsg : public plMessage public: float GetScale() const { return fScale; } std::optional GetSuggestedLocation() const { return fNewLocation; } + +#if WIN32 + static RECT ConvertRect(const plDisplayScaleChangedMsg::ClientWindow& rect) + { + return *((const LPRECT)&rect); + } + + static plDisplayScaleChangedMsg::ClientWindow ConvertRect(const RECT& rect) + { + return *((const plDisplayScaleChangedMsg::ClientWindow*)&rect); + } +#endif }; #endif diff --git a/Sources/Plasma/PubUtilLib/plWinDpi/CMakeLists.txt b/Sources/Plasma/PubUtilLib/plWinDpi/CMakeLists.txt new file mode 100644 index 0000000000..0369c339f6 --- /dev/null +++ b/Sources/Plasma/PubUtilLib/plWinDpi/CMakeLists.txt @@ -0,0 +1,22 @@ +if(WIN32) + +list(APPEND plWinDpi_HEADERS + plWinDpi.h +) + +list(APPEND plWinDpi_SOURCES + plWinDpi.cpp +) + +plasma_library(plWinDpi + SOURCES ${plWinDpi_SOURCES} ${plWinDpi_HEADERS} +) + +target_link_libraries( + plWinDpi + PUBLIC + CoreLib + plStatusLog +) + +endif() \ No newline at end of file diff --git a/Sources/Plasma/Apps/plClient/win32/plWinDpi.cpp b/Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.cpp similarity index 95% rename from Sources/Plasma/Apps/plClient/win32/plWinDpi.cpp rename to Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.cpp index dd53588658..cb4c326700 100644 --- a/Sources/Plasma/Apps/plClient/win32/plWinDpi.cpp +++ b/Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.cpp @@ -45,11 +45,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include #include -#include "hsResMgr.h" - -#include "pnKeyedObject/plFixedKey.h" -#include "pnMessage/plClientMsg.h" - #ifndef WM_GETDPISCALEDSIZE # define WM_GETDPISCALEDSIZE 0x02E4 #endif @@ -252,17 +247,7 @@ void plWinDpi::IEnableNCScaling(HWND hWnd) const } } -void plWinDpi::IHandleDpiChange(HWND hWnd, UINT dpi, const RECT& rect) const -{ - float scale = float(dpi) / 96.0f; - LogWhite("Window DPI changed to {} (scale factor: {.02f})", dpi, scale); - - // Inform the engine about the new DPI. - auto* msg = new plDisplayScaleChangedMsg(scale, ConvertRect(rect)); - msg->Send(); -} - -std::optional plWinDpi::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) const +std::optional plWinDpi::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, const std::function & dpiChangedCallback) const { switch (msg) { case WM_NCCREATE: @@ -272,7 +257,13 @@ std::optional plWinDpi::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPA // For Windows 8.1 and higher. Allows us to handle DPI changes while the game is running, // eg by moving the game window to another monitor with a different scale factor or // the user changed the scale factor while the game is running. - IHandleDpiChange(hWnd, LOWORD(wParam), *((LPRECT)lParam)); + if (dpiChangedCallback) { + UINT dpi = LOWORD(wParam); + float scale = float(dpi) / 96.0f; + LogWhite("Window DPI changed to {} (scale factor: {.02f})", dpi, scale); + + dpiChangedCallback(hWnd, dpi, scale, *((LPRECT)lParam)); + } return 0; case WM_GETDPISCALEDSIZE: // For Windows 10 v1703 and higher. This window message allows us to tell the diff --git a/Sources/Plasma/Apps/plClient/win32/plWinDpi.h b/Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.h similarity index 91% rename from Sources/Plasma/Apps/plClient/win32/plWinDpi.h rename to Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.h index ac486e2ad6..356d314262 100644 --- a/Sources/Plasma/Apps/plClient/win32/plWinDpi.h +++ b/Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.h @@ -49,7 +49,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "hsWindows.h" #include -#include "plMessage/plDisplayScaleChangedMsg.h" #include "plStatusLog/plStatusLog.h" class plWinDpi @@ -114,23 +113,12 @@ class plWinDpi public: float GetScale(HWND hWnd = nullptr) const { return float(GetDpi(hWnd)) / 96.0f; } - static RECT ConvertRect(const plDisplayScaleChangedMsg::ClientWindow& rect) - { - return *((const LPRECT)&rect); - } - - static plDisplayScaleChangedMsg::ClientWindow ConvertRect(const RECT& rect) - { - return *((const plDisplayScaleChangedMsg::ClientWindow*)&rect); - } - private: BOOL ICalcWinSize(HWND hWnd, UINT dpi, SIZE& hWndSize) const; void IEnableNCScaling(HWND hWnd) const; - void IHandleDpiChange(HWND hWnd, UINT dpi, const RECT& rect) const; public: - std::optional WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) const; + std::optional WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, const std::function & dpiChangedCallback) const; }; #endif From bc2eb3457f8dae1201d575bca596d5d3df705f87 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sat, 6 May 2023 20:33:54 -0700 Subject: [PATCH 16/27] Applying suggestions from code review --- Sources/Plasma/Apps/plClient/win32/winmain.cpp | 2 +- Sources/Plasma/PubUtilLib/CMakeLists.txt | 4 +++- Sources/Plasma/PubUtilLib/plWinDpi/CMakeLists.txt | 4 ---- Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.cpp | 2 +- Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.h | 2 +- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Sources/Plasma/Apps/plClient/win32/winmain.cpp b/Sources/Plasma/Apps/plClient/win32/winmain.cpp index 86cba326c7..88f50a08a8 100644 --- a/Sources/Plasma/Apps/plClient/win32/winmain.cpp +++ b/Sources/Plasma/Apps/plClient/win32/winmain.cpp @@ -173,7 +173,7 @@ static void AuthFailedStrings (ENetError authError, void DebugMsgF(const char* format, ...); -void HandleDpiChange(HWND hWnd, UINT dpi, float scale, const RECT& rect) +static void HandleDpiChange(HWND hWnd, UINT dpi, float scale, const RECT& rect) { // Inform the engine about the new DPI. auto* msg = new plDisplayScaleChangedMsg(scale, plDisplayScaleChangedMsg::ConvertRect(rect)); diff --git a/Sources/Plasma/PubUtilLib/CMakeLists.txt b/Sources/Plasma/PubUtilLib/CMakeLists.txt index 522729163c..d32dcdb9c2 100644 --- a/Sources/Plasma/PubUtilLib/CMakeLists.txt +++ b/Sources/Plasma/PubUtilLib/CMakeLists.txt @@ -45,4 +45,6 @@ add_subdirectory(plSurface) add_subdirectory(plTransform) add_subdirectory(plUnifiedTime) add_subdirectory(plVault) -add_subdirectory(plWinDpi) +if(WIN32) + add_subdirectory(plWinDpi) +endif() diff --git a/Sources/Plasma/PubUtilLib/plWinDpi/CMakeLists.txt b/Sources/Plasma/PubUtilLib/plWinDpi/CMakeLists.txt index 0369c339f6..5edf553c9c 100644 --- a/Sources/Plasma/PubUtilLib/plWinDpi/CMakeLists.txt +++ b/Sources/Plasma/PubUtilLib/plWinDpi/CMakeLists.txt @@ -1,5 +1,3 @@ -if(WIN32) - list(APPEND plWinDpi_HEADERS plWinDpi.h ) @@ -18,5 +16,3 @@ target_link_libraries( CoreLib plStatusLog ) - -endif() \ No newline at end of file diff --git a/Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.cpp b/Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.cpp index cb4c326700..e71f26d1c7 100644 --- a/Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.cpp +++ b/Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.cpp @@ -247,7 +247,7 @@ void plWinDpi::IEnableNCScaling(HWND hWnd) const } } -std::optional plWinDpi::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, const std::function & dpiChangedCallback) const +std::optional plWinDpi::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, const std::function& dpiChangedCallback) const { switch (msg) { case WM_NCCREATE: diff --git a/Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.h b/Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.h index 356d314262..3795d17980 100644 --- a/Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.h +++ b/Sources/Plasma/PubUtilLib/plWinDpi/plWinDpi.h @@ -118,7 +118,7 @@ class plWinDpi void IEnableNCScaling(HWND hWnd) const; public: - std::optional WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, const std::function & dpiChangedCallback) const; + std::optional WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam, const std::function& dpiChangedCallback) const; }; #endif From 49bd069f780ac60b32b5735fdfc281ef71894b87 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 7 May 2023 15:31:25 -0700 Subject: [PATCH 17/27] Cleaning up text font and putting Mac in Freetype path --- .../PubUtilLib/plPipeline/plTextFont.cpp | 278 ++++++------------ 1 file changed, 85 insertions(+), 193 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp index 84a3a883d2..bad650cb1f 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp +++ b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp @@ -68,8 +68,6 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #import #endif -#define DisplayableChar(c) (c >= 0 && c <= 128) - //// Constructor & Destructor ///////////////////////////////////////////////// plTextFont::plTextFont( plPipeline *pipe ) @@ -88,16 +86,9 @@ plTextFont::~plTextFont() uint16_t *plTextFont::IInitFontTexture() { -#ifdef HS_BUILD_FOR_WIN32 - int nHeight, x, y, c; + int x, y, c; char myChar[ 2 ] = "x"; - BITMAPINFO bmi; - HDC hDC; - HFONT hFont; - SIZE size; - BYTE bAlpha; - // Figure out our texture size if( fSize > 40 ) @@ -109,38 +100,67 @@ uint16_t *plTextFont::IInitFontTexture() /// Now create the data block uint16_t* data = new uint16_t[fTextureWidth * fTextureHeight](); - - hDC = CreateCompatibleDC(nullptr); - SetMapMode( hDC, MM_TEXT ); - - nHeight = -MulDiv( fSize, GetDeviceCaps(hDC, LOGPIXELSY), 72 ); - fFontHeight = -nHeight; FT_Library library; FT_Face face; FT_Error ftError = FT_Init_FreeType(&library); hsAssert(ftError == FT_Err_Ok, "FreeType did not initialize"); +#ifdef HS_BUILD_FOR_WIN32 + + HDC hDC; + HFONT hFont; + BYTE bAlpha; + + hDC = CreateCompatibleDC(nullptr); + SetMapMode( hDC, MM_TEXT ); + // Get the font data + + int nHeight = -MulDiv( fSize, GetDeviceCaps(hDC, LOGPIXELSY), 72 ); + hFont = CreateFont( nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, fFace ); hsAssert(hFont != nullptr, "Cannot create Windows font"); - SelectObject( hDC, hFont ); - // Get the font data - + SelectObject(hDC, hFont); + DWORD fontDataSize = GetFontData( hDC, 0, 0, 0, 0 ); void* fontData = std::malloc( fontDataSize ); GetFontData(hDC, 0, 0, fontData, fontDataSize); ftError = FT_New_Memory_Face(library, (FT_Byte *) fontData, fontDataSize, 0, &face); - FT_UInt freeTypeResolution = GetDeviceCaps(hDC, LOGPIXELSY); - ftError = FT_Set_Char_Size(face, 0, fSize * 64, freeTypeResolution, freeTypeResolution); - // Set text colors - SetTextColor( hDC, RGB( 255, 255, 255 ) ); - SetBkColor( hDC, 0 ); - SetTextAlign( hDC, TA_TOP ); + DeleteDC( hDC ); + DeleteObject( hFont ); + +#elif defined(HS_BUILD_FOR_APPLE) + + CFStringRef fontName = CFStringCreateWithCString(nullptr, fFace, kCFStringEncodingUTF8); + CTFontDescriptorRef fontDescriptor = CTFontDescriptorCreateWithNameAndSize( fontName, 0.0f ); + CTFontDescriptorRef fulfilledFontDescriptor = CTFontDescriptorCreateMatchingFontDescriptor(fontDescriptor, nullptr); + hsAssert(fulfilledFontDescriptor != nullptr, "Cannot create Mac font"); + CFRelease(fontName); + CFRelease(fontDescriptor); + + CFURLRef fontURL = (CFURLRef) CTFontDescriptorCopyAttribute(fulfilledFontDescriptor, kCTFontURLAttribute); + CFStringRef fileSystemPath = CFURLCopyFileSystemPath(fontURL, kCFURLPOSIXPathStyle); + char cPath[PATH_MAX]; + CFStringGetCString(fileSystemPath, cPath, PATH_MAX, kCFStringEncodingUTF8); + + ftError = FT_New_Face(library, cPath, 0, &face); + + CFRelease(fulfilledFontDescriptor); + CFRelease(fontURL); + CFRelease(fileSystemPath); + + FT_UInt freeTypeResolution = 192; +#else + FT_Done_FreeType(library); + FT_UInt freeTypeResolution = 0; + return nullptr; +#endif + ftError = FT_Set_Char_Size(face, 0, fSize * 64, freeTypeResolution, freeTypeResolution); FT_Size_Metrics fontMetrics = face->size->metrics; fFontHeight = int(fontMetrics.height / 64.f); @@ -148,6 +168,10 @@ uint16_t *plTextFont::IInitFontTexture() data[0] = 0xffff; int maxDescent = abs(int(fontMetrics.descender / 64.f)); + struct { + long cx; + long cy; + } size; // Loop through characters, drawing them one at a time for( c = 32, x = 1, y = 0; c < 127; c++ ) @@ -173,7 +197,7 @@ uint16_t *plTextFont::IInitFontTexture() x = 0; y += size.cy + 1; } - int offset = fFontHeight - cBox.yMax; + int offset = int( fFontHeight - cBox.yMax ); //blit the bitmap into place for (int glyphY = bitmap.rows - 1; glyphY >= 0 ; glyphY--) { @@ -218,134 +242,7 @@ uint16_t *plTextFont::IInitFontTexture() FT_Done_Face(face); FT_Done_FreeType(library); - // Cleanup and return - DeleteDC( hDC ); - DeleteObject( hFont ); - - return data; -#elif defined(HS_BUILD_FOR_APPLE) - int nHeight, x, y, c; - char myChar[ 2 ] = "x"; - uint16_t *tBits; - - uint32_t *bitmapBits; - uint32_t bAlpha; - - - // Figure out our texture size - if( fSize * 2 > 40 ) - fTextureWidth = fTextureHeight = 1024; - else if( fSize * 2 > 20 ) - fTextureWidth = fTextureHeight = 512; - else - fTextureWidth = fTextureHeight = 256; - - - // Create a new DC and bitmap that we can draw characters to - CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); - CGContextRef bitmapContext = CGBitmapContextCreate(nullptr, - fTextureWidth, - fTextureHeight, - 8, - 4 * fTextureWidth, - colorSpace, - kCGImageAlphaPremultipliedLast); - CFRelease(colorSpace); - CGContextSetTextMatrix(bitmapContext, CGAffineTransformMakeScale(1, -1)); - CGContextTranslateCTM(bitmapContext, 0, fTextureHeight); - CGContextScaleCTM(bitmapContext, 1, -1); - CGContextScaleCTM(bitmapContext, 2.0, 2.0); - - bitmapBits = (uint32_t *)CGBitmapContextGetData(bitmapContext); - - //TEMPORARY: Mac client should vend through better data to make this decision - nHeight = fSize; - fFontHeight = (fSize * 144.0f) / 72.0f; - - CFStringRef fontName = CFStringCreateWithCString(nullptr, fFace, kCFStringEncodingUTF8); - CTFontRef font = CTFontCreateWithName(fontName, nHeight, nullptr); - hsAssert(font != nullptr, "Cannot create Mac font"); - CFRelease(fontName); - - // Set text colors - CGFloat color[4] = { 255.0f, 255.0f, 255.0f, 255.0f }; - CGContextSetFillColor(bitmapContext, color); - CGContextSetShouldAntialias(bitmapContext, false); - - // Loop through characters, drawing them one at a time - CGRect r = CGRectMake(0, 0, 10, 10); - - fTextureWidth /= 2; - fTextureHeight /= 2; - - // (Make first character a black dot, for filling rectangles) - bitmapBits[0] = UINT32_MAX; - for( c = 32, x = 1, y = 0; c < 127; c++ ) - { - myChar[ 0 ] = c; - CGGlyph glpyh; - CTFontGetGlyphsForCharacters(font, (UniChar *)myChar, &glpyh, 1); - CGRect rect; - CTFontGetOpticalBoundsForGlyphs(font, &glpyh, &rect, 1, 0); - CGSize size = rect.size; - - if( (uint32_t)( x + size.width + 1 ) > fTextureWidth ) - { - x = 0; - y += size.height + 1; - } - - CGPoint position = CGPointMake(x, -y - size.height - rect.origin.y); - CTFontDrawGlyphs(font, &glpyh, &position, 1, bitmapContext); - - fCharInfo[ c ].fW = (uint16_t)size.width * 2.0f; - fCharInfo[ c ].fH = (uint16_t)size.height * 2.0f; - fCharInfo[ c ].fUVs[ 0 ].fX = (float)x / (float)fTextureWidth; - fCharInfo[ c ].fUVs[ 0 ].fY = (float)y / (float)fTextureHeight; - fCharInfo[ c ].fUVs[ 1 ].fX = (float)( x + size.width ) / (float)fTextureWidth; - fCharInfo[ c ].fUVs[ 1 ].fY = (float)( y + size.height ) / (float)fTextureHeight; - fCharInfo[ c ].fUVs[ 0 ].fZ = fCharInfo[ c ].fUVs[ 1 ].fZ = 0; - - x += ceil(size.width) + 1; - } - fCharInfo[ 32 ].fUVs[ 1 ].fX = fCharInfo[ 32 ].fUVs[ 0 ].fX; - - // Special case the tab key - fCharInfo[ '\t' ].fUVs[ 1 ].fX = fCharInfo[ '\t' ].fUVs[ 0 ].fX = fCharInfo[ 32 ].fUVs[ 0 ].fX; - fCharInfo[ '\t' ].fUVs[ 1 ].fY = fCharInfo[ '\t' ].fUVs[ 0 ].fY = 0; - fCharInfo[ '\t' ].fUVs[ 0 ].fZ = fCharInfo[ '\t' ].fUVs[ 1 ].fZ = 0; - fCharInfo[ '\t' ].fW = fCharInfo[ 32 ].fW * 4; - fCharInfo[ '\t' ].fH = fCharInfo[ 32 ].fH; - - fTextureWidth *= 2; - fTextureHeight *= 2; - - /// Now create the data block - uint16_t *data = new uint16_t[ fTextureWidth * fTextureHeight ]; - tBits = data; - for( y = 0; y < fTextureHeight; y++ ) - { - for( x = 0; x < fTextureWidth; x++ ) - { - bAlpha = (char)( ( bitmapBits[ fTextureWidth * y + x ] & 0xff ) >> 4 ); - - if( bitmapBits[ fTextureWidth * y + x ] ) - *tBits = 0xffff; - else - *tBits = 0; - - tBits++; - } - } - - // Cleanup and return - CFRelease(font); - CFRelease(bitmapContext); - return data; -#else - return nullptr; -#endif } //// Create /////////////////////////////////////////////////////////////////// @@ -384,7 +281,8 @@ void plTextFont::DrawString( const char *string, int sX, int sY, uint32_t hex { static std::vector verts; - int i, j, width, height, count, thisCount, italOffset; + size_t i, j, width, height, count, thisCount; + uint32_t italOffset; float x = (float)sX; char c, *strPtr; @@ -414,49 +312,45 @@ void plTextFont::DrawString( const char *string, int sX, int sY, uint32_t hex for( i = 0, j = 0; i < thisCount; i++, j += 6 ) { c = strPtr[ i ]; - // make sure its a character we will display - if ( DisplayableChar(c) ) + width = fCharInfo[uint8_t(c)].fW + 1; + height = fCharInfo[uint8_t(c)].fH + 1; + + if( rightEdge > 0 && x + width + italOffset >= rightEdge ) { - width = fCharInfo[uint8_t(c)].fW + 1; - height = fCharInfo[uint8_t(c)].fH + 1; - - if( rightEdge > 0 && x + width + italOffset >= rightEdge ) - { - count = 0; - thisCount = i; - break; - } + count = 0; + thisCount = i; + break; + } - verts[ j ].fPoint.fX = x + italOffset; - verts[ j ].fPoint.fY = (float)sY; - verts[ j ].fUV = fCharInfo[uint8_t(c)].fUVs[ 0 ]; + verts[ j ].fPoint.fX = x + italOffset; + verts[ j ].fPoint.fY = (float)sY; + verts[ j ].fUV = fCharInfo[uint8_t(c)].fUVs[ 0 ]; - verts[ j + 1 ].fPoint.fX = x + width + italOffset; - verts[ j + 1 ].fPoint.fY = (float)sY; - verts[ j + 1 ].fUV = fCharInfo[uint8_t(c)].fUVs[ 0 ]; - verts[ j + 1 ].fUV.fX = fCharInfo[uint8_t(c)].fUVs[ 1 ].fX; + verts[ j + 1 ].fPoint.fX = x + width + italOffset; + verts[ j + 1 ].fPoint.fY = (float)sY; + verts[ j + 1 ].fUV = fCharInfo[uint8_t(c)].fUVs[ 0 ]; + verts[ j + 1 ].fUV.fX = fCharInfo[uint8_t(c)].fUVs[ 1 ].fX; - verts[ j + 2 ].fPoint.fX = x; - verts[ j + 2 ].fPoint.fY = (float)sY + height; - verts[ j + 2 ].fUV = fCharInfo[uint8_t(c)].fUVs[ 0 ]; - verts[ j + 2 ].fUV.fY = fCharInfo[uint8_t(c)].fUVs[ 1 ].fY; + verts[ j + 2 ].fPoint.fX = x; + verts[ j + 2 ].fPoint.fY = (float)sY + height; + verts[ j + 2 ].fUV = fCharInfo[uint8_t(c)].fUVs[ 0 ]; + verts[ j + 2 ].fUV.fY = fCharInfo[uint8_t(c)].fUVs[ 1 ].fY; - verts[ j + 3 ].fPoint.fX = x; - verts[ j + 3 ].fPoint.fY = (float)sY + height; - verts[ j + 3 ].fUV = fCharInfo[uint8_t(c)].fUVs[ 0 ]; - verts[ j + 3 ].fUV.fY = fCharInfo[uint8_t(c)].fUVs[ 1 ].fY; + verts[ j + 3 ].fPoint.fX = x; + verts[ j + 3 ].fPoint.fY = (float)sY + height; + verts[ j + 3 ].fUV = fCharInfo[uint8_t(c)].fUVs[ 0 ]; + verts[ j + 3 ].fUV.fY = fCharInfo[uint8_t(c)].fUVs[ 1 ].fY; - verts[ j + 4 ].fPoint.fX = x + width + italOffset; - verts[ j + 4 ].fPoint.fY = (float)sY; - verts[ j + 4 ].fUV = fCharInfo[uint8_t(c)].fUVs[ 0 ]; - verts[ j + 4 ].fUV.fX = fCharInfo[uint8_t(c)].fUVs[ 1 ].fX; + verts[ j + 4 ].fPoint.fX = x + width + italOffset; + verts[ j + 4 ].fPoint.fY = (float)sY; + verts[ j + 4 ].fUV = fCharInfo[uint8_t(c)].fUVs[ 0 ]; + verts[ j + 4 ].fUV.fX = fCharInfo[uint8_t(c)].fUVs[ 1 ].fX; - verts[ j + 5 ].fPoint.fX = x + width; - verts[ j + 5 ].fPoint.fY = (float)sY + height; - verts[ j + 5 ].fUV = fCharInfo[uint8_t(c)].fUVs[ 1 ]; + verts[ j + 5 ].fPoint.fX = x + width; + verts[ j + 5 ].fPoint.fY = (float)sY + height; + verts[ j + 5 ].fUV = fCharInfo[uint8_t(c)].fUVs[ 1 ]; - x += width + 1; - } + x += width + 1; } if( thisCount == 0 ) @@ -502,7 +396,7 @@ void plTextFont::DrawString( const char *string, int sX, int sY, uint32_t hex /// Draw a set of tris now - IDrawPrimitive(thisCount * ((style & plDebugText::kStyleBold) ? 4 : 2), verts.data()); + IDrawPrimitive(thisCount * uint32_t((style & plDebugText::kStyleBold) ? 4 : 2), verts.data()); strPtr += thisCount; } @@ -522,9 +416,7 @@ uint32_t plTextFont::CalcStringWidth( const char *string ) for( i = 0; i < strlen( string ); i++ ) { - // make sure its a character we will display - if ( DisplayableChar(string[i]) ) - width += fCharInfo[uint8_t(string[i])].fW + 2; + width += fCharInfo[uint8_t(string[i])].fW + 2; } return width; From f90e247d54ac4ec2d6679e633d158a64b93b9f79 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 14 May 2023 14:55:22 -0700 Subject: [PATCH 18/27] Fix suggestion for 32 bit Windows --- Sources/Plasma/CoreLib/hsWindows.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Plasma/CoreLib/hsWindows.h b/Sources/Plasma/CoreLib/hsWindows.h index 89a00f5605..013b58c609 100644 --- a/Sources/Plasma/CoreLib/hsWindows.h +++ b/Sources/Plasma/CoreLib/hsWindows.h @@ -153,7 +153,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com > class plOptionalWinCall<_ReturnT(_ArgsT...)> { - using _FuncPtrT = _ReturnT(*)(_ArgsT...); + using _FuncPtrT = _ReturnT(WINAPI*)(_ArgsT...); HMODULE fHModule; _FuncPtrT fProc; From c6167626bf4a5cd65d0bb5a20caafd2a45778691 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Mon, 15 May 2023 22:20:49 -0700 Subject: [PATCH 19/27] Rolling back some anti-hinting to see if Windows is fixed --- Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp index bad650cb1f..3dd0012029 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp +++ b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp @@ -177,7 +177,7 @@ uint16_t *plTextFont::IInitFontTexture() for( c = 32, x = 1, y = 0; c < 127; c++ ) { myChar[ 0 ] = c; - ftError = FT_Load_Char( face, c, FT_LOAD_RENDER | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME | FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING ); + ftError = FT_Load_Char( face, c, FT_LOAD_RENDER | FT_LOAD_TARGET_MONO | FT_LOAD_MONOCHROME | FT_LOAD_NO_AUTOHINT ); FT_GlyphSlot slot = face->glyph; From 1161188cbad9d54587ddc24bfa372d5ed354a728 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 21 May 2023 22:44:47 -0700 Subject: [PATCH 20/27] Alloc larger textures if needed, adding asserts --- .../Plasma/PubUtilLib/plPipeline/plTextFont.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp index 3dd0012029..1bcdfe7b4c 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp +++ b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp @@ -98,9 +98,6 @@ uint16_t *plTextFont::IInitFontTexture() else fTextureWidth = fTextureHeight = 256; - /// Now create the data block - uint16_t* data = new uint16_t[fTextureWidth * fTextureHeight](); - FT_Library library; FT_Face face; FT_Error ftError = FT_Init_FreeType(&library); @@ -132,6 +129,10 @@ uint16_t *plTextFont::IInitFontTexture() DeleteDC( hDC ); DeleteObject( hFont ); + + int iScale = int( ceil( GetDeviceCaps(hDC, LOGPIXELSY) / 96.0f ) ); + fTextureWidth *= iScale; + fTextureHeight *= iScale; #elif defined(HS_BUILD_FOR_APPLE) @@ -154,11 +155,17 @@ uint16_t *plTextFont::IInitFontTexture() CFRelease(fileSystemPath); FT_UInt freeTypeResolution = 192; + + fTextureWidth *= 2; + fTextureHeight *= 2; #else FT_Done_FreeType(library); FT_UInt freeTypeResolution = 0; return nullptr; #endif + + /// Now create the data block + uint16_t* data = new uint16_t[fTextureWidth * fTextureHeight](); ftError = FT_Set_Char_Size(face, 0, fSize * 64, freeTypeResolution, freeTypeResolution); FT_Size_Metrics fontMetrics = face->size->metrics; @@ -215,6 +222,8 @@ uint16_t *plTextFont::IInitFontTexture() int destY = y + glyphY + offset; int destX = glyphX + x + face->glyph->bitmap_left; int destIndex = (destY * fTextureWidth) + destX; + hsAssert( destX < fTextureWidth, "Destination X cannot be out of bounds"); + hsAssert( destY < fTextureHeight, "Destination Y cannot be out of bounds"); data[(destIndex )] = src; } From 3f486269b9bd14e592313014fabb7408adeb9f69 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Wed, 24 May 2023 23:46:01 -0700 Subject: [PATCH 21/27] Using fFontHeight for font texture size calc --- .../PubUtilLib/plPipeline/plTextFont.cpp | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp index 1bcdfe7b4c..672ee5ec8f 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp +++ b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp @@ -63,6 +63,10 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include +#ifdef HS_BUILD_FOR_WIN32 +# include "plWinDpi/plWinDpi.h" +#endif + #if defined(HS_BUILD_FOR_APPLE) #import #import @@ -89,15 +93,6 @@ uint16_t *plTextFont::IInitFontTexture() int x, y, c; char myChar[ 2 ] = "x"; - - // Figure out our texture size - if( fSize > 40 ) - fTextureWidth = fTextureHeight = 1024; - else if( fSize > 20 ) - fTextureWidth = fTextureHeight = 512; - else - fTextureWidth = fTextureHeight = 256; - FT_Library library; FT_Face face; FT_Error ftError = FT_Init_FreeType(&library); @@ -129,10 +124,6 @@ uint16_t *plTextFont::IInitFontTexture() DeleteDC( hDC ); DeleteObject( hFont ); - - int iScale = int( ceil( GetDeviceCaps(hDC, LOGPIXELSY) / 96.0f ) ); - fTextureWidth *= iScale; - fTextureHeight *= iScale; #elif defined(HS_BUILD_FOR_APPLE) @@ -172,6 +163,14 @@ uint16_t *plTextFont::IInitFontTexture() fFontHeight = int(fontMetrics.height / 64.f); + // Figure out our texture size + if (fFontHeight > 40) + fTextureWidth = fTextureHeight = 1024; + else if (fFontHeight > 20) + fTextureWidth = fTextureHeight = 512; + else + fTextureWidth = fTextureHeight = 256; + data[0] = 0xffff; int maxDescent = abs(int(fontMetrics.descender / 64.f)); From a00aca1b7cf8bf951e0c2df184a62fb924063eb7 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sat, 3 Jun 2023 14:25:31 -0700 Subject: [PATCH 22/27] Initialize fTextureWidth and fTextureHeight _before_ allocating the texture buffer --- Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp index 672ee5ec8f..e43b88a29d 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp +++ b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp @@ -154,9 +154,6 @@ uint16_t *plTextFont::IInitFontTexture() FT_UInt freeTypeResolution = 0; return nullptr; #endif - - /// Now create the data block - uint16_t* data = new uint16_t[fTextureWidth * fTextureHeight](); ftError = FT_Set_Char_Size(face, 0, fSize * 64, freeTypeResolution, freeTypeResolution); FT_Size_Metrics fontMetrics = face->size->metrics; @@ -171,6 +168,9 @@ uint16_t *plTextFont::IInitFontTexture() else fTextureWidth = fTextureHeight = 256; + /// Now create the data block + uint16_t* data = new uint16_t[fTextureWidth * fTextureHeight](); + data[0] = 0xffff; int maxDescent = abs(int(fontMetrics.descender / 64.f)); From 020a9a4888375b8463d8063b950f4f27fcfaee85 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 11 Jun 2023 14:47:53 -0700 Subject: [PATCH 23/27] Using plWinDPI for DPI checks --- Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp index e43b88a29d..a1d395bef6 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp +++ b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp @@ -108,7 +108,7 @@ uint16_t *plTextFont::IInitFontTexture() SetMapMode( hDC, MM_TEXT ); // Get the font data - int nHeight = -MulDiv( fSize, GetDeviceCaps(hDC, LOGPIXELSY), 72 ); + int nHeight = -MulDiv( fSize, plWinDpi::Instance().GetDpi(), 72); hFont = CreateFont( nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, VARIABLE_PITCH, fFace ); @@ -120,7 +120,7 @@ uint16_t *plTextFont::IInitFontTexture() void* fontData = std::malloc( fontDataSize ); GetFontData(hDC, 0, 0, fontData, fontDataSize); ftError = FT_New_Memory_Face(library, (FT_Byte *) fontData, fontDataSize, 0, &face); - FT_UInt freeTypeResolution = GetDeviceCaps(hDC, LOGPIXELSY); + FT_UInt freeTypeResolution = plWinDpi::Instance().GetDpi(); DeleteDC( hDC ); DeleteObject( hFont ); From e4702841c25a6304e2a7c978ff79adbadc973a3c Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sat, 24 Jun 2023 21:10:41 -0700 Subject: [PATCH 24/27] Adding link to plWinDPI from plPipeline on Win32 --- Sources/Plasma/PubUtilLib/plPipeline/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/Plasma/PubUtilLib/plPipeline/CMakeLists.txt b/Sources/Plasma/PubUtilLib/plPipeline/CMakeLists.txt index a0c6a6c508..b27cfe62d9 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/CMakeLists.txt +++ b/Sources/Plasma/PubUtilLib/plPipeline/CMakeLists.txt @@ -72,6 +72,7 @@ target_link_libraries(plPipeline pfCamera # plCaptureRender, plDynamicEnvMap, plDXPipeline pfGameGUIMgr # plCaptureRender freetype + $<$:plWinDpi> INTERFACE pnFactory ) From 1a51fa6dba07cc8b35fae3cf7e4cf56125955d87 Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 25 Jun 2023 13:27:05 -0700 Subject: [PATCH 25/27] Apply suggestions from code review Co-authored-by: Adam Johnson --- Sources/Plasma/Apps/plClient/CMakeLists.txt | 1 - Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Sources/Plasma/Apps/plClient/CMakeLists.txt b/Sources/Plasma/Apps/plClient/CMakeLists.txt index 6f451b079b..56a7e27994 100644 --- a/Sources/Plasma/Apps/plClient/CMakeLists.txt +++ b/Sources/Plasma/Apps/plClient/CMakeLists.txt @@ -146,7 +146,6 @@ target_link_libraries( plStatGather plStatusLog plUnifiedTime - $<$:plWinDpi> pfAnimation pfAudio pfCharacter diff --git a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp index a1d395bef6..6a4aa2b1a5 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp +++ b/Sources/Plasma/PubUtilLib/plPipeline/plTextFont.cpp @@ -205,10 +205,10 @@ uint16_t *plTextFont::IInitFontTexture() } int offset = int( fFontHeight - cBox.yMax ); - //blit the bitmap into place + // blit the bitmap into place for (int glyphY = bitmap.rows - 1; glyphY >= 0 ; glyphY--) { for (int glyphX = 0; glyphX < bitmap.width; glyphX++) { - //1 bit Bitmap as source + // 1 bit Bitmap as source int pitch = abs(slot->bitmap.pitch); unsigned char* row = &slot->bitmap.buffer[pitch * glyphY]; unsigned char cValue = row[glyphX >> 3]; @@ -221,8 +221,8 @@ uint16_t *plTextFont::IInitFontTexture() int destY = y + glyphY + offset; int destX = glyphX + x + face->glyph->bitmap_left; int destIndex = (destY * fTextureWidth) + destX; - hsAssert( destX < fTextureWidth, "Destination X cannot be out of bounds"); - hsAssert( destY < fTextureHeight, "Destination Y cannot be out of bounds"); + hsAssert(destX < fTextureWidth, "Destination X cannot be out of bounds"); + hsAssert(destY < fTextureHeight, "Destination Y cannot be out of bounds"); data[(destIndex )] = src; } From 4673e0f944b81ea5419fe96ca551636408e98bec Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 25 Jun 2023 13:43:44 -0700 Subject: [PATCH 26/27] Moving FreeType to end of list --- Sources/Plasma/PubUtilLib/plPipeline/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Plasma/PubUtilLib/plPipeline/CMakeLists.txt b/Sources/Plasma/PubUtilLib/plPipeline/CMakeLists.txt index b27cfe62d9..7ea869f8b4 100644 --- a/Sources/Plasma/PubUtilLib/plPipeline/CMakeLists.txt +++ b/Sources/Plasma/PubUtilLib/plPipeline/CMakeLists.txt @@ -71,8 +71,8 @@ target_link_libraries(plPipeline plSurface pfCamera # plCaptureRender, plDynamicEnvMap, plDXPipeline pfGameGUIMgr # plCaptureRender - freetype $<$:plWinDpi> + freetype INTERFACE pnFactory ) From 8c02a4bb70572ab5c4d9eb39510e9addf120e6ca Mon Sep 17 00:00:00 2001 From: Colin Cornaby Date: Sun, 25 Jun 2023 15:30:45 -0700 Subject: [PATCH 27/27] Making FreeType required --- cmake/Dependencies.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 44f50c1607..309ffe6183 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -42,7 +42,7 @@ set_package_properties( URL "https://www.freetype.org/" DESCRIPTION "Library for rendering fonts" PURPOSE "Used to convert vector fonts to raster assets" - TYPE OPTIONAL + TYPE REQUIRED ) set_package_properties( JPEG PROPERTIES