Skip to content

Commit ab2a1b9

Browse files
committed
Massively increase quality of splash image
1 parent e1119f5 commit ab2a1b9

File tree

5 files changed

+195
-36
lines changed

5 files changed

+195
-36
lines changed

Client/launch/Main.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*****************************************************************************/
1111

1212
#include "StdInc.h"
13+
#include <ShellScalingApi.h>
1314

1415
/*
1516
IMPORTANT
@@ -22,6 +23,43 @@
2223
(set flag.new_client_exe on the build server to generate new exe)
2324
*/
2425

26+
/**
27+
* @brief Applies the highest available form of DPI awareness for this process.
28+
*/
29+
void ApplyDpiAwareness()
30+
{
31+
// Minimum version: Windows 10, version 1607
32+
using SetProcessDpiAwarenessContext_t = BOOL(WINAPI*)(DPI_AWARENESS_CONTEXT value);
33+
34+
static SetProcessDpiAwarenessContext_t Win32SetProcessDpiAwarenessContext = ([] {
35+
HMODULE user32 = LoadLibrary("user32");
36+
return user32 ? reinterpret_cast<SetProcessDpiAwarenessContext_t>(static_cast<void*>(GetProcAddress(user32, "SetProcessDpiAwarenessContext"))) : nullptr;
37+
})();
38+
39+
if (Win32SetProcessDpiAwarenessContext)
40+
{
41+
Win32SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
42+
return;
43+
}
44+
45+
// Minimum version: Windows 8.1
46+
using SetProcessDpiAwareness_t = HRESULT(WINAPI*)(PROCESS_DPI_AWARENESS value);
47+
48+
static SetProcessDpiAwareness_t Win32SetProcessDpiAwareness = ([] {
49+
HMODULE shcore = LoadLibrary("shcore");
50+
return shcore ? reinterpret_cast<SetProcessDpiAwareness_t>(static_cast<void*>(GetProcAddress(shcore, "SetProcessDpiAwareness"))) : nullptr;
51+
})();
52+
53+
if (Win32SetProcessDpiAwareness)
54+
{
55+
Win32SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
56+
return;
57+
}
58+
59+
// Minimum version: Windows Vista
60+
SetProcessDPIAware();
61+
}
62+
2563
///////////////////////////////////////////////////////////////
2664
//
2765
// WinMain
@@ -37,6 +75,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
3775
return 1;
3876
}
3977

78+
ApplyDpiAwareness();
79+
4080
// Load the loader.dll and continue the load
4181
#ifdef MTA_DEBUG
4282
SString strLoaderDllFilename = "loader_d.dll";

Client/loader/Dialogs.cpp

Lines changed: 155 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,109 @@ static bool bCancelPressed = false;
1515
static bool bOkPressed = false;
1616
static bool bOtherPressed = false;
1717
static int iOtherCode = 0;
18-
static HWND hwndSplash = NULL;
18+
static WNDCLASS splashWindowClass{};
19+
static HWND splashWindow{};
1920
static HWND hwndProgressDialog = NULL;
2021
static unsigned long ulProgressStartTime = 0;
2122
static HWND hwndCrashedDialog = NULL;
2223
static HWND hwndGraphicsDllDialog = NULL;
2324
static HWND hwndOptimusDialog = NULL;
2425
static HWND hwndNoAvDialog = NULL;
2526

27+
/**
28+
* @brief Automatically destroys a window on scope-exit.
29+
*/
30+
struct WindowScope
31+
{
32+
WindowScope(HWND handle_) noexcept : handle(handle_) {}
33+
34+
~WindowScope() noexcept { DestroyWindow(handle); }
35+
36+
[[nodiscard]] HWND Release() noexcept { return std::exchange(handle, nullptr); }
37+
38+
HWND handle{};
39+
};
40+
41+
/**
42+
* @brief Provides a compatible memory-only device context for a bitmap handle and
43+
* automatically destroys the device context and bitmap on scope-exit.
44+
*/
45+
struct BitmapScope
46+
{
47+
BitmapScope(HDC deviceContext_, HBITMAP bitmap_) noexcept : bitmap(bitmap_)
48+
{
49+
if (bitmap != nullptr)
50+
{
51+
if (deviceContext = CreateCompatibleDC(deviceContext_))
52+
{
53+
previousObject = SelectObject(deviceContext, bitmap_);
54+
}
55+
}
56+
}
57+
58+
~BitmapScope() noexcept
59+
{
60+
if (previousObject && previousObject != HGDI_ERROR)
61+
SelectObject(deviceContext, previousObject);
62+
63+
if (deviceContext)
64+
DeleteDC(deviceContext);
65+
66+
if (bitmap)
67+
DeleteObject(bitmap);
68+
}
69+
70+
HDC deviceContext{};
71+
HGDIOBJ previousObject{};
72+
HBITMAP bitmap{};
73+
};
74+
75+
/**
76+
* @brief Returns the dots per inch (dpi) value for the specified window.
77+
*/
78+
UINT GetWindowDpi(HWND window)
79+
{
80+
// Minimum version: Windows 10, version 1607
81+
using GetDpiForWindow_t = UINT(WINAPI*)(HWND);
82+
83+
static GetDpiForWindow_t Win32GetDpiForWindow = ([] {
84+
HMODULE user32 = LoadLibrary("user32");
85+
return user32 ? reinterpret_cast<GetDpiForWindow_t>(static_cast<void*>(GetProcAddress(user32, "GetDpiForWindow"))) : nullptr;
86+
})();
87+
88+
if (Win32GetDpiForWindow)
89+
return Win32GetDpiForWindow(window);
90+
91+
HDC screenDC = GetDC(NULL);
92+
auto x = static_cast<UINT>(GetDeviceCaps(screenDC, LOGPIXELSX));
93+
ReleaseDC(NULL, screenDC);
94+
return x;
95+
}
96+
97+
/**
98+
* @brief Returns the width and height of the primary monitor.
99+
*/
100+
SIZE GetPrimaryMonitorSize()
101+
{
102+
POINT zero{};
103+
HMONITOR primaryMonitor = MonitorFromPoint(zero, MONITOR_DEFAULTTOPRIMARY);
104+
105+
MONITORINFO monitorInfo{};
106+
monitorInfo.cbSize = sizeof(monitorInfo);
107+
GetMonitorInfo(primaryMonitor, &monitorInfo);
108+
109+
const RECT& work = monitorInfo.rcWork;
110+
return {work.right - work.left, work.bottom - work.top};
111+
}
112+
113+
/**
114+
* @brief Scales the value from the default screen dpi (96) to the provided dpi.
115+
*/
116+
LONG ScaleToDpi(LONG value, UINT dpi)
117+
{
118+
return static_cast<LONG>(ceil(value * static_cast<float>(dpi) / USER_DEFAULT_SCREEN_DPI));
119+
}
120+
26121
///////////////////////////////////////////////////////////////////////////
27122
//
28123
// Dialog strings
@@ -188,23 +283,63 @@ int CALLBACK DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
188283
void ShowSplash(HINSTANCE hInstance)
189284
{
190285
#ifndef MTA_DEBUG
191-
if (!hwndSplash)
286+
if (splashWindowClass.hInstance != hInstance)
287+
{
288+
splashWindowClass.lpfnWndProc = DefWindowProc;
289+
splashWindowClass.hInstance = hInstance;
290+
splashWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
291+
splashWindowClass.lpszClassName = TEXT("SplashWindow");
292+
RegisterClass(&splashWindowClass);
293+
}
294+
295+
if (splashWindow)
296+
{
297+
ShowWindow(splashWindow, SW_SHOW);
298+
}
299+
else
192300
{
193-
hwndSplash = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), 0, DialogProc);
301+
WindowScope window(CreateWindowEx(WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW, splashWindowClass.lpszClassName, NULL, WS_POPUP | WS_VISIBLE,
302+
0, 0, 0, 0, NULL, NULL, hInstance, NULL));
303+
304+
if (!window.handle)
305+
return;
306+
307+
UINT dpi = GetWindowDpi(window.handle);
308+
SIZE monitorSize = GetPrimaryMonitorSize();
309+
310+
POINT origin{};
311+
SIZE windowSize{ScaleToDpi(500, dpi), ScaleToDpi(245, dpi)};
312+
origin.x = (monitorSize.cx - windowSize.cx) / 2;
313+
origin.y = (monitorSize.cy - windowSize.cy) / 2;
314+
315+
HDC windowHDC = GetWindowDC(window.handle);
316+
{
317+
BitmapScope unscaled(windowHDC, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_BITMAP1)));
194318

195-
HWND hBitmap = GetDlgItem(hwndSplash, IDC_SPLASHBITMAP);
196-
RECT splashRect;
197-
GetWindowRect(hBitmap, &splashRect);
198-
int iScreenWidth = GetSystemMetrics(SM_CXSCREEN);
199-
int iScreenHeight = GetSystemMetrics(SM_CYSCREEN);
200-
int iWindowWidth = splashRect.right - splashRect.left;
201-
int iWindowHeight = splashRect.bottom - splashRect.top;
319+
if (!unscaled.bitmap)
320+
return;
321+
322+
BitmapScope scaled(windowHDC, CreateCompatibleBitmap(windowHDC, windowSize.cx, windowSize.cy));
323+
324+
if (!scaled.bitmap)
325+
return;
326+
327+
BITMAP bitmap{};
328+
GetObject(unscaled.bitmap, sizeof(bitmap), &bitmap);
329+
330+
// Draw the unscaled bitmap to the window-scaled bitmap.
331+
SetStretchBltMode(scaled.deviceContext, HALFTONE);
332+
StretchBlt(scaled.deviceContext, 0, 0, windowSize.cx, windowSize.cy, unscaled.deviceContext, 0, 0, bitmap.bmWidth, bitmap.bmHeight, SRCCOPY);
333+
334+
// Update the splash window and draw the scaled bitmap.
335+
POINT zero{};
336+
BLENDFUNCTION blend{AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
337+
UpdateLayeredWindow(window.handle, windowHDC, &origin, &windowSize, scaled.deviceContext, &zero, RGB(0, 0, 0), &blend, 0);
338+
}
339+
ReleaseDC(window.handle, windowHDC);
202340

203-
// Adjust and center the window (to be DPI-aware)
204-
SetWindowPos(hwndSplash, NULL, (iScreenWidth - iWindowWidth) / 2, (iScreenHeight - iWindowHeight) / 2, iWindowWidth, iWindowHeight, 0);
341+
splashWindow = window.Release();
205342
}
206-
SetForegroundWindow(hwndSplash);
207-
SetWindowPos(hwndSplash, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
208343

209344
// Drain messages to allow for repaint in case picture bits were lost during previous operations
210345
MSG msg;
@@ -224,10 +359,10 @@ void ShowSplash(HINSTANCE hInstance)
224359
//
225360
void HideSplash()
226361
{
227-
if (hwndSplash)
362+
if (splashWindow)
228363
{
229-
DestroyWindow(hwndSplash);
230-
hwndSplash = NULL;
364+
DestroyWindow(splashWindow);
365+
splashWindow = {};
231366
}
232367
}
233368

@@ -236,9 +371,9 @@ void HideSplash()
236371
//
237372
void SuspendSplash()
238373
{
239-
if (hwndSplash)
374+
if (splashWindow)
240375
{
241-
ShowWindow(hwndSplash, SW_HIDE);
376+
ShowWindow(splashWindow, SW_HIDE);
242377
}
243378
}
244379

@@ -247,7 +382,7 @@ void SuspendSplash()
247382
//
248383
void ResumeSplash()
249384
{
250-
if (hwndSplash)
385+
if (splashWindow)
251386
{
252387
HideSplash();
253388
ShowSplash(g_hInstance);

Client/loader/loader.rc

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,6 @@ END
4646

4747
#endif // APSTUDIO_INVOKED
4848

49-
50-
/////////////////////////////////////////////////////////////////////////////
51-
//
52-
// Dialog
53-
//
54-
55-
IDD_DIALOG1 DIALOGEX 0, 0, 333, 151
56-
STYLE DS_SETFONT | DS_CENTER | WS_POPUP | WS_VISIBLE
57-
EXSTYLE WS_EX_TOOLWINDOW
58-
FONT 8, "MS Sans Serif", 0, 0, 0x0
59-
BEGIN
60-
CONTROL IDB_BITMAP1,IDC_SPLASHBITMAP,"Static",SS_BITMAP,0,0,333,151
61-
END
62-
63-
6449
/////////////////////////////////////////////////////////////////////////////
6550
//
6651
// Bitmap

Client/loader/resource.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Microsoft Visual C++ generated include file.
33
// Used by loader.rc
44
//
5-
#define IDD_DIALOG1 103
65
#define IDB_BITMAP1 105
76
#define IDD_PROGRESS_DIALOG 111
87
#define IDD_CRASHED_DIALOG 114

Client/loader/resource/splash.bmp

121 KB
Binary file not shown.

0 commit comments

Comments
 (0)