JavaFX DPI Scaling in Java 9

While porting my projects to Java SE 9 I noticed that the JavaFX team has slipped in some small but important changes concerning DPI scaling that appear to be only documented in the Java bug database, specifically JDK Bug 8091832.

  • The entry for 2016-03-31 notes that the startup flag -Dglass.win.minHiDPI, defining the minimum display DPI setting for full (not just font-only) JavaFX DPI scaling, has been removed as it conflicted with per-monitor DPI support.
  • The same entry notes that the startup flag -Dglass.win.uiScale is still supported, so you could use -Dglass.win.uiScale=125% to force full JavaFX DPI scaling at 120 DPI (= 125%) where JavaFX used to only scale fonts, even after Java SE 8u60.
  • However, an entry for 2016-04-06 states that a “fix brings the rest of the graphics into line with the font size” at 120 DPI, meaning that setting is no longer special-cased and now receives full DPI scaling too. No startup flag should be necessary anymore.
  • We can finally determine the current DPI scaling, in both the Screen class (getOutputScaleX/Y) and the Window class (outputScaleX/Y).
  • Even better, the Window class got writable scaling properties (renderScaleX/Y, forceIntegerRenderScale) so you can override DPI scaling on a per-window basis. (The similar glass.win startup flags were removed, as noted in the entry for 2016-03-31).
    Note — This feature is unfortunately less useful than I had hoped for. See the comments for an explanation.
  • Unrelated to the linked bug, the min/maxWidth and min/maxHeight properties of the Stage class are now correctly DPI-scaled, so you can properly use them to restrict window resizing.

Very good news! Some older blog posts I wrote on these subjects are now happily obsolete, namely JavaFX GUI Scaling at 120-144 DPI and parts of JavaFX and JAVA_TOOL_OPTIONS. I’m not sure if the mentioned flag -Dprism.order=sw to force software rendering still works, but so far I haven’t seen anything to the contrary.

2018-03-11: And here are some bad news. There’s a new layout bug in JavaFX 9 that may cause truncated controls labels in simple windows. See Windows GUI DPI Scaling in 2018 for details.

9 thoughts on “JavaFX DPI Scaling in Java 9”

  1. Hi Christoph,
    Thanks for your post, very useful! I must be missing something though: I tried applying renderScaleX/Y to a scene, but it didn’t change the actual output rendering (only in the buffer, as per the javadoc?). I’ve spent the last hour trying to figure out, how I can actually set the applications scale, but so far I only found the “settings set org.gnome.desktop.interface scaling-factor ‘1’” property to impact the output scale on Linux.

    Am I missing something? Can the scale be really set a per window basis?

    Thanks,
    Szabolcs

    1. Thanks for your comment. I had not experimented with this feature before writing the post, and it turns out I was a bit premature. First of all, the renderScale properties do in fact work. Note that you must set them within a listener binding to outputScale as noted in the API docs, or else JavaFX will eventually overwrite them.

      The tricky part is how these properties work. They do not actually change layout pixel scaling, as you and I had expected. Instead, they only change rendering pixel scaling (as per their names). To see how this works, set the renderScales to something very low (like 0.5 or 0.1) and draw a simple shape like a circle in the window in question. You’ll see that the shape is still the same size in terms of layout pixels, but it is drawn with heavy aliasing, indicating that rendering was downscaled to a correspondingly coarser resolution.

      So unfortunately renderScale is not actually very useful to application developers. I assume it was introduced to help the JavaFX team tweak low-resolution rendering without having to switch the entire test application to the target resolution.

      Speaking of which, on Windows the mentioned startup switch -Dglass.win.uiScale works perfectly to set the actual output scaling for an entire application. Does this not work on Linux?

  2. Thanks for the article. I know it is about JavaFX but I was wondering if any of this can be used to draw on a JPanel canvas on a HiDPI screen. I create a custom Image based on the DPI of the screen but it seems to reduce the quality when I use the Graphics drawImage function to display it on the JPanel, possibly because JPanel getWidth() and getHeight() do not return the actual HiDPI width and height. I would appreciate any insight you can provide.

    1. Sam, I assume you are already on Java 9/10 so that AWT/Swing uses automatic high DPI scaling. In that case you can find out the current scaling factor but it’s somewhat convoluted. I described the method in my article on DPI Scaling in Windows GUIs, though it should be OS-independent:

      https://kynosarges.org/GuiDpiScaling.html#Factor

      However, I don’t know if or how you can use that scaling factor to accurately draw high DPI images in AWT/Swing as I have little experience with that framework. I’d try calling the scale method on the component’s (JPanel) default transform to reset it to one, then call drawImage with your high resolution image. That might work. For setting the JPanel’s coordinates you might now have to scale them manually by the current monitor scaling factor, but again I’m not sure about that.

      1. Thank you for your response. I will take a look at the link you included. With more and more HiDPI laptops and computers available these days, you would think that Java would have some way of allowing access to each pixel. Hopefully soon.

  3. I can’t speak to other versions and setups, but for me -Dglass.win.uiScale appears to have no effect (OpenJDK 13, Arch/KDE late 2019). Aside from the gsettings “scaling-factor 2” mentioned above, only thing that appears to make a difference is the environment variable GDK_SCALE, and even then only with integers.

    I haven’t been able to find anything that would enable fractional scaling–at least on a per-app basis. Several guides suggest 200% app scaling across the board and then zooming out for the entire display itself with xrandr. Sadly they don’t work for me, but that appears to be due to a hardware/driver problem, and it might work for others.

    1. Liara, I haven’t been following JavaFX since it was unbundled starting with JDK 11. Unfortunately there have always been lots of changes from version to version. You’d have to ask the current maintainers at openjfx.io which scaling properties are currently supported on your platform, if any. Or perhaps someone else reading this knows the answer?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.