In this article, we explain how to support high-resolution displays, commonly referred to as HiDPI or Retina displays. Both the Lightspeed view and the GXY view support HiDPI displays out-of-the-box, meaning that map features such as icons, line widths and font sizes are automatically scaled up in response to the DPI scale settings of the host operating system. These settings include a DPI scale factor that can be adjusted in the operating system to change the size of text, apps and other items.

Java only properly supports HiDPI as of JDK 9. We strongly recommend JDK 11 or later if you are using high-resolution displays.

The HiDPI support is largely transparent to the user, but some caveats apply when using custom GXY painters or icons to ensure that the scaled map is taken into account.

Impact on the UI and a GXY map

The figure below illustrates the impact of the DPI scaling factor on UI components. When using a DPI scaling factor other than 100%, the Java runtime environment essentially creates an additional coordinate system. Let us refer to this system as "toolkit coordinates". Toolkit coordinates correspond to screen pixels that have been scaled by the DPI scaling factor. If the DPI scale is 150%, for instance, then the size of the window is equal to the size of screen divided by 1.5 for both the X and Y axes. All UI components will express their dimensions and locations in toolkit coordinates. This means that the width and height reported by, for instance, a JPanel will be smaller than the panel’s size in actual screen pixels. Note that other toolkits sometimes use the alternative term 'device independent coordinate' for toolkit coordinate.

DPI scaling factor

A GXY map automatically adapts itself to this by generating a larger view proportional to the DPI scale factor. In practice, you should not take any action when using existing GXY painter implementations.

Writing a custom GXY painter

Even when writing your own custom GXY painter, DPI scaling is mostly handled for you automatically. A scale factor will have been added to the transformation of the Graphics2D before your ILcdGXYPainter#paint() method is invoked. Fonts, line widths and other graphical elements are thus automatically resized.

If your painter uses Graphics2D#drawImage(), however, the DPI scaling can make the images look blurry or pixelated. Therefore, you may want to load a higher-resolution image when DPI scaling is in effect. The DPI scale factor can be retrieved through the getDPIScale method in TLcdGXYContext.

For instance, if the DPI scale factor is 2.0 and you would have normally drawn a 256 x 256 pixel image onto the Graphics2D, an enlarged image of 512 x 512 pixels should be used instead.

Impact on icon rendering

Image icons

To support high DPI rendering of image-based icons, TLcdImageIcon adopts the @2x naming convention. This convention consists of storing multiple icon versions at different resolutions, in order to visualize the appropriate one according to the active resolution. For instance, if an image.png needs to be displayed at a DPI scale factor of 2.0, the scaled image image@2x.png is used if it is available.

This approach works seamlessly with OGC SLD / SE styling and the use of external graphic elements that refer to (but do not embed) images.

A similar solution is adopted by TLcdIconFactory, which includes @2x versions for the listed icon identifiers. This approach benefits all components that rely on TLcdIconFactory to create UI icons, such as Lucy and the samples. For instance, the UI icons defined in Lucy’s configuration files are all loaded through TLcdIconFactory; consequently, the @2x versions are automatically used if the DPI scale factor is 2.0 or higher.

Vector icons

Vector-based icons such as TLcdSymbol or TLcdSVGIcon automatically support high DPI rendering as long as there is no buffering through an image, because the Graphics instance supplied to the paintIcon method is already scaled appropriately.

Switching off high DPI scaling

You can switch off high-DPI-scaled rendering using the System property -Dsun.java2d.uiScale=1.0 (1.0 corresponds to 100%). You can also use that property to enforce a scale factor other than the one configured in the host operating system.

Views and screen DPI

Though display scaling is usually enabled because a screen has a high DPI, it is largely independent from the DPI. For example, a 1-pixel-wide line will be rendered using two pixels if the display scale factor is 2.0, no matter if the view DPI is 72, 144, or 200.

However, some map content directly depends on the physical size of your display and its DPI value. Such map content includes:

AWT/Swing based views rely on Toolkit.getDefaultToolkit().getScreenResolution() to determine the DPI of a view. JavaFX views use Screen.getPrimary().getDPI(). If you suspect that the detected value is not correct, you can override it by passing the correct DPI to the luciad.dpi system property, for example -Dluciad.dpi=96.