Add initial draft of high DPI overview

Add a topic covering high DPI support.
This commit is contained in:
Vadim Zeitlin
2020-09-21 02:10:27 +02:00
parent 50fc4eb1f3
commit 44c2671e7b
2 changed files with 122 additions and 0 deletions

View File

@@ -99,5 +99,6 @@ topics related to building applications with wxWidgets.
@li @subpage overview_windowdeletion
@li @subpage overview_envvars
@li @subpage overview_customwidgets
@li @subpage overview_high_dpi
*/

View File

@@ -0,0 +1,121 @@
High DPI Support in wxWidgets {#overview_high_dpi}
=============================
[TOC]
[comment]: # (Not sure if the first 2 sections are really worth keeping)
Terms and Definitions
=====================
Many modern displays have much higher pixel density than used to be the norm,
resulting in much higher values of DPI (dots, i.e. pixels, per inch) than the
traditionally used values. While the DPI reported by the system is not exactly
the same as the actual pixel density, i.e. it doesn't exactly correspond to the
number of pixels on the screen divided by the screen physical dimension in
inches, it still needs to change to roughly correspond to it.
This system DPI value is typically expressed using a scaling factor, by which
the baseline DPI value is multiplied. For example, MSW systems may use 125% or
150% scaling, meaning that they use DPI of 120 or 144 respectively, as baseline
DPI value is 96. Similarly, Linux systems may use "2x" scaling, resulting in
DPI value of 192. Macs are slightly different, as even they also may use "2x"
scaling, the effective DPI corresponding to it is 144, as the baseline value on
this platform is 72.
The Problem with High DPI Displays
==================================
If high DPI displays were treated in the same way as normal ones, existing
applications would look tiny of them. For example, a square window 500 pixels
in size would take half of a standard 1920×1080 ("Full HD") display vertically,
but only a quarter on a 3840×2160 ("4K UHD") display. To prevent this from
happening, most platforms automatically scale the windows by the scaling
factor, defined above, when displaying them on high DPI displays. In this
example, scaling factor is 2 and so the actual size of the window on screen
would become 1000 when automatic scaling is in effect.
Automatic scaling is convenient, but doesn't really allow the application to
use the extra pixels available on the display. Visually, this means that the
scaled application appears blurry, in contrast to sharper applications using
the full display resolution, so a better solution is needed.
Pixel Values in High DPI
========================
Some systems automatically scale all the coordinates by the DPI scaling factor,
however not all systems supported by wxWidgets do it -- notably, MSW does not.
This means that "logical pixels", in which all coordinates and sizes are
expressed in wxWidgets API, do _not_ have the same meaning on all platforms
when using high DPI displays. To hide this difference from the application,
wxWidgets provides "device-independent pixels", abbreviated as "DIP", that are
always of the same size on all displays and all platforms.
Thus, the first thing do when preparing your application for high DPI support
is to stop using raw pixel values. Actually, using any pixel values is not
recommended and replacing them with the values based on the text metrics, i.e.
obtained using wxWindow::GetTextExtent(), or expressing them in dialog units
(see wxWindow::ConvertDialogToPixels()) is preferable. However the simplest
change is to just replace the pixel values with the values in DIP: for this,
just use wxWindow::FromDIP() to convert from one to the other.
For example, if you have the existing code:
```cpp
myFrame->SetClientSize(wxSize(400, 300));
```
you can just replace it with
```cpp
myFrame->SetClientSize(myFrame->FromDIP(wxSize(400, 300)));
```
Physical Pixels
===============
In addition to (logical) pixels and DIPs discussed above, you may also need to
work in physical pixel coordinates, corresponding to the actual display pixels.
Physical pixels are never scaled, on any platform, and must be used when
drawing graphics elements to ensure that the best possible resolution is used.
For example, all operations on wxGLCanvas use physical pixels.
To convert between logical and physical pixels, you can use
wxWindow::GetContentScaleFactor(): this is a value greater than or equal to 1,
so a value in logical pixels needs to be multiplied by it in order to obtain
the value in physical pixels.
For example, in a wxGLCanvas created with the size of 100 (logical) pixels, the
rightmost physical pixel coordinate will be `100*GetContentScaleFactor()`.
Platform-Specific Build Issues
==============================
Generally speaking, all systems handle applications not specifically marked as
being "DPI-aware" by emulating low-resolution display for them and scaling them
up, resulting in blurry graphics and fonts, but globally preserving the
application appearance. For the best results, the application needs to be
explicitly marked as DPI-aware in a platform-dependent way.
MSW
---
The behaviour of the application when running on a high-DPI display depends on
the values in its [manifest][1]. If your application include `wx/msw/wx.rc`
from its resource file, you need to predefine `wxUSE_DPI_AWARE_MANIFEST` to
opt-in into high DPI support: define it as `1` for minimal DPI awareness and
`2` for full, per-monitor DPI awareness supported by Windows 10 version 1703 or
later.
[1]: https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests
macOS
-----
DPI-aware applications must set their `NSPrincipalClass` to `wxNSApplication`
(or at least `NSApplication`) in their `Info.plist` file. Also see Apple [high
resolution guidelines][2] for more information.
[2]: https://developer.apple.com/library/archive/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html