The upcoming Java release 8u60 is set to finally fix the JavaFX DPI scaling issues on Windows that I described back in August 2013. To recap, JavaFX would scale the default application font to the current Windows DPI setting and the built-in controls would generally scale themselves around that font size. However, explicitly specified pixel coordinates and sizes, whether in code or CSS, remained unscaled, and so did any images loaded by the application. You had to scale all those manually, by multiplying with a scaling factor based on the (scaled) default font size. Obviously this made writing DPI-aware applications rather cumbersome and encouraged developers to simply not bother at all.
Java 8u60 removes this annoyance by implicitly scaling all pixel coordinates, as well as loaded images, to the current Windows DPI setting relative to the 100% baseline. This is exactly what Windows Presentation Foundation (WPF) and modern browser layout engines are doing, and it’s really a necessity for painless user interface development. Even better, if you were using manual coordinate scaling based on the default font size your application will continue to work because font size queries now simply return the 100% sizes (12 points or 16 pixels for one line of text), turning your manual scaling into a harmless no-op when running on 8u60 or later.
Oracle’s Layout Samples Revisited
I have run some tests on the current preview release, JDK 8u60 build 21, which is theoretically available here. Unfortunately Oracle’s servers are rather broken and I was unable to get a working download for the past couple of days. With the 64-bit JDK I did manage to get before the servers broke down, I checked that my JavaFX applications that employ manual DPI scaling still work fine at 200% (192 DPI) and also re-ran Oracle’s layout tutorials that I used as demonstrations in my original post. Please see there for screenshots of how high DPI settings scrambled the GUI layout in JavaFX 2.2.
There were some changes to the JavaFX layout mechanisms unrelated to DPI scaling, so I’ll first show how the current release Java 8u45 renders the first sample application. The left screenshot shows how the application starts up: the explicitly sized
Scene is still clearly too small for its contents, but at least the hosted
BorderPane now automatically extends to the required size. Enlarging the window shows an already semi-decent layout in the right screenshot. Two errors remain: the
ListView has an explicit width that’s too narrow at 200%, and the explicitly sized “Exit” font is smaller than the 200% default font but was intended to be larger.
The next screenshot shows the same application running on Java 8u60 build 21. Everything looks as intended now, virtually identical to Oracle’s sample screenshot at 100%. The start-up window size is correct, the
ListView is wide enough, and the “Exit” label is bigger rather than smaller than the other labels.
The last screenshot shows the second application I used in my original post. This sample features CSS layout with explicit pixel sizes and PNG images as part of the user interface, all of which remained unscaled in JavaFX 2.2. Java 8u45 still produced the same result (so no separate screenshot) but Java 8u60 build 21 once again fixes everything and produces a layout at 200% that looks identical to the 100% version.
Well, except for one small thing – did you notice? The
HBox behind the “Save/Cancel” buttons is not high enough for the rotated buttons. That, however, is a layout change that must have happened at some point between JavaFX 2.2 and Java 8u60: the
HBox apparently no longer takes the button rotation into account when sizing itself to its contents. I’m not sure if this is intentional or a bug, but I did verify that it looks just the same at 100% as at 200% DPI scaling.
Jim Graham has documented the current state of Windows DPI scaling and planned updates on the OpenJFX mailing list – make sure to read the entire thread, there are lots of interesting comments. Here’s a summary of the most important points:
- Java applications still advertise themselves as “system DPI aware,” i.e. they don’t opt into the new per-monitor DPI scaling of Windows 8.1. Support for this feature is planned for Java 9.
- Three new
java.exeflags allow you to explicitly specify a DPI scaling factor and manipulate scaling strategies. Please see Jim Graham’s post above for a detailed description.
- JavaFX supports an Apple convention on Windows: a pair of image files named
email@example.com the same image, one twice the resolution of the other. The appropriate version is loaded depending on current DPI settings.
- It’s not yet possible to programmatically circumvent implicit coordinate scaling in order to access physical monitor pixels. Such a feature is probably coming in a future release.
Java 8u60 was a very pleasant surprise for me, as I had not expected fully automatic DPI scaling before Java 9 (if ever). This should make JavaFX a great deal more attractive as a WPF alternative. Speaking of which, once Java 8u60 has been officially released I’ll update my article on DPI Scaling in Windows GUIs accordingly, so we’ll have a side-by-side comparison of JavaFX and WPF at 120–144 DPI. Finally, one thing Java 8u60 won’t fix is DPI scaling in AWT/Swing… so NetBeans will unfortunately continue to feature increasingly minuscule button labels.
2015-08-24: While updating my comparison of Windows GUIs at 96–144 DPI, I discovered that JavaFX 8u60 (release version) actually special-cases 120 DPI and does not scale coordinates at that resolution. See this post for more information.
16 thoughts on “JavaFX DPI Scaling: Fixed!”
Excellent, i will be following the updates.
yes, it is working now. But…
Have you used Scene Builder? I had used version 8.0.0 (gluon, exe under windows 10) and preview and design was not o.k.
But when I started SceneBuilder-8.0.0.jar also from gluon it is working good.
Does anybody know any workaround how to start this jar from netbeans?
(for gluon scene builder exe I needed a workaround too NB does not find a valid scenebuilder installation, bat dpi was bad)
No, I’m not using Scene Builder so I wouldn’t know about any issues. You should ask the Gluon guys about that, they’re running a dedicated Scene Builder support forum.
Do you know how to get this system DPI that JavaFX uses for scaling? I tried Screen.getPrimary().getDpi() but it always returns 96, even if system DPI is 200% (192 DPI). Is there any way of getting current JavaFX DPI or DPI scale factor?
Unfortunately there is currently no way to do this. As you noticed, the new scaling system means internally JavaFX always pretends (except at 120 DPI) that it’s running at 96 DPI. Java 9 will introduce a proper API to get the actual DPI used by JavaFX. Right now there’s nothing.
On Windows you could directly read the registry as outlined in this article (for Windows 8.1, didn’t find one for Windows 10). That’s complex with multiple monitors and per-monitor scaling enabled, though. You’d have to experiment how JavaFX 8 behaves when moving between monitors, I haven’t examined that myself.
Thank you for your reply.
I guess we’ll have to wait until Java 9 for more flexible DPI support. In the meantime I managed to get JavaFX DPI scale factor, but it is a hack (uses both AWT and JavaFX methods):
// Number of actual horizontal lines (768p)
double trueHorizontalLines = Toolkit.getDefaultToolkit().getScreenSize().getHeight();
// Number of scaled horizontal lines. (384p for 200%)
double scaledHorizontalLines = Screen.getPrimary().getBounds().getHeight();
// DPI scale factor.
double dpiScaleFactor = trueHoriznotalLines / scaledHorizontalLines;
That’s a pretty clever trick! Thanks for posting this, might come in handy.
Any opinion on this?
Sorry, haven’t actually tried that feature myself. Sounds like it’s still only partially implemented (or buggy).
I know its old thread but anyway someone might find it handy, if you have 125% windows scaling and your GUI from javaFX gluon scenebuilder is not displaying properly while running as executable, try following to fix it:
1. open task manager and find your location of running java.exe
2. right click -> Properties -> Compatibility tab -> check Override high DPI scaling behavior and set Scaling performed by “system”
this should now scale your GUI both in Gluon scenebuilder and in running executable correctly.
hope someone might find this handy
Have a nice day!
Good tip. Note this is a new scaling option introduced in Windows 10 Creators Update, so it’s not available on older systems. See my post from last year: DPI Settings in Windows 10 Creators Update