我们有使用 Qt 4.8 的桌面应用程序。我们正在尝试支持不同的 DPI 屏幕,例如 Mac Retina、Surface Pro 4 设备。对于 Mac,我们可以通过单个函数调用获得设备像素比:
CGFloat devicePixelRatio = [[NSScreen mainScreen] backingScaleFactor];
WinAPI 中是否有任何实用功能可用于获取设备像素比?
谢谢。
最后我发现解决方案使用下面的代码片段来获取比例因子,
FLOAT dpiX, dpiY;
HDC screen = GetDC(0);
dpiX = static_cast<FLOAT>(GetDeviceCaps(screen, LOGPIXELSX));
dpiY = static_cast<FLOAT>(GetDeviceCaps(screen, LOGPIXELSY));
ReleaseDC(0, screen);
FLOAT scaleFactor = dpiX / 96.0f; // this is same as devicePixelRatio
现在需要让您的应用了解 DPI。为此,在Project Settings > Manifest Tool > Input and Output属性页中将Enable DPI Awareness flag 设置为Yes 。
现在在一个简单的问题中隐藏了一个复杂的问题 :-) 我不能真正回答这个问题,而只是提供一些小提示:
首先,Windows 没有 OSX 已知的设备像素与逻辑像素范例,因此您关于“设备像素比率”的问题可能会引起非 OSX 开发人员的愤怒。
对于 Qt,从 4.x 版本到 5.x 版本有一些实质性的变化。事实上,您可能真的想考虑“升级”到 Qt 5。
http://doc.qt.io/qt-5/highdpi.html
引用此链接:
“Qt 5.4 为 Windows 和 Unix (XCB) 的平台插件引入了类似于 OS X 的按设备像素比进行缩放的实验性支持。”
另一方面,Qt 4 仅提供“可扩展应用程序”的那些技巧:
http://doc.qt.io/qt-4.8/scalability.html
相关的SO问题:
这是我计算DPI的方法。请用实际的错误检查替换断言,因为其中一些情况有时确实会发生。
#include <QString>
#include <Windows.h>
#include <SetupApi.h>
#include <cfgmgr32.h>
#include <assert.h>
#include <vector>
#include <stdint.h>
const GUID GUID_CLASS_MONITOR = { 0x4d36e96e, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18 };
// Assumes hEDIDRegKey is valid
bool GetMonitorSizeFromEDID(const HKEY hEDIDRegKey, short& WidthMm, short& HeightMm)
{
DWORD dwType, AcutalValueNameLength = 128;
TCHAR valueName[128];
BYTE EDIDdata[1024];
DWORD edidsize = sizeof(EDIDdata);
for (LONG i = 0, retValue = ERROR_SUCCESS; retValue != ERROR_NO_MORE_ITEMS; ++i)
{
retValue = RegEnumValue(hEDIDRegKey, i, &valueName[0],
&AcutalValueNameLength, NULL, &dwType,
EDIDdata, // buffer
&edidsize); // buffer size
if (retValue != ERROR_SUCCESS || QString(valueName) != "EDID")
continue;
WidthMm = ((EDIDdata[68] & 0xF0) << 4) + EDIDdata[66];
HeightMm = ((EDIDdata[68] & 0x0F) << 8) + EDIDdata[67];
return true; // valid EDID found
}
return false; // EDID not found
}
bool GetSizeForDevID(const QString& TargetDevID, short& WidthMm, short& HeightMm)
{
HDEVINFO devInfo = SetupDiGetClassDevsExA(
&GUID_CLASS_MONITOR, //class GUID
NULL, //enumerator
NULL, //HWND
DIGCF_PRESENT | DIGCF_PROFILE, // Flags //DIGCF_ALLCLASSES|
NULL, // device info, create a new one.
NULL, // machine name, local machine
NULL);// reserved
if (NULL == devInfo)
return false;
bool success = false;
for (ULONG i = 0; ERROR_NO_MORE_ITEMS != GetLastError(); ++i)
{
SP_DEVINFO_DATA devInfoData;
memset(&devInfoData, 0, sizeof(devInfoData));
devInfoData.cbSize = sizeof(devInfoData);
if (SetupDiEnumDeviceInfo(devInfo, i, &devInfoData))
{
CHAR Instance[MAX_DEVICE_ID_LEN];
SetupDiGetDeviceInstanceIdA(devInfo, &devInfoData, Instance, MAX_DEVICE_ID_LEN, NULL);
if (!QString(Instance).contains(TargetDevID))
continue;
HKEY hEDIDRegKey = SetupDiOpenDevRegKey(devInfo, &devInfoData,
DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
if (!hEDIDRegKey || (hEDIDRegKey == INVALID_HANDLE_VALUE))
continue;
success = GetMonitorSizeFromEDID(hEDIDRegKey, WidthMm, HeightMm);
RegCloseKey(hEDIDRegKey);
if (success)
break;
}
}
SetupDiDestroyDeviceInfoList(devInfo);
return success;
}
static bool DisplayDeviceFromHMonitor(HMONITOR hMonitor, DISPLAY_DEVICE& ddMonOut)
{
MONITORINFOEX mi;
mi.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfoA(hMonitor, &mi);
DISPLAY_DEVICE dd;
dd.cb = sizeof(dd);
for (DWORD devIdx = 0; EnumDisplayDevicesA(nullptr, devIdx, &dd, 0); ++devIdx)
{
if (QString(dd.DeviceName) != QString(mi.szDevice))
continue;
DISPLAY_DEVICE ddMon;
memset(&ddMon, 0, sizeof(ddMon));
ddMon.cb = sizeof(ddMon);
if (EnumDisplayDevicesA(dd.DeviceName, 0, &ddMon, 0))
{
ddMonOut = ddMon;
return true;
}
memset(&dd, 0, sizeof(dd));
dd.cb = sizeof(dd);
}
return false;
}
BOOL CALLBACK MonitorEnumProc(
_In_ HMONITOR hMonitor,
_In_ HDC /*hdcMonitor*/,
_In_ LPRECT /*lprcMonitor*/,
_In_ LPARAM context
)
{
std::vector<HMONITOR> * monitors = (std::vector<HMONITOR>*)context;
assert(monitors);
monitors->push_back(hMonitor);
return TRUE;
}
uint32_t CSystemMetrics::screenDpi(void* window, const CRect& rect)
{
// Identify the HMONITOR of interest via the callback MyMonitorEnumProc
HDC dc = GetWindowDC((HWND)window);
assert(dc);
RECT windowRect;
windowRect.top = rect.top;
windowRect.left = rect.left;
windowRect.right = rect.right;
windowRect.bottom = rect.bottom;
std::vector<HMONITOR> monitors;
EnumDisplayMonitors(dc, rect.size().area() > 0 ? (&windowRect) : nullptr, MonitorEnumProc, (LPARAM)&monitors);
ReleaseDC((HWND)window, dc);
assert(!monitors.empty());
DISPLAY_DEVICE ddMon;
assert(DisplayDeviceFromHMonitor(monitors.front(), ddMon));
MONITORINFO monitorInfo;
monitorInfo.cbSize = sizeof(MONITORINFO);
assert(GetMonitorInfoA(monitors.front(), &monitorInfo));
const auto deviceIdSections = QString(ddMon.DeviceID).split("\\");
assert(deviceIdSections.size() > 1);
short widthMm, heightMm;
assert(GetSizeForDevID(deviceIdSections[1], widthMm, heightMm));
const float hDPI = (monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left) * 25.4f / widthMm;
const float vDPI = (monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top) * 25.4f / heightMm;
const uint32_t dpi = uint32_t(((float)hDPI + (float)vDPI / 2.0f) + 0.5f);
assert(dpi < 700 && widthMm > 100 && heightMm > 100);
return dpi;
}