Index: trunk/src/org/openstreetmap/josm/tools/GuiSizesHelper.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/GuiSizesHelper.java	(revision 16473)
+++ trunk/src/org/openstreetmap/josm/tools/GuiSizesHelper.java	(revision 16486)
@@ -55,4 +55,12 @@
     public static float getPixelDensity() {
         return getScreenDPI() / 96f;
+    }
+
+    /**
+     * Sets coefficient of monitor pixel density.
+     * @param pixelDensity coefficient of monitor pixel density to be set.
+     */
+    public static void setPixelDensity(float pixelDensity) {
+        screenDPI = pixelDensity * 96f;
     }
 
Index: trunk/src/org/openstreetmap/josm/tools/HiDPISupport.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/HiDPISupport.java	(revision 16473)
+++ trunk/src/org/openstreetmap/josm/tools/HiDPISupport.java	(revision 16486)
@@ -33,4 +33,5 @@
     private static final Constructor<? extends Image> baseMultiResolutionImageConstructor;
     private static final Method resolutionVariantsMethod;
+    private static final Method resolutionVariantMethod;
 
     static {
@@ -38,4 +39,5 @@
         baseMultiResolutionImageConstructor = initBaseMultiResolutionImageConstructor();
         resolutionVariantsMethod = initResolutionVariantsMethod();
+        resolutionVariantMethod = initResolutionVariantMethod();
     }
 
@@ -57,7 +59,7 @@
         double uiScale = getHiDPIScale();
         if (uiScale != 1.0 && baseMultiResolutionImageConstructor != null) {
-            ImageIcon zoomed = ir.getImageIcon(new Dimension(
+            ImageIcon zoomed = ir.getImageIconAlreadyScaled(new Dimension(
                     (int) Math.round(base.getWidth(null) * uiScale),
-                    (int) Math.round(base.getHeight(null) * uiScale)), false);
+                    (int) Math.round(base.getHeight(null) * uiScale)), false, true);
             Image mrImg = getMultiResolutionImage(Arrays.asList(base, zoomed.getImage()));
             if (mrImg != null) return mrImg;
@@ -139,4 +141,27 @@
 
     /**
+     * Wrapper for method <code>java.awt.image.MultiResolutionImage#getResolutionVariant(double destImageWidth, double destImageHeight)</code>.
+     * <p>
+     * Will return the argument, in case it is not a multi-resolution image.
+     * @param img the image
+     * @return if <code>img</code> is a <code>java.awt.image.BaseMultiResolutionImage</code>,
+     * then the result of the method <code>#getResolutionVariant(destImageWidth, destImageHeight)</code>,
+     * otherwise the image itself
+     */
+    public static Image getResolutionVariant(Image img, double destImageWidth, double destImageHeight) {
+        if (baseMultiResolutionImageClass == null || resolutionVariantsMethod == null) {
+            return img;
+        }
+        if (baseMultiResolutionImageClass.isInstance(img)) {
+            try {
+                return (Image) resolutionVariantMethod.invoke(img, destImageWidth, destImageHeight);
+            } catch (IllegalAccessException | InvocationTargetException ex) {
+                Logging.error("Unexpected error while calling method: " + ex);
+            }
+        }
+        return img;
+    }
+
+    /**
      * Detect the GUI scale for HiDPI mode.
      * <p>
@@ -145,5 +170,5 @@
      * @return the GUI scale for HiDPI mode, a value of 1.0 means standard mode.
      */
-    private static double getHiDPIScale() {
+    static double getHiDPIScale() {
         if (GraphicsEnvironment.isHeadless())
             return 1.0;
@@ -237,3 +262,14 @@
         }
     }
+
+    private static Method initResolutionVariantMethod() {
+        try {
+            return baseMultiResolutionImageClass != null
+                    ? baseMultiResolutionImageClass.getMethod("getResolutionVariant", Double.TYPE, Double.TYPE)
+                    : null;
+        } catch (NoSuchMethodException ex) {
+            Logging.error("Cannot find expected method: " + ex);
+            return null;
+        }
+    }
 }
Index: trunk/src/org/openstreetmap/josm/tools/ImageOverlay.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/ImageOverlay.java	(revision 16473)
+++ trunk/src/org/openstreetmap/josm/tools/ImageOverlay.java	(revision 16486)
@@ -3,5 +3,7 @@
 
 import java.awt.Dimension;
+import java.awt.Image;
 import java.awt.image.BufferedImage;
+import java.util.List;
 
 import javax.swing.ImageIcon;
@@ -67,4 +69,15 @@
     @Override
     public BufferedImage process(BufferedImage ground) {
+        return process(ground, false);
+    }
+
+    /**
+     * Handle overlay. The image passed as argument is modified!
+     *
+     * @param ground the base image for the overlay (gets modified!)
+     * @param highResolution whether the high resolution variant should be used to overlay
+     * @return the modified image (same as argument)
+     */
+    BufferedImage process(BufferedImage ground, boolean highResolution) {
         /* get base dimensions for calculation */
         int w = ground.getWidth();
@@ -78,7 +91,12 @@
             height = (int) (h*(offsetBottom-offsetTop));
         }
-        ImageIcon overlay;
         image = new ImageProvider(image).setMaxSize(new Dimension(width, height));
-        overlay = image.get();
+        ImageIcon overlay = image.get();
+        if (highResolution) {
+            List<Image> resolutionVariants = HiDPISupport.getResolutionVariants(overlay.getImage());
+            if (resolutionVariants.size() >= 2) {
+                overlay = new ImageIcon(resolutionVariants.get(1));
+            }
+        }
         int x, y;
         if (width == -1 && offsetLeft < 0) {
Index: trunk/src/org/openstreetmap/josm/tools/ImageProvider.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 16473)
+++ trunk/src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 16486)
@@ -1318,16 +1318,53 @@
      */
     public static Cursor getCursor(String name, String overlay) {
-        ImageIcon img = get("cursor", name);
-        if (overlay != null) {
-            img = new ImageProvider("cursor", name).setMaxSize(ImageSizes.CURSOR)
-                .addOverlay(new ImageOverlay(new ImageProvider("cursor/modifier/" + overlay)
-                    .setMaxSize(ImageSizes.CURSOROVERLAY))).get();
-        }
         if (GraphicsEnvironment.isHeadless()) {
             Logging.debug("Cursors are not available in headless mode. Returning null for ''{0}''", name);
             return null;
         }
-        return Toolkit.getDefaultToolkit().createCustomCursor(img.getImage(),
-                "crosshair".equals(name) ? new Point(10, 10) : new Point(3, 2), "Cursor");
+
+        Point hotSpot = new Point();
+        Image image = getCursorImage(name, overlay, hotSpot);
+
+        return Toolkit.getDefaultToolkit().createCustomCursor(image, hotSpot, name);
+    }
+
+    /**
+     * Load a cursor image with a given file name, optionally decorated with an overlay image
+     *
+     * @param name the cursor image filename in "cursor" directory
+     * @param overlay optional overlay image
+     * @param hotSpot will be set to the properly scaled hotspot of the cursor
+     * @return cursor with a given file name, optionally decorated with an overlay image
+     */
+    static Image getCursorImage(String name, String overlay, /* out */ Point hotSpot) {
+        ImageProvider imageProvider = new ImageProvider("cursor", name);
+        if (overlay != null) {
+            imageProvider
+                .setMaxSize(ImageSizes.CURSOR)
+                .addOverlay(new ImageOverlay(new ImageProvider("cursor/modifier/" + overlay)
+                                                .setMaxSize(ImageSizes.CURSOROVERLAY)));
+        }
+        hotSpot.setLocation("crosshair".equals(name) ? new Point(10, 10) : new Point(3, 2));
+        ImageIcon imageIcon = imageProvider.get();
+        Image image = imageIcon.getImage();
+        int width = image.getWidth(null);
+        int height = image.getHeight(null);
+
+        // AWT will resize the cursor to bestCursorSize internally anyway, but miss to scale the hotspot as well
+        // (bug JDK-8238734).  So let's do this ourselves, and also scale the hotspot accordingly.
+        Dimension bestCursorSize = Toolkit.getDefaultToolkit().getBestCursorSize(width, height);
+        if (bestCursorSize.width != 0 && bestCursorSize.height != 0) {
+            // In principle, we could pass the MultiResolutionImage itself to AWT, but due to bug JDK-8240568,
+            // this results in bad alpha blending and thus jaggy edges.  So let's select the best variant ourselves.
+            image = HiDPISupport.getResolutionVariant(image, bestCursorSize.width, bestCursorSize.height);
+            if (bestCursorSize.width != image.getWidth(null) || bestCursorSize.height != image.getHeight(null)) {
+                image = image.getScaledInstance(bestCursorSize.width, bestCursorSize.height, Image.SCALE_DEFAULT);
+            }
+
+            hotSpot.x = hotSpot.x * bestCursorSize.width / width;
+            hotSpot.y = hotSpot.y * bestCursorSize.height / height;
+        }
+
+        return image;
     }
 
Index: trunk/src/org/openstreetmap/josm/tools/ImageResource.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/ImageResource.java	(revision 16473)
+++ trunk/src/org/openstreetmap/josm/tools/ImageResource.java	(revision 16486)
@@ -154,12 +154,27 @@
      * @since 12722
      */
-    public ImageIcon getImageIcon(Dimension dim, boolean multiResolution) {
+    ImageIcon getImageIcon(Dimension dim, boolean multiResolution) {
+        return getImageIconAlreadyScaled(GuiSizesHelper.getDimensionDpiAdjusted(dim), multiResolution, false);
+    }
+
+    /**
+     * Get an ImageIcon object for the image of this resource. A potential UI scaling is assumed
+     * to be already taken care of, so dim is already scaled accordingly.
+     * @param  dim The requested dimensions. Use (-1,-1) for the original size and (width, -1)
+     *         to set the width, but otherwise scale the image proportionally.
+     * @param  multiResolution If true, return a multi-resolution image
+     * (java.awt.image.MultiResolutionImage in Java 9), otherwise a plain {@link BufferedImage}.
+     * When running Java 8, this flag has no effect and a plain image will be returned in any case.
+     * @param highResolution whether the high resolution variant should be used for overlays
+     * @return ImageIcon object for the image of this resource, scaled according to dim
+     */
+    ImageIcon getImageIconAlreadyScaled(Dimension dim, boolean multiResolution, boolean highResolution) {
         CheckParameterUtil.ensureThat((dim.width > 0 || dim.width == -1) && (dim.height > 0 || dim.height == -1),
                 () -> dim + " is invalid");
+
         BufferedImage img = imgCache.get(dim);
         if (img == null) {
             if (svg != null) {
-                Dimension realDim = GuiSizesHelper.getDimensionDpiAdjusted(dim);
-                img = ImageProvider.createImageFromSvg(svg, realDim);
+                img = ImageProvider.createImageFromSvg(svg, dim);
                 if (img == null) {
                     return null;
@@ -168,22 +183,20 @@
                 if (baseImage == null) throw new AssertionError();
 
-                int realWidth = GuiSizesHelper.getSizeDpiAdjusted(dim.width);
-                int realHeight = GuiSizesHelper.getSizeDpiAdjusted(dim.height);
                 ImageIcon icon = new ImageIcon(baseImage);
-                if (realWidth == -1 && realHeight == -1) {
-                    realWidth = GuiSizesHelper.getSizeDpiAdjusted(icon.getIconWidth());
-                    realHeight = GuiSizesHelper.getSizeDpiAdjusted(icon.getIconHeight());
-                } else if (realWidth == -1) {
-                    realWidth = Math.max(1, icon.getIconWidth() * realHeight / icon.getIconHeight());
-                } else if (realHeight == -1) {
-                    realHeight = Math.max(1, icon.getIconHeight() * realWidth / icon.getIconWidth());
+                if (dim.width == -1 && dim.height == -1) {
+                    dim.width = GuiSizesHelper.getSizeDpiAdjusted(icon.getIconWidth());
+                    dim.height = GuiSizesHelper.getSizeDpiAdjusted(icon.getIconHeight());
+                } else if (dim.width == -1) {
+                    dim.width = Math.max(1, icon.getIconWidth() * dim.height / icon.getIconHeight());
+                } else if (dim.height == -1) {
+                    dim.height = Math.max(1, icon.getIconHeight() * dim.width / icon.getIconWidth());
                 }
-                Image i = icon.getImage().getScaledInstance(realWidth, realHeight, Image.SCALE_SMOOTH);
-                img = new BufferedImage(realWidth, realHeight, BufferedImage.TYPE_INT_ARGB);
+                Image i = icon.getImage().getScaledInstance(dim.width, dim.height, Image.SCALE_SMOOTH);
+                img = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB);
                 img.getGraphics().drawImage(i, 0, 0, null);
             }
             if (overlayInfo != null) {
                 for (ImageOverlay o : overlayInfo) {
-                    o.process(img);
+                    o.process(img, highResolution);
                 }
             }
