高DPI顯示器越來越普及,軟件自然也要適應這個變化,最近實習的時候也遇到了一個關於DPI縮放的問題。因為內部框架的一個控件有BUG,會導致內容的顯示出問題,后來實在沒辦法改成了用Windows Native API來自己定義字體,但是這一寫就出問題了,本來在內部開發機100%放縮下好好的,一跑到我自己的WIN10,在2K屏放上縮放125%就字體就顯示不正常了(字體變得過大)。
Window Vista以后的系統可以直接來個SetProcessDpiAwareness來控制程序的DPI問題,但是這個函數不是很好用,還是沒有辦法精確控制縮放,而且這個函數只有在Windows 8.1以上的系統才能用(SetProcessDPIAware也行,不過也必須是Windows Vista以上的系統),萬一我們的程序需要在XP上運行呢?這就需要用另外一個辦法了。
其實這個辦法也很簡單,就是用GetDeviceCaps來獲取當前環境句柄的DPI就可以了,然后和默認的DPI(96)做運算,獲得我們真正想要的DPI
case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd,&ps); auto curDPIX = GetDeviceCaps(hdc, LOGPIXELSX); auto curDPIY = GetDeviceCaps(hdc, LOGPIXELSY); std::wstring str(L"Hello World"); LOGFONT lf; HGDIOBJ hObject; ZeroMemory(&lf, sizeof(LOGFONT)); f.lfCharSet = GB2312_CHARSET; lf.lfWidth = MulDiv(20, 96, curDPIX); lf.lfHeight = MulDiv(55, 96, curDPIY); lf.lfPitchAndFamily = VARIABLE_PITCH; swprintf_s(lf.lfFaceName, _countof(lf.lfFaceName), L"微軟雅黑"); hObject = SelectObject(hdc, CreateFontIndirect(&lf)); TextOut(hdc, 21, 100, str.data(), str.length()); DeleteObject(SelectObject(hdc, hObject)); lf.lfWidth = 20; lf.lfHeight = 55; hObject = SelectObject(hdc, CreateFontIndirect(&lf)); TextOut(hdc, 21, 200, str.data(), str.length()); DeleteObject(SelectObject(hdc, hObject)); EndPaint(hwnd, &ps); return 0; }
這里演示的是在屏幕上輸出Hello Wolrd,現在假設我們的字體被放大了,但是假設我們其他控件沒有被放大,那么字在控件里面就會顯示不正常,這個時候就要縮小字的尺寸,要想和100%的時候類似,就需要MulDiv(尺寸, 96, curDPI);一下,字體被縮小了同理
這下我們可以看到,第二行是在120%放大下被放大的高度為55,寬度為22的字體,字體偏大,我們通過上面的方法,把字體縮小回正常的尺寸