Win32桌面程序为什么要适配高DPI

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dybb8999/article/details/53641820
新买了台电脑,屏幕是1080P的,感觉屏幕是清晰了,不像768P的屏幕有明显的颗粒感,但是一些软件却感觉很模糊,查阅资料发现是因为软件没有针对高DPI进行优化,决定研究一下。

首先,DPI全名是Dots Per Inch 就是每英寸中像素点的个数。Windows中默认为96,这是很久很久以前Windows规定的。

那为什么DPI变了,就变模糊呢?是因为,原来DPI是100%的时候,每英寸96个像素,当你换了一个显示器,显示器大小没变,但是从1366*768变成了1920*1080,如果系统还是按照每英寸96像素显示窗口,那个窗口会变的非常小,所以就需要把系统的DPI从100%提升至125%,才能让要显示的东西变大一些让我们看起来不会太累。

但是变大为什么就会变模糊呢?(这个例子我把DPI从125%提升到175%)
这里写图片描述

Windows有一个DWM(Desktop Window Manager) ,它会负责处理窗口的显示。当你的DPI较高,而你的程序又不会自己处理的时候,它就会来帮助程序完成DPI适配。

首先假设你的DPI是200%,我们程序运行的时候,系统会告诉程序DPI是100%,然后程序就会继续按照100%的方式来绘制窗口,这个坐标是缩小以后的(我们调用GetSystemMetrics(SM_CXSCREEN)会发现获得的结果是经过处理的),当我们的程序绘制完成后,DWM再把整个窗口进行200%的放大(就像把一张小图片放大),然后绘制到屏幕上,看起来就像程序支持高DPI一样,但是结果就是会变模糊。

当我把DPI设置为175%,我们窗口获取到的数据:
这里写图片描述
能看到我的屏幕X轴方向原来是1920像素,但是我们获得的数据为1097(1920/1.75)

然后我告诉系统,我们的窗口会自动处理DPI缩放,不需要帮助:
这里写图片描述
可以看出,获得的数据变回了正常的1920,获取的DPI变为了168(1.75*96)

虽然说使用GetWindowRect()也能获得经过缩放后的坐标,但是我没成功- -

Windows在Windows8.1和WindowsServer 2012 R2开始加入了一个API:SetProcessDpiAwareness 它负责告诉系统是否需要DWM的帮助。

HRESULT WINAPI SetProcessDpiAwareness(
  _In_ PROCESS_DPI_AWARENESS value
);
typedef enum _PROCESS_DPI_AWARENESS { 
  PROCESS_DPI_UNAWARE            = 0,
  PROCESS_SYSTEM_DPI_AWARE       = 1,
  PROCESS_PER_MONITOR_DPI_AWARE  = 2
} PROCESS_DPI_AWARENESS;

PROCESS_DPI_UNAWARE 告诉系统改程序自己无法处理不同DPI,获取的数据都是被处理过的。
PROCESS_SYSTEM_DPI_AWARE 告诉系统该程序会在启动的显示器上自己支持不同的DPI,但是如果程序被拖动到了其他DPI不一样的显示器上时,需要DWM的帮助。
PROCESS_PER_MONITOR_DPI_AWARE 告诉系统任何时候该程序都能适应不同的DPI,不需要任何帮助。

如果你的程序支持 PROCESS_PER_MONITOR_DPI_AWARE,当你的窗口移动到DPI不同的显示器上时,会收到 WM_DPICHANGED 消息。

主要相关API:
SetProcessDpiAwareness :设置当前进程的DPI感知等级。
GetDpiForMonitor :查询显示的DPI。
MonitorFromPoint :获取指定点的显示器的句柄。
MonitorFromRect :获取与指定矩形相交面积最大的显示器句柄。

MonitorFromPoint和MonitorFromRect可以得到GetDpiForMonitor需要的句柄

要让我们的程序能适配高DPI,只需要告诉系统我们能自己处理,然后根据分辨率和DPI计算不同的数据应该就行了,文章的第一张图我就是这样做到的。

差不多就这些了╮(╯▽╰)╭

参考文档:
关于Windows高DPI的一些简单总结
Tutorial: Writing High-DPI Win32 Applications

展开阅读全文

没有更多推荐了,返回首页