-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement pattern/order editor DPI scaling, fix pixellation on Qt 6 HiDPI #407
Conversation
sigh, |
Linux/KDE status2x DPI scaling pixelation is fixed on Qt 5 and 6 by Fractional DPI scaling pixelation is unfixed on Qt 5 and 6. There's not much that can be done about it, since KDE sets I'm not sure if Version check?Additionally, the AppVeyor Windows 7 builds are being made on 5.10.1 rather than the latest 5.15.2 (should this be changed?), meaning that they won't call |
We already do this to differentiate WinXP from Win7+ builds & enable newer Windows features for the latter (
AppVeyor build stuff wasn't really touched in awhile. The dissonance in Qt versions between GHA & AV is due to negligence of the latter and not intentional. We can make a PR bumping the builder image to Visual Studio 2019 and pinning the Qt version to MinGW 5.15.2 to address this.
If it looks worse on Qt5 (I'm taking your word on it here) then it's fine if it's only enabled for Qt6, maybe with a short code comment explaining the fact. |
Windows Qt5 is uglier (especially at 200% DPI) than Windows Qt6 (though this can be fixed the same way as I did in exotracker, by switching to Segoe UI, are you interested in this?). However setting the rounding mode doesn't harm Windows Qt5 compared to not setting it (and in my subjective opinion improves 150% slightly). I think I'll wait for the AppVeyor update to Qt 5.15.2 before working further on this PR. |
Oh, this is about the default font choice instead of the setting. What I'm reading from this is that Segoe UI is only available since Vista? If your suggested change doesn't cause problems on XP then I'd be interested, for the sake of consistency across versions. Although… different Windows locales seem to select different fonts, see #402. Qt5's MS UI Gothic (apparently the default shell font for Japanese Windows) vs Qt6's Segoe UI + Yu Gothic UI. I assume your method would cause weird font mixes in such scenarios, in which case maybe it's better to just leave it to Qt & the system to figure out the preferred font & "deal with it". |
The exact code is My guess is that Win32 message box fonts can be configured by the user (at least on Windows Classic) because they won't break message boxes which reflow dynamically, whereas Win32 app fonts are hard-coded so they take up a known amount of space, which is necessary because apps build hard-coded layouts around them. BambooTracker uses dynamic layout, so it can use system fonts that vary between OSes and languages. |
I bumped the Qt version of Win7 development builds to 5.15.2. |
338033a
to
e551ce6
Compare
Argh...
|
e551ce6
to
af92099
Compare
On Qt 5.14+, rounding 150% down to 1x painter scaling results in less awkwardly large widget padding and non-pixelated icons. The downside is that toolbar icons are smaller, making them harder to see and click. On Qt 6, this ensures that a QWidget's size in physical pixels is a integer multiple of its size in virtual pixels and remains stable upon resizing, so we can create QPixmaps the same size as a widget which can be painted 1:1 onto the widget. Additionally, QPainter operations at integer virtual-pixel coordinates remain physical-pixel-aligned regardless of the screen's DPI, fixing both custom painting and Qt's Windows theme. Text remains scaled to fractional DPI.
af92099
to
caa2ffc
Compare
Should prolly be done in another PR? It affects more than DPI scaling & should be tested individually.
Sounds fine to me.
|
Note that DPI switching is still broken.
caa2ffc
to
3f59561
Compare
Is the "Remove unnecessary unique_ptr from main()" commit OK? (lol github is complaining changes are requested, even though the review is marked as resolved) (oops i requested review again... what is github doing) |
I'll take your word on this working properly on Windows, since I can't test it. |
This PR was mostly tested on Windows 10 x64 at various DPI settings. It fails on Qt 5 and 6 on KDE, due to KDE/Qt bugs rather than my code or BambooTracker's code. I did not test on Mac.
Honestly I did this mostly out of personal interest and for learning, since I'm not sure if other people use high DPI displays or Qt 6. However it serves as a demonstration that pixel-perfect fractional DPI scaling is possible on Qt 6 (which I thought was impossible until recently) through
HighDpiScaleFactorRoundingPolicy
. And this may become more important as Qt 5 transitions into a legacy program over time.Overview
Qt 6 forces applications to use virtual pixels rather than physical pixels. This makes it difficult to perform pixel-level paint operations on widgets, or to create a QPixmap/QImage the same size as a widget. Worse yet, at fractional scale factors (physical pixels per virtual pixel), it's impossible to convert a widget size in virtual pixels to an image size in physical pixels, and have them always match up entirely.
I use
HighDpiScaleFactorRoundingPolicy::RoundPreferFloor
so Qt make each virtual pixel an integer number of physical pixels. Then when calling functions that take physical pixels, I multiply the size in virtual pixels by the integer scale factor. The only functions I've found so far that take physical pixels areQPixmap
's constructor (taking a size in physical pixels), callingQPainter::drawPixmap()
(taking a source rect in physical pixels), or callingQPixmap::scroll()
for quick draw (both delta and region in physical pixels). If I've missed any, then it would result in redrawing defects. The current version of the PR has all rendering defects I've seen so far fixed.(All QPainter operations acting on a
QPixmap
/QWidget
, both of which implementQPaintDevice
, multiply all coordinates by the target'sQPaintDevice::devicePixelRatio[F]()
.)Results
I rewrote the pattern and order editors to look sharp at high DPIs. I didn't touch the oscilloscope (which for some reason draws individual points rather than lines, meaning it's not antialiased) or the FM/PSG/ADPCM instrument editors.
The resulting app looks sharp, and lines are appropriately thick, on both 125% and 200% scaling, on both Qt 5 and 6. However, this approach does have some flaws: At 125-150% scaling, elements defined in terms of pixel count rather than font size (I don't think BambooTracker's pattern editor does this, unsure about instrument editor) do not scale compared to 100% (though this can be fixed with more work). At 200% scaling, positions are quantized to multiples of 2 pixels (though this isn't a deal-breaker). Nonetheless this is the best approach achievable on Qt (to my knowledge) if you want sharp and uniformly thick lines.
What are the alternative approaches?
Eliminating virtual pixels entirely, and positioning all widgets in physical pixels (increasing line thicknesses at high DPI unless you want to upset 200%/300% users), is not possible on Qt 6+ (not sure about 5, not sure about AA_DisableHighDpiScaling).
Another approach option is virtual pixels or vector graphics, then performing DPI scaling by rendering at a higher resolution. (If no intrinsic grid, will be fuzzy at all resolutions, if intrinsic grid, will be fuzzy at non-integer scale, unless UI elements are grid-snapped to integer physical pixels). This is the approach taken by many VST GUIs, and
HighDpiScaleFactorRoundingPolicy::PassThrough
kinda works like this. However the usual approach of using alpha-blended antialiasing (rather than multisampling/etc.) means coinciding edges at non-integer pixel boundaries are drawn incorrectly ("conflation artifacts"). Additionally BambooTracker's use of multiple off-screenQPixmap
rather than painting directly to the widget isn't how vector graphics works, and makes it difficult to impossible (not sure which) to even size theQPixmap
to the same resolution as the widgets being painted on (which can be seen as a special case of non-integer edges).One possible concern with this PR that rendering at full pixel resolution will slow down pattern redrawing at high resolutions. I'm not sure if it's worth adding an application-level setting to control whether to render at virtual resolution rather than physical resolution (at 200% DPI, only render 1/2 of pixels horizontally and vertically, and upscale using nearest or linear interpolation).
Bugs
Unfortunately
HighDpiScaleFactorRoundingPolicy
has no effect on Linux KDE due to QTBUG-95930 (which I reported but has not been fixed), so this PR won't fix inconsistent pixel sizes there (I suggest setting display scaling to 100% and boosting font DPI instead).Additionally, switching DPI after launching the app causes broken pattern/order editor layout, until you open the settings and click Apply to recompute font metrics. This is because BambooTracker isn't designed to switch DPIs. (Switching the DPI of running apps is easy on Windows, but probably not possible on Linux KDE X11 without manually the
Xft/DPI
XSettings variable, which I think KDE doesn't touch or Qt doesn't pick up. No clue about Wayland.)Additionally switching DPI without switching
QScreen
objects (changing screen DPI, or switching between "laptop only" and "monitor only") is broken in Qt itself withHighDpiScaleFactorRoundingPolicy
set, due to QTBUG-95925 and related bugs. I have a workaround to fix this bug, but I think it belongs in a separate PR if it's even approved at all.Additionally at 2x integer scaling and up (175% and higher OS scale factor), the "bottom line" of the pattern/order editor's headers is misplaced too high, creating a gray line at the bottom of the header. This is a BambooTracker bug exposed by high scale factors, and
I didn't fix it because it's minor and shifting a line is not strictly related to the rest of the PR (which adds/fixes scaling factors)I added a surgical fix. To avoid this kind of bug, in exotracker, I avoidedQRect
's lines and rectangle outlines entirely in favor of my own rectangle left/right border functions.Code Style
I was rather sloppy with the code style and didn't match the project (
#pragma once
, removing trailing whitespace from files I touched, placing open braces on the same line as the function, not sure how to break long lines, etc.) So I marked the PR as a draft.Screenshots on Qt 6
Before (125%):
After (125%):
After (200%):
Qt 5 looks uglier since the window font doesn't scale with DPI, even at 200%. Maybe
Qt::AA_EnableHighDpiScaling
would help, or theQApplication::setFont(QApplication::font("QMessageBox"));
trick.