Qt 5.6 brings improved high-DPI support, in the form of better support for the devicePixelRatio scaling mode. In this blog we’ll look at how to configure and enable it, from the perspective of a Qt application user and a Qt application developer.
Developing applications won’t be covered in depth now, but has to some extent been covered earlier. The short story is that applications generally benefit from this high-DPI mode even without modifications.
This high-DPI mode is a virtualization mode, where there is not necessarily a 1:1 correspondence between a unit in the QWidget/Quick item coordinate system and a pixel on screen. One “unit” then has constant visual size across systems with different display densities, and actual screen pixel density is to a large degree hidden from the application.
Dots Per Inch (DPI) is the traditional measurement for display density, where a standard density display has a DPI value of 72 or 96. Qt as always scaled fonts automatically according to the system DPI, and then the application code was responsible for scaling hardcoded layout sizes. The Qt styles would to a certain degree adapt to the font size. The devicePixelRatio mode is different in two ways: First, the display density is expressed as a scale factor — the devicePixelRatio in Qt — ranging from 1 to n. Second, the scale factor is applied lower in the stack (on the Apple platforms at the OS level), and is less directly used in application code.
There is often a fixed relation between DPI and scale factors on a given platform:
From stackoverflow. Quiz: Why is 1x 160 DPI on Android, compared to ~90 on desktop?
Demonstrating rendering at display densities on a blog is difficult. What we can do instead change the devicePixelRatio Qt sees while keeping the display scale factor constant. This results in larger visual sizes at higher devicePixelRatios:
Enabling high-DPI support: Qt needs to be provided with a scale factor for all displays on the system. There are several possible sources for the scale factors: The values can be provided directly by the OS, Qt can compute them based on traditional display metrics provided by the operating system (such as the DPI value), or the user or developer can provide them directly. The mechanisms for setting and enabling the sources are environment variables and application attributes.
Historical Sidebar:Qt 5.4 and Qt 5.5 on X11 and Windows supports setting the devicePixelRatio with QT_DEVICE_PIXEL_RATIO=n (integer only). This setter has now been deprecated and replaced with several others, as described below.
Let’s look at three different cases:
Case I: The operating implements high-DPI scaling and provides a scale factor.
This is the situation for the Apple platforms – the OS enables the high-dpi mode, and Qt and the application come along for the ride.
This is also the case for Qt on Wayland when the Wayland display server is configured with scaling enabled:
./weston --scale 2
For the experimentally inclined, Qt for Native Client also gets a scale factor set when browser zoom is activated.
Case II: Qt supports the scaling and computes a scale factor.
Supported platforms: X11, Windows, Android, Eglfs
Qt can enable devicePixelRatio scaling on platforms that do not support it natively. This can be done via an environment variable or an application attribute in the application source code:
Qt will then query the the operating system for display metrics using native API, or in the eglfs case fall back on QT_QPA_EGLFS_PHYSICAL_WIDTH, QT_QPA_EGLFS_PHYSICAL_HEIGHT and the display pixel size.
Enabling can also be vetoed, either by environment variable or by the application:
The use-cases for vetoing are “I’m not getting correct DPI values from my displays” and “my application really needs to work in display pixels”. Note that this vetoing only disables “Case II” scaling: Qt can of course not change how the OS works, and manual set scale factors (below) are also kept as as a separate case.
Case III: Setting a scale factor manually.
Sets the scale factors for all screens. Screen order is QApplication::screens() order. This setter assumes that text has already been properly scaled for the display, through the DPI setting, and then scales the rest of the user interface to match.
Sets a global scale factor for the entire application, scaling everything uniformly. This final option is useful for development and testing, and allows you to test any scale factor on any hardware. It can also be useful for some embedded scenarios – for example if you are targeting a single display type with a full-screen application: tweak the scale factor until the UI has the correct visual size.
The finer points:
Q: What happens if I use more than one of these setters?
A: The scale factors are multiplicative. Setting QT_SCALE_FACTOR=2 on a 2x device gives an effective devicePixelRatio of 4.
Q: Are non-integer scale factors supported?
A: Qt uses qreal in the API, and will allow setting non-integer scale factors via QT_SCALE_FACTOR. However, Qt does not guarantee that graphics and styles will be glitch-free in that case. Styles may break first: the fusion style is generally most scalable. The Qt platform plugins round the reported scale factors to the nearest integer.
Q: Could a scale factor of 0.5 allow me to work in device pixels on a 2x device?
A: That’s uncharted waters, but a possibility.
The new High-DPI functionality is part of Qt 5.6 release. To try it out, please check out the Qt 5.6 Beta.