In recent years, High-DPI displays have become common. Having a lot more pixels to display your application on seems like a simple recipe for crisper graphics but, counterintuitively, the opposite is often the case. Many applications were written without taking the DPI of displays into account and are not able to natively render their contents on High-DPI displays. The visual elements (e.g., text, images, icons) of these applications will appear blurry.
GDI was for many years the de facto win32 2D API and is behind many of these older pre-high-DPI applications. GDI is used by applications to render graphics and formatted text on displays and printers. Beginning with the Creators Update for Windows, we have added a new feature, called GDI Scaling, that allows GDI to natively scale visual content on behalf of DPI unaware applications. Visual elements, especially text, can appear much sharper for these applications when this feature is enabled. This will give Desktop users and IT Pros a way to improve existing applications that are difficult or unpleasant to use because of blurry text and graphics.
In this article, we will describe the challenge presented by high DPI displays, show you several ways to configure GDI Scaling, discuss how GDI Scaling works and give instructions to developers on updating their apps to run with GDI Scaling. While consumers and IT professionals may find this discussion informative, the primary target for this article are developers wanting to understand how best to make legacy GDI apps look right on high DPI monitors.
Higher DPI, yet Lower Quality
There is a jarring difference between the physical and logical scale of computer displays. As an example, this is the layout of my displays and high DPI Laptop on my desk.
The displays I use for my desktop work use larger physical screens and are oriented to facilitate the tasks I regularly do. Of the three displays, my laptop has the highest quality display but it’s physically smaller than the other displays. This is in stark contrast to how Windows and the software on my PC see my displays.
You can see here that Windows thinks my laptop display is enormous. And it is. It’s configured to run at a recommended 200% scaling so that my apps don’t appear tiny displaying on all those pixels. This is a common scenario for mixing different DPI displays using a “docked high DPI Laptop.”
The reverse of this scenario occurs as well. Users could be docking laptops that look good at 100% Scaling with a high DPI display on their desk or, using Windows display settings, establish one of their normal DPI displays on their desk as their main display while using the high DPI display on their laptop as a secondary display.
The changes made to Windows to handle evolving DPI have been gradual and were based on the availability of hardware and the needs of software developers starting in Windows XP. Applications built prior to high DPI support from Windows assume that the density of pixels will be fixed at a specific scale. This scale is referred to as “100%” and is around 96 Dots per Inch (DPI). Applications written at 100% DPI simply don’t have enough graphical detail to look good on high DPI displays. Windows assures that each application takes up the right amount of space on the screen regardless of the DPI scale. This ensures that text and controls are roughly the right size regardless of the displays DPI. Because of this, apps that cannot handle different DPI scales will look “pixilated” as Windows stretches the application to take up the right amount of space on the screen.
It would be great if everything could just be updated, but many popular applications were written before there was graphics API support for high DPI screens, and they do not have any native support for handling changes to scale. GDI Scaling is an effort to address this problem by transparently scaling graphics for the app.
How will a GDI Scaled applications look?
GDI Scaling overwrites an application’s awareness of DPI. This can be good, bad or difficult to notice based on the applications original DPI capabilities. To understand the impact of using GDI Scaling, we need to take some time and discuss the different ways that applications can be built to handle display scaling.
Applications use one of the following modes for handling DPI:
- DPI Unaware – These are apps that are always rendered assuming 100% Scaling (96 DPI). There is no effort made to compensate for the scale by the app itself.
- System DPI Aware – These are apps that know the DPI of the main display at the time the user logged in to their computer (called the “System DPI”). These apps scale well on the main display but look blurry on a secondary display at a different DPI.
- Per Monitor DPI Aware – These are apps that are rendering content at different DPIs and can change the DPI scaling on the fly as the applications are moved between monitors with different DPIs. If well made, these apps look good regardless of the monitor DPI.
- Mixed Mode DPI Aware – These are apps where there may be multiple top level windows, each with its own DPI awareness, that can be shown on different displays. This is just a mix of the 3 DPI awareness listed above, each window will be impacted by GDI scaling as a single window app of the same DPI Awareness. We do not cover this anywhere else in the blog.
You can find out more about DPI Awareness on MSDN.
With GDI Scaling enabled, Text and Graphics are drawn using the GDI API at a much larger scale to take advantage of the High DPI screen. Not everything is improved though: some of the graphics and icons are bitmaps that can’t be rendered by GDI with more detail. Also, some text may be rendered by another Graphics API. That text will not be improved by GDI Scaling.
System DPI Awareness
Apps that are aware of the system DPI will render at the correct scale for a computer’s main display, though these apps will still have issues if they are displayed on a second display that is a different DPI. This is a very common scenario given the increasing availability of high-DPI laptops (like Microsoft’s Surface Book), and their use in business environments docked with larger displays or projectors with lower DPI.
When apps are shown on a display with a higher DPI than the system DPI, they will appear blurry like the DPI unaware apps:
With GDI Scaling enabled, that same app will have graphics and text rendered to the correct scale regardless of which display it is displayed on.
Additionally, there is an interesting limitation with System DPI aware apps. System DPI is set when the user logs into the computer. If a user should change their main display (to one with a different DPI) or change the DPI of their main display, the display will be updated but the system DPI value won’t. These apps will appear blurry or off until the user logs out and back on. GDI Scaling, on the other hand, re-renders the app with each display change and will not have this issue.
In the image above there is a ‘)’ that is “clipped.” This and other kerning errors can occur with GDI Scaling. GDI Scaling scales up the objects (like text boxes) and fonts displayed in the app by the same ratio. For instance, if GDI Scaling is doubling the DPI of an app and the app was originally rendered with a 10pt font, it will display the scaled app with a 20pt font. In some cases, text doesn’t scale linearly and can be a few pixels off. The actual characters maybe too large to fit in an object that is proportionally scaled.
Per Monitor DPI Awareness
Apps that have been developed to be per monitor DPI aware should have graphics correctly scaled to all your computer’s displays. These should not only look good but they are likely to look better than GDI Scaled apps because static content like bitmaps will be loaded and rendered at the right DPI. This is a close-up detail of the Character Map app (which is per monitor DPI aware) with GDI Scaling turned off. It looks good.
This is a close-up view of the same app run with GDI Scaling turned on:
There are some innocuous differences such as the scale of the key map letters (both images look good though a little different.) You can also see that the check in the checkbox and the carat in the pull down boxes look much worse. That is because these are both static images that are a part of the control and must be scaled.
You can also see an issue with the kerning on a few of the labels. In this case the larger sized font version is not precisely an integer multiple of the smaller sized font. For example, a 20pt font is not exactly twice the size of a 10pt font.
As this example shows, users and IT pros should try to run the app both with and without GDI Scaling to determine which looks best before making a permanent change.
Configuration of GDI Scaling
Each application can be independently configured to run with GDI Scaling. This completely replaces the default scaling of the application. You should test to make sure the app looks improved before deploying these changes.
Configuring a local app to run with enhanced system scaling
The simplest way to enable GDI Scaling for an application is use the application compatibility tab UI.
To do this, you can:
- Right click on the application icon and click “Properties.”
- Go over to the Compatibility tab, enable “Override High DPI scaling behavior” and set the Scaling Behavior to “System (Enhanced).”
- Restart the app and move it between your different displays to see if there is an improvement.
Note: all the examples in this document are created using system applications (like Task Manager and Character Map). System applications do not have a compatibility tab so this setting cannot be adjusted.
Using Group Policy and MDM for configuring apps to run with GDI Scaling
For IT pros with access to policy management tools–such as Microsoft’s System Center Configuration Manager (SCCM) or Intune– applications can be configured to run with GDI Scaling with the “Turn on GdiDPIScaling” and “Turn off GdiDPIScaling” policies.
Enabling GDI Scaling
IT pros can enable GDI Scaling for a list of applications using the “Turn on GdiDPIScaling” policy. Here is a view of the SCCM GP definition of this policy:
You can find documentation for the MDM policy on MSDN at Display/TurnOnGdiDPIScalingForApps.
Disabling GDI Scaling
Because users and app developers can configure apps to run with GDI Scaling on their own (and this may not be the desired configuration) there is also a similar “Turn off GdiDPIScaling” policy. Setting this policy on users or devices will ensure an application will not run with GDI Scaling enabled, regardless of the user or application settings. In all cases, “Turn off GdiDPIScaling” has a higher priority than “Turn on GdiDPIScaling.”
Here is a view of the SCCM GP definition of this policy:
You can find documentation for the MDM policy on MSDN at Display/TurnOffGdiDPIScalingForApps.
Using Application Compatibility Toolkit to configure apps for GDI Scaling
We have also added support for using the Application Compatibility Toolkit (ACT), which manages your apps’ compatibility settings. The behavior of ACT configuration policies is like the GP solution above:
GdiDPIScaling (same as “Turn on GdiDPIScaling”)
GdiDPIScalingForceDisable (Same as “Turn off GdiDPIScaling”)
You can find out more about using ACT to manage your devices here: https://technet.microsoft.com/en-us/library/dd837648(WS.10).aspx.
GDI Scaling for Developers
GDI apps that are not DPI aware can look bad for users and Enterprises that use high-DPI displays and docked laptops. Beginning with the Creators Update for Windows, GDI apps that were never implemented to be DPI aware can be updated or configured to be per monitor DPI aware, dramatically improving their usability and appearance. Here, we will provide a deep dive to developers wanting to update their apps to take advantage of GDI Scaling and provide some background on how it works.
How does GDI Scaling work?
Historically, GDI does not support scenarios where the apps are shown at anything other than 100% scaling (96 DPI). App developers have had the ability to get System or Display DPI information and update their apps to handle scaling on their own.
When an application is configured to be GDI scaled, a new DPI Scaling step is performed using built-in GDI scaling. Scaling will be done transparently to the application and will result in vector graphics and text content being rendered at higher resolution.
In many cases the DPI Scaling is performed in two steps:
- Rendering of vector graphics and text at an integral scaling factor (x2, x3, x4, etc.) using GDI.
- Scaling down of all graphics during Desktop Window Manager (DWM) composition.
When the application is running on a high-DPI display that happens to be multiple of 100% scaling (96 DPI), then no DWM scaling is used. Vector graphics and text will look crisp. Bitmaps and other static resources (icons, toolbars) will be stretched to match the display DPI.
When the application is running on a display that is not a multiple of 100% (96 DPI) then vector graphics and text will be rendered to the first integral multiple of 100% above the display scale factor. For instance, if the application is on a display that is at 225% scale, then vector graphics and bitmaps will be rendered at 300%. DWM will then scale down the rendered content to 225% scale. In this case, the application will have noticeable fuzziness because of the scaling down, but it will look better than just scaling up the 100% rendered content.
Limitations with our GDI Scaling Solution
There is some functionality where we know GDI Scaling is incomplete or non-functional: Device Independent Bitmap (DIB) and Compatible bitmaps.
Device independent Bitmap (DIB)
DIBs are bitmaps that are updated by the application and must be stored at 96 DPI. For the Creators update for Windows, DIBs are not supported by GDI Scaling. When the DIB is copied to the screen, it will be stretched based on the monitor’s DPI.
Compatible bitmaps
Compatible bitmaps are supported by GDI Scaling with a known limitation. When used in multi-monitor configuration, off-screen content will not be updated when the application window is moved between displays with different DPIs. The off-screen content will have the DPI that was most recently used and will stretch to meet the new display DPI. The image’s resolution will not be updated until the window is resized or the window is moved between displays.
Kerning and clipping with imprecisely scaled fonts
As we mentioned above, many fonts are not precisely scaled. For example, 20pt font is not exactly twice the height and width of 10pt fonts. The GDI Scaling is done using the size of the 100% scaled applications. Any difference in scale will impact how the characters look next to each other (too close or too far apart) or how they fit into the objects within the app.
GDI/D3D Interop
Some applications are rendered using both GDI and D3D/D2D (DirectX) APIs. GDI Scaling does not render the D3D/D2D content correctly. Portions of the screen rendered by GDI will look correct while the bits generated by D3D will be drawn at the original unscaled location and size.
Here is an example of an app rendered with D2D/D3D while GDI Scaling is enabled:
Updating your GDI app to use GDI Scaling
If you have a GDI-based app then you can update its manifest to run with GDI Scaling.
In the Creators Update for Windows release, we have added a “gdiScaling” element that, when set to “true,” enables GDI Scaling.
Here’s how the manifest for MMC enables GDI Scaling:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!-- Copyright (c) Microsoft Corporation --> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" manifestVersion="1.0"> <assemblyIdentity processorArchitecture="x86" version="5.1.0.0" name="Microsoft.Windows.MMC" type="win32" /> <description>Microsoft Management Console</description> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level="highestAvailable" uiAccess="false" /> </requestedPrivileges> </security> </trustInfo> <asmv3:application> <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings"> <gdiScaling>true</gdiScaling> </asmv3:windowsSettings> </asmv3:application> </assembly>
Taking advantage of GDI Scaling
Use these recommendations if you want to update a GDI app to ensure the best scaling for high-DPI displays.
- Use compatible bitmaps instead of DIBs. If your application doesn’t need access bitmap bits directly then compatible bitmaps will produce more scalable results.
- Do not cache off-screen bitmaps between WM_PAINT calls. Instead recreate those bitmaps each time the app is rendering. Although not optimal, it will ensure that those bitmaps will be properly scaled to match the current hWnd display DPI. Vector graphics and text will be rendered at the proper DPI by the GDI transparently to the application.
Code Sample:
LRESULT WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { ... case WM_PAINT: { PAINTSTRUCT ps; // Get a paint DC for current window. // Paint DC contains the right scaling to match // the monitor DPI where the window is located. HDC hdc = BeginPaint(hWnd, &ps); RECT rect; GetClientRect(hDlg, &rect); UINT cx = (rect.right - rect.left); UITN cy = (rect.bottom - rect.top); // Create a compatible bitmap using paint DC. // Compatible bitmap will be properly scaled in size internally and // transparently to the app to match current monitor DPI where // the window is located. HBITMAP memBitmap = CreateCompatibleBitmap(hdc, cx, cy); // Create a compatible DC, even without a bitmap selected, // compatible DC will inherit the paint DC GDI scaling // matching the window monitor DPI. HDC memDC = CreateCompatibleDC(hdc); // Selecting GDI scaled compatible bitmap in the // GDI scaled compatible DC. HBTIMAP oldBitmap = (HBITMAP)SelectObject(memDC, memBitmap); // Setting some properties in the compatible GDI scaled DC. SetTextColor(memDC, GetSysColor(COLOR_INFOTEXT)); SetBkMode(memDC, TRANSPARENT); SelectObject(memDC, g_hFont); // Drawing content on the compatible GDI scaled DC. // If the monitor DPI was 150% or 200%, text internally will // be draw at next integral scaling value, in current example // 200%. DrawText(memDC, ctx.balloonText, -1, &rect, DT_NOCLIP | DT_LEFT | DT_NOPREFIX | DT_WORDBREAK); // Copying the content back from compatible DC to paint DC. // Since both compatible DC and paint DC are GDI scaled, // content is copied without any stretching thus preserving // the quality of the rendering. BitBlt(hdc, 0, 0, cx, cy, memDC, 0, 0); // Cleanup. SelectObject(memDC, oldBitmap); DeleteObject(memBitmap); DeleteDC(memDC); // At this time the content is presented to the screen. // DWM (Desktop Window Manager) will scale down if required the // content to actual monitor DPI. // If the monitor DPI is already an integral one, for example 200%, // there would be no DWM down scaling. // If the monitor DPI is 150%, DWM will scale down rendered content // from 200% to 150%. // While not a perfect solution, it's better to scale-down content // instead of scaling-up since a lot of the details will be preserved // during scale-down. // The end result is that with GDI Scaling enabled, the content will // look less blurry on screen and in case of monitors with DPI setting // set to an integral value (200%, 300%) the vector based and text // content will be rendered natively at the monitor DPI looking crisp // on screen. EndPaint(hWnd, &ps); } break; ... } }
To sum up
High DPI displays are a hit but computer users are being blocked from getting the value out of all that extra pixels for older apps. It turns that changing the scale of the display is a pretty big breaking change for older applications. To help, the GDI team at Microsoft has come up with a feature that lets some of these apps compensate for this change with minimum changes–GDI Scaling.
Whether you are configuring apps for your own computer or you are making changes on behalf of your organization, we have provided you with tools to enable and disable this new feature.
For App developers, you will be able to update GDI apps that were never designed for modern high-DPI displays to look better by knowing GDI Scaling’s limitations and following our tips on updating your apps.
The post Improving the high-DPI experience in GDI based Desktop Apps appeared first on Building Apps for Windows.