要使用Windows图形进行有效编程,您必须了解两个相关的概念:

  • 每英寸点数(DPI)
  • 设备无关像素(DIP)。

我们从DPI开始。这将需要短暂的绕行排版。在印刷术中,类型的大小以称为的单位来测量。一点等于1/72英寸。
1点=1/72英寸1点=1/72英寸1点=1/72英寸
注意 这是桌面出版的定义。历史上,一个点的具体措施已经变化。
例如,12点字体被设计成适合1/6”(12/72)行的文本。显然,这并不意味着字体中的每个字符恰好是1/6”高。实际上,某些字符可能会比1/6”更高,例如,在许多字体中,字符Å比字体标称高度要高,为了正确显示,字体之间需要一些额外的空间,这个空间是称为领先
下图显示了一个72磅字体。实线在文本周围显示1”高的边界框,虚线称为基线,字体中的大部分字符位于基线上,字体的高度包括基线上方的部分(上升)和基线以下的部分(下降)。在这里显示的字体中,上升56点,下降16点。

显示72点字体的插图。

但是,当涉及到计算机显示器时,测量文字大小是有问题的,因为像素的大小不尽相同。像素的大小取决于两个因素:显示器分辨率和显示器的物理尺寸。因此,物理英寸不是一个有用的度量,因为物理英寸和像素之间没有固定的关系。相反,字体是以逻辑单位来衡量的。72点字体被定义为一个逻辑英寸高。逻辑英寸然后被转换为像素。多年来,Windows使用以下转换:一个逻辑英寸等于96像素。使用这个缩放因子,72点字体被渲染成96像素高。12点字体是16像素高。
12点=12/72逻辑英寸=1/6逻辑英寸=96/6像素=16像素12点=12/72逻辑英寸=1/6逻辑英寸=96/6像素=16像素12点= 12/72逻辑英寸= 1/6逻辑英寸= 96/6像素= 16像素
这个比例因子被描述为每英寸96点(DPI)。术语圆点来源于印刷,其中油墨的物理点放在纸上。对于计算机显示器来说,每逻辑英寸说96像素更准确,但术语DPI已经停滞。
由于实际像素大小不同,在一台监视器上可读的文本在其他监视器上可能太小。另外,人们有不同的喜好——有些人喜欢较大的文字。因此,Windows使用户可以更改DPI设置。例如,如果用户将显示设置为144 DPI,则72点字体的高度为144像素。标准DPI设置为100%(96 DPI),125%(120 DPI)和150%(144 DPI)。用户也可以应用自定义设置。从Windows 7开始,DPI是按用户设置的。
DWM缩放
如果一个程序没有考虑DPI,在高DPI设置下可能会出现以下缺陷:

  • 剪裁的UI元素。
  • 不正确的布局。
  • 像素化的位图和图标。
  • 不正确的鼠标坐标,这可能影响命中测试,拖放等等。

为了确保较旧的程序在高DPI设置下工作,DWM实现了有用的回退。如果某个程序未标记为DPI,则DWM将缩放整个UI以匹配DPI设置。例如,在144 DPI时,UI缩放了150%,包括文本,图形,控件和窗口大小。如果程序创建一个500×500窗口,窗口实际上显示为750×750像素,窗口内容将相应地缩放。
这种行为意味着较旧的程序在高DPI设置下“正常工作”。但是,缩放也会导致模糊的外观,因为缩放是在窗口绘制之后应用的。
DPI感知应用程序
为了避免DWM缩放,程序可以将自己标记为DPI感知。这告诉DWM不要执行任何自动DPI缩放。所有新的应用程序都应设计为DPI感知型,因为DPI感知能够在更高的DPI设置下改善UI的外观。
一个程序通过其应用程序清单声明自己的DPI。清单是一个简单的描述DLL或应用程序的XML文件。清单通常嵌入在可执行文件中,尽管它可以作为单独的文件提供。 清单包含诸如DLL依赖关系,请求的特权级别以及该程序设计的Windows版本等信息。
要声明您的程序是支持DPI的,请在清单中包含以下信息。

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" ><asmv3:application><asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"><dpiAware>true</dpiAware></asmv3:windowsSettings></asmv3:application>
</assembly>

此处显示的列表仅仅是部分清单,但Visual Studio链接程序会自动为您生成其他清单。要在项目中包含部分清单,请在Visual Studio中执行以下步骤。

  1. 项目菜单上,单击属性
  2. 在左侧窗格中,展开配置属性,展开清单工具,然后单击输入和输出
  3. 其他清单文件文本框中,键入清单文件的名称,然后单击确定

通过将您的程序标记为支持DPI,您告诉DWM不要缩放您的应用程序窗口。现在,如果您创建500×500的窗口,无论用户的DPI设置如何,窗口都将占用500×500像素。
GDI和DPI
GDI绘图以像素为单位进行测量。这意味着如果您的程序被标记为支持DPI,并且您要求GDI绘制一个200×100的矩形,则得到的矩形将在屏幕上宽200像素,高100像素。但是,GDI字体大小被缩放到当前的DPI设置。换句话说,如果创建72磅字体,则96 DPI时的字体大小为96像素,144 DPI时为144像素。这是使用GDI以144 DPI呈现的72点字体。

在GDI中显示DPI字体缩放的图表。

如果您的应用程序支持DPI并使用GDI进行绘制,则缩放所有绘图坐标以匹配DPI。
Direct2D和DPI
Direct2D自动执行缩放以匹配DPI设置。在Direct2D中,坐标是以独立于设备的像素(DIP)为单位测量的。DIP被定义为逻辑英寸的1/96。在Direct2D中,所有绘图操作都在DIP中指定,然后按比例缩放到当前的DPI设置。

DPI设置 DIP尺寸
96 1 像素
120 1.25 像素
144 1.5 像素

例如,如果用户的DPI设置为144 DPI,并且要求Direct2D绘制200×100的矩形,则该矩形将为300×150个物理像素。此外,DirectWrite测量DIP中的字体大小,而不是点数。要创建12点字体,请指定16个DIP(12点=1/6逻辑英寸=96/6 DIP)。在屏幕上绘制文本时,Direct2D将DIP转换为物理像素。该系统的好处是无论当前的DPI设置如何,测量单位对于文本和绘图都是一致的。
小心一点:鼠标和窗口坐标仍然以物理像素给出,而不是DIP。例如,如果处理WM_LBUTTONDOWN消息鼠标向下的位置以物理像素给出。要在该位置绘制一个点,必须将像素坐标转换为DIP。
将物理像素转换为DIP
从物理像素到DIP的转换使用以下公式。
DIPs=像素/(DPI/96.0)DIPs=像素/(DPI/96.0)DIPs=像素/(DPI/96.0)
要获得DPI设置,请调用ID2D1Factory::GetDesktopDpi方法。DPI返回为两个浮点值,一个用于x轴,另一个用于y轴。从理论上讲,这些价值可能不同。为每个轴计算一个单独的比例因子。

float g_DPIScaleX=1.0f;
float g_DPIScaleY=1.0f;
void InitializeDPIScale(ID2D1Factory *pFactory){FLOAT dpiX,dpiY;pFactory->GetDesktopDpi(&dpiX,&dpiY);g_DPIScaleX=dpiX/96.0f;g_DPIScaleY=dpiY/96.0f;
}
template<typename T>
float PixelsToDipsX(T x){return static_cast<float>(x)/g_DPIScaleX;
}
template<typename T>
float PixelsToDipsY(T y){return static_cast<float>(y)/g_DPIScaleY;
}

如果您不使用Direct2D,则可以使用另一种方法获取DPI设置:

void InitializeDPIScale(HWND hwnd){HDC hdc=GetDC(hwnd);g_DPIScaleX=GetDeviceCaps(hdc,LOGPIXELSX)/96.0f;g_DPIScaleY=GetDeviceCaps(hdc,LOGPIXELSY)/96.0f;ReleaseDC(hwnd,hdc);
}

调整渲染目标的大小
如果窗口大小发生变化,则必须调整渲染目标的大小以匹配。在大多数情况下,您还需要更新布局并重新绘制窗口。以下代码显示了这些步骤。

void MainWindow::Resize(){if(pRenderTarget!=NULL){RECT rc;GetClientRect(m_hwnd,&rc);D2D1_SIZE_U size=D2D1::SizeU(rc.right,rc.bottom);pRenderTarget->Resize(size);CalculateLayout();InvalidateRect(m_hwnd,NULL,FALSE);}
}

GetClientRect函数以物理像素(不是DIP)获取客户区的新大小。 ID2D1HwndRenderTarget::Resize方法更新渲染目标的大小,也以像素指定。InvalidateRect函数通过将整个客户区添加到窗口的更新区域来强制重绘。(请参阅第1单元的”绘制窗口“)
随着窗口的增长或缩小,您通常需要重新计算绘制的对象的位置。例如,在圆形程序中,必须更新半径和中心点:

void MainWindow::CalculateLayout(){if(pRenderTarget!=NULL){D2D1_SIZE_F size=pRenderTarget->GetSize();const float x=size.width/2;const float y=size.height/2;const float radius=min(x,y);ellipse=D2D1::Ellipse(D2D1::Point2F(x,y),radius,radius);}
}

ID2D1RenderTarget::GetSize方法返回DIP(非像素)渲染目标的大小,这是计算布局的合适单位。有一个密切相关的方法,ID2D1RenderTarget::GetPixelSize,返回物理像素的大小。对于HWND呈现目标,此值与GetClientRect返回的大小相匹配。但请记住,绘图是在DIP中执行的,而不是像素。
下一个
在Direct2D中使用颜色


原文链接:DPI and Device-Independent Pixels

Windows桌面应用程序(1-2-4-7th) DPI和设备无关的像素相关推荐

  1. Windows桌面应用程序(1-2-4-2nd) 桌面窗口管理器

    在Windows Vista之前,Windows程序会直接画到屏幕上.换句话说,程序会直接写入显卡所显示的内存缓冲区.如果窗口没有正确重绘,这种方法会导致视觉失真.例如,如果用户在另一个窗口上拖动了一 ...

  2. 对Windows桌面应用程序进行UI自动化测试

    所谓UI自动化测试,就是模拟一个用户,对应用程序的UI进行操作,以完成特定场景的功能性集成测试. 要对Windows桌面应用程序进行UI自动化测试,目前可选的技术主要是两种:VS自带的CodedUI ...

  3. 使用C++代码创建一个Windows桌面应用程序

    下面六星教育整理使用C++代码创建一个Windows桌面应用程序,供大家学习参考. WinMain函数 Windows应用程序的唯一程序入口. 函数原型 int WINAPI WinMain { HI ...

  4. python 桌面程序自动化测试_对Windows桌面应用程序进行UI自动化测试

    题记:本文简述如何利用appium对Windows桌面应用程序进行UI自动化测试. 所谓UI自动化测试,就是模拟一个用户,对应用程序的UI进行操作,以完成特定场景的功能性集成测试. 要对Windows ...

  5. C#基于.Net框架的学生信息管理系统(Windows桌面应用程序)

    简介 自己用C#编写了一个学生信息管理系统的Windows桌面应用程序,第一次实践,某些地方可能还不够完善,但其中有些函数.方法等的使用可以参考. 工具 我用的集成开发环境是Visual Studio ...

  6. windows桌面应用程序_如何将Windows桌面应用程序转换为通用Windows应用程序

    windows桌面应用程序 With Windows 10's Anniversary Update, Microsoft is making it possible for developers t ...

  7. C#【自动化测试】对Windows桌面应用程序进行UI自动化测试

    文章目录 1.文章一: 题记 UI自动化测试 参考 2.文章二: 3.重要资源[实例]: 1)已验证可用[非常好的例子,便于理解] 关键代码 2)未验证 1.文章一: 题记 本文简述如何利用appiu ...

  8. windows桌面应用程序UI自动化工具(转载)

    原文地址:windows桌面应用程序UI自动化工具 - anobscureretreat - 博客园 最近在研究针对windows桌面应用程序的自动化工具,查找了网上相关资料,UI自动化工具很多,但是 ...

  9. 如何创建一个 Windows 桌面应用程序?

    工具:Visual Studio 2017 Community 在大学期间,不过学习 C/C++ 还是 Java,每次写的程序都是控制台黑框框,有时候想设计一个可视化的应用程序却不知从何下手,大学里面 ...

最新文章

  1. oracle+trunkc,Oracle常用备份与恢复操作
  2. 批量ping 查看主机能否ping通,能否ssh上.md,
  3. 舔狗日记网PHP源码
  4. ios更改UITabBarController背景以及选中背景图片的方法
  5. Android笔记 意图传值demo
  6. C#之Xml去掉前面的空格
  7. 3 笔经小结-智力篇
  8. Spring(七)Spring中的四种增强和顾问
  9. 圈圈USB学习笔记5--关于HID协议
  10. windows server 2016安装网卡驱动【引用】
  11. creator tween复杂用法
  12. SAP 成套销售按项目销售
  13. DHT11温湿度传感器(基于树莓派)
  14. mp3 编辑 linux,Linux_Ubuntu 32/64位安装音乐标签编辑器Kid3的方法,  Kid3能够修改mp3中ID3的tag标 - phpStudy...
  15. amd、cmd、esmodule、commonjs区别
  16. bpm,bpm千万里我找的就是你!!
  17. java 代码实现各数据的正则校验
  18. 【MFC】学习笔记:常用控件之组合框(Combo Box)
  19. photoshop cs5 基础教程 直接选择工具
  20. OPENGL读取OBJ模型(包围盒、法向等计算)附加源码与资源下载页面

热门文章

  1. 关于iOS10需要支持https才能通关苹果App Store审核问题
  2. 功率器件参数测试仪系统@半导体器件电学特性测试
  3. c语言中calc用法,calc是什么意思_calc怎么读_calc翻译_用法_发音_词组_同反义词_[计] 计算-新东方在线英语词典...
  4. 安卓10自带录屏_手把手教你安卓手机怎么录屏,收下这份屏幕录制指南
  5. 【毕业设计_课程设计】基于Android的人脸门禁系统
  6. 邮件正文及其附件的发送的C++实现
  7. Ubuntu 14.04 升级到 Ubuntu 16.04
  8. 14种鼻型图解_专家将人类鼻子分为14种,看看你属于哪种?
  9. linux运行qsql,QSqlDatabase:未在Ubuntu 15.04 64位上加​​载QMYSQL驱动程序
  10. 杰理之扫描设备【篇】