001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.tools;
003
004import java.awt.Dimension;
005import java.awt.GraphicsEnvironment;
006import java.awt.Toolkit;
007
008import org.openstreetmap.josm.spi.preferences.Config;
009
010/**
011 * Support class to handle size information of Gui elements
012 * This is needed, because display resolution may vary a lot and a common set
013 * of sizes wont work for all users alike.
014 * @since 12682 (moved from {@code gui.util} package)
015 * @since 10358
016 */
017public final class GuiSizesHelper {
018
019    private GuiSizesHelper() {
020        // Hide default constructor for utils classes
021    }
022
023    /** cache value for screen resolution */
024    private static float screenDPI = -1;
025
026    /**
027     * Request the screen resolution (cached)
028     * @return screen resolution in DPI
029     */
030    private static float getScreenDPI() {
031        if (screenDPI == -1) {
032            synchronized (GuiSizesHelper.class) {
033                if (screenDPI == -1) {
034                    float scalePref = (float) Config.getPref().getDouble("gui.scale", 1.0);
035                    if (scalePref != 0) {
036                        screenDPI = 96f * scalePref;
037                    } else {
038                        if (!GraphicsEnvironment.isHeadless()) {
039                            screenDPI = Toolkit.getDefaultToolkit().getScreenResolution();
040                        } else {
041                            screenDPI = 96;
042                        }
043                    }
044                }
045            }
046        }
047        return screenDPI;
048    }
049
050    /**
051     * Returns coefficient of monitor pixel density. All hardcoded sizes must be multiplied by this value.
052     *
053     * @return float value. 1 - means standard monitor, 2 and high - "retina" display.
054     */
055    public static float getPixelDensity() {
056        return getScreenDPI() / 96f;
057    }
058
059    /**
060     * Check if a high DPI resolution is used
061     * @return <code>true</code> for HIDPI screens
062     */
063    public static boolean isHiDPI() {
064        return getPixelDensity() >= 2f;
065    }
066
067    /**
068     * Returns a resolution adapted size
069     * @param size Size value to adapt (base size is a low DPI screen)
070     * @return adapted size (may be unmodified)
071     */
072    public static int getSizeDpiAdjusted(int size) {
073        if (size <= 0) return size;
074        return Math.round(size * getScreenDPI() / 96);
075    }
076
077    /**
078     * Returns a resolution adapted size
079     * @param size Size value to adapt (base size is a low DPI screen)
080     * @return adapted size (may be unmodified)
081     */
082    public static float getSizeDpiAdjusted(float size) {
083        if (size <= 0f) return size;
084        return size * getScreenDPI() / 96;
085    }
086
087    /**
088     * Returns a resolution adapted size
089     * @param size Size value to adapt (base size is a low DPI screen)
090     * @return adapted size (may be unmodified)
091     */
092    public static double getSizeDpiAdjusted(double size) {
093        if (size <= 0d) return size;
094        return size * getScreenDPI() / 96;
095    }
096
097    /**
098     * Returns a resolution adapted Dimension
099     * @param dim Dimension value to adapt (base size is a low DPI screen)
100     * @return adapted dimension (may be unmodified)
101     */
102    public static Dimension getDimensionDpiAdjusted(Dimension dim) {
103        float pixelPerInch = getScreenDPI();
104        int width = dim.width;
105        int height = dim.height;
106        if (dim.width > 0) {
107            width = Math.round(dim.width * pixelPerInch / 96);
108        }
109
110        if (dim.height > 0) {
111            height = Math.round(dim.height * pixelPerInch / 96);
112        }
113
114        return new Dimension(width, height);
115    }
116}