Priority: P2: Important
Affects Version/s: 5.8.0, 5.9.0, 5.9.1
Fix Version/s: 5.10.0 RC
Environment:Android (tested here) / possibly other devices too
Qt 5.9.1, 5.9.0, 5.8.0
Windows / Linux
- I have first noticed this issue in Qt version 5.8.0, but it might have been around for longer.
- The issue only occurs on certain screen configurations and only when high dpi scaling is active.
- Potentially related bug (or actually this issue wrongly formulated and not researched)
The problem is that when converting from and back to native pixels (meaning the actual, not scaled size of the screen) on several displays, due to approximations (or the wrong usage of types), one pixel is lost, resulting in an application window that does not completely fill the device screen, thus leaving a white 1px-wide line on the edges of the screen.
In order to gain a better understanding of the issue, I have developed two simple applications, both attached:
- one used for unit testing the method that converts from device independent pixels (dp) to native (or physical) pixels (px)
- one used for debugging qt sources and following the steps that lead to the issue and also output a teal-colored screen with QML in order to visualize the issue on different devices (both physical and emulated)
- The data used for unit testing was obtained from Google's Official Material Design - Device Metrics website and adapted by correcting the last column (density - the equivalent to QScreen's devicePixelRatio member) for several devices (such as iPhone 6 Plus, Google Pixel, MacBook 12, etc) in order to have precise values, since one decimal place was not always enough or correct, or some values were rounded. The adjusted version of the data is contained in a file named inside the attached archive .
- Out of 74 tests, 63 passed and 11 failed. The output is contained in an attached file named . As a note here, the 4 test results from the watch devices that show up as failed do not seem reliable to me.
- Stepping into QtSources while debugging (screenshots taken for each step that follows, inside the attached archive named ). The device used is a physical OnePlus3, resolution 1080x1920, 5.5 inch display, ~401 ppi:
- First hit of the breakpoint set in the qhighdpiscaling_p.h on line 140. Notice that the size passed is 1080x1920 (device was in portrait orientation), and the scale factor is 2.625 (this is retrieved directly from the device and is correct). The value returned by the method is a QSize, so while the division result is 411.4285714285714 x 731.4285714285714 (remember these values, they will prove to be useful later), the actual return value of the method is a QSize(411, 731).
- A call to the QRect-returning overload of the fromNative that determines the screen's available geometry, the one that excludes the system bar (the Android System Bar is 24dp tall, which means 24dp * 2.625scale = 63px exactly, hence the 1857 value in the screenshot)
- The result for the previus call is again approximated, leading to a QSize(411, 707).
- Calculating the deviceIndepententGeometry, again the QSize(411, 731).
- Call for determining a QRect needed when updating the screen for High DPI.
- Result for the previous call.
- Obtaining dp values for setting the Android Platform Screen size (the total one, 1080x1920 > 411x731).
- Window creation, initial size seems to be (0, 0).
- Window geometry, size is still (0, 0).
- The window geometry is updated after creation, the values retrieved are once again scaled, resulting in a QSize(411, 731).
- Since the issue was not visible at the application startup (sometimes it's there in the begining, sometimes the window geometry needs to be updated for it to occur), the device was flipped to landscape, and from now on the geometry width and height are swapped, so we'll work with QSize(731, 411) and the window starts being updated.
- In order to determine the physical size of the screen that needs to be filled by the application, the geometry is converted back from dp to px, and this is the moment in which the issue becomes visible. Notice that the result of size * scaleFactor is QSize(1919, 1079) (remember, it's landscape), which is not correct. Now this is due to the fact that 731 * 2.625 = 1918,875, which QSize approximates to 1919, respectively 411 * 2.625 = 1078,875 that is once again approximated to 1079. Let's go back to step one and take those useful values (411.4285714285714 x 731.4285714285714) but swapped, and use those instead of the rounded ones. The result would be, then, correct.
- This shows how the incorrect values of (1919, 1079) are used from now on.
- A look into the platform window geometry rectangle, having the incorect values.
- there is also an image file for this step named inside the archive showing the black application rectangle.
- The archive also contains two teal .png images, names starting with the prefix. They are screenshots taken during a and having the device flipped to both orientations.
- The application was also deployed to 3 emulated devices on which the tests in the first application determined that the issue would occur. These are the Nexus 5X, Nexus 6 and Nexus 6P, and the issue is visible in the screenshots in the attached archive .