【翻译】DPI and Device-Independent Pixels

来源:互联网 发布:图像对比识别软件 编辑:程序博客网 时间:2024/05/17 09:04

因为不理解为什么GetDeviceCaps和GetSystemMetrics返回的分辨率和真实的分辨不一样,所以翻译了这个链接
https://msdn.microsoft.com/en-us/library/windows/desktop/ff684173(v=vs.85).aspx

DPI and Device-Independent Pixels

DPI and Device-Independent Pixels
To program effectively with Windows graphics, you must understand two related concepts:

  • Dots per inch (DPI)
  • Device-independent pixel (DIPs).

为了有效的编写windows 图形程序,我们必须理解两个相关感念。

Let’s start with DPI. This will require a short detour into typography. In typography, the size of type is measured in units called points. One point equals 1/72 of an inch.

1 pt = 1/72 inch

让我们从DPI开始,这需要简单介绍一下活版印刷。在活版印刷中,排(码)字的大小我们用点(point)这个单位来测量。一个点等于1/72 英寸

Note This is the desktop publishing definition of point. Historically, the exact measure of a point has varied.

这是桌面出版点的定义。历史上点的精确定义可能会有变动

For example, a 12-point font is designed to fit within a 1/6” (12/72) line of text. Obviously, this does not mean that every character in the font is exactly 1/6” tall. In fact, some characters might be taller than 1/6”. For example, in many fonts the character Å is taller than the nominal height of the font. To display correctly, the font needs some additional space between the text. This space is called the leading.

举一个例子,12点的字体被设计为一个1/6英寸(12/72)英寸的文本。显然,不能认为字体就是精确的 1/6英寸高。事实上可能比1/6英寸高。例如在很多字体中Å 高于普通字体的。为了正确显示,字体需要更多的空间。这个空间我们称为leading

The following illustration shows a 72-point font. The solid lines show a 1” tall bounding box around the text. The dashed line is called the baseline. Most of the characters in a font rest on the baseline. The height of the font includes the portion above the baseline (the ascent) and the portion below the baseline (the descent). In the font shown here, the ascent is 56 points and the descent is 16 points.

这里写图片描述
An illustration that shows a 72-point font.

下面显示了一个72点的字体。(图中紫色)实线表示一个1英寸高围绕字体的边框。虚线称为基线(baseline.)。字体中的大部分字符是在站在(rest on)基线上的。字体的高度包括基线上面部分(称为 ascent)和下面部分(descent).图片中显示的ascent是56点。descent是16点

When it comes to a computer display, however, measuring text size is problematic, because pixels are not all the same size. The size of a pixel depends on two factors: the display resolution, and the physical size of the monitor. Therefore, physical inches are not a useful measure, because there is no fixed relation between physical inches and pixels. Instead, fonts are measured in logical units. A 72-point font is defined to be one logical inch tall. Logical inches are then converted to pixels. For many years, Windows used the following conversion: One logical inch equals 96 pixels. Using this scaling factor, a 72-point font is rendered as 96 pixels tall. A 12-point font is 16 pixels tall.

12 points = 12/72 logical inch = 1/6 logical inch = 96/6 pixels = 16 pixels

当谈到计算机中的显示时,测量字体的大小是很头疼的。因为像素不总是一样大的,它的大小取决于两个因素:分辨率(display resolution)和 物理显示器的尺寸。因此现实生活中英尺单位是不能测量计算机中的字体大小的,因为它和像素是没有联系的。所以我们使用逻辑(logical)单位来测量字体。72点被定义成一逻辑英寸高,然后将逻辑英寸转换成像素。很多年过来了,Windows使用如下的转换:一逻辑英尺等于96像素。使用这个缩放比例。72点的字体就是96像素的,12点的字体就是16像素的。

This scaling factor is described as 96 dots per inch (DPI). The term dots derives from printing, where physical dots of ink are put onto paper. For computer displays, it would be more accurate to say 96 pixels per logical inch, but the term DPI has stuck.

这个缩放比例被被描述为每英寸96点(DPI)。术语 dots 来源与打印时墨水滴在纸上的点。对于计算机的显示而言 这个缩放应该称为每逻辑英寸96像素。而不是以前的DPI解释。

Because actual pixel sizes vary, text that is readable on one monitor might be too small on another monitor. Also, people have different preferences—some people prefer larger text. For this reason, Windows enables the user to change the DPI setting. For example, if the user sets the display to 144 DPI, a 72-point font is 144 pixels tall. The standard DPI settings are 100% (96 DPI), 125% (120 DPI), and 150% (144 DPI). The user can also apply a custom setting. Starting in Windows 7, DPI is a per-user setting.

因为(显示器上的)像素大小是不确定的,所以字体可能在一台显示器上适合阅读,在另一台显示器上就有可能太小了。也有可能有人喜欢小字体有人喜欢大字体。因为这些原因windows 允许用户更改DPI设置。例如可以显示144DPI,这样的话72点的字就会是144像素高的。标准的DPI设置可以是是100%(96DPI),125% (120 DPI)和150% (144 DPI)。用户可以选择一个自己喜欢的设置。从 Windows 7,开始,每个用户都可以拥有自己的DPI。

下图是win10的DPI设置
这里写图片描述

DWM Scaling
If a program does not account for DPI, the following defects might be apparent at high-DPI settings:

  • Clipped UI elements.
  • Incorrect layout.
  • Pixilated bitmaps and icons.
    -Incorrect mouse coordinates, which can affect hit testing, drag and drop, and so forth.

如果一个程序指名DPI的话。下列问题在高DPI( high-DPI )设置下可能会很明显

  • 被撕裂的UI元素
  • 不正确的布局
  • 位图和图标明显的颗粒感
  • 不正确的鼠标坐标,这个会印象点击测试(window的一种消息),推拽等等

To ensure that older programs work at high-DPI settings, the DWM implements a useful fallback. If a program is not marked as being DPI aware, the DWM will scale the entire UI to match the DPI setting. For example, at 144 DPI, the UI is scaled by 150%, including text, graphics, controls, and window sizes. If the program creates a 500 × 500 window, the window actually appears as 750 × 750 pixels, and the contents of the window are scaled accordingly.
This behavior means that older programs “just work” at high-DPI settings. However, scaling also results in a somewhat blurry appearance, because the scaling is applied after the window is drawn.

为了确保老程序工作在高DPI的环境下,DWM 实现了一个有用的回退。如果程序没有标记为DPI aware,那么DWM 将缩放全部的UI去适应高DPI的环境。例如在144 DPI,UI会被缩放150%,包括字体、图形、控件、窗口大小。如果程序创建了一个 500 × 500的窗口,这个窗口的被显示为750 × 750像素,于是窗口的内容也会被缩放。

这个特性只是为了让程序工作在高DPI的环境下。然后,缩放的结果可能印象外观。因为它是在绘制窗口后进行缩放的

DPI-Aware Applications
To avoid DWM scaling, a program can mark itself as DPI-aware. This tells the DWM not to perform any automatic DPI scaling. All new applications should be designed to be DPI-aware, because DPI awareness improves the appearance of the UI at higher DPI settings.
A program declares itself DPI-aware through its application manifest. A manifest is a simply an XML file that describes a DLL or application. The manifest is typically embedded in the executable file, although it can be provided as a separate file. A manifest contains information such as DLL dependencies, the requested privilege level, and what version of Windows the program was designed for.
To declare that your program is DPI-aware, include the following information in the manifest.

为了避免DWM的缩放。程序可以标记自己为DPI-aware。这样就告诉DWM 不要去做任何DPI缩放工作。所有的新程序应该被设计为DPI-aware。因为DPI-aware可以改善在高DPI环境下UI的外观。
一个程序声明自己为DPI-aware 可以通过应用的manifest,一个manifest是一个简单的XML文件。它是介绍dll和应用的一种文件。这个manifest一般情况下是嵌入可执行文件的。虽然你也可以分离它们。manifest的内容包含dll的依赖信息,请求权限等级,程序为什么Windows 版本设计的信息.你可以像下面这样编写manifest以声明DPI-aware

<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>

The listing shown here is only a partial manifest, but the Visual Studio linker generates the rest of the manifest for you automatically. To include a partial manifest in your project, perform the following steps in Visual Studio.
1. On the Project menu, click Property.
2. In the left pane, expand Configuration Properties, expand Manifest Tool, and then click Input and Output.
3. In the Additional Manifest Files text box, type the name of the manifest file, and then click OK.

上面xml只是部分manifest,但是Visual Studio 连接器会自动生成剩下的manifest。为了完成完成这个功能
这里写图片描述
这里写图片描述

By marking your program as DPI-aware, you are telling the DWM not to scale your application window. Now if you create a 500 × 500 window, the window will occupy 500 × 500 pixels, regardless of the user’s DPI setting.

通过标记DPI-aware ,你的程序会告诉DWM 不要缩放窗口。现在如果你后才能关键一个500 × 500 的窗口,他会不顾用户的DPI设置而显示 500 × 500 像素的窗口。

GDI and DPI
GDI drawing is measured in pixels. That means if your program is marked as DPI-aware, and you ask GDI to draw a 200 × 100 rectangle, the resulting rectangle will be 200 pixels wide and 100 pixels tall on the screen. However, GDI font sizes are scaled to the current DPI setting. In other words, if you create a 72-point font, the size of the font will be 96 pixels at 96 DPI, but 144 pixels at 144 DPI. Here is a 72 point font rendered at 144 DPI using GDI.

GDI 函数的绘制是以像素为单位的。着意味着如果你的程序被标记为DPI-aware 。那么你叫GDI画200 × 100 的矩形,其结果就是矩形 在屏幕中是200 像素宽100像素高的。然而GDI还是会根据当前的DPI设置来缩放字体的。换句话说。如果你创建一个72点的字体。那么他的大小在96DPI上是96像素,在1444DPI上是144像素。下图是一个72点的字体在144DPI下使用GDI函数绘制的结果

这里写图片描述
A diagram that shows DPI font scaling in GDI.
If your application is DPI-aware and you use GDI for drawing, scale all of your drawing coordinates to match the DPI.
如果你的的应用是是DPI-aware 且你你用GDI函数来绘制。你的所有绘制的坐都会被缩放以匹配DPI

Direct2D and DPI
Direct2D automatically performs scaling to match the DPI setting. In Direct2D, coordinates are measured in units called device-independent pixels (DIPs). A DIP is defined as 1/96th of a logical inch. In Direct2D, all drawing operations are specified in DIPs and then scaled to the current DPI setting.

Direct2D 会自动缩放以适应DPI。在Direct2D中坐标是通过device-independent pixels (DIPs)这个单位来测量的。一DPI 被定义为1/96逻辑英寸。在Direct2D中。所有的绘制操作都以DIPS为单位的,然后会被缩放到适应当前DPI的大小

For example, if the user’s DPI setting is 144 DPI, and you ask Direct2D to draw a 200 × 100 rectangle, the rectangle will be 300 × 150 physical pixels. In addition, DirectWrite measures font sizes in DIPs, rather than points. To create a 12-point font, specify 16 DIPs (12 points = 1/6 logical inch = 96/6 DIPs). When the text is drawn on the screen, Direct2D converts the DIPs to physical pixels. The benefit of this system is that the units of measurement are consistent for both text and drawing, regardless of the current DPI setting.
A word of caution: Mouse and window coordinates are still given in physical pixels, not DIPs. For example, if you process the WM_LBUTTONDOWN message, the mouse-down position is given in physical pixels. To draw a point at that position

举个例子,如果你使用144 DPI,然后你呼叫Direct2D去画一个200 × 100的矩形。这个矩形将会是300 × 150 物理像素的。此外DirectWrite 是以DPIs来测量字体的而不是点,为了生成一个12点的字体,需要指定16 DIPs (12 points = 1/6 logical inch = 96/6 DIPs)。当文本显示在屏幕上的时候,Direct2D会转换DIPs为实际的像素点。这样的好处测量单位对于文本和绘制是一样的,而不管使用当前的DPI是什么。

警告:鼠标和窗口坐标还是特定的物理像素,而不是DPIS。例如,如果你处理WM_LBUTTONDOWN消息鼠,你可以从鼠标下的地方得到实际的像素点位置,然后在上面画一个点。

Converting Physical Pixels to DIPs
The conversion from physical pixels to DIPs uses the following formula.
DIPs = pixels / (DPI/96.0)
To get the DPI setting, call the ID2D1Factory::GetDesktopDpi method. The DPI is returned as two floating-point values, one for the x-axis and one for the y-axis. In theory, these values can differ. Calculate a separate scaling factor for each axis.

上上卖弄个给出了DIPs和实际像素的转换公式。为了得到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;}

备用方法

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);}

Resizing the Render Target
if the size of the window changes, you must resize the render target to match. In most cases, you will also need to update the layout and repaint the window. The following code shows these steps.

如果窗口的大小改变,你必须重新调整渲染目标以适应一些东西。大多数情况下你也需要更新布局和重画窗口。例如下面的代码

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);    }}

The GetClientRect function gets the new size of the client area, in physical pixels (not DIPs). The ID2D1HwndRenderTarget::Resize method updates the size of the render target, also specified in pixels. The InvalidateRect function forces a repaint by adding the entire client area to the window’s update region. (See Painting the Window, in Module 1.)
As the window grows or shrinks, you will typically need to recalculate the position of the objects that you draw. For example, in the circle program, the radius and center point must be update

GetClientRect 函数得到新窗口的区域。它是实际的像素坐标(不是DPIs)。ID2D1HwndRenderTarget::Resize方法更新渲染目标。也是是像素为单位。InvalidateRect函数是通过添加整个客户端区域到窗口更新区域来完来强制重绘

当窗口的的放大和缩小。你一般需要去重新计算你要绘制对象的位置。例如画元的程序,半径和圆心必须要更新

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);    }}

The ID2D1RenderTarget::GetSize method returns the size of the render target in DIPs (not pixels), which is the appropriate unit for calculating layout. There is a closely related method, ID2D1RenderTarget::GetPixelSize, that returns the size in physical pixels. For an HWND render target, this value matches the size returned by GetClientRect. But remember that drawing is performed in DIPs, not pixels.

ID2D1RenderTarget::GetSize 函数以DIPs为单位返回渲染目标的大小。这个单位很好用。你可以使用ID2D1RenderTarget::GetPixelSize,来返回物理像素单位(pixels)。对于一个HWND 的渲染目标。这个值和GetClientRect返回的是一样的。但是记住要使用DIPs单位来绘制,不要使用pixels单位

总结:
GDI函数返回的像素单位,但是这个像素单位被DPI放缩小了。所以如果你在计算机中设置DPI的话,那么你在使用GDI 的时候得到的坐标信息都是进过放缩例如,我的显示器是1920*1080的。但是我们的DPI设置了125%所以我在使用GetSystemMetrics和GetDeviceCaps时得到的是1560*864分辨率。在得到坐标是也是在这个范围之类的。

但是direct2D中使用的是DIP,这个值如果是96 的话就会在显示器上显示一英寸的大小(2.54cm)

原创粉丝点击