Reasonable DPI in Plasma Next

We are currently looking into how we can improve Plasma (but in extension also other applications, including QWidget-based ones) on hardware that sports unusually high DPI (also called “reasonable DPI”). In this article, I’ll outline the problem space and explain our tools to improve support for high DPI devices in Plasma.

First, let’s take a step back, however, to explain the problem space to those who haven’t yet spent that much thinking on this. Most of today’s desktops and laptops have have roughly the same amount of pixels per square inch of screen space. The amount of pixels per inch is measured in DPI (dots per inch) or PPI (pixels per inch). This value is around 100 DPI for laptops. Tablets and smartphones are already available with much higher DPI screens, making for sharper fonts and overall higher fidelity graphics. A while ago, Apple has started producing laptops with high DPI displays as well, these are the so-called Retina screens. There are Macbooks on the market with 220 DPI.

Test Plasmoid showing a scaled UI

Test Plasmoid showing a scaled UI

Some people have complained for years about the low DPI value of screens available on the market (and I am one of them), higher DPI simply allows for nicer looks, and reduces the need for “dirty tricks” such as subpixel rendering algorithms (which come with their own set of problems). Graphics chips also have become fast enough (and with enough memory) to not have a problem with high resolutions (think 4K on your laptop, for example).

I’ve done some research (Well, lazy-webbing, mostly), and it seems that higher DPI screens for desktop systems, but also for laptops are still quite rare, and when you find one, they’re often really, really expensive. I believe this is caused by two reasons: production-line quality is not always good enough to produce high DPI screens, one could say that the more pixels there are on a given device, the higher the chance that one or more of them are dead, making the display unsellable, and thus increasing the price of the ones that are pixel-perfect. Also, tablets and smartphones, which often already sport high DPI screens are currently taking up almost all of the production capacity. The obvious result is scarcity and a high price. I believe it’s only a matter of time until high DPI displays become more common, however. A few friends of mine already have high DPI displays, so there is a real need to support this kind of screen well.

Battery Widget without icon scaling

Battery Widget without icon scaling

So what’s the problem? Many applications assume a fixed DPI size, or at least that the DPI value of the screen is within a certain range, so that when you render an icon at 22 pixels, it will look like a small icon, compared to text, and that its details are visible. Also, icons and sizing of fonts are loosely related, so an icon that is way smaller than the size of the text as rendered will look weird and cause layouting problems. (Imagine huge letters and cut off text, for example.)
For graphical elements, this is a real problem, but less so for text. Today’s text rendering engines (Freetype, for example) take the DPI value of the screen into account. This means that this at least partly solves our problem. Luckily, it solves the problem for very complex cases, so at least this is not of great concern. It’s only a partial solution, however, so this at best eases finding a complete solution. We need to fix these problems in different areas, and all need to have well-thought out solutions.

Limitations in X11

The bad news is that this problem is only partly solvable in an X11 world. X11 has no real concept of different DPI values per screen. This is not so much a problem for single screen systems — we just use the DPI of the only screen. As soon as you attach a second screen that has a different DPI, this is going to be a problem, and it’s not possible to solve this completely in an X11 world. We’re planning to first make it work in a single DPI environment, and then work towards supporting different DPI values per screen. (Of course we’ll keep multi-DPI-screens in mind, so that we don’t have to take too many steps back once we are actually in a position to be able to support this. This means Wayland, basically.)

Battery Widget using icon scaling

Battery Widget using icon scaling

Cheating with fonts

A pretty easy, yet neat solution is to use font metrics to compute sensible dimensions for graphical elements on the screen. The very short version is to stop thinking in numbers of pixels, and to start thinking in lines of text and with of characters as they end up on the screen. This is not a pure solution to the DPI problem, which in many cases is actually an advantage. The size (for example height) of a given letter rendered on the screen depends on a number of properties:

  • The DPI value of the screen which is used to render the text
  • The font size setting
  • The size of the font itself, as it is designed (this is usually more relevant for the aspect ratio between width and height)

This means that taking the font height as rendered, we actually can compute values for sizing elements that not only take the low-level technical properties of the hardware into account, but also user preferences (or system presets). In Plasma 2, we started using this mechanism in a number of places, and so far we’re pretty happy with the results. Some examples which where we use this is the sizing of popups coming out of the panel (for the notification area and the calendar for example), but also the sizing of the icons in the notification area (or system tray). This means instead of hardcoding pixel sizes, these UI elements grow and shrink depending on your settings. This solves a part of the problem, but is obviously not a complete solution. If you would like to implement this mechanism, here’s two snippets of code, which, with some necessary adaption, you can use to make your app work well on High DPI devices.

in Qt C++ code:

const int fontHeight = QFontMetrics(QApplication::font()).boundingRect("M").size().height();

This gives you the height of the letter “M” as it would be rendered on the screen. It’s a useful mechanism to get you a pixelsize that is dependent on the DPI value of the screen. Note that you want to use an int here, in order to not end up aligning UI elements at half pixels, as this leads to blurriness in your UI.)

We’ve bridged this API in the Plasma Theme class, which is available from QML applications by importing org.kde.plasma.core (the global property theme will be automatically set, which allows you easy access to Plasma::Theme from everywhere, in case you’re wondering where the “theme” variable is coming from).

import org.kde.plasma.core 2.0 as PlasmaCore

/* Paint an orange rectangle on the screen that scales with DPI 
   This example makes the rect big enough to paint about 8 rows
   of text (minus spacing), and allows for a column with of about 
   60 characters. Mileage varies on fonts used, and the text 
   itself, so this is an approximation.
 */
Rectangle {
    width: theme.mSize(theme.defaultFont).height * 8
    height: theme.mSize(theme.defaultFont).width * 60
    anchors.margins: units.largeSpacing

    Item {
        anchors.fill: parent
        /* ... more stuff ... */
    }
}

In the second example, you see that we’re using another property, “units.largeSpacing” for the margins. This is another piece of DPI-dependent UI which you can use to apply margins and spacing that take DPI (actually font-as-rendered-settings) into account.
To get the size of a piece of text on the screen, in QtQuick code, you can use the paintedWidth property of a Text elements, but not that this can be tricky due to text elision, line breaks, etc., so this is to be dealt with with care.

Icons and other graphical elements

Another interesting case which affects the usability of our graphcal interfaces on high-DPI screens is the sizing of icons. We are using standard sizes there, which you access via properties “units.iconSizes.small”, “units.iconSizes.large”, etc.. In Plasma, these sizes now get interpolated with the DPI value of the screen, while making sure the icons are still getting rendered correctly. The sizing is done in steps, and not linearly to avoid getting washed-out icons. This mechanism works well and automatically solves another piece of the puzzle. The result of doing this is a more balanced look and better alignment and spacing across some widgets (notably the battery gains quite a bit of beauty with this treatment).
In other UI elements, especially the toolkitty widgets we provide in PlasmaComponents, we often already scale with the text, which works just fine for reasonably-but-not-excessively high DPI values. We have done some experiments with scaling up the graphical elements that are used to, for example, render a button. As we are using SVG graphics throughout the user interface, we don’t have to resolve to dirty tricks such as doubling pixels, and we can get quite neat results. This needs some more work, and it hasn’t been merged into master yet.

The Higher-Level Picture

Now, having discussed lots of technical details, how does the user-facing side of this look and work? The changes we’ve done right now only affect the user in one way: More UI elements scale with the DPI of the screen, this means no change for displays around 100 DPI. On my laptop (which has a 180 DPI screen), this gives a more balanced picture.
As Plasma is making its way onto a wider range of target devices (think tablets, media centers, as well as high-dpi laptops or monitors), this topic becomes increasinly important. I have good hopes that we will be able to solve this problem in a way that really leverages the power of this kind of hardware. In Plasma Next, we’re now providing some of the basic tools to make UIs work well on high-DPI displays, and we’ll continue this route further on. The user will notice that in Plasma Next, most of the problems that could be seen on high DPI screen are gone, and that the whole behaves much better without many tricks.

16 Responses to “Reasonable DPI in Plasma Next”

  1. It’s great you’re doing this! And it’s not just useful those huge DPI screens, but also for accessibility. Even with DPIs of 100 – 150, people with less than perfect eyesight may already have problems reading text. And if they increase default font size, UIs often start looking like crap.
    To me, using sizes relative to text often makes way more sense than using pixel sizes. That’s why CSS has had em as a unit for ages, which I think web developers should use instead of pixels whenever possible.
    I wonder why, given that it borrowed other concepts from CSS as well, QML doesn’t natively support em. This would have saved you quite some time and code because you wouldn’t have had to calculate your own textsize-related units.

    • sebas says:

      Partly, perhaps. On the other hand, not being able to control rendering at pixel-level makes it extremely hard to display graphics correctly on low-dpi displays (basically everything under 250) DPI, since we’re not able to align to physical pixels correctly, leading to a washed out result. Not everything can be scaled fully dynamically.

      For icons this is solved by taking the next bigger icon, we don’t scale in between, but pick icons that have been rendered for a given pixel size, but we pick them based on the scaling factor (so correcting for DPI). This means with more pixels, we actually get more fidelity, and we’re not just filling it with pixels.

      Applying this to SVG elements also seems to work quite well. The results I’ve seen with scaling our vector graphics are quite promising, I haven’t seen any major problem, but we need to apply these things with some care in order to not make your whole UI go teletubby. :)

  2. Diggory says:

    I’ve actually been using KDE on a very high DPI screen (275 DPI I think; Yoga 2 Pro) for a couple of months, mostly successfully. My approach has been (a) to set the “font” DPI to around 225, (b) to increase KDE’s icon sizes, and (c) to use extensions in web-browsers (e.g. NoSquint in Firefox).

    Result: some applications don’t scale, system tray icons are tiny, plasma popups (and often application windows) are too small, and there’s a few bugs here and there (if you use KMail, you’ve seen how that under- and over-scales messages), but on the whole things work pretty well.

    What I would suggest is handling the scaling more like Windows 7: set a scaling factor (normally 1) and set both icon and font sizes from that. Maybe also try to detect non-compliant applications and have the window manager scale them in entirety (the ugly way). That way when Firefox finally supports scaling properly it will have a nice hint as to how it should scale content and get higher resolution images.

    Hmm, am I talking any sense or just blathering?

    • Diggory says:

      Oh, actually, good what you’ve already done! I guess many of the plasma fixes mentioned will land in KDE 5.x or after distros switch to the frameworks packages?

    • csslayer says:

      The problem is in X11 world scale a window is not quite possible. It’s visually possible, but without proper input redirection. I think it’s easy to let kwin scale a window up, but it’s not possible to make your mouse click on the correct position of such window. The internal coordination system of such application doesn’t change, which is not a problem any more in wayland.

    • sebas says:

      The Plasma-related bugs you describe should be resolved in Plasma Next. The system tray icons are now scaleable, and dialog sizing is fixed as well. This is the exact class of problems we’re tackling here.

      The global scaling factor is something we’re working towards to. You can see this in the screenshot with the three Widgets, They are scaled at different factors. In this screenshot, I’ve applied the scaling factor also to the slider, you can see it growing with the DPI. (Of course the screenshot is just an approximation of the actual result, since it’s not actually rendered on that DPI.)

  3. Diederik says:

    Good that you’re thinking about this and providing code examples.
    Even better would be to abstract it away, so that ppl building plasma (UI) components don’t have to think about it, but they get it automatically. This would also prevent every plasma (UI) component builder having to implement it or worse implement it differently, which would make inconsistent UIs.

    • I agree with Diederik. I’m using a Haswell laptop with a DPI of 98. The fonts are microscopic!! It’s ridiculous. And only a problem with KDE. Providing code examples for work arounds is great but not the way to solve this.

      Properly abstract it out so that 72 points is one inch, regardless of the DPI.

      — Smittie

      • sebas says:

        Those are not workaround, but proper solutions to these problems at a low-level. The things I’m discussing provide the tools to fix the described problem throughout our components.

        As to abstracting out the DPI: We’re doing exactly that. This article describes, how. :)

    • sebas says:

      That’s the idea. What I’m describing are the low-level tools we use to achieve it. We’re already using some of the tricks described, and are experimenting with others.

      As QtQuick is a very flexible system, it is easy to screw it up for developers (basically, as soon as you’re using hard-coded pixel sizes, you’re doing it wrong). The code examples explain how this kind of problems can be solved when developers are otherwise tempted to hard-code sizes.

      These things also help consistency a lot, it has become way easier to apply consistent spacing and sizing across applets and inside.

      • Diederik says:

        What I’m describing are the low-level tools we use to achieve it.

        Ah ok, I didn’t realize that. I thought those code examples were for users of the classes/APIs you’re writing.
        Thanks for the clarification.

  4. Many thanks for doing these things properly.

    According to your description, the size of text and icons will be consistent even on devices such as my laptop, which I prefer to set to 144 DPI (even though the real hardware DPI is 166: 1920×1080 @ 13.3″). GNOME people just refused to support this, limiting their HiDPI work to integer scaling only.

    • Diggory says:

      By integer scaling only, you mean doubling or tripling the size? Sounds a bit crude. On my 3200×1800 13″ screen doubling would be okay, but I prefer to set the DPI around 225.

      I don’t know how icon scaling works here though. From Sebas’s comments it sounds like SVG icons would scale properly (obviously) while raster graphics get stretched a bit from the next-nearest size (probably this isn’t so bad; it’s basically what I’ve been doing to browse the web).