Index: src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java
===================================================================
--- src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java	(revision 12702)
+++ src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java	(working copy)
@@ -81,6 +81,7 @@
 import org.openstreetmap.josm.tools.ImageProvider;
 import org.openstreetmap.josm.tools.JosmRuntimeException;
 import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.HiDPISupport;
 import org.openstreetmap.josm.tools.Utils;
 import org.openstreetmap.josm.tools.bugreport.BugReport;
 
@@ -416,7 +417,11 @@
                     g.setClip(oldClip);
                 }
             } else {
-                TexturePaint texture = new TexturePaint(fillImage.getImage(disabled),
+                Image img = fillImage.getImage(disabled);
+                // TexturePaint requires BufferedImage -> get base image from
+                // possible multi-resolution image
+                img = HiDPISupport.getBaseImage(img);
+                TexturePaint texture = new TexturePaint((BufferedImage) img,
                         new Rectangle(0, 0, fillImage.getWidth(), fillImage.getHeight()));
                 g.setPaint(texture);
                 Float alpha = fillImage.getAlphaFloat();
@@ -656,7 +661,7 @@
 
         double startOffset = computeStartOffset(phase, repeat);
 
-        BufferedImage image = pattern.getImage(disabled);
+        Image image = pattern.getImage(disabled);
 
         path.visitClippedLine(repeat, (inLineOffset, start, end, startIsOldEnd) -> {
             final double segmentLength = start.distanceToInView(end);
@@ -1149,7 +1154,7 @@
         }
         displayText(() -> {
             AffineTransform defaultTransform = g.getTransform();
-            g.setTransform(at);
+            g.transform(at);
             g.setFont(text.font);
             g.drawString(name, 0, 0);
             g.setTransform(defaultTransform);
Index: src/org/openstreetmap/josm/gui/MapView.java
===================================================================
--- src/org/openstreetmap/josm/gui/MapView.java	(revision 12702)
+++ src/org/openstreetmap/josm/gui/MapView.java	(working copy)
@@ -8,6 +8,7 @@
 import java.awt.Graphics2D;
 import java.awt.Point;
 import java.awt.Rectangle;
+import java.awt.Shape;
 import java.awt.event.ComponentAdapter;
 import java.awt.event.ComponentEvent;
 import java.awt.event.KeyEvent;
@@ -14,6 +15,7 @@
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseMotionListener;
+import java.awt.geom.AffineTransform;
 import java.awt.geom.Area;
 import java.awt.image.BufferedImage;
 import java.beans.PropertyChangeEvent;
@@ -510,6 +512,16 @@
     }
 
     private void drawMapContent(Graphics g) {
+        Graphics2D gg = (Graphics2D) g;
+        AffineTransform trOrig = gg.getTransform();
+        double uiScaleX = gg.getTransform().getScaleX();
+        double uiScaleY = gg.getTransform().getScaleY();
+        int width = (int) Math.round(getWidth() * uiScaleX);
+        int height = (int) Math.round(getHeight() * uiScaleY);
+
+        AffineTransform trDef = AffineTransform.getScaleInstance(uiScaleX, uiScaleY);
+        Shape scaledClip = trDef.createTransformedShape(g.getClip());
+        
         List<Layer> visibleLayers = layerManager.getVisibleLayersInZOrder();
 
         int nonChangedLayersCount = 0;
@@ -528,22 +540,20 @@
                 && lastClipBounds.contains(g.getClipBounds())
                 && nonChangedLayers.equals(visibleLayers.subList(0, nonChangedLayers.size()));
 
-        if (null == offscreenBuffer || offscreenBuffer.getWidth() != getWidth() || offscreenBuffer.getHeight() != getHeight()) {
-            offscreenBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_3BYTE_BGR);
+        if (null == offscreenBuffer || offscreenBuffer.getWidth() != width || offscreenBuffer.getHeight() != height) {
+            offscreenBuffer = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
         }
 
-        Graphics2D tempG = offscreenBuffer.createGraphics();
-        tempG.setClip(g.getClip());
-
         if (!canUseBuffer || nonChangedLayersBuffer == null) {
             if (null == nonChangedLayersBuffer
-                    || nonChangedLayersBuffer.getWidth() != getWidth() || nonChangedLayersBuffer.getHeight() != getHeight()) {
-                nonChangedLayersBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_3BYTE_BGR);
+                    || nonChangedLayersBuffer.getWidth() != width || nonChangedLayersBuffer.getHeight() != height) {
+                nonChangedLayersBuffer = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
             }
             Graphics2D g2 = nonChangedLayersBuffer.createGraphics();
-            g2.setClip(g.getClip());
+            g2.setClip(scaledClip);
+            g2.setTransform(trDef);
             g2.setColor(PaintColors.getBackgroundColor());
-            g2.fillRect(0, 0, getWidth(), getHeight());
+            g2.fillRect(0, 0, width, height);
 
             for (int i = 0; i < nonChangedLayersCount; i++) {
                 paintLayer(visibleLayers.get(i), g2);
@@ -552,7 +562,8 @@
             // Maybe there were more unchanged layers then last time - draw them to buffer
             if (nonChangedLayers.size() != nonChangedLayersCount) {
                 Graphics2D g2 = nonChangedLayersBuffer.createGraphics();
-                g2.setClip(g.getClip());
+                g2.setClip(scaledClip);
+                g2.setTransform(trDef);
                 for (int i = nonChangedLayers.size(); i < nonChangedLayersCount; i++) {
                     paintLayer(visibleLayers.get(i), g2);
                 }
@@ -564,14 +575,20 @@
         lastViewID = getViewID();
         lastClipBounds = g.getClipBounds();
 
+        Graphics2D tempG = offscreenBuffer.createGraphics();
+        tempG.setClip(scaledClip);
+        tempG.setTransform(new AffineTransform());
         tempG.drawImage(nonChangedLayersBuffer, 0, 0, null);
-
+        tempG.setTransform(trDef);
+        
         for (int i = nonChangedLayersCount; i < visibleLayers.size(); i++) {
             paintLayer(visibleLayers.get(i), tempG);
         }
 
         try {
-            drawTemporaryLayers(tempG, getLatLonBounds(g.getClipBounds()));
+            drawTemporaryLayers(tempG, getLatLonBounds(new Rectangle(
+                    (int) Math.round(g.getClipBounds().x * uiScaleX),
+                    (int) Math.round(g.getClipBounds().y * uiScaleY))));
         } catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException e) {
             BugReport.intercept(e).put("temporaryLayers", temporaryLayers).warn();
         }
@@ -596,7 +613,8 @@
         }
 
         try {
-            g.drawImage(offscreenBuffer, 0, 0, null);
+            gg.setTransform(new AffineTransform(1, 0, 0, 1, trOrig.getTranslateX(), trOrig.getTranslateY()));
+            gg.drawImage(offscreenBuffer, 0, 0, null);
         } catch (ClassCastException e) {
             // See #11002 and duplicate tickets. On Linux with Java >= 8 Many users face this error here:
             //
@@ -622,6 +640,8 @@
             //
             // But the application seems to work fine after, so let's just log the error
             Logging.error(e);
+        } finally {
+            gg.setTransform(trOrig);
         }
     }
 
Index: src/org/openstreetmap/josm/gui/mappaint/styleelement/AreaElement.java
===================================================================
--- src/org/openstreetmap/josm/gui/mappaint/styleelement/AreaElement.java	(revision 12702)
+++ src/org/openstreetmap/josm/gui/mappaint/styleelement/AreaElement.java	(working copy)
@@ -2,6 +2,8 @@
 package org.openstreetmap.josm.gui.mappaint.styleelement;
 
 import java.awt.Color;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
 import java.util.Objects;
 
 import org.openstreetmap.josm.Main;
@@ -15,6 +17,7 @@
 import org.openstreetmap.josm.gui.mappaint.Environment;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.IconReference;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
+import org.openstreetmap.josm.tools.HiDPISupport;
 import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -78,8 +81,11 @@
         IconReference iconRef = c.get(FILL_IMAGE, null, IconReference.class);
         if (iconRef != null) {
             fillImage = new MapImage(iconRef.iconName, iconRef.source, false);
-
-            color = new Color(fillImage.getImage(false).getRGB(
+            Image img = fillImage.getImage(false);
+            // get base image from possible multi-resolution image, so we can
+            // cast to BufferedImage and get pixel value at the center of the image
+            img = HiDPISupport.getBaseImage(img);
+            color = new Color(((BufferedImage) img).getRGB(
                     fillImage.getWidth() / 2, fillImage.getHeight() / 2)
             );
 
Index: src/org/openstreetmap/josm/gui/mappaint/styleelement/MapImage.java
===================================================================
--- src/org/openstreetmap/josm/gui/mappaint/styleelement/MapImage.java	(revision 12702)
+++ src/org/openstreetmap/josm/gui/mappaint/styleelement/MapImage.java	(working copy)
@@ -31,7 +31,7 @@
     /**
      * ImageIcon can change while the image is loading.
      */
-    private BufferedImage img;
+    private Image img;
 
     /**
      * The alpha (opacity) value of the image. It is multiplied to the image alpha channel.
@@ -101,7 +101,7 @@
      * @param disabled {@code} true to request disabled version, {@code false} for the standard version
      * @return the image
      */
-    public BufferedImage getImage(boolean disabled) {
+    public Image getImage(boolean disabled) {
         if (disabled) {
             return getDisabled();
         } else {
@@ -109,7 +109,7 @@
         }
     }
 
-    private BufferedImage getDisabled() {
+    private Image getDisabled() {
         if (disabledImgCache != null)
                 return disabledImgCache;
         if (img == null)
@@ -126,7 +126,7 @@
         return disabledImgCache;
     }
 
-    private BufferedImage getImage() {
+    private Image getImage() {
         if (img != null)
             return img;
         temporary = false;
@@ -143,9 +143,9 @@
                         if (result == null) {
                             source.logWarning(tr("Failed to locate image ''{0}''", name));
                             ImageIcon noIcon = MapPaintStyles.getNoIconIcon(source);
-                            img = noIcon == null ? null : (BufferedImage) noIcon.getImage();
+                            img = noIcon == null ? null : noIcon.getImage();
                         } else {
-                            img = (BufferedImage) rescale(result.getImage());
+                            img = rescale(result.getImage());
                         }
                         if (temporary) {
                             disabledImgCache = null;
@@ -159,7 +159,7 @@
         );
         synchronized (this) {
             if (img == null) {
-                img = (BufferedImage) ImageProvider.get("clock").getImage();
+                img = ImageProvider.get("clock").getImage();
                 temporary = true;
             }
         }
Index: src/org/openstreetmap/josm/gui/util/GuiHelper.java
===================================================================
--- src/org/openstreetmap/josm/gui/util/GuiHelper.java	(revision 12702)
+++ src/org/openstreetmap/josm/gui/util/GuiHelper.java	(working copy)
@@ -11,6 +11,7 @@
 import java.awt.DisplayMode;
 import java.awt.Font;
 import java.awt.Frame;
+import java.awt.GraphicsConfiguration;
 import java.awt.GraphicsDevice;
 import java.awt.GraphicsEnvironment;
 import java.awt.GridBagLayout;
@@ -23,6 +24,7 @@
 import java.awt.event.KeyEvent;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.awt.geom.AffineTransform;
 import java.awt.image.FilteredImageSource;
 import java.lang.reflect.InvocationTargetException;
 import java.util.Arrays;
@@ -65,6 +67,7 @@
 import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
 import org.openstreetmap.josm.tools.LanguageInfo;
 import org.openstreetmap.josm.tools.Logging;
+import org.openstreetmap.josm.tools.Utils;
 import org.openstreetmap.josm.tools.bugreport.BugReport;
 import org.openstreetmap.josm.tools.bugreport.ReportedException;
 
Index: src/org/openstreetmap/josm/tools/HiDPISupport.java
===================================================================
--- src/org/openstreetmap/josm/tools/HiDPISupport.java	(nonexistent)
+++ src/org/openstreetmap/josm/tools/HiDPISupport.java	(working copy)
@@ -0,0 +1,205 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.tools;
+
+import java.awt.Dimension;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsEnvironment;
+import java.awt.Image;
+import java.awt.geom.AffineTransform;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import javax.swing.ImageIcon;
+
+/**
+ * Helper class for HiDPI support.
+ * 
+ * Gives access to the class <code>BaseMultiResolutionImage</code> via reflection,
+ * in case it is on classpath. This is to be expected for Java 9, but not for Java 8
+ * runtime.
+ * 
+ * @since xxx
+ */
+public class HiDPISupport {
+
+    private static volatile Optional<Class<? extends Image>> baseMultiResolutionImageClass;
+    private static volatile Optional<Constructor<? extends Image>> baseMultiResolutionImageConstructor;
+    private static volatile Optional<Method> resolutionVariantsMethod;
+    
+    public static Image getMultiResolutionImage(Image base, ImageResource ir) {
+        double uiScale = getHiDPIScale();
+        if (uiScale != 1.0 && getBaseMultiResolutionImageConstructor().isPresent()) {
+            ImageIcon zoomed = ir.getImageIconBasic(new Dimension(
+                    (int) Math.round(base.getWidth(null) * uiScale),
+                    (int) Math.round(base.getHeight(null) * uiScale)));
+            Image mrImg = getMultiResolutionImage(Arrays.asList(base, zoomed.getImage()));
+            if (mrImg != null) return mrImg;
+        }
+        return base;
+    }
+
+    public static Image getMultiResolutionImage(List<Image> imgs) {
+        if (getBaseMultiResolutionImageConstructor().isPresent()) {
+            Constructor<? extends Image> c = getBaseMultiResolutionImageConstructor().get();
+            try {
+                return c.newInstance((Object) imgs.toArray(new Image[0]));
+            } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
+                Logging.error("Unexpected error while instantiating object of class BaseMultiResolutionImage: " + ex);
+            }
+        }
+        return imgs.get(0);
+    }
+
+    public static Image getBaseImage(Image img) {
+        if (!getBaseMultiResolutionImageClass().isPresent() || !getResolutionVariantsMethod().isPresent()) {
+            return img;
+        }
+        if (getBaseMultiResolutionImageClass().get().isInstance(img)) {
+            try {
+                @SuppressWarnings("unchecked")
+                List<Image> imgVars = (List) getResolutionVariantsMethod().get().invoke(img);
+                if (!imgVars.isEmpty()) {
+                    return imgVars.get(0);
+                }
+            } catch (IllegalAccessException | InvocationTargetException ex) {
+                Logging.error("Unexpected error while calling method: " + ex);
+            }
+        }
+        return img;
+    }
+    
+    public static List<Image> getResolutionVariants(Image img) {
+        if (!getBaseMultiResolutionImageClass().isPresent() || !getResolutionVariantsMethod().isPresent()) {
+            return Collections.singletonList(img);
+        }
+        if (getBaseMultiResolutionImageClass().get().isInstance(img)) {
+            try {
+                @SuppressWarnings("unchecked")
+                List<Image> imgVars = (List) getResolutionVariantsMethod().get().invoke(img);
+                if (!imgVars.isEmpty()) {
+                    return imgVars;
+                }
+            } catch (IllegalAccessException | InvocationTargetException ex) {
+                Logging.error("Unexpected error while calling method: " + ex);
+            }
+        }
+        return Collections.singletonList(img);
+    }
+    
+    private static double getHiDPIScale() {
+        GraphicsConfiguration gc = GraphicsEnvironment
+                .getLocalGraphicsEnvironment()
+                .getDefaultScreenDevice().
+                getDefaultConfiguration();        
+        AffineTransform transform = gc.getDefaultTransform();
+        if (!Utils.equalsEpsilon(transform.getScaleX(), transform.getScaleY())) {
+            Logging.warn("Unexpected ui transform: " + transform);
+        }
+        return transform.getScaleX();
+    }
+
+    private static Optional<Class<? extends Image>> getBaseMultiResolutionImageClass() {
+        if (baseMultiResolutionImageClass == null) {
+            synchronized (HiDPISupport.class) {
+                if (baseMultiResolutionImageClass == null) {
+                    try {
+                        @SuppressWarnings("unchecked")
+                        Class<? extends Image> c = (Class) Class.forName("java.awt.image.BaseMultiResolutionImage");
+                        baseMultiResolutionImageClass = Optional.ofNullable(c);
+                    } catch (ClassNotFoundException ex) {
+                        // class is not present in Java 8
+                        baseMultiResolutionImageClass = Optional.empty();
+                    }
+                }
+            }
+        }
+        return baseMultiResolutionImageClass;
+    }
+    
+    
+    private static Optional<Constructor<? extends Image>> getBaseMultiResolutionImageConstructor() {
+        if (baseMultiResolutionImageConstructor == null) {
+            synchronized (HiDPISupport.class) {
+                if (baseMultiResolutionImageConstructor == null) {
+                    getBaseMultiResolutionImageClass().ifPresent(klass -> {
+                        try {
+                            Constructor<? extends Image> constr = klass.getConstructor(Image[].class);
+                            baseMultiResolutionImageConstructor = Optional.ofNullable(constr);
+                        } catch (NoSuchMethodException ex) {
+                            Logging.error("Cannot find expected constructor: " + ex);
+                        }
+                    });
+                    if (baseMultiResolutionImageConstructor == null) {
+                        baseMultiResolutionImageConstructor = Optional.empty();
+                    }
+                }
+            }
+        }
+        return baseMultiResolutionImageConstructor;
+    }
+
+    private static Optional<Method> getResolutionVariantsMethod() {
+        if (resolutionVariantsMethod == null) {
+            synchronized (HiDPISupport.class) {
+                if (resolutionVariantsMethod == null) {
+                    getBaseMultiResolutionImageClass().ifPresent(klass -> {
+                        try {
+                            Method m = klass.getMethod("getResolutionVariants");
+                            resolutionVariantsMethod = Optional.ofNullable(m);
+                        } catch (NoSuchMethodException ex) {
+                            Logging.error("Cannot find expected method: "+ex);
+                        }
+                    });
+                    if (resolutionVariantsMethod == null) {
+                        resolutionVariantsMethod = Optional.empty();
+                    }
+                }
+            }
+        }
+        return resolutionVariantsMethod;
+    }
+ 
+    public static Image processMRImage(Image img, Function<Image, Image> processor) {
+        return processMRImages(Collections.singletonList(img), imgs -> processor.apply(imgs.get(0)));
+    }
+
+    /**
+     * Perform an operation on multi-resolution images.
+     * 
+     * When input images are not multi-resolution, it will simply apply the processor once.
+     * Otherwise, the processor will be called for each resolution variant and the
+     * resulting images assembled to become the output multi-resolution image.
+     * @param imgs input images, possibly multi-resolution
+     * @param processor processor taking a list of plain images as input and returning
+     * a single plain image as output
+     * @return multi-resolution image assembled from the output of calls to <code>processor</code>
+     * for each resolution variant
+     */
+    public static Image processMRImages(List<Image> imgs, Function<List<Image>, Image> processor) {
+        CheckParameterUtil.ensureThat(imgs.size() >= 1, "at least on element expected");
+        if (!getBaseMultiResolutionImageClass().isPresent()) {
+            return processor.apply(imgs);
+        }
+        List<List<Image>> allVars = imgs.stream().map(HiDPISupport::getResolutionVariants).collect(Collectors.toList());
+        int maxVariants = allVars.stream().mapToInt(lst -> lst.size()).max().getAsInt();
+        if (maxVariants == 1)
+            return processor.apply(imgs);
+        List<Image> imgsProcessed = IntStream.range(0, maxVariants)
+                .mapToObj(
+                        k -> processor.apply(
+                                allVars.stream().map(vars -> vars.get(k)).collect(Collectors.toList())
+                        )
+                ).collect(Collectors.toList());
+        return getMultiResolutionImage(imgsProcessed);
+    }
+    
+    
+}
Index: src/org/openstreetmap/josm/tools/ImageOverlay.java
===================================================================
--- src/org/openstreetmap/josm/tools/ImageOverlay.java	(revision 12702)
+++ src/org/openstreetmap/josm/tools/ImageOverlay.java	(working copy)
@@ -80,9 +80,7 @@
             height = (int) (h*(offsetBottom-offsetTop));
         }
         ImageIcon overlay;
-        if (width != -1 || height != -1) {
-            image = new ImageProvider(image).resetMaxSize(new Dimension(width, height));
-        }
+        image = new ImageProvider(image).setMaxSize(new Dimension(width, height));
         overlay = image.get();
         int x, y;
         if (width == -1 && offsetLeft < 0) {
Index: src/org/openstreetmap/josm/tools/ImageProvider.java
===================================================================
--- src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 12702)
+++ src/org/openstreetmap/josm/tools/ImageProvider.java	(working copy)
@@ -277,6 +277,8 @@
     /** <code>true</code> if icon must be grayed out */
     protected boolean isDisabled;
 
+    protected boolean multiResolution = true;
+    
     private static SVGUniverse svgUniverse;
 
     /**
@@ -287,7 +289,7 @@
     /**
      * Caches the image data for rotated versions of the same image.
      */
-    private static final Map<Image, Map<Long, ImageResource>> ROTATE_CACHE = new HashMap<>();
+    private static final Map<Image, Map<Long, Image>> ROTATE_CACHE = new HashMap<>();
 
     private static final ExecutorService IMAGE_FETCHER =
             Executors.newSingleThreadExecutor(Utils.newThreadFactory("image-fetcher-%d", Thread.NORM_PRIORITY));
@@ -333,6 +335,7 @@
         this.additionalClassLoaders = image.additionalClassLoaders;
         this.overlayInfo = image.overlayInfo;
         this.isDisabled = image.isDisabled;
+        this.multiResolution = image.multiResolution;
     }
 
     /**
@@ -594,6 +597,11 @@
         return this;
     }
 
+    public ImageProvider setMultiResolution(boolean multiResolution) {
+        this.multiResolution = multiResolution;
+        return this;
+    }
+    
     /**
      * Execute the image request and scale result.
      * @return the requested image or null if the request failed
@@ -605,9 +613,9 @@
             return null;
         }
         if (virtualMaxWidth != -1 || virtualMaxHeight != -1)
-            return ir.getImageIconBounded(new Dimension(virtualMaxWidth, virtualMaxHeight));
+            return ir.getImageIconBounded(new Dimension(virtualMaxWidth, virtualMaxHeight), multiResolution);
         else
-            return ir.getImageIcon(new Dimension(virtualWidth, virtualHeight));
+            return ir.getImageIcon(new Dimension(virtualWidth, virtualHeight), multiResolution);
     }
 
     /**
@@ -1275,14 +1283,13 @@
     }
 
     /**
-     * Creates a rotated version of the input image, scaled to the given dimension.
+     * Creates a rotated version of the input image.
      *
      * @param img the image to be rotated.
      * @param rotatedAngle the rotated angle, in degree, clockwise. It could be any double but we
      * will mod it with 360 before using it. More over for caching performance, it will be rounded to
      * an entire value between 0 and 360.
-     * @param dimension The requested dimensions. Use (-1,-1) for the original size
-     * and (width, -1) to set the width, but otherwise scale the image proportionally.
+     * @param dimension ignored
      * @return the image after rotating and scaling.
      * @since 6172
      */
@@ -1290,67 +1297,64 @@
         CheckParameterUtil.ensureParameterNotNull(img, "img");
 
         // convert rotatedAngle to an integer value from 0 to 360
-        Long originalAngle = Math.round(rotatedAngle % 360);
-        if (rotatedAngle != 0 && originalAngle == 0) {
-            originalAngle = 360L;
-        }
+        Long angleLong = Math.round(rotatedAngle % 360);
+        Long originalAngle = rotatedAngle != 0 && angleLong == 0 ? 360L : angleLong;
 
-        ImageResource imageResource;
-
         synchronized (ROTATE_CACHE) {
-            Map<Long, ImageResource> cacheByAngle = ROTATE_CACHE.get(img);
+            Map<Long, Image> cacheByAngle = ROTATE_CACHE.get(img);
             if (cacheByAngle == null) {
                 cacheByAngle = new HashMap<>();
                 ROTATE_CACHE.put(img, cacheByAngle);
             }
 
-            imageResource = cacheByAngle.get(originalAngle);
+            Image rotatedImg = cacheByAngle.get(originalAngle);
 
-            if (imageResource == null) {
+            if (rotatedImg == null) {
                 // convert originalAngle to a value from 0 to 90
                 double angle = originalAngle % 90;
                 if (originalAngle != 0 && angle == 0) {
                     angle = 90.0;
                 }
-
                 double radian = Utils.toRadians(angle);
 
-                new ImageIcon(img); // load completely
-                int iw = img.getWidth(null);
-                int ih = img.getHeight(null);
-                int w;
-                int h;
+                rotatedImg = HiDPISupport.processMRImage(img, img0 -> {
+                    new ImageIcon(img0); // load completely
+                    int iw = img0.getWidth(null);
+                    int ih = img0.getHeight(null);
+                    int w;
+                    int h;
 
-                if ((originalAngle >= 0 && originalAngle <= 90) || (originalAngle > 180 && originalAngle <= 270)) {
-                    w = (int) (iw * Math.sin(DEGREE_90 - radian) + ih * Math.sin(radian));
-                    h = (int) (iw * Math.sin(radian) + ih * Math.sin(DEGREE_90 - radian));
-                } else {
-                    w = (int) (ih * Math.sin(DEGREE_90 - radian) + iw * Math.sin(radian));
-                    h = (int) (ih * Math.sin(radian) + iw * Math.sin(DEGREE_90 - radian));
-                }
-                Image image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
-                imageResource = new ImageResource(image);
-                cacheByAngle.put(originalAngle, imageResource);
-                Graphics g = image.getGraphics();
-                Graphics2D g2d = (Graphics2D) g.create();
+                    if ((originalAngle >= 0 && originalAngle <= 90) || (originalAngle > 180 && originalAngle <= 270)) {
+                        w = (int) (iw * Math.sin(DEGREE_90 - radian) + ih * Math.sin(radian));
+                        h = (int) (iw * Math.sin(radian) + ih * Math.sin(DEGREE_90 - radian));
+                    } else {
+                        w = (int) (ih * Math.sin(DEGREE_90 - radian) + iw * Math.sin(radian));
+                        h = (int) (ih * Math.sin(radian) + iw * Math.sin(DEGREE_90 - radian));
+                    }
+                    Image image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
+                    Graphics g = image.getGraphics();
+                    Graphics2D g2d = (Graphics2D) g.create();
 
-                // calculate the center of the icon.
-                int cx = iw / 2;
-                int cy = ih / 2;
+                    // calculate the center of the icon.
+                    int cx = iw / 2;
+                    int cy = ih / 2;
 
-                // move the graphics center point to the center of the icon.
-                g2d.translate(w / 2, h / 2);
+                    // move the graphics center point to the center of the icon.
+                    g2d.translate(w / 2, h / 2);
 
-                // rotate the graphics about the center point of the icon
-                g2d.rotate(Utils.toRadians(originalAngle));
+                    // rotate the graphics about the center point of the icon
+                    g2d.rotate(Utils.toRadians(originalAngle));
 
-                g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
-                g2d.drawImage(img, -cx, -cy, null);
+                    g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+                    g2d.drawImage(img0, -cx, -cy, null);
 
-                g2d.dispose();
-                new ImageIcon(image); // load completely
+                    g2d.dispose();
+                    new ImageIcon(image); // load completely
+                    return image;
+                });
+                cacheByAngle.put(originalAngle, rotatedImg);
             }
-            return imageResource.getImageIcon(dimension).getImage();
+            return rotatedImg;
         }
     }
 
@@ -1411,7 +1415,7 @@
                                 BufferedImage.TYPE_INT_ARGB);
                         double scaleFactor = Math.min(backgroundRealWidth / (double) iconRealWidth, backgroundRealHeight
                                 / (double) iconRealHeight);
-                        BufferedImage iconImage = icon.getImage(false);
+                        Image iconImage = icon.getImage(false);
                         Image scaledIcon;
                         final int scaledWidth;
                         final int scaledHeight;
Index: src/org/openstreetmap/josm/tools/ImageResource.java
===================================================================
--- src/org/openstreetmap/josm/tools/ImageResource.java	(revision 12702)
+++ src/org/openstreetmap/josm/tools/ImageResource.java	(working copy)
@@ -156,6 +156,22 @@
      * @return ImageIcon object for the image of this resource, scaled according to dim
      */
     public ImageIcon getImageIcon(Dimension dim) {
+        return getImageIcon(dim, true);
+    }
+    
+    public ImageIcon getImageIcon(Dimension dim, boolean multiResolution) {
+        ImageIcon img0 = getImageIconBasic(dim);
+        if (!multiResolution)
+            return img0;
+        try {
+            Image mrImg = HiDPISupport.getMultiResolutionImage(img0.getImage(), this);
+            return new ImageIcon(mrImg);
+        } catch (NoClassDefFoundError e) {
+            return img0;
+        }
+    }
+    
+    ImageIcon getImageIconBasic(Dimension dim) {
         if (dim.width < -1 || dim.width == 0 || dim.height < -1 || dim.height == 0)
             throw new IllegalArgumentException(dim+" is invalid");
         Image img = imgCache.get(dim);
@@ -216,6 +232,10 @@
      * @return ImageIcon object for the image of this resource, scaled down if needed, according to maxSize
      */
     public ImageIcon getImageIconBounded(Dimension maxSize) {
+        return getImageIconBounded(maxSize, true);
+    }
+    
+    public ImageIcon getImageIconBounded(Dimension maxSize, boolean multiResolution) {
         if (maxSize.width < -1 || maxSize.width == 0 || maxSize.height < -1 || maxSize.height == 0)
             throw new IllegalArgumentException(maxSize+" is invalid");
         float sourceWidth;
@@ -239,14 +259,14 @@
         }
 
         if (maxWidth == -1 && maxHeight == -1)
-            return getImageIcon(DEFAULT_DIMENSION);
+            return getImageIcon(DEFAULT_DIMENSION, multiResolution);
         else if (maxWidth == -1)
-            return getImageIcon(new Dimension(-1, maxHeight));
+            return getImageIcon(new Dimension(-1, maxHeight), multiResolution);
         else if (maxHeight == -1)
-            return getImageIcon(new Dimension(maxWidth, -1));
+            return getImageIcon(new Dimension(maxWidth, -1), multiResolution);
         else if (sourceWidth / maxWidth > sourceHeight / maxHeight)
-            return getImageIcon(new Dimension(maxWidth, -1));
+            return getImageIcon(new Dimension(maxWidth, -1), multiResolution);
         else
-            return getImageIcon(new Dimension(-1, maxHeight));
+            return getImageIcon(new Dimension(-1, maxHeight), multiResolution);
    }
 }
