Index: trunk/src/org/openstreetmap/josm/tools/ImageProvider.java
===================================================================
--- trunk/src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 14283)
+++ trunk/src/org/openstreetmap/josm/tools/ImageProvider.java	(revision 14284)
@@ -34,5 +34,4 @@
 import java.util.Base64;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -271,7 +270,4 @@
     }
 
-    /** small cache of critical images used in many parts of the application */
-    private static final Map<OsmPrimitiveType, ImageIcon> osmPrimitiveTypeCache = Collections.synchronizedMap(new HashMap<>());
-
     /** directories in which images are searched */
     protected Collection<String> dirs;
@@ -317,4 +313,10 @@
     private static final Map<Image, Map<Long, Image>> ROTATE_CACHE = new HashMap<>();
 
+    /** small cache of critical images used in many parts of the application */
+    private static final Map<OsmPrimitiveType, ImageIcon> osmPrimitiveTypeCache = new HashMap<>();
+
+    /** larger cache of critical padded image icons used in many parts of the application */
+    private static final Map<Dimension, Map<MapImage, ImageIcon>> paddedImageCache = new HashMap<>();
+
     private static final ExecutorService IMAGE_FETCHER =
             Executors.newSingleThreadExecutor(Utils.newThreadFactory("image-fetcher-%d", Thread.NORM_PRIORITY));
@@ -834,5 +836,5 @@
 
     /**
-     * Clears the internal image cache.
+     * Clears the internal image caches.
      * @since 11021
      */
@@ -840,4 +842,13 @@
         synchronized (cache) {
             cache.clear();
+        }
+        synchronized (ROTATE_CACHE) {
+            ROTATE_CACHE.clear();
+        }
+        synchronized (paddedImageCache) {
+            paddedImageCache.clear();
+        }
+        synchronized (osmPrimitiveTypeCache) {
+            osmPrimitiveTypeCache.clear();
         }
     }
@@ -1478,5 +1489,7 @@
     public static ImageIcon get(OsmPrimitiveType type) {
         CheckParameterUtil.ensureParameterNotNull(type, "type");
-        return osmPrimitiveTypeCache.computeIfAbsent(type, t -> get("data", t.getAPIName()));
+        synchronized (osmPrimitiveTypeCache) {
+            return osmPrimitiveTypeCache.computeIfAbsent(type, t -> get("data", t.getAPIName()));
+        }
     }
 
@@ -1490,50 +1503,7 @@
         // Check if the current styles have special icon for tagged objects.
         if (primitive.isTagged()) {
-            Pair<StyleElementList, Range> nodeStyles;
-            DataSet ds = primitive.getDataSet();
-            if (ds != null) {
-                ds.getReadLock().lock();
-            }
-            try {
-                nodeStyles = MapPaintStyles.getStyles().generateStyles(primitive, 100, false);
-            } finally {
-                if (ds != null) {
-                    ds.getReadLock().unlock();
-                }
-            }
-            for (StyleElement style : nodeStyles.a) {
-                if (style instanceof NodeElement) {
-                    NodeElement nodeStyle = (NodeElement) style;
-                    MapImage icon = nodeStyle.mapImage;
-                    if (icon != null) {
-                        int backgroundRealWidth = GuiSizesHelper.getSizeDpiAdjusted(iconSize.width);
-                        int backgroundRealHeight = GuiSizesHelper.getSizeDpiAdjusted(iconSize.height);
-                        int iconRealWidth = icon.getWidth();
-                        int iconRealHeight = icon.getHeight();
-                        BufferedImage image = new BufferedImage(backgroundRealWidth, backgroundRealHeight,
-                                BufferedImage.TYPE_INT_ARGB);
-                        double scaleFactor = Math.min(backgroundRealWidth / (double) iconRealWidth, backgroundRealHeight
-                                / (double) iconRealHeight);
-                        Image iconImage = icon.getImage(false);
-                        Image scaledIcon;
-                        final int scaledWidth;
-                        final int scaledHeight;
-                        if (scaleFactor < 1) {
-                            // Scale icon such that it fits on background.
-                            scaledWidth = (int) (iconRealWidth * scaleFactor);
-                            scaledHeight = (int) (iconRealHeight * scaleFactor);
-                            scaledIcon = iconImage.getScaledInstance(scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
-                        } else {
-                            // Use original size, don't upscale.
-                            scaledWidth = iconRealWidth;
-                            scaledHeight = iconRealHeight;
-                            scaledIcon = iconImage;
-                        }
-                        image.getGraphics().drawImage(scaledIcon, (backgroundRealWidth - scaledWidth) / 2,
-                                (backgroundRealHeight - scaledHeight) / 2, null);
-
-                        return new ImageIcon(image);
-                    }
-                }
+            ImageIcon icon = getTaggedPadded(primitive, iconSize);
+            if (icon != null) {
+                return icon;
             }
         }
@@ -1556,4 +1526,80 @@
         // Use generic default icon.
         return ImageProvider.get(primitive.getDisplayType());
+    }
+
+    /**
+     * Computes a new padded icon for the given tagged primitive, using map paint styles.
+     * This is a slow operation.
+     * @param primitive tagged OSM primitive
+     * @param iconSize icon size in pixels
+     * @return a new padded icon for the given tagged primitive, or null
+     */
+    private static ImageIcon getTaggedPadded(OsmPrimitive primitive, Dimension iconSize) {
+        Pair<StyleElementList, Range> nodeStyles;
+        DataSet ds = primitive.getDataSet();
+        if (ds != null) {
+            ds.getReadLock().lock();
+        }
+        try {
+            nodeStyles = MapPaintStyles.getStyles().generateStyles(primitive, 100, false);
+        } finally {
+            if (ds != null) {
+                ds.getReadLock().unlock();
+            }
+        }
+        for (StyleElement style : nodeStyles.a) {
+            if (style instanceof NodeElement) {
+                NodeElement nodeStyle = (NodeElement) style;
+                MapImage icon = nodeStyle.mapImage;
+                if (icon != null) {
+                    return getPaddedIcon(icon, iconSize);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns an {@link ImageIcon} for the given map image, at the specified size.
+     * Uses a cache to improve performance.
+     * @param mapImage map image
+     * @param iconSize size in pixels
+     * @return an {@code ImageIcon} for the given map image, at the specified size
+     * @see #clearCache
+     * @since 14284
+     */
+    public static ImageIcon getPaddedIcon(MapImage mapImage, Dimension iconSize) {
+        synchronized (paddedImageCache) {
+            return paddedImageCache.computeIfAbsent(iconSize, x -> new HashMap<>()).computeIfAbsent(mapImage, icon -> {
+                int backgroundRealWidth = GuiSizesHelper.getSizeDpiAdjusted(iconSize.width);
+                int backgroundRealHeight = GuiSizesHelper.getSizeDpiAdjusted(iconSize.height);
+                int iconRealWidth = icon.getWidth();
+                int iconRealHeight = icon.getHeight();
+                BufferedImage image = new BufferedImage(backgroundRealWidth, backgroundRealHeight, BufferedImage.TYPE_INT_ARGB);
+                double scaleFactor = Math.min(
+                        backgroundRealWidth / (double) iconRealWidth,
+                        backgroundRealHeight / (double) iconRealHeight);
+                Image iconImage = icon.getImage(false);
+                Image scaledIcon;
+                final int scaledWidth;
+                final int scaledHeight;
+                if (scaleFactor < 1) {
+                    // Scale icon such that it fits on background.
+                    scaledWidth = (int) (iconRealWidth * scaleFactor);
+                    scaledHeight = (int) (iconRealHeight * scaleFactor);
+                    scaledIcon = iconImage.getScaledInstance(scaledWidth, scaledHeight, Image.SCALE_SMOOTH);
+                } else {
+                    // Use original size, don't upscale.
+                    scaledWidth = iconRealWidth;
+                    scaledHeight = iconRealHeight;
+                    scaledIcon = iconImage;
+                }
+                image.getGraphics().drawImage(scaledIcon,
+                        (backgroundRealWidth - scaledWidth) / 2,
+                        (backgroundRealHeight - scaledHeight) / 2, null);
+
+                return new ImageIcon(image);
+            });
+        }
     }
 
