关于Windows高DPI的一些简单总结

咱们知道,关于高DPI的支持, Windows XP时代就开始有了, 那时关于高DPI的支持比较简单, 可是从Vista/Win7 到如今Win8 /Win8.1, Windows关于高DPI的支持已经发生了很大的变化, 下面咱们依次简单介绍下。

若是说之前XP时代咱们还有理由不关注高DPI,  那么在移动设备时代和大显示器的高分辨率时代, 咱们就没有理由不关注高DPI了, 好比Surface Pro的分辨率是1920x1080, 这种状况下若是系统咱们不设置高DPI, 基本上就无法触摸和操做了,因此如今普通程序对高DPI的支持已经成为趋势了。 

什么DPI? 全称是dots per inch (DPI), 也就是每英寸的点数,在显示器上就是每英寸的像素个数,Window上通常默认是96 dpi 做为100% 的缩放比率, 可是要注意的是该值未必是真正的显示器物理值, 只是Windows里咱们的一个参考标准。

下面咱们思考为何DPI设置高了以后, 咱们看到的字体会变大? 由于系统字体是是以固定大小(宋体10号字,物理尺寸为(10/72)英寸)设计的, 当咱们DPI设置高了以后 ,说明该字体要占有更多的像素, 在屏幕分辨率不变的前提下, 看起来也就大了。因此若是咱们设置高DPI,一般也意味着咱们的显示器是高分辨率, 里面的字体看起来过小了, 咱们须要提升DPI来把内容放大。

那么咱们的程序如何才能支持高DPI? 对于高DPI的支持, 不一样操做系统有不一样的方案。一般来讲若是咱们程序支持高DPI, 意味着咱们要对绘画的内容进行相应的放大, 好比字体,图片和控件等。固然, 若是咱们用的是系统字体(好比GetStockObject(DEFAULT_GUI_FONT)), 那么这种状况下咱们不用操心, 由于系统会对该字体在高DPI时进行相应的放大; 若是咱们是用CreateFont本身建立的字体, 那就要咱们本身对该字体进行放大了。

下面咱们看XP是如何对高DPI进行支持的? 

XP对高DPI的支持比较差劲, 大部分状况下就是字体的放大, 固然咱们程序也能够经过GetDeviceCaps(hDC, LOGPIXELSX)获取DPI后本身对绘画的内容进行缩放。

下面咱们看Vista/Win7/Win8是如何对高DPI进行支持的?

咱们知道Vista/Win7咱们能够禁止DWM(Desktop Window Manager), 该模式咱们称之为Basic模式, 这种模式下的高DPI效果和XP同样。

对于DWM没有禁掉的状况, Vista/Win7/Win8 对高DPI的支持又分为2种状况, 具体看下图: 

一种XP风格的高DPi支持, 这种方式咱们上面讨论过了;
还有一种是经过 DWM 虚拟化支持的 高DPI方式, 下面咱们讨论下该方式: 

该种方式的高DPI支持是经过DWM的缩放实现的, 具体过程是这样的, 好比咱们当前系统的DPI是200%, 咱们程序运行时,系统会告诉你当前DPI仍然是96(100%), 因此咱们程序会仍然按照100%的方式进行绘画, 可是可是系统给咱们的坐标是根据DPI缩小事后的(也就是咱们对窗口调用GetWindowRect或是经过GetSystemMetrics(SM_CXSCREEN)获得的大小会比实际大小减半) , 当咱们画完以后, DWM再对整个窗口进行200% 放大后画到屏幕上, 这样看起来咱们的程序就自动支持高DPI了。

 这种方式看起来很美妙, 可是它也有缺点, 主要是通过缩放后的内容看起来会变模糊, 好比文字会有明显的锯齿。

既然DWM虚拟化用户效果有时不是那么好, 那么咱们不少时候可能会本身支持高DPI, 如何让咱们的程序禁用该效果?
事实上咱们能够对每一个进程对DWM虚拟化的支持进行设置和查询, 系统给咱们提供了2个APi:   SetProcessDPIAware   IsProcessDPIAware , 经过调用 SetProcessDPIAware , 咱们告诉系统不要对咱们的程序进行DWM虚拟化。

这里还有特殊状况也提一下: 咱们在高DPI下经过窗口句柄取到的坐标信息是和目标程序是否支持DWM虚拟化相关联的, 咱们对其余支持DWM虚拟化的程序窗口调用GetWindowRect, 取到的坐标也是通过DWM缩放后的坐标; 对禁用DWM虚拟化程序的窗口调用GetWindowRect, 取到的坐标则是没有通过缩放的原始坐标。

 最后咱们再讨论下Win8.1 对高DPI的支持, WIn8.1对高DPi以3种方式支持   Process_DPI_Awareness : 
 
typedef enum _Process_DPI_Awareness { 
  Process_DPI_Unaware            = 0,
  Process_System_DPI_Aware       = 1,
  Process_Per_Monitor_DPI_Aware  = 2
} Process_DPI_Awareness;
 
下面咱们依次讨论这3种方式: 
 
第一种Unaware, 该种方式是告诉系统, 个人程序不支持DPI aware, 请经过DWM虚拟化帮咱们实现。 该方式和上面Win7/Win8对高DPI的支持的实现基本同样,主要区别是它经过GetWindowRect取到的坐标都是通过DWM缩放后的, 不管对方窗口是否是支持DWM虚拟化。

第二种方式是System DPI aware, 该方式下告诉系统, 个人程序会在启动的显示器上本身支持DPI aware, 因此不须要对我进行DWM 虚拟化。 可是当个人程序被拖动到其余DPI不同的显示器时, 请对咱们先进行system DWM虚拟化缩放。

第三种方式是Per Monitor DPI aware, 该方式是告诉系统, 请永远不要对我进行DWM虚拟化,我会本身针对不一样的Monitor的DPi缩放比率进行缩放。

再介绍下相关API:
SetProcessDpiAwareness :设置当前进程对高DPi的支持方式
GetProcessDpiAwareness :查询某个进程对高DPI的支持方式
GetDpiForMonitor : 获取某个Monitor的DPI
WM_DPICHANGED :当某个程序窗口被拖到另一个DPI的Monitor时收到

最后,简单总结下, 从上面咱们能够看到微软在不一样操做系统上对高DPI支持的改进线路,不少方面也体现了他们对老程序兼容性上的考虑, DWM虚拟化虽然很简单, 却丢失了用户体验。  

PS, 我在我机器上测试发现,桌面程序基本上只有微软本身的程序能作到在高DPI下完美支持, 其余大部分程序(即便如Chrome)也是经过DWM虚拟化实现的高DPI支持。固然如今WPF和Window store App基本上都是内置支持高DPI的。

统计下, 大家的程序支持高DPI吗? 

                   High DPI Settings in Windows
相关文章
相关标签/搜索