您的位置:首页 > 其它

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

2015-06-08 17:01 337 查看
文章来源:http://www.cnblogs.com/weiym/p/3555068.html

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

如果说以前XP时代我们还有理由不关注高DPI,那么在移动设备时代和大显示器的高分辨率时代,我们就没有理由不关注高DPI了,比如SurfacePro的分辨率是1920x1080,这种情况下如果系统我们不设置高DPI,基本上就没法触摸和操作了,所以现在普通程序对高DPI的支持已经成为趋势了。

什么DPI?全称是dotsperinch(DPI),也就是每英寸的点数,在显示器上就是每英寸的像素个数,Window上一般默认是96dpi作为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(DesktopWindowManager),该模式我们称之为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:

typedefenum_Process_DPI_Awareness{
Process_DPI_Unaware=0,
Process_System_DPI_Aware=1,
Process_Per_Monitor_DPI_Aware=2
}Process_DPI_Awareness;



下面我们依次讨论这3种方式:

第一种Unaware,该种方式是告诉系统,我的程序不支持DPIaware,请通过DWM虚拟化帮我们实现。该方式和上面Win7/Win8对高DPI的支持的实现基本一样,主要区别是它通过GetWindowRect取到的坐标都是经过DWM缩放后的,无论对方窗口是不是支持DWM虚拟化。

第二种方式是SystemDPIaware,该方式下告诉系统,我的程序会在启动的显示器上自己支持DPIaware,所以不需要对我进行DWM虚拟化。但是当我的程序被拖动到其他DPI不一样的显示器时,请对我们先进行systemDWM虚拟化缩放。

第三种方式是PerMonitorDPIaware,该方式是告诉系统,请永远不要对我进行DWM虚拟化,我会自己针对不同的Monitor的DPi缩放比率进行缩放。

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

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

PS,我在我机器上测试发现,桌面程序基本上只有微软自己的程序能做到在高DPI下完美支持,其他大部分程序(即使如Chrome)也是通过DWM虚拟化实现的高DPI支持。当然现在WPF和WindowstoreApp基本上都是内置支持高DPI的。

统计下,你们的程序支持高DPI吗?

参考资料:WritingDPI-AwareDesktopandWin32Applications
HighDPISettingsinWindows
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: